summaryrefslogtreecommitdiff
path: root/src/leap/common/keymanager
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/common/keymanager')
-rw-r--r--src/leap/common/keymanager/__init__.py103
-rw-r--r--src/leap/common/keymanager/keys.py71
-rw-r--r--src/leap/common/keymanager/openpgp.py20
-rw-r--r--src/leap/common/keymanager/util.py103
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()