From 8f1fe8dd4a54fd2bdda2fc78c339ce9b3d0fc331 Mon Sep 17 00:00:00 2001 From: Zara Gebru Date: Fri, 8 Jul 2016 11:55:55 +0200 Subject: [feature] keymanager - background update keys - refresh random key in random time - add get key by fingerprint - refactor nicknym methods to own file - tests - note this do not include a check for revoked key, since that need some changes in gnupg - Related: #6089 --- src/leap/keymanager/refresher.py | 132 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/leap/keymanager/refresher.py (limited to 'src/leap/keymanager/refresher.py') diff --git a/src/leap/keymanager/refresher.py b/src/leap/keymanager/refresher.py new file mode 100644 index 0000000..cd9db28 --- /dev/null +++ b/src/leap/keymanager/refresher.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# refresher.py +# Copyright (C) 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 . + + +""" +A service which continuous refreshes the (public) key directories randomly in a random time interval. +""" + +import logging + +from twisted.internet.task import LoopingCall +from twisted.internet import defer +from random import choice, randrange + +DEBUG_STOP_REFRESH = "Stop to refresh the key directory ..." +DEBUG_START_REFRESH = "Start to refresh the key directory ..." +ERROR_UNEQUAL_FINGERPRINTS = "[WARNING] Your provider might be cheat on you, " \ + "and gave a wrong key back. Fingerprints are unequal, old %s new %s " + +MIN_RANDOM_INTERVAL_RANGE = 4 * 60 # four minutes +MAX_RANDOM_INTERVAL_RANGE = 6 * 60 # six minutes + +logger = logging.getLogger(__name__) + + +class RandomRefreshPublicKey(object): + + def __init__(self, openpgp, keymanager): + """ + Initialize the RandomRefreshPublicKey. + :param openpgp: Openpgp object. + :param keymanager: The key manager. + """ + self._openpgp = openpgp + self._keymanger = keymanager + self._loop = LoopingCall(self._refresh_continuous) + self._loop.interval = self._random_interval_to_refersh() + + def start(self): + """ + Start the looping call with random interval + [MIN_RANDOM_INTERVAL_RANGE, MAX_RANDOM_INTERVAL_RANGE] + :return: LoopingCall to start the service. + :rtype: A deferred. + """ + self._loop.start(self._random_interval_to_refersh(), False) + logger.debug(DEBUG_START_REFRESH) + + def stop(self): + """ + Stop the looping call with random interval. + """ + self._loop.stop() + logger.debug(DEBUG_STOP_REFRESH) + + @defer.inlineCallbacks + def _get_random_key(self): + """ + Get a random key of all the keys in a users key doc. + :return: A random key. + :rtype: A deferred. + """ + # TODO maybe make a check first if key is active and get another one then. + keys = yield self._openpgp.get_all_keys() + defer.returnValue(None if keys is None or keys == [] else choice(keys)) + + @defer.inlineCallbacks + def _refresh_continuous(self): + """ + The LoopingCall to refresh the key doc continuously. + """ + self._loop.interval = self._random_interval_to_refersh() + yield self.maybe_refresh_key() + + @defer.inlineCallbacks + def _maybe_unactivate_key(self, key): + """ + Unactivate a given key. + :param key: The key to be unactivated. + """ + if key.is_expired() and key.is_active(): # TODO or is_revoked + yield self._openpgp.unactivate_key(key.address) + key.set_unactive() + + @defer.inlineCallbacks + def maybe_refresh_key(self): + """ + Get key from nicknym and try to refresh. + """ + old_key = yield self._get_random_key() + + if old_key is None: + defer.returnValue(None) + + old_updated_key = yield self._keymanger._nicknym.\ + fetch_key_with_fingerprint(old_key.fingerprint) + + if old_updated_key.fingerprint != old_key.fingerprint: + logger.error(ERROR_UNEQUAL_FINGERPRINTS % + (old_key.fingerprint, old_updated_key.fingerprint)) + defer.returnValue(None) + + yield self._maybe_unactivate_key(old_updated_key) + yield self._openpgp.put_key(old_updated_key) + + # No new fetch by address needed, bc that will happen before sending an email + # could be discussed since fetching before sending an email leaks information. + + def _random_interval_to_refersh(self): + """ + Random interval. + :return: A number in this interval. + """ + return randrange(MIN_RANDOM_INTERVAL_RANGE, MAX_RANDOM_INTERVAL_RANGE) + + + + -- cgit v1.2.3