diff options
-rw-r--r-- | changes/feature-4375_add-verification-of-detached-sigs | 1 | ||||
-rw-r--r-- | src/leap/keymanager/__init__.py | 12 | ||||
-rw-r--r-- | src/leap/keymanager/keys.py | 9 | ||||
-rw-r--r-- | src/leap/keymanager/openpgp.py | 30 | ||||
-rw-r--r-- | src/leap/keymanager/tests/test_keymanager.py | 14 |
5 files changed, 52 insertions, 14 deletions
diff --git a/changes/feature-4375_add-verification-of-detached-sigs b/changes/feature-4375_add-verification-of-detached-sigs new file mode 100644 index 0000000..ee09cbe --- /dev/null +++ b/changes/feature-4375_add-verification-of-detached-sigs @@ -0,0 +1 @@ + o Add verification of detached signatures. Closes #4375. diff --git a/src/leap/keymanager/__init__.py b/src/leap/keymanager/__init__.py index a550598..61213d3 100644 --- a/src/leap/keymanager/__init__.py +++ b/src/leap/keymanager/__init__.py @@ -473,15 +473,18 @@ class KeyManager(object): data, privkey, digest_algo=digest_algo, clearsign=clearsign, detach=detach, binary=binary) - def verify(self, data, pubkey): + def verify(self, data, pubkey, detached_sig=None): """ - Verify signed C{data} with C{pubkey}. + Verify signed C{data} with C{pubkey}, eventually using + C{detached_sig}. :param data: The data to be verified. :type data: str - :param pubkey: The public key to be used on verification. :type pubkey: EncryptionKey + :param detached_sig: A detached signature. If given, C{data} is + verified using this detached signature. + :type detached_sig: str :return: The signed data. :rtype: str @@ -489,7 +492,8 @@ class KeyManager(object): leap_assert_type(pubkey, EncryptionKey) leap_assert(pubkey.__class__ in self._wrapper_map, 'Unknown key type.') leap_assert(pubkey.private is False, 'Key is not public.') - return self._wrapper_map[pubkey.__class__].verify(data, pubkey) + return self._wrapper_map[pubkey.__class__].verify( + data, pubkey, detached_sig=detached_sig) from ._version import get_versions __version__ = get_versions()['version'] diff --git a/src/leap/keymanager/keys.py b/src/leap/keymanager/keys.py index b8e88d4..ec1bfeb 100644 --- a/src/leap/keymanager/keys.py +++ b/src/leap/keymanager/keys.py @@ -340,15 +340,18 @@ class EncryptionScheme(object): pass @abstractmethod - def verify(self, data, pubkey): + def verify(self, data, pubkey, detached_sig=None): """ - Verify signed C{data} with C{pubkey}. + Verify signed C{data} with C{pubkey}, eventually using + C{detached_sig}. :param data: The data to be verified. :type data: str - :param pubkey: The public key to be used on verification. :type pubkey: EncryptionKey + :param detached_sig: A detached signature. If given, C{data} is + verified against this sdetached signature. + :type detached_sig: str :return: The signed data. :rtype: str diff --git a/src/leap/keymanager/openpgp.py b/src/leap/keymanager/openpgp.py index 8ec8639..a4dc1b8 100644 --- a/src/leap/keymanager/openpgp.py +++ b/src/leap/keymanager/openpgp.py @@ -27,9 +27,11 @@ import re import shutil import tempfile import locale +from contextlib import closing from gnupg import GPG from gnupg.gnupg import GPGUtilities +from gnupg._util import _make_binary_stream from leap.common.check import leap_assert, leap_assert_type from leap.keymanager import errors @@ -46,6 +48,10 @@ from leap.keymanager.keys import ( logger = logging.getLogger(__name__) +# +# A temporary GPG keyring wrapped to provide OpenPGP functionality. +# + class TempGPGWrapper(object): """ A context manager that wraps a temporary GPG keyring which only contains @@ -243,7 +249,7 @@ class OpenPGPScheme(EncryptionScheme): key_length=4096, name_real=address, name_email=address, - name_comment='Generated by LEAP Key Manager.') + name_comment='') logger.info("About to generate keys... This might take SOME time.") gpg.gen_key(params) logger.info("Keys for %s have been successfully " @@ -570,15 +576,18 @@ class OpenPGPScheme(EncryptionScheme): '%s != %s' % (rfprint, kfprint)) return result.data - def verify(self, data, pubkey): + def verify(self, data, pubkey, detached_sig=None): """ - Verify signed C{data} with C{pubkey}. + Verify signed C{data} with C{pubkey}, eventually using + C{detached_sig}. :param data: The data to be verified. :type data: str - :param pubkey: The public key to be used on verification. :type pubkey: OpenPGPKey + :param detached_sig: A detached signature. If given, C{data} is + verified against this detached signature. + :type detached_sig: str :return: The ascii-armored signed data. :rtype: str @@ -586,7 +595,18 @@ class OpenPGPScheme(EncryptionScheme): leap_assert_type(pubkey, OpenPGPKey) leap_assert(pubkey.private is False) with self._temporary_gpgwrapper(pubkey) as gpg: - result = gpg.verify(data) + result = None + if detached_sig is None: + result = gpg.verify(data) + else: + # to verify using a detached sig we have to use + # gpg.verify_file(), which receives the data as a binary + # stream and the name of a file containing the signature. + sf, sfname = tempfile.mkstemp() + with os.fdopen(sf, 'w') as sfd: + sfd.write(detached_sig) + with closing(_make_binary_stream(data, gpg._encoding)) as df: + result = gpg.verify_file(df, sig_file=sfname) gpgpubkey = gpg.list_keys().pop() valid = result.valid rfprint = result.fingerprint diff --git a/src/leap/keymanager/tests/test_keymanager.py b/src/leap/keymanager/tests/test_keymanager.py index 2512604..67676e9 100644 --- a/src/leap/keymanager/tests/test_keymanager.py +++ b/src/leap/keymanager/tests/test_keymanager.py @@ -294,7 +294,7 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase): pgp.put_ascii_key(PRIVATE_KEY) data = 'data' privkey = pgp.get_key(ADDRESS, private=True) - signed = pgp.sign(data, privkey) + signed = pgp.sign(data, privkey, detach=False) pubkey = pgp.get_key(ADDRESS, private=False) self.assertTrue(pgp.verify(signed, pubkey)) @@ -314,6 +314,16 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase): encrypted_and_signed, privkey2, verify=pubkey) self.assertTrue(data, res) + def test_sign_verify_detached_sig(self): + pgp = openpgp.OpenPGPScheme( + self._soledad, gpgbinary=GPG_BINARY_PATH) + pgp.put_ascii_key(PRIVATE_KEY) + data = 'data' + privkey = pgp.get_key(ADDRESS, private=True) + signature = pgp.sign(data, privkey, detach=True) + pubkey = pgp.get_key(ADDRESS, private=False) + self.assertTrue(pgp.verify(data, pubkey, detached_sig=signature)) + class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase): @@ -490,7 +500,7 @@ class KeyManagerCryptoTestCase(KeyManagerWithSoledadTestCase): privkey = km.get_key( ADDRESS, OpenPGPKey, private=True, fetch_remote=False) # encrypt - signdata = km.sign(self.RAW_DATA, privkey) + signdata = km.sign(self.RAW_DATA, privkey, detach=False) self.assertNotEqual(self.RAW_DATA, signdata) # get public key for verifying pubkey = km.get_key( |