From 933731e4671c8ed3b7fa16bf1222e06f76eea215 Mon Sep 17 00:00:00 2001
From: drebs <drebs@leap.se>
Date: Mon, 4 Nov 2013 15:09:40 -0200
Subject: Add verification of detached signatures.

---
 src/leap/keymanager/__init__.py              | 12 ++++++++----
 src/leap/keymanager/keys.py                  |  9 ++++++---
 src/leap/keymanager/openpgp.py               | 25 +++++++++++++++++++++----
 src/leap/keymanager/tests/test_keymanager.py | 14 ++++++++++++--
 4 files changed, 47 insertions(+), 13 deletions(-)

(limited to 'src')

diff --git a/src/leap/keymanager/__init__.py b/src/leap/keymanager/__init__.py
index a5505981..61213d38 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 b8e88d46..ec1bfeb4 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 8ec86391..111dfafa 100644
--- a/src/leap/keymanager/openpgp.py
+++ b/src/leap/keymanager/openpgp.py
@@ -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
diff --git a/src/leap/keymanager/tests/test_keymanager.py b/src/leap/keymanager/tests/test_keymanager.py
index 25126047..67676e9c 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(
-- 
cgit v1.2.3


From 45f158060a4ed2f95a94d839a869a76a02d5ceab Mon Sep 17 00:00:00 2001
From: drebs <drebs@leap.se>
Date: Mon, 4 Nov 2013 15:15:46 -0200
Subject: Remove key comment on OpenPGP key generation.

---
 src/leap/keymanager/openpgp.py | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

(limited to 'src')

diff --git a/src/leap/keymanager/openpgp.py b/src/leap/keymanager/openpgp.py
index 111dfafa..a4dc1b8b 100644
--- a/src/leap/keymanager/openpgp.py
+++ b/src/leap/keymanager/openpgp.py
@@ -27,6 +27,7 @@ import re
 import shutil
 import tempfile
 import locale
+from contextlib import closing
 
 from gnupg import GPG
 from gnupg.gnupg import GPGUtilities
@@ -47,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
@@ -244,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 "
@@ -598,12 +603,10 @@ class OpenPGPScheme(EncryptionScheme):
                 # 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()
+                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
-- 
cgit v1.2.3


From 666eb414ebc4d852410699f061c4b2d505f59308 Mon Sep 17 00:00:00 2001
From: Ivan Alejandro <ivanalejandro0@gmail.com>
Date: Mon, 11 Nov 2013 16:24:28 -0300
Subject: Use 'utf-8' as default/fallback encoding.

- Also reorder stdlib imports alphabetically :)
---
 src/leap/keymanager/openpgp.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/leap/keymanager/openpgp.py b/src/leap/keymanager/openpgp.py
index a4dc1b8b..1670e1c9 100644
--- a/src/leap/keymanager/openpgp.py
+++ b/src/leap/keymanager/openpgp.py
@@ -21,12 +21,14 @@ Infrastructure for using OpenPGP keys in Key Manager.
 """
 
 
+import locale
 import logging
 import os
 import re
 import shutil
+import sys
 import tempfile
-import locale
+
 from contextlib import closing
 
 from gnupg import GPG
@@ -516,6 +518,10 @@ class OpenPGPScheme(EncryptionScheme):
             # https://github.com/isislovecruft/python-gnupg/\
             #   blob/master/gnupg/_meta.py#L121
             encoding = locale.getpreferredencoding()
+            if encoding is None:
+                encoding = sys.stdin.encoding
+            if encoding is None:
+                encoding = 'utf-8'
             return result.data.decode(encoding, 'replace')
 
     def is_encrypted(self, data):
-- 
cgit v1.2.3


From 0050dcd11137f544c6c78d236163e19f76a24b38 Mon Sep 17 00:00:00 2001
From: Ivan Alejandro <ivanalejandro0@gmail.com>
Date: Wed, 13 Nov 2013 18:09:15 -0300
Subject: Refactor code to support parsing ascii keys.

---
 src/leap/keymanager/errors.py  | 12 ++++++++
 src/leap/keymanager/openpgp.py | 63 ++++++++++++++++++++++++++++++------------
 2 files changed, 58 insertions(+), 17 deletions(-)

(limited to 'src')

diff --git a/src/leap/keymanager/errors.py b/src/leap/keymanager/errors.py
index 89949d29..27180dbd 100644
--- a/src/leap/keymanager/errors.py
+++ b/src/leap/keymanager/errors.py
@@ -84,3 +84,15 @@ class SignFailed(Exception):
     Raised when failed to sign.
     """
     pass
