Some packaging improvements
[keymanager.git] / src / leap / keymanager / __init__.py
index e1f318c..05cbcca 100644 (file)
 Key Manager is a Nicknym agent for LEAP client.
 """
 
+import logging
 import requests
 
-from leap.common.check import leap_assert
-from leap.keymanager.errors import (
-    KeyNotFound,
-    NoPasswordGiven,
-)
+from leap.common.check import leap_assert, leap_assert_type
+from leap.common.events import signal
+from leap.common.events import events_pb2 as proto
+
+from leap.keymanager.errors import KeyNotFound
+
 from leap.keymanager.keys import (
+    EncryptionKey,
     build_key_from_dict,
     KEYMANAGER_KEY_TAG,
     TAGS_PRIVATE_INDEX,
@@ -37,6 +40,8 @@ from leap.keymanager.openpgp import (
     OpenPGPScheme,
 )
 
+logger = logging.getLogger(__name__)
+
 
 #
 # The Key Manager
@@ -52,7 +57,8 @@ class KeyManager(object):
     PUBKEY_KEY = "user[public_key]"
 
     def __init__(self, address, nickserver_uri, soledad, session_id=None,
-                 ca_cert_path=None, api_uri=None, api_version=None, uid=None):
+                 ca_cert_path=None, api_uri=None, api_version=None, uid=None,
+                 gpgbinary=None):
         """
         Initialize a Key Manager for user's C{address} with provider's
         nickserver reachable in C{url}.
@@ -73,6 +79,8 @@ class KeyManager(object):
         :type api_version: str
         :param uid: The users' UID.
         :type uid: str
+        :param gpgbinary: Name for GnuPG binary executable.
+        :type gpgbinary: C{str}
         """
         self._address = address
         self._nickserver_uri = nickserver_uri
@@ -84,7 +92,7 @@ class KeyManager(object):
         self.uid = uid
         # a dict to map key types to their handlers
         self._wrapper_map = {
-            OpenPGPKey: OpenPGPScheme(soledad),
+            OpenPGPKey: OpenPGPScheme(soledad, gpgbinary=gpgbinary),
             # other types of key will be added to this mapper.
         }
         # the following are used to perform https requests
@@ -166,12 +174,18 @@ class KeyManager(object):
         @raise KeyNotFound: If the key was not found on nickserver.
         """
         # request keys from the nickserver
-        server_keys = self._get(
-            self._nickserver_uri, {'address': address}).json()
-        # insert keys in local database
-        if self.OPENPGP_KEY in server_keys:
-            self._wrapper_map[OpenPGPKey].put_ascii_key(
-                server_keys['openpgp'])
+        res = None
+        try:
+            res = self._get(self._nickserver_uri, {'address': address})
+            server_keys = res.json()
+            # insert keys in local database
+            if self.OPENPGP_KEY in server_keys:
+                self._wrapper_map[OpenPGPKey].put_ascii_key(
+                    server_keys['openpgp'])
+        except Exception as e:
+            logger.warning("Error retrieving the keys: %r" % (e,))
+            if res:
+                logger.warning("%s" % (res.content,))
 
     #
     # key management
@@ -209,6 +223,7 @@ class KeyManager(object):
             self._api_version,
             self._uid)
         self._put(uri, data)
+        signal(proto.KEYMANAGER_DONE_UPLOADING_KEYS, self._address)
 
     def get_key(self, address, ktype, private=False, fetch_remote=True):
         """
@@ -233,15 +248,26 @@ class KeyManager(object):
             ktype in self._wrapper_map,
             'Unkown key type: %s.' % str(ktype))
         try:
+            signal(proto.KEYMANAGER_LOOKING_FOR_KEY, address)
             # return key if it exists in local database
-            return self._wrapper_map[ktype].get_key(address, private=private)
+            key = self._wrapper_map[ktype].get_key(address, private=private)
+            signal(proto.KEYMANAGER_KEY_FOUND, address)
+
+            return key
         except KeyNotFound:
+            signal(proto.KEYMANAGER_KEY_NOT_FOUND, address)
+
             # we will only try to fetch a key from nickserver if fetch_remote
             # is True and the key is not private.
             if fetch_remote is False or private is True:
                 raise
+
+            signal(proto.KEYMANAGER_LOOKING_FOR_KEY, address)
             self._fetch_keys_from_server(address)
-            return self._wrapper_map[ktype].get_key(address, private=False)
+            key = self._wrapper_map[ktype].get_key(address, private=False)
+            signal(proto.KEYMANAGER_KEY_FOUND, address)
+
+            return key
 
     def get_all_keys_in_local_db(self, private=False):
         """
@@ -283,7 +309,11 @@ class KeyManager(object):
         :return: The generated key.
         :rtype: EncryptionKey
         """
-        return self._wrapper_map[ktype].gen_key(self._address)
+        signal(proto.KEYMANAGER_STARTED_KEY_GENERATION, self._address)
+        key = self._wrapper_map[ktype].gen_key(self._address)
+        signal(proto.KEYMANAGER_FINISHED_KEY_GENERATION, self._address)
+
+        return key
 
     #
     # Setters/getters
@@ -334,3 +364,94 @@ class KeyManager(object):
 
     uid = property(
         _get_uid, _set_uid, doc='The uid of the user.')
+
+    #
+    # encrypt/decrypt and sign/verify API
+    #
+
+    def encrypt(self, data, pubkey, passphrase=None, sign=None):
+        """
+        Encrypt C{data} using public @{key} and sign with C{sign} key.
+
+        :param data: The data to be encrypted.
+        :type data: str
+        :param pubkey: The key used to encrypt.
+        :type pubkey: EncryptionKey
+        :param sign: The key used for signing.
+        :type sign: EncryptionKey
+
+        :return: The encrypted data.
+        :rtype: str
+        """
+        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__].encrypt(
+            data, pubkey, passphrase, sign)
+
+    def decrypt(self, data, privkey, passphrase=None, verify=None):
+        """
+        Decrypt C{data} using private @{privkey} and verify with C{verify} key.
+
+        :param data: The data to be decrypted.
+        :type data: str
+        :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_type(privkey, EncryptionKey)
+        leap_assert(
+            privkey.__class__ in self._wrapper_map,
+            'Unknown key type.')
+        leap_assert(privkey.private is True, 'Key is not private.')
+        return self._wrapper_map[privkey.__class__].decrypt(
+            data, privkey, passphrase, verify)
+
+    def sign(self, data, privkey):
+        """
+        Sign C{data} with C{privkey}.
+
+        :param data: The data to be signed.
+        :type data: str
+
+        :param privkey: The private key to be used to sign.
+        :type privkey: EncryptionKey
+
+        :return: The signed data.
+        :rtype: str
+        """
+        leap_assert_type(privkey, EncryptionKey)
+        leap_assert(
+            privkey.__class__ in self._wrapper_map,
+            'Unknown key type.')
+        leap_assert(privkey.private is True, 'Key is not private.')
+        return self._wrapper_map[privkey.__class__].sign(data, privkey)
+
+    def verify(self, data, pubkey):
+        """
+        Verify signed C{data} with C{pubkey}.
+
+        :param data: The data to be verified.
+        :type data: str
+
+        :param pubkey: The public key to be used on verification.
+        :type pubkey: EncryptionKey
+
+        :return: The signed data.
+        :rtype: str
+        """
+        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)
+
+from ._version import get_versions
+__version__ = get_versions()['version']
+del get_versions