summaryrefslogtreecommitdiff
path: root/src/leap/keymanager/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/keymanager/__init__.py')
-rw-r--r--src/leap/keymanager/__init__.py198
1 files changed, 45 insertions, 153 deletions
diff --git a/src/leap/keymanager/__init__.py b/src/leap/keymanager/__init__.py
index 0b8a5b3..24f37a9 100644
--- a/src/leap/keymanager/__init__.py
+++ b/src/leap/keymanager/__init__.py
@@ -22,13 +22,9 @@ import fileinput
import os
import sys
import tempfile
-import json
-import urllib
from leap.common import ca_bundle
-from twisted.web import client
-from twisted.web._responses import NOT_FOUND
-
+from leap.keymanager.refresher import RandomRefreshPublicKey
from ._version import get_versions
try:
@@ -63,10 +59,9 @@ import logging
from twisted.internet import defer
from urlparse import urlparse
-from leap.common.check import leap_assert
from leap.common.http import HTTPClient
from leap.common.events import emit_async, catalog
-from leap.common.decorators import memoized_method
+from leap.keymanager.nicknym import Nicknym
from leap.keymanager.errors import (
KeyNotFound,
@@ -132,7 +127,8 @@ class KeyManager(object):
self._openpgp = OpenPGPScheme(soledad, gpgbinary=gpgbinary)
self._combined_ca_bundle = self._create_combined_bundle_file()
self._async_client = HTTPClient(self._combined_ca_bundle)
- self._async_client_pinned = HTTPClient(self._ca_cert_path)
+ self._nicknym = Nicknym(self._nickserver_uri, self._ca_cert_path, self._token)
+ self.refresher = None
#
# destructor
@@ -151,6 +147,13 @@ class KeyManager(object):
# utilities
#
+ def start_refresher(self):
+ self.refresher = RandomRefreshPublicKey(self._openpgp, self)
+ self.refresher.start()
+
+ def stop_refresher(self):
+ self.refresher.stop()
+
def _create_combined_bundle_file(self):
leap_ca_bundle = ca_bundle.where()
@@ -170,65 +173,6 @@ class KeyManager(object):
return tmp_file.name
@defer.inlineCallbacks
- def _get_key_from_nicknym(self, address):
- """
- Send a GET request to C{uri} containing C{data}.
-
- :param address: The URI of the request.
- :type address: str
-
- :return: A deferred that will be fired with GET content as json (dict)
- :rtype: Deferred
- """
- try:
- uri = self._nickserver_uri + '?address=' + address
- content = yield self._fetch_and_handle_404_from_nicknym(uri, address)
- json_content = json.loads(content)
-
- except KeyNotFound:
- raise
- except IOError as e:
- logger.warning("HTTP error retrieving key: %r" % (e,))
- logger.warning("%s" % (content,))
- raise KeyNotFound(e.message), None, sys.exc_info()[2]
- except ValueError as v:
- logger.warning("Invalid JSON data from key: %s" % (uri,))
- raise KeyNotFound(v.message + ' - ' + uri), None, sys.exc_info()[2]
-
- except Exception as e:
- logger.warning("Error retrieving key: %r" % (e,))
- raise KeyNotFound(e.message), None, sys.exc_info()[2]
- # Responses are now text/plain, although it's json anyway, but
- # this will fail when it shouldn't
- # leap_assert(
- # res.headers['content-type'].startswith('application/json'),
- # 'Content-type is not JSON.')
- defer.returnValue(json_content)
-
- def _fetch_and_handle_404_from_nicknym(self, uri, address):
- """
- Send a GET request to C{uri} containing C{data}.
-
- :param uri: The URI of the request.
- :type uri: str
- :param address: The email corresponding to the key.
- :type address: str
-
- :return: A deferred that will be fired with GET content as json (dict)
- :rtype: Deferred
- """
- def check_404(response):
- if response.code == NOT_FOUND:
- message = '%s: %s key not found.' % (response.code, address)
- logger.warning(message)
- raise KeyNotFound(message), None, sys.exc_info()[2]
- return response
-
- d = self._async_client_pinned.request(str(uri), 'GET', callback=check_404)
- d.addCallback(client.readBody)
- return d
-
- @defer.inlineCallbacks
def _get_with_combined_ca_bundle(self, uri, data=None):
"""
Send a GET request to C{uri} containing C{data}.
@@ -253,76 +197,6 @@ class KeyManager(object):
raise KeyNotFound(uri)
defer.returnValue(content)
- @defer.inlineCallbacks
- def _put(self, uri, data=None):
- """
- Send a PUT request to C{uri} containing C{data}.
-
- The request will be sent using the configured CA certificate path to
- verify the server certificate and the configured session id for
- authentication.
-
- :param uri: The URI of the request.
- :type uri: str
- :param data: The body of the request.
- :type data: dict, str or file
-
- :return: A deferred that will be fired when PUT request finishes
- :rtype: Deferred
- """
- leap_assert(
- self._token is not None,
- 'We need a token to interact with webapp!')
- if type(data) == dict:
- data = urllib.urlencode(data)
- headers = {'Authorization': [str('Token token=%s' % self._token)]}
- headers['Content-Type'] = ['application/x-www-form-urlencoded']
- try:
- res = yield self._async_client_pinned.request(str(uri), 'PUT',
- body=str(data),
- headers=headers)
- except Exception as e:
- logger.warning("Error uploading key: %r" % (e,))
- raise e
- if 'error' in res:
- # FIXME: That's a workaround for 500,
- # we need to implement a readBody to assert response code
- logger.warning("Error uploading key: %r" % (res,))
- raise Exception(res)
-
- @memoized_method(invalidation=300)
- @defer.inlineCallbacks
- def _fetch_keys_from_server(self, address):
- """
- Fetch keys bound to address from nickserver and insert them in
- local database.
-
- :param address: The address bound to the keys.
- :type address: str
-
- :return: A Deferred which fires when the key is in the storage,
- or which fails with KeyNotFound if the key was not found on
- nickserver.
- :rtype: Deferred
-
- """
- # request keys from the nickserver
- server_keys = yield self._get_key_from_nicknym(address)
-
- # insert keys in local database
- if self.OPENPGP_KEY in server_keys:
- # nicknym server is authoritative for its own domain,
- # for other domains the key might come from key servers.
- validation_level = ValidationLevels.Weak_Chain
- _, domain = _split_email(address)
- if (domain == _get_domain(self._nickserver_uri)):
- validation_level = ValidationLevels.Provider_Trust
-
- yield self.put_raw_key(
- server_keys['openpgp'],
- address=address,
- validation=validation_level)
-
#
# key management
#
@@ -341,14 +215,7 @@ class KeyManager(object):
:raise UnsupportedKeyTypeError: if invalid key type
"""
def send(pubkey):
- data = {
- self.PUBKEY_KEY: pubkey.key_data
- }
- uri = "%s/%s/users/%s.json" % (
- self._api_uri,
- self._api_version,
- self._uid)
- d = self._put(uri, data)
+ d = self._nicknym.put_key(self.uid, pubkey.key_data, self._api_uri, self._api_version)
d.addCallback(lambda _:
emit_async(catalog.KEYMANAGER_DONE_UPLOADING_KEYS,
self._address))
@@ -359,6 +226,36 @@ class KeyManager(object):
d.addCallback(send)
return d
+ @defer.inlineCallbacks
+ def _fetch_keys_from_server_and_store_local(self, address):
+ """
+ Fetch keys from nickserver and insert them in locale database.
+
+ :param address: The address bound to the keys.
+ :type address: str
+
+ :return: A Deferred which fires when the key is in the storage,
+ or which fails with KeyNotFound if the key was not found on
+ nickserver.
+ :rtype: Deferred
+
+ """
+ server_keys = yield self._nicknym.fetch_key_with_address(address)
+
+ # insert keys in local database
+ if self.OPENPGP_KEY in server_keys:
+ # nicknym server is authoritative for its own domain,
+ # for other domains the key might come from key servers.
+ validation_level = ValidationLevels.Weak_Chain
+ _, domain = _split_email(address)
+ if (domain == _get_domain(self._nickserver_uri)):
+ validation_level = ValidationLevels.Provider_Trust
+
+ yield self.put_raw_key(
+ server_keys['openpgp'],
+ address=address,
+ validation=validation_level)
+
def get_key(self, address, private=False, fetch_remote=True):
"""
Return a key bound to address.
@@ -402,7 +299,7 @@ class KeyManager(object):
return failure
emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address)
- d = self._fetch_keys_from_server(address)
+ d = self._fetch_keys_from_server_and_store_local(address)
d.addCallback(
lambda _: self._openpgp.get_key(address, private=False))
d.addCallback(key_found)
@@ -434,7 +331,6 @@ class KeyManager(object):
:raise UnsupportedKeyTypeError: if invalid key type
"""
-
def signal_finished(key):
emit_async(
catalog.KEYMANAGER_FINISHED_KEY_GENERATION, self._address)
@@ -674,7 +570,6 @@ class KeyManager(object):
:raise UnsupportedKeyTypeError: if invalid key type
"""
-
def verify(pubkey):
signed = self._openpgp.verify(
data, pubkey, detached_sig=detached_sig)
@@ -724,7 +619,6 @@ class KeyManager(object):
:raise UnsupportedKeyTypeError: if invalid key type
"""
-
def old_key_not_found(failure):
if failure.check(KeyNotFound):
return None
@@ -801,12 +695,10 @@ class KeyManager(object):
:raise UnsupportedKeyTypeError: if invalid key type
"""
-
logger.info("Fetch key for %s from %s" % (address, uri))
- ascii_content = yield self._get_with_combined_ca_bundle(uri)
+ key_content = yield self._get_with_combined_ca_bundle(uri)
- # XXX parse binary keys
- pubkey, _ = self._openpgp.parse_key(ascii_content, address)
+ pubkey, _ = self._openpgp.parse_key(key_content, address)
if pubkey is None:
raise KeyNotFound(uri)