Add verification of detached signatures.
authordrebs <drebs@leap.se>
Mon, 4 Nov 2013 17:09:40 +0000 (15:09 -0200)
committerdrebs <drebs@leap.se>
Mon, 4 Nov 2013 17:10:26 +0000 (15:10 -0200)
changes/feature-4375_add-verification-of-detached-sigs [new file with mode: 0644]
src/leap/keymanager/__init__.py
src/leap/keymanager/keys.py
src/leap/keymanager/openpgp.py
src/leap/keymanager/tests/test_keymanager.py

diff --git a/changes/feature-4375_add-verification-of-detached-sigs b/changes/feature-4375_add-verification-of-detached-sigs
new file mode 100644 (file)
index 0000000..ee09cbe
--- /dev/null
@@ -0,0 +1 @@
+  o Add verification of detached signatures. Closes #4375.
index a550598..61213d3 100644 (file)
@@ -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']
index b8e88d4..ec1bfeb 100644 (file)
@@ -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
index 8ec8639..111dfaf 100644 (file)
@@ -30,6 +30,7 @@ import locale
 
 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
@@ -570,15 +571,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 +590,20 @@ 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()
+                sfd = os.fdopen(sf, 'w')
+                sfd.write(detached_sig)
+                sfd.close()
+                df = _make_binary_stream(data, gpg._encoding)
+                result = gpg.verify_file(df, sig_file=sfname)
+                df.close()
             gpgpubkey = gpg.list_keys().pop()
             valid = result.valid
             rfprint = result.fingerprint
index 2512604..67676e9 100644 (file)
@@ -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(