Verified Commit 94304647 by NavaL Committed by meskio

[feat] expired public key are remotely fetched if expired

- private key is not allowed to be fetched remotely
- fetch_remote needs to be specifically set
- if a new key is fetched (ie different KeyID), the validation
  rule applies
parent 6b5c2e80
......@@ -270,6 +270,53 @@ p/c59ceStlbqgYybVHhnFtse0d/dpl7rTi0JO9sph/Mg
-----END PGP PRIVATE KEY BLOCK-----
"""
# the corresponding public key
# this is signed by the other key 2F455E2824D18DDF above
DIFFERENT_PUBLIC_KEY = """
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
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
=X/jT
-----END PGP PUBLIC KEY BLOCK-----
"""
# key 7FEE575A: public key "anotheruser <anotheruser@leap.se>"
KEY_FINGERPRINT_2 = "F6E2B572ADB84EA58BD2E9A57F9DFA687FEE575A"
PUBLIC_KEY_2 = """
......
......@@ -229,8 +229,9 @@ class KeyManager(object):
"""
Return a key bound to address.
First, search for the key in local storage. If it is not available,
then try to fetch from nickserver.
First, search for the key in local storage. When it is available
locally but is expired or when it is not available locally,
then a fetch from nickserver is tried.
:param address: The address bound to the key.
:type address: str
......@@ -244,6 +245,8 @@ class KeyManager(object):
or which fails with KeyNotFound if no key was found neither
locally or in keyserver or fail with KeyVersionError if the
key has a format not supported by this version of KeyManager
or KeyNotValidUpgrade if the key is renewed remotely but fails
the validation rule
:rtype: Deferred
:raise UnsupportedKeyTypeError: if invalid key type
......@@ -255,17 +258,27 @@ class KeyManager(object):
emit_async(catalog.KEYMANAGER_KEY_FOUND, address)
return key
def ensure_valid(key):
if key.is_expired():
logger.info('Found expired key for %s.' % self._address)
return _fetch_remotely(key)
key_found(key)
return key
def key_not_found(failure):
if not failure.check(keymanager_errors.KeyNotFound):
return failure
emit_async(catalog.KEYMANAGER_KEY_NOT_FOUND, address)
return _fetch_remotely(failure)
def _fetch_remotely(passthru):
# we will only try to fetch a key from nickserver if fetch_remote
# is True and the key is not private.
if fetch_remote is False or private is True:
return failure
return passthru
logger.debug('Fetching remotely key for %s.' % self._address)
emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)
d = self._fetch_keys_from_server_and_store_local(address)
d.addCallback(
......@@ -275,7 +288,7 @@ class KeyManager(object):
# return key if it exists in local database
d = self._openpgp.get_key(address, private=private)
d.addCallbacks(key_found, key_not_found)
d.addCallbacks(ensure_valid, key_not_found)
return d
@defer.inlineCallbacks
......
......@@ -259,6 +259,53 @@ p/c59ceStlbqgYybVHhnFtse0d/dpl7rTi0JO9sph/Mg
-----END PGP PRIVATE KEY BLOCK-----
"""
# the corresponding public key
# this is signed by the other key 2F455E2824D18DDF above
DIFFERENT_PUBLIC_KEY = """
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
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
=X/jT
-----END PGP PUBLIC KEY BLOCK-----
"""
# key 7FEE575A: public key "anotheruser <anotheruser@leap.se>"
KEY_FINGERPRINT_2 = "F6E2B572ADB84EA58BD2E9A57F9DFA687FEE575A"
PUBLIC_KEY_2 = """
......
......@@ -49,11 +49,13 @@ from common import (
PRIVATE_KEY,
PRIVATE_KEY_2,
ADDRESS_EXPIRING,
KEY_EXPIRING_CREATION_DATE,
PRIVATE_EXPIRING_KEY,
NEW_PUB_KEY,
OLD_AND_NEW_KEY_ADDRESS,
DIFFERENT_PRIVATE_KEY, DIFFERENT_KEY_FPR)
DIFFERENT_PRIVATE_KEY,
DIFFERENT_KEY_FPR,
DIFFERENT_PUBLIC_KEY,
)
NICKSERVER_URI = "http://leap.se/"
REMOTE_KEY_URL = "http://site.domain/key"
......@@ -171,6 +173,28 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):
self.assertFalse(key.private)
@defer.inlineCallbacks
def test_get_public_key_fetches_remotely_if_key_expired(self):
km = self._key_manager()
nicknym_response = {'address': ADDRESS,
'openpgp': DIFFERENT_PUBLIC_KEY}
km._nicknym.fetch_key_with_address = mock.Mock(
return_value=nicknym_response)
# put key
yield km._openpgp.put_raw_key(PUBLIC_KEY, ADDRESS)
# get the key
with mock.patch('leap.bitmask.keymanager.keys.OpenPGPKey.is_expired',
return_value=True):
key = yield km.get_key(ADDRESS, private=False,
fetch_remote=True)
km._nicknym.fetch_key_with_address.assert_called_once_with(ADDRESS)
self.assertTrue(key is not None)
self.assertEqual(
key.fingerprint.lower(), DIFFERENT_KEY_FPR.lower())
self.assertFalse(key.private)
@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)
......@@ -577,13 +601,14 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):
km = self._key_manager(user=ADDRESS_EXPIRING)
yield km._openpgp.put_raw_key(PRIVATE_EXPIRING_KEY, ADDRESS_EXPIRING)
old_key = yield km.get_key(ADDRESS_EXPIRING)
old_key = yield km.get_key(ADDRESS_EXPIRING, fetch_remote=False)
new_key = yield km.regenerate_key()
today = datetime.now()
new_expiry_date = date(today.year + 1, today.month, today.day)
renewed_public_key = yield km.get_key(ADDRESS_EXPIRING)
renewed_public_key = yield km.get_key(ADDRESS_EXPIRING,
fetch_remote=False)
renewed_private_key = yield km.get_key(ADDRESS_EXPIRING, private=True)
self.assertEqual(new_expiry_date,
......@@ -600,11 +625,12 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):
km = self._key_manager(user=ADDRESS_EXPIRING)
yield km._openpgp.put_raw_key(PRIVATE_EXPIRING_KEY, ADDRESS_EXPIRING)
old_key = yield km.get_key(ADDRESS_EXPIRING)
old_key = yield km.get_key(ADDRESS_EXPIRING, fetch_remote=False)
new_key = yield km.regenerate_key()
inactive_private_keys = yield km.get_inactive_private_keys()
renewed_public_key = yield km.get_key(ADDRESS_EXPIRING, private=False)
renewed_public_key = yield km.get_key(ADDRESS_EXPIRING, private=False,
fetch_remote=False)
self.assertEqual(1, len(inactive_private_keys))
retrieved_old_key = inactive_private_keys[0]
......@@ -632,14 +658,15 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):
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)
key = yield km.get_key(ADDRESS_EXPIRING, fetch_remote=False)
invalid_validity_option = '2xw'
with self.assertRaises(KeyExpiryExtensionError):
yield km.extend_key(validity=invalid_validity_option)
renewed_public_key = yield km.get_key(ADDRESS_EXPIRING)
renewed_public_key = yield km.get_key(ADDRESS_EXPIRING,
fetch_remote=False)
renewed_private_key = yield km.get_key(ADDRESS_EXPIRING, private=True)
self.assertEqual(key.expiry_date, renewed_public_key.expiry_date)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment