diff options
Diffstat (limited to 'src/leap/common/keymanager')
-rw-r--r-- | src/leap/common/keymanager/__init__.py | 103 | ||||
-rw-r--r-- | src/leap/common/keymanager/keys.py | 71 | ||||
-rw-r--r-- | src/leap/common/keymanager/openpgp.py | 20 | ||||
-rw-r--r-- | src/leap/common/keymanager/util.py | 103 |
4 files changed, 170 insertions, 127 deletions
diff --git a/src/leap/common/keymanager/__init__.py b/src/leap/common/keymanager/__init__.py index f939a4e..82fa99b 100644 --- a/src/leap/common/keymanager/__init__.py +++ b/src/leap/common/keymanager/__init__.py @@ -31,6 +31,9 @@ from leap.common.keymanager.errors import ( KeyNotFound, KeyAlreadyExists, ) +from leap.common.keymanager.keys import ( + build_key_from_dict, +) from leap.common.keymanager.openpgp import ( OpenPGPKey, OpenPGPScheme, @@ -39,6 +42,14 @@ from leap.common.keymanager.openpgp import ( from leap.common.keymanager.http import HTTPClient +TAGS_INDEX = 'by-tags' +TAGS_AND_PRIVATE_INDEX = 'by-tags-and-private' +INDEXES = { + TAGS_INDEX: ['tags'], + TAGS_AND_PRIVATE_INDEX: ['tags', 'bool(private)'], +} + + class KeyManager(object): def __init__(self, address, url, soledad): @@ -55,10 +66,45 @@ class KeyManager(object): """ self._address = address self._http_client = HTTPClient(url) + self._soledad = soledad self._wrapper_map = { OpenPGPKey: OpenPGPScheme(soledad), # other types of key will be added to this mapper. } + self._init_indexes() + + # + # utilities + # + + def _key_class_from_type(self, ktype): + """ + Return key class from string representation of key type. + """ + return filter( + lambda klass: str(klass) == ktype, + self._wrapper_map).pop() + + def _init_indexes(self): + """ + Initialize the database indexes. + """ + # Ask the database for currently existing indexes. + db_indexes = dict(self._soledad.list_indexes()) + # Loop through the indexes we expect to find. + for name, expression in INDEXES.items(): + if name not in db_indexes: + # The index does not yet exist. + self._soledad.create_index(name, *expression) + continue + if expression == db_indexes[name]: + # The index exists and is up to date. + continue + # The index exists but the definition is not what expected, so we + # delete it and add the proper index expression. + self._soledad.delete_index(name) + self._soledad.create_index(name, *expression) + def send_key(self, ktype, send_private=False, password=None): """ @@ -104,7 +150,7 @@ class KeyManager(object): json.dumps(data), headers) - def get_key(self, address, ktype, private=False): + def get_key(self, address, ktype, private=False, fetch_remote=True): """ Return a key of type C{ktype} bound to C{address}. @@ -129,14 +175,21 @@ class KeyManager(object): try: return self._wrapper_map[ktype].get_key(address, private=private) except KeyNotFound: - key = filter(lambda k: isinstance(k, ktype), - self._fetch_keys(address)) - if key is None: + if fetch_remote is False: + raise + # fetch keys from server and discard unwanted types. + keys = filter(lambda k: isinstance(k, ktype), + self.fetch_keys_from_server(address)) + if len(keys) is 0: raise KeyNotFound() - self._wrapper_map[ktype].put_key(key) + leap_assert( + len(keys) == 1, + 'Got more than one key of type %s for %s.' % + (str(ktype), address)) + self._wrapper_map[ktype].put_key(keys[0]) return key - def _fetch_keys(self, address): + def fetch_keys_from_server(self, address): """ Fetch keys bound to C{address} from nickserver. @@ -153,18 +206,42 @@ class KeyManager(object): leap_assert( keydata['address'] == address, "Fetched key for wrong address.") + keys = [] for key in keydata['keys']: - # find the key class in the mapper - keyCLass = filter( - lambda klass: str(klass) == key['type'], - self._wrapper_map).pop() - yield _build_key_from_dict(kClass, address, key) + keys.append( + build_key_from_dict( + self._key_class_from_type(key['type']), + address, + key)) + return keys + + def get_all_keys_in_local_db(self, private=False): + """ + Return all keys stored in local database. + + @return: A list with all keys in local db. + @rtype: list + """ + return map( + lambda doc: build_key_from_dict( + self._key_class_from_type(doc.content['type']), + doc.content['address'], + doc.content), + self._soledad.get_from_index( + TAGS_AND_PRIVATE_INDEX, + 'keymanager-key', + '1' if private else '0')) def refresh_keys(self): """ - Update the user's db of validated keys to see if there are changes. + Fetch keys from nickserver and update them locally. """ - raise NotImplementedError(self.refresh_keys) + addresses = set(map( + lambda doc: doc.address, + self.get_all_keys_in_local_db(False))) + for address in addresses: + for key in self.fetch_keys_from_server(address): + self._wrapper_map[key.__class__].put_key(key) def gen_key(self, ktype): """ diff --git a/src/leap/common/keymanager/keys.py b/src/leap/common/keymanager/keys.py index 250c2fa..453e0ed 100644 --- a/src/leap/common/keymanager/keys.py +++ b/src/leap/common/keymanager/keys.py @@ -25,11 +25,81 @@ try: import simplejson as json except ImportError: import json # noqa +import re +from hashlib import sha256 from abc import ABCMeta, abstractmethod +from leap.common.check import leap_assert +# +# Key handling utilities +# + +def is_address(address): + """ + Return whether the given C{address} is in the form user@provider. + + @param address: The address to be tested. + @type address: str + @return: Whether C{address} is in the form user@provider. + @rtype: bool + """ + return bool(re.match('[\w.-]+@[\w.-]+', address)) + + +def build_key_from_dict(kClass, address, kdict): + """ + Build an C{kClass} key bound to C{address} based on info in C{kdict}. + + @param address: The address bound to the key. + @type address: str + @param kdict: Dictionary with key data. + @type kdict: dict + @return: An instance of the key. + @rtype: C{kClass} + """ + leap_assert(address == kdict['address'], 'Wrong address in key data.') + return kClass( + address, + key_id=kdict['key_id'], + fingerprint=kdict['fingerprint'], + key_data=kdict['key_data'], + private=kdict['private'], + length=kdict['length'], + expiry_date=kdict['expiry_date'], + first_seen_at=kdict['first_seen_at'], + last_audited_at=kdict['last_audited_at'], + validation=kdict['validation'], # TODO: verify for validation. + ) + + +def keymanager_doc_id(ktype, address, private=False): + """ + Return the document id for the document containing a key for + C{address}. + + @param address: The type of the key. + @type address: KeyType + @param address: The address bound to the key. + @type address: str + @param private: Whether the key is private or not. + @type private: bool + @return: The document id for the document that stores a key bound to + C{address}. + @rtype: str + """ + leap_assert(is_address(address), "Wrong address format: %s" % address) + ktype = str(ktype) + visibility = 'private' if private else 'public' + return sha256('keymanager-'+address+'-'+ktype+'-'+visibility).hexdigest() + + +# +# Abstraction for encryption keys +# + class EncryptionKey(object): """ Abstract class for encryption keys. @@ -82,6 +152,7 @@ class EncryptionKey(object): 'validation': self.validation, 'first_seen_at': self.first_seen_at, 'last_audited_at': self.last_audited_at, + 'tags': ['keymanager-key'], }) diff --git a/src/leap/common/keymanager/openpgp.py b/src/leap/common/keymanager/openpgp.py index ace8c1e..fa3f732 100644 --- a/src/leap/common/keymanager/openpgp.py +++ b/src/leap/common/keymanager/openpgp.py @@ -33,13 +33,11 @@ from leap.common.keymanager.errors import ( from leap.common.keymanager.keys import ( EncryptionKey, EncryptionScheme, + is_address, + keymanager_doc_id, + build_key_from_dict, ) from leap.common.keymanager.gpg import GPGWrapper -from leap.common.keymanager.util import ( - _is_address, - _build_key_from_doc, - _keymanager_doc_id, -) # @@ -307,7 +305,7 @@ class OpenPGPScheme(EncryptionScheme): @raise KeyAlreadyExists: If key already exists in local database. """ # make sure the key does not already exist - leap_assert(_is_address(address), 'Not an user address: %s' % address) + leap_assert(is_address(address), 'Not an user address: %s' % address) try: self.get_key(address) raise KeyAlreadyExists(address) @@ -358,11 +356,11 @@ class OpenPGPScheme(EncryptionScheme): @rtype: OpenPGPKey @raise KeyNotFound: If the key was not found on local storage. """ - leap_assert(_is_address(address), 'Not an user address: %s' % address) + leap_assert(is_address(address), 'Not an user address: %s' % address) doc = self._get_key_doc(address, private) if doc is None: raise KeyNotFound(address) - return _build_key_from_doc(OpenPGPKey, address, doc) + return build_key_from_dict(OpenPGPKey, address, doc.content) def put_key_raw(self, data): """ @@ -422,7 +420,7 @@ class OpenPGPScheme(EncryptionScheme): if doc is None: self._soledad.create_doc_from_json( key.get_json(), - doc_id=_keymanager_doc_id( + doc_id=keymanager_doc_id( OpenPGPKey, key.address, key.private)) else: doc.set_json(key.get_json()) @@ -442,7 +440,7 @@ class OpenPGPScheme(EncryptionScheme): @rtype: leap.soledad.backends.leap_backend.LeapDocument """ return self._soledad.get_doc( - _keymanager_doc_id(OpenPGPKey, address, private)) + keymanager_doc_id(OpenPGPKey, address, private)) def delete_key(self, key): """ @@ -458,5 +456,5 @@ class OpenPGPScheme(EncryptionScheme): if stored_key.__dict__ != key.__dict__: raise KeyAttributesDiffer(key) doc = self._soledad.get_doc( - _keymanager_doc_id(OpenPGPKey, key.address, key.private)) + keymanager_doc_id(OpenPGPKey, key.address, key.private)) self._soledad.delete_doc(doc) diff --git a/src/leap/common/keymanager/util.py b/src/leap/common/keymanager/util.py deleted file mode 100644 index 667d2b2..0000000 --- a/src/leap/common/keymanager/util.py +++ /dev/null @@ -1,103 +0,0 @@ -# -*- coding: utf-8 -*- -# util.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - - -""" -Utilities for the Key Manager. -""" - - -import re - - -from hashlib import sha256 -from binascii import b2a_base64 - - -from leap.common.check import leap_assert - - -def _is_address(address): - """ - Return whether the given C{address} is in the form user@provider. - - @param address: The address to be tested. - @type address: str - @return: Whether C{address} is in the form user@provider. - @rtype: bool - """ - return bool(re.match('[\w.-]+@[\w.-]+', address)) - - -def _build_key_from_dict(kClass, address, kdict): - """ - Build an C{kClass} key bound to C{address} based on info in C{kdict}. - - @param address: The address bound to the key. - @type address: str - @param kdict: Dictionary with key data. - @type kdict: dict - @return: An instance of the key. - @rtype: C{kClass} - """ - return kClass( - address, - key_id=kdict['key_id'], - fingerprint=kdict['fingerprint'], - key_data=kdict['key_data'], - private=kdict['private'], - length=kdict['length'], - expiry_date=kdict['expiry_date'], - first_seen_at=kdict['first_seen_at'], - last_audited_at=kdict['last_audited_at'], - validation=kdict['validation'], # TODO: verify for validation. - ) - - -def _build_key_from_doc(kClass, address, doc): - """ - Build an C{kClass} for C{address} based on C{doc} from local storage. - - @param address: The address bound to the key. - @type address: str - @param doc: Document obtained from Soledad storage. - @type doc: leap.soledad.backends.leap_backend.LeapDocument - @return: An instance of the key. - @rtype: C{kClass} - """ - return _build_key_from_dict(kClass, address, doc.content) - - -def _keymanager_doc_id(ktype, address, private=False): - """ - Return the document id for the document containing a key for - C{address}. - - @param address: The type of the key. - @type address: KeyType - @param address: The address bound to the key. - @type address: str - @param private: Whether the key is private or not. - @type private: bool - @return: The document id for the document that stores a key bound to - C{address}. - @rtype: str - """ - leap_assert(_is_address(address), "Wrong address format: %s" % address) - ktype = str(ktype) - visibility = 'private' if private else 'public' - return sha256('key-manager-'+address+'-'+ktype+'-'+visibility).hexdigest() |