+
+
+class KeyAddressMismatch(Exception):
+    """
+    A mismatch between addresses.
+    """
+
+
+class KeyFingerprintMismatch(Exception):
+    """
+    A mismatch between fingerprints.
+    """
diff --git a/src/leap/keymanager/openpgp.py b/src/leap/keymanager/openpgp.py
index 1670e1c9..78acd6ad 100644
--- a/src/leap/keymanager/openpgp.py
+++ b/src/leap/keymanager/openpgp.py
@@ -35,7 +35,7 @@ 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.common.check import leap_assert, leap_assert_type, leap_check
 from leap.keymanager import errors
 from leap.keymanager.keys import (
     EncryptionKey,
@@ -303,16 +303,22 @@ class OpenPGPScheme(EncryptionScheme):
             raise errors.KeyNotFound(address)
         return build_key_from_dict(OpenPGPKey, address, doc.content)
 
-    def put_ascii_key(self, key_data):
+    def parse_ascii_key(self, key_data):
         """
-        Put key contained in ascii-armored C{key_data} in local storage.
+        Parses an ascii armored key (or key pair) data and returns
+        the OpenPGPKey keys.
 
-        :param key_data: The key data to be stored.
+        :param key_data: the key data to be parsed.
         :type key_data: str or unicode
+
+        :returns: the public key and private key (if applies) for that data.
+        :rtype: (public, private) -> tuple(OpenPGPKey, OpenPGPKey)
+                the tuple may have one or both components None
         """
         leap_assert_type(key_data, (str, unicode))
         # TODO: add more checks for correct key data.
         leap_assert(key_data is not None, 'Data does not represent a key.')
+        mail_regex = '.*<([\w.-]+@[\w.-]+)>.*'
 
         with self._temporary_gpgwrapper() as gpg:
             # TODO: inspect result, or use decorator
@@ -325,31 +331,54 @@ class OpenPGPScheme(EncryptionScheme):
             except IndexError:
                 pass
             pubkey = gpg.list_keys(secret=False).pop()  # unitary keyring
+
             # extract adress from first uid on key
-            match = re.match('.*<([\w.-]+@[\w.-]+)>.*', pubkey['uids'].pop())
+            match = re.match(mail_regex, pubkey['uids'].pop())
             leap_assert(match is not None, 'No user address in key data.')
             address = match.group(1)
+
             if privkey is not None:
-                match = re.match(
-                    '.*<([\w.-]+@[\w.-]+)>.*', privkey['uids'].pop())
+                match = re.match(mail_regex, privkey['uids'].pop())
                 leap_assert(match is not None, 'No user address in key data.')
                 privaddress = match.group(1)
-                leap_assert(
-                    address == privaddress,
-                    'Addresses in pub and priv key differ.')
-                leap_assert(
-                    pubkey['fingerprint'] == privkey['fingerprint'],
-                    'Fingerprints for pub and priv key differ.')
-                # insert private key in storage
+
+                # build private key
                 openpgp_privkey = _build_key_from_gpg(
-                    address, privkey,
+                    privaddress, privkey,
                     gpg.export_keys(privkey['fingerprint'], secret=True))
-                self.put_key(openpgp_privkey)
-            # insert public key in storage
+
+                leap_check(address == privaddress,
+                           'Addresses in public and private key differ.',
+                           errors.KeyAddressMismatch)
+                leap_check(pubkey['fingerprint'] == privkey['fingerprint'],
+                           'Fingerprints for public and private key differ.',
+                           errors.KeyFingerprintMismatch)
+
+            # build public key
             openpgp_pubkey = _build_key_from_gpg(
                 address, pubkey,
                 gpg.export_keys(pubkey['fingerprint'], secret=False))
+
+            return (openpgp_pubkey, openpgp_privkey)
+
+    def put_ascii_key(self, key_data):
+        """
+        Put key contained in ascii-armored C{key_data} in local storage.
+
+        :param key_data: The key data to be stored.
+        :type key_data: str or unicode
+        """
+        leap_assert_type(key_data, (str, unicode))
+
+        try:
+            openpgp_pubkey, openpgp_privkey = self.parse_ascii_key(key_data)
+        except (errors.KeyAddressMismatch, errors.KeyFingerprintMismatch) as e:
+            leap_assert(False, repr(e))
+
+        if openpgp_pubkey is not None:
             self.put_key(openpgp_pubkey)
+        if openpgp_privkey is not None:
+            self.put_key(openpgp_privkey)
 
     def put_key(self, key):
         """
-- 
cgit v1.2.3


From 875f8ed071f3dae06818eba992a8dd8ddc2298b3 Mon Sep 17 00:00:00 2001
From: Ivan Alejandro <ivanalejandro0@gmail.com>
Date: Thu, 14 Nov 2013 11:59:28 -0300
Subject: Expose openpgp methods in keymanager.

- parse_ascii_key
- delete_key
- put_key
---
 src/leap/keymanager/__init__.py | 42 +++++++++++++++++++++++++++++++++++++++++
 src/leap/keymanager/openpgp.py  |  6 +++++-
 2 files changed, 47 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/leap/keymanager/__init__.py b/src/leap/keymanager/__init__.py
index 61213d38..dbc54891 100644
--- a/src/leap/keymanager/__init__.py
+++ b/src/leap/keymanager/__init__.py
@@ -495,6 +495,48 @@ class KeyManager(object):
         return self._wrapper_map[pubkey.__class__].verify(
             data, pubkey, detached_sig=detached_sig)
 
+    def parse_openpgp_ascii_key(self, key_data):
+        """
+        Parses an ascii armored key (or key pair) data and returns
+        the OpenPGPKey keys.
+
+        :param key_data: the key data to be parsed.
+        :type key_data: str or unicode
+
+        :returns: the public key and private key (if applies) for that data.
+        :rtype: (public, private) -> tuple(OpenPGPKey, OpenPGPKey)
+                the tuple may have one or both components None
+        """
+        return self._wrapper_map[OpenPGPKey].parse_ascii_key(key_data)
+
+    def delete_key(self, key):
+        """
+        Remove C{key} from storage.
+
+        May raise:
+            openpgp.errors.KeyNotFound
+            openpgp.errors.KeyAttributesDiffer
+
+        :param key: The key to be removed.
+        :type key: EncryptionKey
+        """
+        try:
+            self._wrapper_map[type(key)].delete_key(key)
+        except IndexError as e:
+            leap_assert(False, "Unsupported key type. Error {0!r}".format(e))
+
+    def put_key(self, key):
+        """
+        Put C{key} in local storage.
+
+        :param key: The key to be stored.
+        :type key: OpenPGPKey
+        """
+        try:
+            self._wrapper_map[type(key)].put_key(key)
+        except IndexError as e:
+            leap_assert(False, "Unsupported key type. Error {0!r}".format(e))
+
 from ._version import get_versions
 __version__ = get_versions()['version']
 del get_versions
diff --git a/src/leap/keymanager/openpgp.py b/src/leap/keymanager/openpgp.py
index 78acd6ad..f6223d57 100644
--- a/src/leap/keymanager/openpgp.py
+++ b/src/leap/keymanager/openpgp.py
@@ -424,10 +424,14 @@ class OpenPGPScheme(EncryptionScheme):
         """
         Remove C{key} from storage.
 
+        May raise:
+            errors.KeyNotFound
+            errors.KeyAttributesDiffer
+
         :param key: The key to be removed.
         :type key: EncryptionKey
         """
-        leap_assert(key.__class__ is OpenPGPKey, 'Wrong key type.')
+        leap_assert_type(key, OpenPGPKey)
         stored_key = self.get_key(key.address, private=key.private)
         if stored_key is None:
             raise errors.KeyNotFound(key)
-- 
cgit v1.2.3