summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/changelog.rst1
-rw-r--r--docs/keymanager/index.rst10
-rw-r--r--src/leap/bitmask/keymanager/__init__.py9
-rw-r--r--src/leap/bitmask/keymanager/keys.py2
-rw-r--r--tests/integration/keymanager/test_keymanager.py33
5 files changed, 54 insertions, 1 deletions
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 20c6b7d6..4b2558a2 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -7,6 +7,7 @@ Changelog
Features
~~~~~~~~
+- `#8217 <https://0xacab.org/leap/bitmask-dev/issues/8217>`_: renew OpenPGP keys before they expire.
- Set a windows title, so that Bitmask windows can be programmatically manipulated.
Misc
diff --git a/docs/keymanager/index.rst b/docs/keymanager/index.rst
index 5bc66b6f..033292d5 100644
--- a/docs/keymanager/index.rst
+++ b/docs/keymanager/index.rst
@@ -47,6 +47,16 @@ Currently Bitmask can discover new public keys from different sources:
Other methods are planned to be added in the future, like discovery from signatures in emails, headers (autocrypt spec) or other kind of key servers.
+Key expiration dates
+--------------------
+
+KeyManager creates the OpenPGP key with the default expiration of gnupg, that currently is 2 years after the key creation. We want keys with expiration date, to be able to roll new ones if the key material get lost.
+
+We will reduce the default expiration lenght in the future. That will require the rest of OpenPGP ecosystem to have good refresh mechanisms for keys, situation that is improving in the last years.
+
+KeyManager extends one year the expiration date automatically two months before the key gets expired.
+
+
Implementation: using Soledad Documents
---------------------------------------
diff --git a/src/leap/bitmask/keymanager/__init__.py b/src/leap/bitmask/keymanager/__init__.py
index c1095877..dc6b88d3 100644
--- a/src/leap/bitmask/keymanager/__init__.py
+++ b/src/leap/bitmask/keymanager/__init__.py
@@ -254,6 +254,13 @@ class KeyManager(object):
self.log.debug('Getting key for %s' % (address,))
emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)
+ @defer.inlineCallbacks
+ def maybe_extend_expiration(key):
+ if key.needs_renewal():
+ key = yield self._openpgp.expire(key, expiration_time='1y')
+ yield self.send_key()
+ defer.returnValue(key)
+
def key_found(key):
emit_async(catalog.KEYMANAGER_KEY_FOUND, address)
return key
@@ -288,6 +295,8 @@ class KeyManager(object):
# return key if it exists in local database
d = self._openpgp.get_key(address, private=private)
+ if private:
+ d.addCallback(maybe_extend_expiration)
d.addCallbacks(ensure_valid, key_not_found)
return d
diff --git a/src/leap/bitmask/keymanager/keys.py b/src/leap/bitmask/keymanager/keys.py
index 0f68c06b..efc6f925 100644
--- a/src/leap/bitmask/keymanager/keys.py
+++ b/src/leap/bitmask/keymanager/keys.py
@@ -338,6 +338,8 @@ class OpenPGPKey(object):
:return: True if the current date is within the threshold
:rtype: Boolean
"""
+ if self.expiry_date is None:
+ return False
days_till_expiry = (self.expiry_date - datetime.now())
return days_till_expiry.days < pre_expiration_threshold
diff --git a/tests/integration/keymanager/test_keymanager.py b/tests/integration/keymanager/test_keymanager.py
index 8ed70bdf..544a1a18 100644
--- a/tests/integration/keymanager/test_keymanager.py
+++ b/tests/integration/keymanager/test_keymanager.py
@@ -21,7 +21,7 @@ import json
import urllib
import tempfile
import pkg_resources
-from datetime import datetime, date
+from datetime import datetime, date, timedelta
from twisted.internet import defer
from twisted.trial import unittest
@@ -191,6 +191,37 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):
self.assertFalse(key.private)
@defer.inlineCallbacks
+ def test_get_expired_private_key_extends_expiration(self):
+ token = "mytoken"
+ km = self._key_manager(user=ADDRESS_EXPIRING, token=token)
+ km._nicknym.put_key = mock.Mock(return_value=defer.succeed(''))
+
+ yield km.put_raw_key(PRIVATE_EXPIRING_KEY, ADDRESS_EXPIRING)
+ key = yield km.get_key(ADDRESS_EXPIRING, private=True)
+
+ def assert_expiration_date(key):
+ expected = datetime.now() + timedelta(days=365)
+ self.assertTrue(expected - key.expiry_date < timedelta(days=1))
+
+ # check that the right key is returned with the expiration extended
+ self.assertTrue(key.private)
+ assert_expiration_date(key)
+ self.assertTrue(km._nicknym.put_key.called)
+ key_sent_data = km._nicknym.put_key.call_args[0][1]
+ key_sent_pub, key_sent_priv = km._openpgp.parse_key(key_sent_data)
+ self.assertTrue(key_sent_priv is None)
+ assert_expiration_date(key_sent_pub)
+
+ # check that the key in the keyring has the right expiration and
+ # a second get key doesn't try to extend the expiration again
+ km._nicknym.put_key = mock.Mock(return_value=defer.succeed(''))
+ pubkey = yield km.get_key(ADDRESS_EXPIRING)
+ privkey = yield km.get_key(ADDRESS_EXPIRING, private=True)
+ self.assertFalse(km._nicknym.put_key.called)
+ assert_expiration_date(privkey)
+ assert_expiration_date(pubkey)
+
+ @defer.inlineCallbacks
def test_get_public_key_with_binary_private_key(self):
km = self._key_manager()
yield km._openpgp.put_raw_key(self.get_private_binary_key(), ADDRESS)