summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNavaL <ayoyo@thoughtworks.com>2016-11-18 20:22:14 +0100
committerRuben Pollan <meskio@sindominio.net>2017-12-03 20:43:30 +0100
commite5c567d79557eecbd708f4fabe8d04c92cf66ea1 (patch)
tree107f3835eb76b0d1e907e00363699eb75681d1dc
parent3eab1813bfb9efeea65ab3ad9798b8e8dba6d163 (diff)
[feat] first phase of key regeneration
- if current key pair is expired, it'll be extended for a day first - new key pair will be signed by the old key
-rw-r--r--src/leap/bitmask/keymanager/__init__.py19
-rw-r--r--src/leap/bitmask/keymanager/keys.py3
-rw-r--r--src/leap/bitmask/keymanager/openpgp.py42
-rw-r--r--tests/integration/keymanager/test_keymanager.py23
-rw-r--r--tox.ini2
5 files changed, 74 insertions, 15 deletions
diff --git a/src/leap/bitmask/keymanager/__init__.py b/src/leap/bitmask/keymanager/__init__.py
index 03ade634..483c7e51 100644
--- a/src/leap/bitmask/keymanager/__init__.py
+++ b/src/leap/bitmask/keymanager/__init__.py
@@ -330,6 +330,25 @@ class KeyManager(object):
d.addCallback(signal_finished)
return d
+ def regenerate_key(self):
+ """
+ Regenerate a key bound to the user's address.
+
+ :return: A Deferred which fires with the generated EncryptionKey.
+ :rtype: Deferred
+ """
+
+ def signal_finished(key):
+ emit_async(
+ catalog.KEYMANAGER_FINISHED_KEY_GENERATION, self._address)
+ return key
+ self.log.info('Regenerating key for %s.' % self._address)
+ emit_async(catalog.KEYMANAGER_STARTED_KEY_GENERATION, self._address)
+
+ d = self._openpgp.regenerate_key(self._address)
+ d.addCallback(signal_finished)
+ return d
+
#
# Setters/getters
#
diff --git a/src/leap/bitmask/keymanager/keys.py b/src/leap/bitmask/keymanager/keys.py
index 54609c05..d37c6d21 100644
--- a/src/leap/bitmask/keymanager/keys.py
+++ b/src/leap/bitmask/keymanager/keys.py
@@ -283,6 +283,9 @@ class OpenPGPKey(object):
value = str(value)
return key, value
+ def has_expired(self):
+ return self.expiry_date < datetime.now()
+
def __iter__(self):
return self
diff --git a/src/leap/bitmask/keymanager/openpgp.py b/src/leap/bitmask/keymanager/openpgp.py
index 99e5bc72..d0acca54 100644
--- a/src/leap/bitmask/keymanager/openpgp.py
+++ b/src/leap/bitmask/keymanager/openpgp.py
@@ -22,10 +22,8 @@ Infrastructure for using OpenPGP keys in Key Manager.
import os
import re
import tempfile
-import traceback
import io
-
from datetime import datetime
from multiprocessing import cpu_count
from twisted.internet import defer
@@ -162,6 +160,41 @@ class OpenPGPScheme(object):
#
# Keys management
#
+ @defer.inlineCallbacks
+ def regenerate_key(self, address):
+ """
+ Deactivate Current keypair,
+ Generate a new OpenPGP keypair bound to C{address},
+ and sign the new key with the old key.
+
+ :param address: The address bound to the key.
+ :type address: str
+
+ :return: A Deferred which fires with the new key bound to address.
+ :rtype: Deferred
+ """
+ leap_assert(is_address(address), 'Not an user address: %s' % address)
+ current_sec_key = yield self.get_key(address, private=True)
+ with TempGPGWrapper([current_sec_key], self._gpgbinary) as gpg:
+ if current_sec_key.has_expired():
+ temporary_extension_period = '1' # extend for 1 extra day
+ gpg.extend_key(current_sec_key.fingerprint,
+ validity=temporary_extension_period)
+ yield self.unactivate_key(address)
+ new_key = yield self.gen_key(address)
+ gpg.import_keys(new_key.key_data)
+ key_signing = yield from_thread(gpg.sign_key, new_key.fingerprint)
+ if key_signing.status == 'ok':
+ fetched_keys = gpg.list_keys(secret=False)
+ fetched_key = filter(lambda k: k['fingerprint'] ==
+ new_key.fingerprint, fetched_keys)[0]
+ key_data = gpg.export_keys(new_key.fingerprint, secret=False)
+ renewed_key = self._build_key_from_gpg(
+ fetched_key,
+ key_data,
+ new_key.address)
+ yield self.put_key(renewed_key)
+ defer.returnValue(new_key)
def gen_key(self, address):
"""
@@ -382,6 +415,7 @@ class OpenPGPScheme(object):
:return: A Deferred which fires when the key is in the storage.
:rtype: Deferred
"""
+
def merge_and_put((keydoc, activedoc)):
if not keydoc:
return put_new_key(activedoc)
@@ -440,6 +474,7 @@ class OpenPGPScheme(object):
(keydoc, activedoc) or None if it does not exist.
:rtype: Deferred
"""
+
def get_key_from_active_doc(activedoc):
if not activedoc:
return (None, None)
@@ -654,7 +689,7 @@ class OpenPGPScheme(object):
yield self.put_key(renewed_key)
defer.returnValue(renewed_key)
except Exception as e:
- logger.warn('Failed to Extend Key: %s expiration date.' % str(e))
+ log.warn('Failed to Extend Key: %s expiration date.' % str(e))
raise errors.KeyExpiryExtensionError(str(e))
@defer.inlineCallbacks
@@ -844,6 +879,7 @@ class OpenPGPScheme(object):
the deletions are completed
:rtype: Deferred
"""
+
def log_key_doc(doc):
self.log.error("\t%s: %s" % (doc.content[KEY_UIDS_KEY],
doc.content[KEY_FINGERPRINT_KEY]))
diff --git a/tests/integration/keymanager/test_keymanager.py b/tests/integration/keymanager/test_keymanager.py
index 466414a1..4e6d62c8 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, timedelta
+from datetime import datetime, timedelta, date
from twisted.internet import defer
from twisted.trial import unittest
@@ -554,26 +554,27 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):
km.send_key.assert_called_once_with()
@defer.inlineCallbacks
- def test_keymanager_extend_key_expiry_date_for_key_pair(self):
+ def test_key_regenerate_gets_new_expiry_date_and_signed_by_old_key(self):
km = self._key_manager(user=ADDRESS_EXPIRING)
yield km._openpgp.put_raw_key(PRIVATE_EXPIRING_KEY, ADDRESS_EXPIRING)
- key = yield km.get_key(ADDRESS_EXPIRING)
+ old_key = yield km.get_key(ADDRESS_EXPIRING)
- yield km.extend_key(validity='1w')
+ new_key = yield km.regenerate_key()
- new_expiry_date = datetime.strptime(
- KEY_EXPIRING_CREATION_DATE, '%Y-%m-%d')
- new_expiry_date += timedelta(weeks=1)
+ today = datetime.now()
+ new_expiry_date = date(today.year + 1, today.month, today.day)
renewed_public_key = yield km.get_key(ADDRESS_EXPIRING)
renewed_private_key = yield km.get_key(ADDRESS_EXPIRING, private=True)
- self.assertEqual(new_expiry_date.date(),
+ self.assertEqual(new_expiry_date,
renewed_public_key.expiry_date.date())
- self.assertEqual(new_expiry_date.date(),
+ self.assertEqual(new_expiry_date,
renewed_private_key.expiry_date.date())
- self.assertEqual(key.fingerprint, renewed_public_key.fingerprint)
- self.assertEqual(key.fingerprint, renewed_private_key.fingerprint)
+ self.assertNotEqual(old_key.fingerprint,
+ renewed_public_key.fingerprint)
+ self.assertEqual(new_key.fingerprint, renewed_public_key.fingerprint)
+ self.assertIn(old_key.fingerprint[-16:], renewed_public_key.signatures)
@defer.inlineCallbacks
def test_key_extension_with_invalid_period_throws_exception(self):
diff --git a/tox.ini b/tox.ini
index fbcc8548..6d181452 100644
--- a/tox.ini
+++ b/tox.ini
@@ -50,7 +50,7 @@ commands =
py.test -k 'not bench' --pep8 -x {posargs}
deps =
-r{toxinidir}/pkg/requirements-testing.pip
- -egit+https://github.com/pixelated/python-gnupg.git@feat_extend_key#egg=gnupg
+ -egit+https://github.com/pixelated/python-gnupg.git@key_extension_and_sign#egg=gnupg
-e../leap_commondev
-e../soledad
setenv =