diff options
-rw-r--r-- | keymanager/src/leap/keymanager/__init__.py | 7 | ||||
-rw-r--r-- | keymanager/src/leap/keymanager/documents.py | 101 | ||||
-rw-r--r-- | keymanager/src/leap/keymanager/keys.py | 163 | ||||
-rw-r--r-- | keymanager/src/leap/keymanager/migrator.py | 70 | ||||
-rw-r--r-- | keymanager/src/leap/keymanager/openpgp.py | 4 | ||||
-rw-r--r-- | keymanager/src/leap/keymanager/tests/test_migrator.py | 2 | ||||
-rw-r--r-- | keymanager/src/leap/keymanager/tests/test_openpgp.py | 6 |
7 files changed, 174 insertions, 179 deletions
diff --git a/keymanager/src/leap/keymanager/__init__.py b/keymanager/src/leap/keymanager/__init__.py index 32e5581..6420f9a 100644 --- a/keymanager/src/leap/keymanager/__init__.py +++ b/keymanager/src/leap/keymanager/__init__.py @@ -76,11 +76,8 @@ from leap.keymanager.errors import ( ) from leap.keymanager.validation import ValidationLevels, can_upgrade -from leap.keymanager.keys import ( - build_key_from_dict, - KEYMANAGER_KEY_TAG, - TAGS_PRIVATE_INDEX, -) +from leap.keymanager.keys import build_key_from_dict +from leap.keymanager.documents import KEYMANAGER_KEY_TAG, TAGS_PRIVATE_INDEX from leap.keymanager.openpgp import OpenPGPScheme __version__ = get_versions()['version'] diff --git a/keymanager/src/leap/keymanager/documents.py b/keymanager/src/leap/keymanager/documents.py new file mode 100644 index 0000000..2ed5376 --- /dev/null +++ b/keymanager/src/leap/keymanager/documents.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# documents.py +# Copyright (C) 2013-2016 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/>. +""" +Soledad documents +""" +from twisted.internet import defer +from leap.common.check import leap_assert + +# +# Dictionary keys used for storing cryptographic keys. +# + +KEY_VERSION_KEY = 'version' +KEY_UIDS_KEY = 'uids' +KEY_ADDRESS_KEY = 'address' +KEY_TYPE_KEY = 'type' +KEY_FINGERPRINT_KEY = 'fingerprint' +KEY_DATA_KEY = 'key_data' +KEY_PRIVATE_KEY = 'private' +KEY_LENGTH_KEY = 'length' +KEY_EXPIRY_DATE_KEY = 'expiry_date' +KEY_LAST_AUDITED_AT_KEY = 'last_audited_at' +KEY_REFRESHED_AT_KEY = 'refreshed_at' +KEY_VALIDATION_KEY = 'validation' +KEY_ENCR_USED_KEY = 'encr_used' +KEY_SIGN_USED_KEY = 'sign_used' +KEY_TAGS_KEY = 'tags' + + +# +# Key storage constants +# + +KEYMANAGER_KEY_TAG = 'keymanager-key' +KEYMANAGER_ACTIVE_TAG = 'keymanager-active' +KEYMANAGER_ACTIVE_TYPE = '-active' + +# Version of the Soledad Document schema, +# it should be bumped each time the document format changes +KEYMANAGER_DOC_VERSION = 1 + + +# +# key indexing constants. +# + +TAGS_PRIVATE_INDEX = 'by-tags-private' +TYPE_FINGERPRINT_PRIVATE_INDEX = 'by-type-fingerprint-private' +TYPE_ADDRESS_PRIVATE_INDEX = 'by-type-address-private' +INDEXES = { + TAGS_PRIVATE_INDEX: [ + KEY_TAGS_KEY, + 'bool(%s)' % KEY_PRIVATE_KEY, + ], + TYPE_FINGERPRINT_PRIVATE_INDEX: [ + KEY_TYPE_KEY, + KEY_FINGERPRINT_KEY, + 'bool(%s)' % KEY_PRIVATE_KEY, + ], + TYPE_ADDRESS_PRIVATE_INDEX: [ + KEY_TYPE_KEY, + KEY_ADDRESS_KEY, + 'bool(%s)' % KEY_PRIVATE_KEY, + ] +} + + +@defer.inlineCallbacks +def init_indexes(soledad): + """ + Initialize the database indexes. + """ + leap_assert(soledad is not None, + "Cannot init indexes with null soledad") + + indexes = yield soledad.list_indexes() + db_indexes = dict(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. + yield soledad.create_index(name, *expression) + elif expression != db_indexes[name]: + # The index exists but the definition is not what expected, + # so we delete it and add the proper index expression. + yield soledad.delete_index(name) + yield soledad.create_index(name, *expression) diff --git a/keymanager/src/leap/keymanager/keys.py b/keymanager/src/leap/keymanager/keys.py index 296ed86..c0ee21b 100644 --- a/keymanager/src/leap/keymanager/keys.py +++ b/keymanager/src/leap/keymanager/keys.py @@ -27,78 +27,17 @@ import logging import re import time - from datetime import datetime -from leap.common.check import leap_assert -from twisted.internet import defer from leap.keymanager import errors from leap.keymanager.wrapper import TempGPGWrapper from leap.keymanager.validation import ValidationLevels +from leap.keymanager import documents as doc logger = logging.getLogger(__name__) # -# Dictionary keys used for storing cryptographic keys. -# - -KEY_VERSION_KEY = 'version' -KEY_UIDS_KEY = 'uids' -KEY_ADDRESS_KEY = 'address' -KEY_TYPE_KEY = 'type' -KEY_FINGERPRINT_KEY = 'fingerprint' -KEY_DATA_KEY = 'key_data' -KEY_PRIVATE_KEY = 'private' -KEY_LENGTH_KEY = 'length' -KEY_EXPIRY_DATE_KEY = 'expiry_date' -KEY_LAST_AUDITED_AT_KEY = 'last_audited_at' -KEY_REFRESHED_AT_KEY = 'refreshed_at' -KEY_VALIDATION_KEY = 'validation' -KEY_ENCR_USED_KEY = 'encr_used' -KEY_SIGN_USED_KEY = 'sign_used' -KEY_TAGS_KEY = 'tags' - - -# -# Key storage constants -# - -KEYMANAGER_KEY_TAG = 'keymanager-key' -KEYMANAGER_ACTIVE_TAG = 'keymanager-active' -KEYMANAGER_ACTIVE_TYPE = '-active' - -# Version of the Soledad Document schema, -# it should be bumped each time the document format changes -KEYMANAGER_DOC_VERSION = 1 - - -# -# key indexing constants. -# - -TAGS_PRIVATE_INDEX = 'by-tags-private' -TYPE_FINGERPRINT_PRIVATE_INDEX = 'by-type-fingerprint-private' -TYPE_ADDRESS_PRIVATE_INDEX = 'by-type-address-private' -INDEXES = { - TAGS_PRIVATE_INDEX: [ - KEY_TAGS_KEY, - 'bool(%s)' % KEY_PRIVATE_KEY, - ], - TYPE_FINGERPRINT_PRIVATE_INDEX: [ - KEY_TYPE_KEY, - KEY_FINGERPRINT_KEY, - 'bool(%s)' % KEY_PRIVATE_KEY, - ], - TYPE_ADDRESS_PRIVATE_INDEX: [ - KEY_TYPE_KEY, - KEY_ADDRESS_KEY, - 'bool(%s)' % KEY_PRIVATE_KEY, - ] -} - - -# # Key handling utilities # @@ -132,27 +71,27 @@ def build_key_from_dict(key, active=None): sign_used = False if active: - address = active[KEY_ADDRESS_KEY] + address = active[doc.KEY_ADDRESS_KEY] try: - validation = ValidationLevels.get(active[KEY_VALIDATION_KEY]) + validation = ValidationLevels.get(active[doc.KEY_VALIDATION_KEY]) except ValueError: logger.error("Not valid validation level (%s) for key %s", - (active[KEY_VALIDATION_KEY], - active[KEY_FINGERPRINT_KEY])) - last_audited_at = _to_datetime(active[KEY_LAST_AUDITED_AT_KEY]) - encr_used = active[KEY_ENCR_USED_KEY] - sign_used = active[KEY_SIGN_USED_KEY] + (active[doc.KEY_VALIDATION_KEY], + active[doc.KEY_FINGERPRINT_KEY])) + last_audited_at = _to_datetime(active[doc.KEY_LAST_AUDITED_AT_KEY]) + encr_used = active[doc.KEY_ENCR_USED_KEY] + sign_used = active[doc.KEY_SIGN_USED_KEY] - expiry_date = _to_datetime(key[KEY_EXPIRY_DATE_KEY]) - refreshed_at = _to_datetime(key[KEY_REFRESHED_AT_KEY]) + expiry_date = _to_datetime(key[doc.KEY_EXPIRY_DATE_KEY]) + refreshed_at = _to_datetime(key[doc.KEY_REFRESHED_AT_KEY]) return OpenPGPKey( address=address, - uids=key[KEY_UIDS_KEY], - fingerprint=key[KEY_FINGERPRINT_KEY], - key_data=key[KEY_DATA_KEY], - private=key[KEY_PRIVATE_KEY], - length=key[KEY_LENGTH_KEY], + uids=key[doc.KEY_UIDS_KEY], + fingerprint=key[doc.KEY_FINGERPRINT_KEY], + key_data=key[doc.KEY_DATA_KEY], + private=key[doc.KEY_PRIVATE_KEY], + length=key[doc.KEY_LENGTH_KEY], expiry_date=expiry_date, last_audited_at=last_audited_at, refreshed_at=refreshed_at, @@ -271,16 +210,16 @@ class OpenPGPKey(object): refreshed_at = _to_unix_time(self.refreshed_at) return json.dumps({ - KEY_UIDS_KEY: self.uids, - KEY_TYPE_KEY: self.__class__.__name__, - KEY_FINGERPRINT_KEY: self.fingerprint, - KEY_DATA_KEY: self.key_data, - KEY_PRIVATE_KEY: self.private, - KEY_LENGTH_KEY: self.length, - KEY_EXPIRY_DATE_KEY: expiry_date, - KEY_REFRESHED_AT_KEY: refreshed_at, - KEY_VERSION_KEY: KEYMANAGER_DOC_VERSION, - KEY_TAGS_KEY: [KEYMANAGER_KEY_TAG], + doc.KEY_UIDS_KEY: self.uids, + doc.KEY_TYPE_KEY: self.__class__.__name__, + doc.KEY_FINGERPRINT_KEY: self.fingerprint, + doc.KEY_DATA_KEY: self.key_data, + doc.KEY_PRIVATE_KEY: self.private, + doc.KEY_LENGTH_KEY: self.length, + doc.KEY_EXPIRY_DATE_KEY: expiry_date, + doc.KEY_REFRESHED_AT_KEY: refreshed_at, + doc.KEY_VERSION_KEY: doc.KEYMANAGER_DOC_VERSION, + doc.KEY_TAGS_KEY: [doc.KEYMANAGER_KEY_TAG], }) def get_active_json(self): @@ -293,16 +232,17 @@ class OpenPGPKey(object): last_audited_at = _to_unix_time(self.last_audited_at) return json.dumps({ - KEY_ADDRESS_KEY: self.address, - KEY_TYPE_KEY: self.__class__.__name__ + KEYMANAGER_ACTIVE_TYPE, - KEY_FINGERPRINT_KEY: self.fingerprint, - KEY_PRIVATE_KEY: self.private, - KEY_VALIDATION_KEY: str(self.validation), - KEY_LAST_AUDITED_AT_KEY: last_audited_at, - KEY_ENCR_USED_KEY: self.encr_used, - KEY_SIGN_USED_KEY: self.sign_used, - KEY_VERSION_KEY: KEYMANAGER_DOC_VERSION, - KEY_TAGS_KEY: [KEYMANAGER_ACTIVE_TAG], + doc.KEY_ADDRESS_KEY: self.address, + doc.KEY_TYPE_KEY: (self.__class__.__name__ + + doc.KEYMANAGER_ACTIVE_TYPE), + doc.KEY_FINGERPRINT_KEY: self.fingerprint, + doc.KEY_PRIVATE_KEY: self.private, + doc.KEY_VALIDATION_KEY: str(self.validation), + doc.KEY_LAST_AUDITED_AT_KEY: last_audited_at, + doc.KEY_ENCR_USED_KEY: self.encr_used, + doc.KEY_SIGN_USED_KEY: self.sign_used, + doc.KEY_VERSION_KEY: doc.KEYMANAGER_DOC_VERSION, + doc.KEY_TAGS_KEY: [doc.KEYMANAGER_ACTIVE_TAG], }) def next(self): @@ -351,34 +291,3 @@ def parse_address(address): if match is None: return None return ''.join(match.group(2, 4)) - - -def init_indexes(soledad): - """ - Initialize the database indexes. - """ - leap_assert(soledad is not None, - "Cannot init indexes with null soledad") - - def create_idexes(indexes): - deferreds = [] - db_indexes = dict(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. - d = soledad.create_index(name, *expression) - deferreds.append(d) - elif expression != db_indexes[name]: - # The index exists but the definition is not what expected, - # so we delete it and add the proper index expression. - d = soledad.delete_index(name) - d.addCallback( - lambda _: - soledad.create_index(name, *expression)) - deferreds.append(d) - return defer.gatherResults(deferreds, consumeErrors=True) - - d = soledad.list_indexes() - d.addCallback(create_idexes) - return d diff --git a/keymanager/src/leap/keymanager/migrator.py b/keymanager/src/leap/keymanager/migrator.py index 9e4ae77..c73da2e 100644 --- a/keymanager/src/leap/keymanager/migrator.py +++ b/keymanager/src/leap/keymanager/migrator.py @@ -26,21 +26,7 @@ Document migrator from collections import namedtuple from twisted.internet.defer import gatherResults, succeed -from leap.keymanager.keys import ( - TAGS_PRIVATE_INDEX, - KEYMANAGER_KEY_TAG, - KEYMANAGER_ACTIVE_TAG, - - KEYMANAGER_DOC_VERSION, - KEY_ADDRESS_KEY, - KEY_UIDS_KEY, - KEY_VERSION_KEY, - KEY_FINGERPRINT_KEY, - KEY_VALIDATION_KEY, - KEY_LAST_AUDITED_AT_KEY, - KEY_ENCR_USED_KEY, - KEY_SIGN_USED_KEY, -) +from leap.keymanager import documents as doc from leap.keymanager.validation import ValidationLevels @@ -70,12 +56,12 @@ class KeyDocumentsMigrator(object): private_value = '1' if private else '0' deferred_keys = self._soledad.get_from_index( - TAGS_PRIVATE_INDEX, - KEYMANAGER_KEY_TAG, + doc.TAGS_PRIVATE_INDEX, + doc.KEYMANAGER_KEY_TAG, private_value) deferred_active = self._soledad.get_from_index( - TAGS_PRIVATE_INDEX, - KEYMANAGER_ACTIVE_TAG, + doc.TAGS_PRIVATE_INDEX, + doc.KEYMANAGER_ACTIVE_TAG, private_value) return gatherResults([deferred_keys, deferred_active]) @@ -99,7 +85,7 @@ class KeyDocumentsMigrator(object): def _buildKeyDict(self, keys, actives): keydict = { - fp2id(key.content[KEY_FINGERPRINT_KEY]): KeyDocs(key, []) + fp2id(key.content[doc.KEY_FINGERPRINT_KEY]): KeyDocs(key, []) for key in keys} deferreds = [] @@ -119,7 +105,7 @@ class KeyDocumentsMigrator(object): def _filter_outdated(self, keydict): outdated = {} for key_id, docs in keydict.items(): - if ((docs.key and KEY_VERSION_KEY not in docs.key.content) or + if ((docs.key and doc.KEY_VERSION_KEY not in docs.key.content) or docs.active): outdated[key_id] = docs return outdated @@ -136,43 +122,43 @@ class KeyDocumentsMigrator(object): last_audited = 0 encr_used = False sign_used = False - fingerprint = key.content[KEY_FINGERPRINT_KEY] - if len(actives) == 1 and KEY_VERSION_KEY not in key.content: + fingerprint = key.content[doc.KEY_FINGERPRINT_KEY] + if len(actives) == 1 and doc.KEY_VERSION_KEY not in key.content: # we can preserve the validation of the key if there is only one # active address for the key - validation = key.content[KEY_VALIDATION_KEY] - last_audited = key.content[KEY_LAST_AUDITED_AT_KEY] - encr_used = key.content[KEY_ENCR_USED_KEY] - sign_used = key.content[KEY_SIGN_USED_KEY] + validation = key.content[doc.KEY_VALIDATION_KEY] + last_audited = key.content[doc.KEY_LAST_AUDITED_AT_KEY] + encr_used = key.content[doc.KEY_ENCR_USED_KEY] + sign_used = key.content[doc.KEY_SIGN_USED_KEY] deferreds = [] for active in actives: - if KEY_VERSION_KEY in active.content: + if doc.KEY_VERSION_KEY in active.content: continue - active.content[KEY_VERSION_KEY] = KEYMANAGER_DOC_VERSION - active.content[KEY_FINGERPRINT_KEY] = fingerprint - active.content[KEY_VALIDATION_KEY] = validation - active.content[KEY_LAST_AUDITED_AT_KEY] = last_audited - active.content[KEY_ENCR_USED_KEY] = encr_used - active.content[KEY_SIGN_USED_KEY] = sign_used + active.content[doc.KEY_VERSION_KEY] = doc.KEYMANAGER_DOC_VERSION + active.content[doc.KEY_FINGERPRINT_KEY] = fingerprint + active.content[doc.KEY_VALIDATION_KEY] = validation + active.content[doc.KEY_LAST_AUDITED_AT_KEY] = last_audited + active.content[doc.KEY_ENCR_USED_KEY] = encr_used + active.content[doc.KEY_SIGN_USED_KEY] = sign_used del active.content[KEY_ID_KEY] d = self._soledad.put_doc(active) deferreds.append(d) return gatherResults(deferreds) def _migrate_key(self, key): - if not key or KEY_VERSION_KEY in key.content: + if not key or doc.KEY_VERSION_KEY in key.content: return succeed(None) - key.content[KEY_VERSION_KEY] = KEYMANAGER_DOC_VERSION - key.content[KEY_UIDS_KEY] = key.content[KEY_ADDRESS_KEY] - del key.content[KEY_ADDRESS_KEY] + key.content[doc.KEY_VERSION_KEY] = doc.KEYMANAGER_DOC_VERSION + key.content[doc.KEY_UIDS_KEY] = key.content[doc.KEY_ADDRESS_KEY] + del key.content[doc.KEY_ADDRESS_KEY] del key.content[KEY_ID_KEY] - del key.content[KEY_VALIDATION_KEY] - del key.content[KEY_LAST_AUDITED_AT_KEY] - del key.content[KEY_ENCR_USED_KEY] - del key.content[KEY_SIGN_USED_KEY] + del key.content[doc.KEY_VALIDATION_KEY] + del key.content[doc.KEY_LAST_AUDITED_AT_KEY] + del key.content[doc.KEY_ENCR_USED_KEY] + del key.content[doc.KEY_SIGN_USED_KEY] return self._soledad.put_doc(key) diff --git a/keymanager/src/leap/keymanager/openpgp.py b/keymanager/src/leap/keymanager/openpgp.py index fae5600..08b69f7 100644 --- a/keymanager/src/leap/keymanager/openpgp.py +++ b/keymanager/src/leap/keymanager/openpgp.py @@ -36,10 +36,12 @@ from leap.keymanager import errors from leap.keymanager.wrapper import TempGPGWrapper from leap.keymanager.keys import ( OpenPGPKey, - init_indexes, is_address, parse_address, build_key_from_dict, +) +from leap.keymanager.documents import ( + init_indexes, TYPE_FINGERPRINT_PRIVATE_INDEX, TYPE_ADDRESS_PRIVATE_INDEX, KEY_UIDS_KEY, diff --git a/keymanager/src/leap/keymanager/tests/test_migrator.py b/keymanager/src/leap/keymanager/tests/test_migrator.py index 2d9782b..76323e5 100644 --- a/keymanager/src/leap/keymanager/tests/test_migrator.py +++ b/keymanager/src/leap/keymanager/tests/test_migrator.py @@ -26,7 +26,7 @@ from mock import Mock from twisted.internet.defer import succeed, inlineCallbacks from leap.keymanager.migrator import KeyDocumentsMigrator, KEY_ID_KEY -from leap.keymanager.keys import ( +from leap.keymanager.documents import ( TAGS_PRIVATE_INDEX, KEYMANAGER_ACTIVE_TAG, KEYMANAGER_KEY_TAG, diff --git a/keymanager/src/leap/keymanager/tests/test_openpgp.py b/keymanager/src/leap/keymanager/tests/test_openpgp.py index 0e39dab..47e9005 100644 --- a/keymanager/src/leap/keymanager/tests/test_openpgp.py +++ b/keymanager/src/leap/keymanager/tests/test_openpgp.py @@ -29,7 +29,7 @@ from leap.keymanager import ( KeyNotFound, openpgp, ) -from leap.keymanager.keys import ( +from leap.keymanager.documents import ( TYPE_FINGERPRINT_PRIVATE_INDEX, TYPE_ADDRESS_PRIVATE_INDEX, ) @@ -57,7 +57,7 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase): self._soledad, gpgbinary=self.gpg_binary_path) yield self._assert_key_not_found(pgp, 'user@leap.se') key = yield pgp.gen_key('user@leap.se') - self.assertIsInstance(key, openpgp.OpenPGPKey) + self.assertIsInstance(key, OpenPGPKey) self.assertEqual( ['user@leap.se'], key.address, 'Wrong address bound to key.') self.assertEqual( @@ -80,7 +80,7 @@ class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase): yield self._assert_key_not_found(pgp, ADDRESS) yield pgp.put_raw_key(PUBLIC_KEY, ADDRESS) key = yield pgp.get_key(ADDRESS, private=False) - self.assertIsInstance(key, openpgp.OpenPGPKey) + self.assertIsInstance(key, OpenPGPKey) self.assertTrue( ADDRESS in key.address, 'Wrong address bound to key.') self.assertEqual( |