Encrypt/decrypt can also sign/verify.
authordrebs <drebs@leap.se>
Thu, 9 May 2013 20:53:07 +0000 (17:53 -0300)
committerdrebs <drebs@leap.se>
Sat, 11 May 2013 20:17:46 +0000 (17:17 -0300)
Also Implement code review comments for openpgp sign.

* Add assertions and exceptions to openpgp encrypt/decrypt/sign/verify methods.
* Added comments where they will help.
* Make code more clear by encapsulating more the access to GPG wrapper and
  removing concatenation of ascii armored keys.
* Add verification of fingerprint of verifying key.
* Shorten check for signing key id.

src/leap/common/keymanager/errors.py
src/leap/common/keymanager/openpgp.py
src/leap/common/tests/test_keymanager.py

index 1cf506e..f712975 100644 (file)
@@ -25,12 +25,14 @@ class KeyNotFound(Exception):
     """
     Raised when key was no found on keyserver.
     """
+    pass
 
 
 class KeyAlreadyExists(Exception):
     """
     Raised when attempted to create a key that already exists.
     """
+    pass
 
 
 class KeyAttributesDiffer(Exception):
@@ -38,9 +40,35 @@ class KeyAttributesDiffer(Exception):
     Raised when trying to delete a key but the stored key differs from the key
     passed to the delete_key() method.
     """
+    pass
 
 class NoPasswordGiven(Exception):
     """
     Raised when trying to perform some action that needs a password without
     providing one.
     """
+    pass
+
+class InvalidSignature(Exception):
+    """
+    Raised when signature could not be verified.
+    """
+    pass
+
+class EncryptionFailed(Exception):
+    """
+    Raised upon failures of encryption.
+    """
+    pass
+
+class DecryptionFailed(Exception):
+    """
+    Raised upon failures of decryption.
+    """
+    pass
+
+class SignFailed(Exception):
+    """
+    Raised when failed to sign.
+    """
+    pass
index 0fd314a..0cb1308 100644 (file)
@@ -29,7 +29,11 @@ from leap.common.check import leap_assert, leap_assert_type
 from leap.common.keymanager.errors import (
     KeyNotFound,
     KeyAlreadyExists,
-    KeyAttributesDiffer
+    KeyAttributesDiffer,
+    InvalidSignature,
+    EncryptionFailed,
+    DecryptionFailed,
+    SignFailed,
 )
 from leap.common.keymanager.keys import (
     EncryptionKey,
@@ -45,84 +49,163 @@ from leap.common.keymanager.gpg import GPGWrapper
 # API functions
 #
 
-def encrypt_sym(data, passphrase):
+def encrypt_sym(data, passphrase, sign=None):
     """
-    Encrypt C{data} with C{passphrase}.
+    Encrypt C{data} with C{passphrase} and sign with C{sign} private key.
 
     @param data: The data to be encrypted.
     @type data: str
     @param passphrase: The passphrase used to encrypt C{data}.
     @type passphrase: str
+    @param sign: The private key used for signing.
+    @type sign: OpenPGPKey
 
     @return: The encrypted data.
     @rtype: str
     """
+    leap_assert_type(passphrase, str)
+    if sign is not None:
+        leap_assert_type(sign, OpenPGPKey)
+        leap_assert(sign.private == True)
 
     def _encrypt_cb(gpg):
-        return gpg.encrypt(
-                data, None, passphrase=passphrase, symmetric=True).data
-
-    return _safe_call(_encrypt_cb)
-
-
-def decrypt_sym(data, passphrase):
+        result = gpg.encrypt(
+            data, None,
+            sign=sign.key_id if sign else None,
+            passphrase=passphrase, symmetric=True)
+        # Here we cannot assert for correctness of sig because the sig is in
+        # the ciphertext.
+        # result.ok    - (bool) indicates if the operation succeeded
+        # result.data  - (bool) contains the result of the operation
+        if result.ok is False:
+            raise EncryptionFailed('Failed to encrypt: %s' % result.stderr)
+        return result.data
+
+    return _safe_call(_encrypt_cb, [sign])
+
+
+def decrypt_sym(data, passphrase, verify=None):
     """
-    Decrypt C{data} with C{passphrase}.
+    Decrypt C{data} with C{passphrase} and verify with C{verify} public
+    key.
 
     @param data: The data to be decrypted.
     @type data: str
     @param passphrase: The passphrase used to decrypt C{data}.
     @type passphrase: str
+    @param verify: The key used to verify a signature.
+    @type verify: OpenPGPKey
 
     @return: The decrypted data.
     @rtype: str
+
+    @raise InvalidSignature: Raised if unable to verify the signature with
+        C{verify} key.
     """
+    leap_assert_type(passphrase, str)
+    if verify is not None:
+        leap_assert_type(verify, OpenPGPKey)
+        leap_assert(verify.private == False)
 
     def _decrypt_cb(gpg):
-        return gpg.decrypt(data, passphrase=passphrase).data
-
-    return _safe_call(_decrypt_cb)
-
-
-def encrypt_asym(data, key):
+        result = gpg.decrypt(data, passphrase=passphrase)
+        # result.ok    - (bool) indicates if the operation succeeded
+        # result.valid - (bool) indicates if the signature was verified
+        # result.data  - (bool) contains the result of the operation
+        # result.pubkey_fingerpring  - (str) contains the fingerprint of the
+        #                              public key that signed this data.
+        if result.ok is False:
+            raise DecryptionFailed('Failed to decrypt: %s', result.stderr)
+        if verify is not None:
+            if result.valid is False or \
+                    verify.fingerprint != result.pubkey_fingerprint:
+                raise InvalidSignature(
+                    'Failed to verify signature with key %s: %s' %
+                    (verify.key_id, result.stderr))
+        return result.data
+
+    return _safe_call(_decrypt_cb, [verify])
+
+
+def encrypt_asym(data, pubkey, sign=None):
     """
-    Encrypt C{data} using public @{key}.
+    Encrypt C{data} using public @{key} and sign with C{sign} key.
 
     @param data: The data to be encrypted.
     @type data: str
-    @param key: The key used to encrypt.
-    @type key: OpenPGPKey
+    @param pubkey: The key used to encrypt.
+    @type pubkey: OpenPGPKey
+    @param sign: The key used for signing.
+    @type sign: OpenPGPKey
 
     @return: The encrypted data.
     @rtype: str
     """
-    leap_assert(key.private is False, 'Key is not public.')
+    leap_assert_type(pubkey, OpenPGPKey)
+    leap_assert(pubkey.private is False, 'Key is not public.')
+    if sign is not None:
+        leap_assert_type(sign, OpenPGPKey)
+        leap_assert(sign.private == True)
 
     def _encrypt_cb(gpg):
-        return gpg.encrypt(
-                data, key.fingerprint, symmetric=False).data
-
-    return _safe_call(_encrypt_cb, key.key_data)
-
-
-def decrypt_asym(data, key):
+        result = gpg.encrypt(
+            data, pubkey.fingerprint,
+            sign=sign.key_id if sign else None,
+            symmetric=False)
+        # Here we cannot assert for correctness of sig because the sig is in
+        # the ciphertext.
+        # result.ok    - (bool) indicates if the operation succeeded
+        # result.data  - (bool) contains the result of the operation
+        if result.ok is False:
+            raise EncryptionFailed(
+                'Failed to encrypt with key %s: %s' %
+                (pubkey.key_id, result.stderr))
+        return result.data
+
+    return _safe_call(_encrypt_cb, [pubkey, sign])
+
+
+def decrypt_asym(data, privkey, verify=None):
     """
-    Decrypt C{data} using private @{key}.
+    Decrypt C{data} using private @{key} and verify with C{verify} key.
 
     @param data: The data to be decrypted.
     @type data: str
-    @param key: The key used to decrypt.
-    @type key: OpenPGPKey
+    @param privkey: The key used to decrypt.
+    @type privkey: OpenPGPKey
+    @param verify: The key used to verify a signature.
+    @type verify: OpenPGPKey
 
     @return: The decrypted data.
     @rtype: str
+
+    @raise InvalidSignature: Raised if unable to verify the signature with
+        C{verify} key.
     """
-    leap_assert(key.private is True, 'Key is not private.')
+    leap_assert(privkey.private is True, 'Key is not private.')
+    if verify is not None:
+        leap_assert_type(verify, OpenPGPKey)
+        leap_assert(verify.private == False)
 
     def _decrypt_cb(gpg):
-        return gpg.decrypt(data).data
-
-    return _safe_call(_decrypt_cb, key.key_data)
+        result = gpg.decrypt(data)
+        # result.ok    - (bool) indicates if the operation succeeded
+        # result.valid - (bool) indicates if the signature was verified
+        # result.data  - (bool) contains the result of the operation
+        # result.pubkey_fingerpring  - (str) contains the fingerprint of the
+        #                              public key that signed this data.
+        if result.ok is False:
+            raise DecryptionFailed('Failed to decrypt with key %s: %s' %
+                                   (privkey.key_id, result.stderr))
+        if verify is not None:
+            if result.valid is False or \
+                    verify.fingerprint != result.pubkey_fingerprint:
+                raise InvalidSignature(
+                    'Failed to verify signature with key %s: %s' %
+                    (verify.key_id, result.stderr))
+        return result.data
+
+    return _safe_call(_decrypt_cb, [privkey, verify])
 
 
 def is_encrypted(data):
@@ -175,45 +258,61 @@ def is_encrypted_asym(data):
 
     return _safe_call(_is_encrypted_cb)
 
-def sign(data, key):
+def sign(data, privkey):
     """
-    Sign C{data} with C{key}.
+    Sign C{data} with C{privkey}.
 
     @param data: The data to be signed.
     @type data: str
-    @param key: The key to be used to sign.
-    @type key: OpenPGPKey
+    @param privkey: The private key to be used to sign.
+    @type privkey: OpenPGPKey
 
     @return: The ascii-armored signed data.
     @rtype: str
     """
-    leap_assert_type(key, OpenPGPKey)
-    leap_assert(key.private == True)
+    leap_assert_type(privkey, OpenPGPKey)
+    leap_assert(privkey.private == True)
 
     def _sign_cb(gpg):
-        return gpg.sign(data, keyid=key.key_id).data
+        result = gpg.sign(data, keyid=privkey.key_id)
+        # result.fingerprint - contains the fingerprint of the key used to
+        #                      sign.
+        if result.fingerprint is None:
+            raise SignFailed(
+                'Failed to sign with key %s: %s' %
+                (privkey.key_id, result.stderr))
+        leap_assert(
+            result.fingerprint == privkey.fingerprint,
+            'Signature and private key fingerprints mismatch: %s != %s' %
+            (result.fingerprint, privkey.fingerprint))
+        return result.data
 
-    return _safe_call(_sign_cb, key.key_data)
+    return _safe_call(_sign_cb, [privkey])
 
-def verify(data, key):
+def verify(data, pubkey):
     """
-    Verify signed C{data} with C{key}.
+    Verify signed C{data} with C{pubkey}.
 
     @param data: The data to be verified.
     @type data: str
-    @param key: The key to be used on verification.
-    @type key: OpenPGPKey
+    @param pubkey: The public key to be used on verification.
+    @type pubkey: OpenPGPKey
 
     @return: The ascii-armored signed data.
     @rtype: str
     """
-    leap_assert_type(key, OpenPGPKey)
-    leap_assert(key.private == False)
+    leap_assert_type(pubkey, OpenPGPKey)
+    leap_assert(pubkey.private == False)
 
     def _verify_cb(gpg):
-        return gpg.verify(data).valid
+        result = gpg.verify(data)
+        if result.valid is False or \
+                result.fingerprint != pubkey.fingerprint:
+            raise InvalidSignature(
+                'Failed to verify signature with key %s.' % pubkey.key_id)
+        return True
 
-    return _safe_call(_verify_cb, key.key_data)
+    return _safe_call(_verify_cb, [pubkey])
 
 #
 # Helper functions
@@ -248,35 +347,48 @@ def _build_key_from_gpg(address, key, key_data):
     )
 
 
-def _build_unitary_gpgwrapper(key_data=None):
+def _build_keyring(keys=[]):
     """
-    Return a temporary GPG wrapper keyring containing exactly zero or one
-    keys.
 
-    Temporary unitary keyrings allow the to use GPG's facilities for exactly
-    one key. This function creates an empty temporary keyring and imports
-    C{key_data} if it is not None.
+    Create an empty GPG keyring and import C{keys} into it.
+
+    @param keys: List of keys to add to the keyring.
+    @type keys: list of OpenPGPKey
 
-    @param key_data: ASCII armored key data.
-    @type key_data: str
     @return: A GPG wrapper with a unitary keyring.
     @rtype: gnupg.GPG
     """
+    privkeys = filter(lambda key: key.private == True, keys)
+    pubkeys = filter(lambda key: key.private == False, keys)
+    # here we filter out public keys that have a correspondent private key in
+    # the list because the private key_data by itself is enough to also have
+    # the public key in the keyring, and we want to count the keys afterwards.
+    privaddrs = map(lambda privkey: privkey.address, privkeys)
+    pubkeys = filter(lambda pubkey: pubkey.address not in privaddrs, pubkeys)
+    # create temporary dir for temporary gpg keyring
     tmpdir = tempfile.mkdtemp()
     gpg = GPGWrapper(gnupghome=tmpdir)
     leap_assert(len(gpg.list_keys()) is 0, 'Keyring not empty.')
-    if key_data:
-        gpg.import_keys(key_data)
-        leap_assert(
-            len(gpg.list_keys()) is 1,
-            'Unitary keyring has wrong number of keys: %d.'
-            % len(gpg.list_keys()))
+    # import keys into the keyring
+    gpg.import_keys(
+        reduce(
+            lambda x, y: x+y,
+            [key.key_data for key in pubkeys+privkeys], ''))
+    # assert the number of keys in the keyring
+    leap_assert(
+        len(gpg.list_keys()) == len(pubkeys)+len(privkeys),
+        'Wrong number of public keys in keyring: %d, should be %d)' %
+        (len(gpg.list_keys()), len(pubkeys)+len(privkeys)))
+    leap_assert(
+        len(gpg.list_keys(secret=True)) == len(privkeys),
+        'Wrong number of private keys in keyring: %d, should be %d)' %
+        (len(gpg.list_keys(secret=True)), len(privkeys)))
     return gpg
 
 
-def _destroy_unitary_gpgwrapper(gpg):
+def _destroy_keyring(gpg):
     """
-    Securely erase a unitary keyring.
+    Securely erase a keyring.
 
     @param gpg: A GPG wrapper instance.
     @type gpg: gnupg.GPG
@@ -292,23 +404,21 @@ def _destroy_unitary_gpgwrapper(gpg):
     shutil.rmtree(gpg.gnupghome)
 
 
-def _safe_call(callback, key_data=None, **kwargs):
+def _safe_call(callback, keys=[]):
     """
-    Run C{callback} in an unitary keyring containing C{key_data}.
+    Run C{callback} over a keyring containing C{keys}.
 
     @param callback: Function whose first argument is the gpg keyring.
     @type callback: function(gnupg.GPG)
-    @param key_data: ASCII armored key data.
-    @type key_data: str
-    @param **kwargs: Other eventual parameters for the callback.
-    @type **kwargs: **dict
+    @param keys: List of keys to add to the keyring.
+    @type keys: list of OpenPGPKey
 
     @return: The results of the callback.
     @rtype: str or bool
     """
-    gpg = _build_unitary_gpgwrapper(key_data)
-    val = callback(gpg, **kwargs)
-    _destroy_unitary_gpgwrapper(gpg)
+    gpg = _build_keyring(filter(lambda key: key is not None, keys))
+    val = callback(gpg)
+    _destroy_keyring(gpg)
     return val
 
 
@@ -404,18 +514,17 @@ class OpenPGPScheme(EncryptionScheme):
             raise KeyNotFound(address)
         return build_key_from_dict(OpenPGPKey, address, doc.content)
 
-    def put_key_raw(self, data):
+    def put_ascii_key(self, key_data):
         """
-        Put key contained in raw C{data} in local storage.
+        Put key contained in ascii-armored C{key_data} in local storage.
 
-        @param data: The key data to be stored.
-        @type data: str
+        @param key_data: The key data to be stored.
+        @type key_data: str
         """
-        # TODO: add more checks for correct key data.
-        leap_assert(data is not None, 'Data does not represent a key.')
-
-        def _put_key_raw_cb(gpg):
+        leap_assert_type(key_data, str)
 
+        def _put_ascii_key_cb(gpg):
+            gpg.import_keys(key_data)
             privkey = None
             pubkey = None
             try:
@@ -449,7 +558,7 @@ class OpenPGPScheme(EncryptionScheme):
                 gpg.export_keys(pubkey['fingerprint'], secret=False))
             self.put_key(openpgp_pubkey)
 
