summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/leap/bitmask/cli/keys.py21
-rw-r--r--src/leap/bitmask/core/dispatcher.py10
-rw-r--r--src/leap/bitmask/core/mail_services.py9
-rw-r--r--src/leap/bitmask/keymanager/__init__.py37
-rw-r--r--tests/integration/keymanager/test_keymanager.py37
-rw-r--r--ui/app/lib/bitmask.js13
6 files changed, 126 insertions, 1 deletions
diff --git a/src/leap/bitmask/cli/keys.py b/src/leap/bitmask/cli/keys.py
index 82de3868..ab73bfff 100644
--- a/src/leap/bitmask/cli/keys.py
+++ b/src/leap/bitmask/cli/keys.py
@@ -41,6 +41,7 @@ SUBCOMMANDS:
list List all known keys
export Export a given key
+ fetch Fetch key by fingerprint
insert Insert a key to the key storage
delete Delete a key from the key storage
'''.format(name=command.appname)
@@ -97,6 +98,26 @@ SUBCOMMANDS:
return self._send(self._print_key)
+ def fetch(self, raw_args):
+ parser = argparse.ArgumentParser(
+ description='Bitmask fetch key by fingerprint',
+ prog='%s %s %s' % tuple(sys.argv[:3]))
+ parser.add_argument('-u', '--userid', default='',
+ help='Select the userid of the keyring')
+ parser.add_argument('address', nargs=1,
+ help='email address to pin to the key')
+ parser.add_argument('fingerprint', nargs=1,
+ help='fingerprint to fetch of the key')
+ subargs = parser.parse_args(raw_args)
+
+ userid = subargs.userid
+ if not userid:
+ userid = self.cfg.get('bonafide', 'active', default='')
+ self.data += ['fetch', userid, subargs.address[0],
+ subargs.fingerprint[0]]
+
+ return self._send(self._print_key)
+
def insert(self, raw_args):
parser = argparse.ArgumentParser(
description='Bitmask import key',
diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py
index 91168d87..f2b3e8ca 100644
--- a/src/leap/bitmask/core/dispatcher.py
+++ b/src/leap/bitmask/core/dispatcher.py
@@ -363,6 +363,16 @@ class KeysCmd(SubCommand):
return service.do_export(uid, address, private, fetch_remote)
@register_method('dict')
+ def do_FETCH(self, service, *parts, **kw):
+ if len(parts) < 5:
+ raise ValueError("An email address is needed")
+ uid = parts[2]
+ address = parts[3]
+ fingerprint = parts[4]
+
+ return service.do_fetch(uid, address, fingerprint)
+
+ @register_method('dict')
def do_INSERT(self, service, *parts, **kw):
if len(parts) < 6:
raise ValueError("An email address is needed")
diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py
index 31c27364..23105c7a 100644
--- a/src/leap/bitmask/core/mail_services.py
+++ b/src/leap/bitmask/core/mail_services.py
@@ -422,6 +422,15 @@ class KeymanagerService(HookableService):
d.addCallback(lambda key: dict(key))
return d
+ def do_fetch(self, userid, address, fingerprint):
+ km = self._container.get_instance(userid)
+ if km is None:
+ return defer.fail(ValueError("User " + userid + " has no active "
+ "keymanager"))
+ d = km.fetch_key_fingerprint(address, fingerprint)
+ d.addCallback(lambda key: dict(key))
+ return d
+
def do_insert(self, userid, address, rawkey, validation='Fingerprint'):
km = self._container.get_instance(userid)
if km is None:
diff --git a/src/leap/bitmask/keymanager/__init__.py b/src/leap/bitmask/keymanager/__init__.py
index 45b7e582..e3ff3b87 100644
--- a/src/leap/bitmask/keymanager/__init__.py
+++ b/src/leap/bitmask/keymanager/__init__.py
@@ -261,6 +261,43 @@ class KeyManager(object):
d.addCallbacks(key_found, key_not_found)
return d
+ @defer.inlineCallbacks
+ def fetch_key_fingerprint(self, address, fingerprint):
+ """
+ Fetch a key from the key servers by fingerprint.
+
+ It will replace any key assigned to the address in the keyring and have
+ validation level Fingerprint.
+
+ :param address: The address bound to the key.
+ :type address: str
+ :param fingerprint: The fingerprint of the key to fetch.
+ :type fingerprint: str
+
+ :return: A Deferred which fires with an EncryptionKey fetched,
+ or which fails with KeyNotFound if no key was found in the
+ keyserver for this fingerprint.
+ :rtype: Deferred
+ """
+ key_data = yield self._nicknym.fetch_key_with_fingerprint(fingerprint)
+ key, _ = self._openpgp.parse_key(key_data, address)
+ key.validation = ValidationLevels.Fingerprint
+
+ if key.fingerprint != fingerprint:
+ raise keymanager_errors.KeyNotFound("Got wrong fingerprint")
+
+ try:
+ old_key = yield self._openpgp.get_key(address)
+ if old_key.fingerprint == key.fingerprint:
+ key.last_audited_at = old_key.last_audited_at
+ key.encr_used = old_key.encr_used
+ key.sign_used = old_key.sign_used
+ except keymanager_errors.KeyNotFound:
+ pass
+
+ yield self._openpgp.put_key(key)
+ defer.returnValue(key)
+
def get_all_keys(self, private=False):
"""
Return all keys stored in local database.
diff --git a/tests/integration/keymanager/test_keymanager.py b/tests/integration/keymanager/test_keymanager.py
index 981fc6ba..1e836a5d 100644
--- a/tests/integration/keymanager/test_keymanager.py
+++ b/tests/integration/keymanager/test_keymanager.py
@@ -47,6 +47,8 @@ from common import (
PUBLIC_KEY_2,
PRIVATE_KEY,
PRIVATE_KEY_2,
+ NEW_PUB_KEY,
+ OLD_AND_NEW_KEY_ADDRESS
)
@@ -211,7 +213,7 @@ class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):
pubkey = yield km.get_key(ADDRESS, fetch_remote=False)
# setup expected args
data = urllib.urlencode({
- km.PUBKEY_KEY: pubkey.key_data,
+ km._nicknym.PUBKEY_KEY: pubkey.key_data,
})
headers = {'Authorization': [str('Token token=%s' % token)]}
headers['Content-Type'] = ['application/x-www-form-urlencoded']
@@ -608,6 +610,39 @@ class KeyManagerCryptoTestCase(KeyManagerWithSoledadTestCase):
fetch_remote=False))
return self.assertFailure(d, errors.KeyNotFound)
+ @defer.inlineCallbacks
+ def test_fetch_key_fingerprint(self):
+ km = self._key_manager(user=ADDRESS_2)
+ km._nicknym.fetch_key_with_fingerprint = mock.Mock(
+ return_value=defer.succeed(PUBLIC_KEY))
+ yield km.fetch_key_fingerprint(ADDRESS, KEY_FINGERPRINT)
+ key = yield km.get_key(ADDRESS, fetch_remote=False)
+ self.assertEqual(key.fingerprint, KEY_FINGERPRINT)
+
+ def test_fetch_key_fingerprint_wrong_fp(self):
+ km = self._key_manager(user=ADDRESS_2)
+ km._nicknym.fetch_key_with_fingerprint = mock.Mock(
+ return_value=defer.succeed(NEW_PUB_KEY))
+ d = km.fetch_key_fingerprint(OLD_AND_NEW_KEY_ADDRESS, KEY_FINGERPRINT)
+ return self.assertFailure(d, errors.KeyNotFound)
+
+ @defer.inlineCallbacks
+ def test_fetch_key_fingerprint_keep_usage(self):
+ km = self._key_manager(user=ADDRESS_2)
+ key, _ = km._openpgp.parse_key(PUBLIC_KEY, ADDRESS)
+ key.sign_used = True
+ yield km.put_key(key)
+
+ km._nicknym.fetch_key_with_fingerprint = mock.Mock(
+ return_value=defer.succeed(PUBLIC_KEY))
+ yield km.fetch_key_fingerprint(ADDRESS, KEY_FINGERPRINT)
+
+ key = yield km.get_key(ADDRESS, fetch_remote=False)
+ self.assertEqual(key.fingerprint, KEY_FINGERPRINT)
+ self.assertTrue(key.sign_used)
+ self.assertFalse(key.encr_used)
+
+
if __name__ == "__main__":
import unittest
unittest.main()
diff --git a/ui/app/lib/bitmask.js b/ui/app/lib/bitmask.js
index 37d09979..31a1e938 100644
--- a/ui/app/lib/bitmask.js
+++ b/ui/app/lib/bitmask.js
@@ -428,6 +428,19 @@ var bitmask = function(){
},
/**
+ * Fetch key by fingerprint
+ *,
+ * @param {string} uid The uid of the keyring.
+ * @param {string} address The email address of the key.
+ * @param {string} fingerprint The key fingerprnit.
+ *
+ * @return {Promise<KeyObject>} The key
+ */
+ fetch: function(uid, address, fingerprint) {
+ return call(['keys', 'fetch', address, fingerprint]);
+ },
+
+ /**
* Insert key
*
* @param {string} uid The uid of the keyring.