diff options
| -rw-r--r-- | bench/keymanager/common.py | 65 | ||||
| -rw-r--r-- | src/leap/bitmask/keymanager/__init__.py | 65 | ||||
| -rw-r--r-- | src/leap/bitmask/keymanager/keys.py | 2 | ||||
| -rw-r--r-- | tests/integration/keymanager/common.py | 64 | ||||
| -rw-r--r-- | tests/integration/keymanager/test_keymanager.py | 63 | 
5 files changed, 253 insertions, 6 deletions
| diff --git a/bench/keymanager/common.py b/bench/keymanager/common.py index a7713944..aa907dab 100644 --- a/bench/keymanager/common.py +++ b/bench/keymanager/common.py @@ -205,6 +205,71 @@ RZXoH+FTg9UAW87eqU610npOkT6cRaBxaMK/mDtGNdc=  -----END PGP PRIVATE KEY BLOCK-----  """ + +# different private key to the same address +# A4337956D27DC89C Leap Test Key <leap@leap.se> +# no expiry date +DIFFERENT_KEY_FPR = "EFA5842560D6462AD9CCE7A2A4337956D27DC89C" +DIFFERENT_PRIVATE_KEY = """ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1 + +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 +=3mjv +-----END PGP PRIVATE KEY BLOCK----- +""" +  # key 7FEE575A: public key "anotheruser <anotheruser@leap.se>"  KEY_FINGERPRINT_2 = "F6E2B572ADB84EA58BD2E9A57F9DFA687FEE575A"  PUBLIC_KEY_2 = """ diff --git a/src/leap/bitmask/keymanager/__init__.py b/src/leap/bitmask/keymanager/__init__.py index a273b87d..87f411e7 100644 --- a/src/leap/bitmask/keymanager/__init__.py +++ b/src/leap/bitmask/keymanager/__init__.py @@ -207,7 +207,47 @@ class KeyManager(object):          yield self.put_raw_key(              raw_key, address=address, validation=validation_level) -    def get_key(self, address, private=False, fetch_remote=True): +    @defer.inlineCallbacks +    def get_key(self, address, private=False, active=True, fetch_remote=True): +        """ +        Return a key bound to address. + +        For an active key: first, search for the key in local storage. +        If it is not available, then try to fetch from nickserver. +        The inactive key is fetched locally, for the case of multiple keys +        for the same address. This can be used to attempt decryption +        from multiple keys. + +        :param address: The address bound to the key. +        :type address: str +        :param private: Look for a private key instead of a public one? +        :type private: bool +        :param active: Look for the current active key +        :type private: bool +        :param fetch_remote: If key not found in local storage try to fetch +                             from nickserver +        :type fetch_remote: bool + +        :return: A Deferred which fires with an EncryptionKey bound to address, +                 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 +        :rtype: Deferred + +        :raise UnsupportedKeyTypeError: if invalid key type +        """ + +        if active: +            key = yield self._get_key(address, private, fetch_remote) +            defer.returnValue(key) +        all_keys = yield self.get_all_keys(private) +        inactive_keys = filter(lambda _key: not _key.is_active(), all_keys) +        if inactive_keys: +            inactive_keys = sorted(inactive_keys, +                                   key=lambda _key: _key.expiry_date) +            defer.returnValue(inactive_keys[-1]) + +    def _get_key(self, address, private=False, fetch_remote=True):          """          Return a key bound to address. @@ -462,7 +502,8 @@ class KeyManager(object):                  fetch_remote=True):          """          Decrypt data using private key from address and verify with public key -        bound to verify address. +        bound to verify address. If the decryption using the active private +        key fails, then decription using the inactive key, if any, is tried.          :param data: The data to be decrypted.          :type data: str @@ -489,7 +530,7 @@ class KeyManager(object):          """          @defer.inlineCallbacks -        def decrypt(keys): +        def _decrypt(keys):              pubkey, privkey = keys              decrypted, signed = yield self._openpgp.decrypt(                  data, privkey, passphrase=passphrase, verify=pubkey) @@ -507,6 +548,24 @@ class KeyManager(object):                      (pubkey.fingerprint,))              defer.returnValue((decrypted, signature)) +        @defer.inlineCallbacks +        def decrypt_with_inactive_key(keys, original_decrypt_error): +            verify_key, active_key = keys +            inactive_key = yield self.get_key(address, private=True, +                                              active=False) +            if inactive_key: +                result = yield _decrypt([verify_key, inactive_key]) +                defer.returnValue(result) +            raise original_decrypt_error + +        @defer.inlineCallbacks +        def decrypt(keys): +            try: +                result = yield _decrypt(keys) +            except keymanager_errors.DecryptError as e: +                result = yield decrypt_with_inactive_key(keys, e) +            defer.returnValue(result) +          dpriv = self.get_key(address, private=True)          dpub = defer.succeed(None)          if verify is not None: diff --git a/src/leap/bitmask/keymanager/keys.py b/src/leap/bitmask/keymanager/keys.py index 6aca0320..d26f8b31 100644 --- a/src/leap/bitmask/keymanager/keys.py +++ b/src/leap/bitmask/keymanager/keys.py @@ -289,7 +289,7 @@ class OpenPGPKey(object):          return key, value      def has_expired(self): -        return self.expiry_date < datetime.now() +        return self.expiry_date and self.expiry_date < datetime.now()      def __iter__(self):          return self diff --git a/tests/integration/keymanager/common.py b/tests/integration/keymanager/common.py index 9e4b9ee1..c2fd77be 100644 --- a/tests/integration/keymanager/common.py +++ b/tests/integration/keymanager/common.py @@ -195,6 +195,70 @@ RZXoH+FTg9UAW87eqU610npOkT6cRaBxaMK/mDtGNdc=  -----END PGP PRIVATE KEY BLOCK-----  """ +# different private key to the same address +# A4337956D27DC89C Leap Test Key <leap@leap.se> +# no expiry date +DIFFERENT_KEY_FPR = "EFA5842560D6462AD9CCE7A2A4337956D27DC89C" +DIFFERENT_PRIVATE_KEY = """ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1 + +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 +=3mjv +-----END PGP PRIVATE KEY BLOCK----- +""" +  # key 7FEE575A: public key "anotheruser <anotheruser@leap.se>"  KEY_FINGERPRINT_2 = "F6E2B572ADB84EA58BD2E9A57F9DFA687FEE575A"  PUBLIC_KEY_2 = """ diff --git a/tests/integration/keymanager/test_keymanager.py b/tests/integration/keymanager/test_keymanager.py index e11e19b8..7bacd050 100644 --- a/tests/integration/keymanager/test_keymanager.py +++ b/tests/integration/keymanager/test_keymanager.py @@ -52,8 +52,8 @@ from common import (      KEY_EXPIRING_CREATION_DATE,      PRIVATE_EXPIRING_KEY,      NEW_PUB_KEY, -    OLD_AND_NEW_KEY_ADDRESS -) +    OLD_AND_NEW_KEY_ADDRESS, +    DIFFERENT_PRIVATE_KEY, DIFFERENT_KEY_FPR)  NICKSERVER_URI = "http://leap.se/"  REMOTE_KEY_URL = "http://site.domain/key" @@ -195,6 +195,25 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):          self.assertTrue(key.private)      @defer.inlineCallbacks +    def test_create_and_get_two_private_keys_sets_first_key_inactive(self): +        km = self._key_manager() +        yield km._openpgp.put_raw_key(PRIVATE_KEY, ADDRESS) +        yield km._openpgp.put_raw_key(DIFFERENT_PRIVATE_KEY, ADDRESS) +        # get the key +        inactive_key = yield km.get_key(ADDRESS, private=True, +                                        active=False, fetch_remote=False) +        active_key = yield km.get_key(ADDRESS, private=True, +                                      active=True, fetch_remote=False) +        self.assertEqual( +            inactive_key.fingerprint.lower(), KEY_FINGERPRINT.lower()) +        self.assertEqual( +            active_key.fingerprint.lower(), DIFFERENT_KEY_FPR.lower()) +        self.assertTrue(inactive_key.private) +        self.assertTrue(active_key.private) +        self.assertFalse(inactive_key.is_active()) +        self.assertTrue(active_key.is_active()) + +    @defer.inlineCallbacks      def test_send_key(self):          """          Test that request is well formed when sending keys to server. @@ -577,6 +596,25 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):          self.assertIn(old_key.fingerprint[-16:], renewed_public_key.signatures)      @defer.inlineCallbacks +    def test_key_regenerate_deactivate_the_old_private_key(self): +        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) + +        new_key = yield km.regenerate_key() +        retrieved_old_key = yield km.get_key(ADDRESS_EXPIRING, +                                             private=True, active=False) +        renewed_public_key = yield km.get_key(ADDRESS_EXPIRING, private=False) + +        self.assertEqual(old_key.fingerprint, +                         retrieved_old_key.fingerprint) +        self.assertNotEqual(old_key.fingerprint, +                            new_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_regenerate_resets_all_public_key_sign_used(self):          km = self._key_manager(user=ADDRESS_EXPIRING) @@ -630,6 +668,27 @@ class KeyManagerCryptoTestCase(KeyManagerWithSoledadTestCase):          self.assertEqual(signingkey.fingerprint, key.fingerprint)      @defer.inlineCallbacks +    def test_keymanager_openpgp_decryption_tries_inactive_valid_key(self): +        km = self._key_manager() +        # put raw private key +        yield km._openpgp.put_raw_key(PRIVATE_KEY, ADDRESS) +        yield km._openpgp.put_raw_key(PRIVATE_KEY_2, ADDRESS_2) +        # encrypt +        encdata = yield km.encrypt(self.RAW_DATA, ADDRESS, sign=ADDRESS_2, +                                   fetch_remote=False) +        self.assertNotEqual(self.RAW_DATA, encdata) + +        # renew key +        new_key = yield km.regenerate_key() + +        # decrypt +        rawdata, signingkey = yield km.decrypt( +            encdata, ADDRESS, verify=ADDRESS_2, fetch_remote=False) +        self.assertEqual(self.RAW_DATA, rawdata) +        key = yield km.get_key(ADDRESS_2, private=False, fetch_remote=False) +        self.assertEqual(signingkey.fingerprint, key.fingerprint) + +    @defer.inlineCallbacks      def test_keymanager_openpgp_encrypt_decrypt_wrong_sign(self):          km = self._key_manager()          # put raw keys | 