-        _safe_call(_put_key_raw_cb, data)
+        _safe_call(_put_ascii_key_cb)
 
     def put_key(self, key):
         """
index d3dee40..e52766a 100644 (file)
@@ -51,6 +51,7 @@ from leap.common.keymanager import errors
 
 
 ADDRESS = 'leap@leap.se'
+ADDRESS_2 = 'anotheruser@leap.se'
 
 
 class KeyManagerUtilTestCase(BaseLeapTest):
@@ -182,15 +183,15 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase):
     def test_openpgp_put_delete_key(self):
         pgp = openpgp.OpenPGPScheme(self._soledad)
         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
-        pgp.put_key_raw(PUBLIC_KEY)
+        pgp.put_ascii_key(PUBLIC_KEY)
         key = pgp.get_key(ADDRESS, private=False)
         pgp.delete_key(key)
         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
 
-    def test_openpgp_put_key_raw(self):
+    def test_openpgp_put_ascii_key(self):
         pgp = openpgp.OpenPGPScheme(self._soledad)
         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
-        pgp.put_key_raw(PUBLIC_KEY)
+        pgp.put_ascii_key(PUBLIC_KEY)
         key = pgp.get_key(ADDRESS, private=False)
         self.assertIsInstance(key, openpgp.OpenPGPKey)
         self.assertEqual(
@@ -203,7 +204,7 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase):
     def test_get_public_key(self):
         pgp = openpgp.OpenPGPScheme(self._soledad)
         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
-        pgp.put_key_raw(PUBLIC_KEY)
+        pgp.put_ascii_key(PUBLIC_KEY)
         self.assertRaises(
             KeyNotFound, pgp.get_key, ADDRESS, private=True)
         key = pgp.get_key(ADDRESS, private=False)
@@ -216,7 +217,7 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase):
     def test_openpgp_encrypt_decrypt_asym(self):
         # encrypt
         pgp = openpgp.OpenPGPScheme(self._soledad)
-        pgp.put_key_raw(PUBLIC_KEY)
+        pgp.put_ascii_key(PUBLIC_KEY)
         pubkey = pgp.get_key(ADDRESS, private=False)
         cyphertext = openpgp.encrypt_asym('data', pubkey)
         # assert
@@ -229,7 +230,7 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase):
         # decrypt
         self.assertRaises(
             KeyNotFound, pgp.get_key, ADDRESS, private=True)
-        pgp.put_key_raw(PRIVATE_KEY)
+        pgp.put_ascii_key(PRIVATE_KEY)
         privkey = pgp.get_key(ADDRESS, private=True)
         plaintext = openpgp.decrypt_asym(cyphertext, privkey)
         pgp.delete_key(pubkey)
@@ -250,13 +251,146 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase):
         plaintext = openpgp.decrypt_sym(cyphertext, 'pass')
         self.assertEqual('data', plaintext)
 
+    def test_verify_with_private_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        signed = openpgp.sign(data, privkey)
+        self.assertRaises(
+            AssertionError,
+            openpgp.verify, signed, privkey)
+
+    def test_sign_with_public_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PUBLIC_KEY)
+        data = 'data'
+        pubkey = pgp.get_key(ADDRESS, private=False)
+        self.assertRaises(
+            AssertionError,
+            openpgp.sign, data, pubkey)
+
+    def test_verify_with_wrong_key_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        signed = openpgp.sign(data, privkey)
+        pgp.put_ascii_key(PUBLIC_KEY_2)
+        wrongkey = pgp.get_key(ADDRESS_2)
+        self.assertRaises(
+            errors.InvalidSignature,
+            openpgp.verify, signed, wrongkey)
+
+    def test_encrypt_asym_sign_with_public_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        pubkey = pgp.get_key(ADDRESS, private=False)
+        self.assertRaises(
+            AssertionError,
+            openpgp.encrypt_asym, data, privkey, sign=pubkey)
+
+    def test_encrypt_sym_sign_with_public_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PUBLIC_KEY)
+        data = 'data'
+        pubkey = pgp.get_key(ADDRESS, private=False)
+        self.assertRaises(
+            AssertionError,
+            openpgp.encrypt_sym, data, '123', sign=pubkey)
+
+    def test_decrypt_asym_verify_with_private_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        pubkey = pgp.get_key(ADDRESS, private=False)
+        encrypted_and_signed = openpgp.encrypt_asym(
+            data, pubkey, sign=privkey)
+        self.assertRaises(
+            AssertionError,
+            openpgp.decrypt_asym,
+            encrypted_and_signed, privkey, verify=privkey)
+
+    def test_decrypt_asym_verify_with_wrong_key_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        pubkey = pgp.get_key(ADDRESS, private=False)
+        encrypted_and_signed = openpgp.encrypt_asym(data, pubkey, sign=privkey)
+        pgp.put_ascii_key(PUBLIC_KEY_2)
+        wrongkey = pgp.get_key('anotheruser@leap.se')
+        self.assertRaises(
+            errors.InvalidSignature,
+            openpgp.verify, encrypted_and_signed, wrongkey)
+
+    def test_decrypt_sym_verify_with_private_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        encrypted_and_signed = openpgp.encrypt_sym(data, '123', sign=privkey)
+        self.assertRaises(
+            AssertionError,
+            openpgp.decrypt_sym,
+            encrypted_and_signed, 'decrypt', verify=privkey)
+
+    def test_decrypt_sym_verify_with_private_raises(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        encrypted_and_signed = openpgp.encrypt_sym(data, '123', sign=privkey)
+        pgp.put_ascii_key(PUBLIC_KEY_2)
+        wrongkey = pgp.get_key('anotheruser@leap.se')
+        self.assertRaises(
+            errors.InvalidSignature,
+            openpgp.verify, encrypted_and_signed, wrongkey)
+
+    def test_sign_verify(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        signed = openpgp.sign(data, privkey)
+        pubkey = pgp.get_key(ADDRESS, private=False)
+        self.assertTrue(openpgp.verify(signed, pubkey))
+
+    def test_encrypt_asym_sign_decrypt_verify(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        pubkey = pgp.get_key(ADDRESS, private=False)
+        privkey = pgp.get_key(ADDRESS, private=True)
+        pgp.put_ascii_key(PRIVATE_KEY_2)
+        pubkey2 = pgp.get_key(ADDRESS_2, private=False)
+        privkey2 = pgp.get_key(ADDRESS_2, private=True)
+        data = 'data'
+        encrypted_and_signed = openpgp.encrypt_asym(data, pubkey2, sign=privkey)
+        res = openpgp.decrypt_asym(
+                encrypted_and_signed, privkey2, verify=pubkey)
+        self.assertTrue(data, res)
+
+    def test_encrypt_sym_sign_decrypt_verify(self):
+        pgp = openpgp.OpenPGPScheme(self._soledad)
+        pgp.put_ascii_key(PRIVATE_KEY)
+        data = 'data'
+        privkey = pgp.get_key(ADDRESS, private=True)
+        pubkey = pgp.get_key(ADDRESS, private=False)
+        encrypted_and_signed = openpgp.encrypt_sym(data, '123', sign=privkey)
+        res = openpgp.decrypt_sym(
+                encrypted_and_signed, '123', verify=pubkey)
+        self.assertEqual(data, res)
+
 
 class KeyManagerKeyManagementTestCase(
     KeyManagerWithSoledadTestCase):
 
     def test_get_all_keys_in_db(self):
         km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PRIVATE_KEY)
+        km._wrapper_map[OpenPGPKey].put_ascii_key(PRIVATE_KEY)
         # get public keys
         keys = km.get_all_keys_in_local_db(False)
         self.assertEqual(len(keys), 1, 'Wrong number of keys')
@@ -270,7 +404,7 @@ class KeyManagerKeyManagementTestCase(
 
     def test_get_public_key(self):
         km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PRIVATE_KEY)
+        km._wrapper_map[OpenPGPKey].put_ascii_key(PRIVATE_KEY)
         # get the key
         key = km.get_key(ADDRESS, OpenPGPKey, private=False,
                          fetch_remote=False)
@@ -282,7 +416,7 @@ class KeyManagerKeyManagementTestCase(
 
     def test_get_private_key(self):
         km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PRIVATE_KEY)
+        km._wrapper_map[OpenPGPKey].put_ascii_key(PRIVATE_KEY)
         # get the key
         key = km.get_key(ADDRESS, OpenPGPKey, private=True,
                          fetch_remote=False)
@@ -300,7 +434,7 @@ class KeyManagerKeyManagementTestCase(
 
     def test_send_private_key_raises_key_not_found(self):
         km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PUBLIC_KEY)
+        km._wrapper_map[OpenPGPKey].put_ascii_key(PUBLIC_KEY)
         self.assertRaises(
             KeyNotFound,
             km.send_key, OpenPGPKey, send_private=True,
@@ -308,14 +442,14 @@ class KeyManagerKeyManagementTestCase(
 
     def test_send_private_key_without_password_raises(self):
         km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PUBLIC_KEY)
+        km._wrapper_map[OpenPGPKey].put_ascii_key(PUBLIC_KEY)
         self.assertRaises(
             NoPasswordGiven,
             km.send_key, OpenPGPKey, send_private=True)
 
     def test_send_public_key(self):
         km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PUBLIC_KEY)
+        km._wrapper_map[OpenPGPKey].put_ascii_key(PUBLIC_KEY)
         km._fetcher.put = mock.Mock()
         km.token = '123'
         km.send_key(OpenPGPKey, send_private=False)
@@ -356,41 +490,13 @@ class KeyManagerKeyManagementTestCase(
     def test_refresh_keys(self):
         # TODO: maybe we should not attempt to refresh our own public key?
         km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PUBLIC_KEY)
+        km._wrapper_map[OpenPGPKey].put_ascii_key(PUBLIC_KEY)
         km.fetch_keys_from_server = mock.Mock(return_value=[])
         km.refresh_keys()
         km.fetch_keys_from_server.assert_called_once_with(
             'leap@leap.se'
         )
 
-    def test_verify_with_private_raises(self):
-        km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PRIVATE_KEY)
-        data = 'data'
-        privkey = km.get_key(ADDRESS, OpenPGPKey, private=True)
-        signed = openpgp.sign(data, privkey)
-        self.assertRaises(
-            AssertionError,
-            openpgp.verify, signed, privkey)
-
-    def test_sign_with_public_raises(self):
-        km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PUBLIC_KEY)
-        data = 'data'
-        pubkey = km.get_key(ADDRESS, OpenPGPKey, private=False)
-        self.assertRaises(
-            AssertionError,
-            openpgp.sign, data, pubkey)
-
-    def test_sign_verify(self):
-        km = self._key_manager()
-        km._wrapper_map[OpenPGPKey].put_key_raw(PRIVATE_KEY)
-        data = 'data'
-        privkey = km.get_key(ADDRESS, OpenPGPKey, private=True)
-        signed = openpgp.sign(data, privkey)
-        pubkey = km.get_key(ADDRESS, OpenPGPKey, private=False)
-        self.assertTrue(openpgp.verify(signed, pubkey))
-
 
 # Key material for testing
 KEY_FINGERPRINT = "E36E738D69173C13D709E44F2F455E2824D18DDF"
@@ -554,3 +660,61 @@ RZXoH+FTg9UAW87eqU610npOkT6cRaBxaMK/mDtGNdc=
 =JTFu
 -----END PGP PRIVATE KEY BLOCK-----
 """
+
+PUBLIC_KEY_2 = """
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+mI0EUYwJXgEEAMbTKHuPJ5/Gk34l9Z06f+0WCXTDXdte1UBoDtZ1erAbudgC4MOR
+gquKqoj3Hhw0/ILqJ88GcOJmKK/bEoIAuKaqlzDF7UAYpOsPZZYmtRfPC2pTCnXq
+Z1vdeqLwTbUspqXflkCkFtfhGKMq5rH8GV5a3tXZkRWZhdNwhVXZagC3ABEBAAG0
+IWFub3RoZXJ1c2VyIDxhbm90aGVydXNlckBsZWFwLnNlPoi4BBMBAgAiBQJRjAle
+AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRB/nfpof+5XWotuA/4tLN4E
+gUr7IfLy2HkHAxzw7A4rqfMN92DIM9mZrDGaWRrOn3aVF7VU1UG7MDkHfPvp/cFw
+ezoCw4s4IoHVc/pVlOkcHSyt4/Rfh248tYEJmFCJXGHpkK83VIKYJAithNccJ6Q4
+JE/o06Mtf4uh/cA1HUL4a4ceqUhtpLJULLeKo7iNBFGMCV4BBADsyQI7GR0wSAxz
+VayLjuPzgT+bjbFeymIhjuxKIEwnIKwYkovztW+4bbOcQs785k3Lp6RzvigTpQQt
+Z/hwcLOqZbZw8t/24+D+Pq9mMP2uUvCFFqLlVvA6D3vKSQ/XNN+YB919WQ04jh63
+yuRe94WenT1RJd6xU1aaUff4rKizuQARAQABiJ8EGAECAAkFAlGMCV4CGwwACgkQ
+f536aH/uV1rPZQQAqCzRysOlu8ez7PuiBD4SebgRqWlxa1TF1ujzfLmuPivROZ2X
+Kw5aQstxgGSjoB7tac49s0huh4X8XK+BtJBfU84JS8Jc2satlfwoyZ35LH6sDZck
+I+RS/3we6zpMfHs3vvp9xgca6ZupQxivGtxlJs294TpJorx+mFFqbV17AzQ=
+=Thdu
+-----END PGP PUBLIC KEY BLOCK-----
+"""
+
+PRIVATE_KEY_2 = """
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+lQHYBFGMCV4BBADG0yh7jyefxpN+JfWdOn/tFgl0w13bXtVAaA7WdXqwG7nYAuDD
+kYKriqqI9x4cNPyC6ifPBnDiZiiv2xKCALimqpcwxe1AGKTrD2WWJrUXzwtqUwp1
+6mdb3Xqi8E21LKal35ZApBbX4RijKuax/BleWt7V2ZEVmYXTcIVV2WoAtwARAQAB
+AAP7BLuSAx7tOohnimEs74ks8l/L6dOcsFQZj2bqs4AoY3jFe7bV0tHr4llypb/8
+H3/DYvpf6DWnCjyUS1tTnXSW8JXtx01BUKaAufSmMNg9blKV6GGHlT/Whe9uVyks
+7XHk/+9mebVMNJ/kNlqq2k+uWqJohzC8WWLRK+d1tBeqDsECANZmzltPaqUsGV5X
+C3zszE3tUBgptV/mKnBtopKi+VH+t7K6fudGcG+bAcZDUoH/QVde52mIIjjIdLje
+uajJuHUCAO1mqh+vPoGv4eBLV7iBo3XrunyGXiys4a39eomhxTy3YktQanjjx+ty
+GltAGCs5PbWGO6/IRjjvd46wh53kzvsCAO0J97gsWhzLuFnkxFAJSPk7RRlyl7lI
+1XS/x0Og6j9XHCyY1OYkfBm0to3UlCfkgirzCYlTYObCofzdKFIPDmSqHbQhYW5v
+dGhlcnVzZXIgPGFub3RoZXJ1c2VyQGxlYXAuc2U+iLgEEwECACIFAlGMCV4CGwMG
+CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEH+d+mh/7ldai24D/i0s3gSBSvsh
+8vLYeQcDHPDsDiup8w33YMgz2ZmsMZpZGs6fdpUXtVTVQbswOQd8++n9wXB7OgLD
+izgigdVz+lWU6RwdLK3j9F+Hbjy1gQmYUIlcYemQrzdUgpgkCK2E1xwnpDgkT+jT
+oy1/i6H9wDUdQvhrhx6pSG2kslQst4qjnQHYBFGMCV4BBADsyQI7GR0wSAxzVayL
+juPzgT+bjbFeymIhjuxKIEwnIKwYkovztW+4bbOcQs785k3Lp6RzvigTpQQtZ/hw
+cLOqZbZw8t/24+D+Pq9mMP2uUvCFFqLlVvA6D3vKSQ/XNN+YB919WQ04jh63yuRe
+94WenT1RJd6xU1aaUff4rKizuQARAQABAAP9EyElqJ3dq3EErXwwT4mMnbd1SrVC
+rUJrNWQZL59mm5oigS00uIyR0SvusOr+UzTtd8ysRuwHy5d/LAZsbjQStaOMBILx
+77TJveOel0a1QK0YSMF2ywZMCKvquvjli4hAtWYz/EwfuzQN3t23jc5ny+GqmqD2
+3FUxLJosFUfLNmECAO9KhVmJi+L9dswIs+2Dkjd1eiRQzNOEVffvYkGYZyKxNiXF
+UA5kvyZcB4iAN9sWCybE4WHZ9jd4myGB0MPDGxkCAP1RsXJbbuD6zS7BXe5gwunO
+2q4q7ptdSl/sJYQuTe1KNP5d/uGsvlcFfsYjpsopasPjFBIncc/2QThMKlhoEaEB
+/0mVAxpT6SrEvUbJ18z7kna24SgMPr3OnPMxPGfvNLJY/Xv/A17YfoqjmByCvsKE
+JCDjopXtmbcrZyoEZbEht9mko4ifBBgBAgAJBQJRjAleAhsMAAoJEH+d+mh/7lda
+z2UEAKgs0crDpbvHs+z7ogQ+Enm4EalpcWtUxdbo83y5rj4r0TmdlysOWkLLcYBk
+o6Ae7WnOPbNIboeF/FyvgbSQX1POCUvCXNrGrZX8KMmd+Sx+rA2XJCPkUv98Hus6
+THx7N776fcYHGumbqUMYrxrcZSbNveE6SaK8fphRam1dewM0
+=a5gs
+-----END PGP PRIVATE KEY BLOCK-----
+"""