diff options
author | Zara Gebru <zgebru@thoughtworks.com> | 2016-07-08 11:55:55 +0200 |
---|---|---|
committer | Kali Kaneko (leap communications) <kali@leap.se> | 2016-11-22 15:57:03 +0100 |
commit | e5717e853af7d2f91ac69e66c1b2ee058289e78d (patch) | |
tree | 1fd7058206dd934a8ad0444c0f40a82e3d95dd09 /src/leap/bitmask/keymanager/refresher.py | |
parent | f02921a627e9ea0e6524e4b8e7744806e654a733 (diff) |
[feature] keymanager: background update keys
Port of the original commit:
8f1fe8dd4a54fd2bdda2fc78c339ce9b3d0fc331
by Zara Gebru that introduced updating keys in the background.
This was made in the legacy leapcode/keymanager repo, but was lost in
the merge to the unified bitmask-dev.
Original commit message follows:
--------------------------------
- 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
Diffstat (limited to 'src/leap/bitmask/keymanager/refresher.py')
-rw-r--r-- | src/leap/bitmask/keymanager/refresher.py | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/leap/bitmask/keymanager/refresher.py b/src/leap/bitmask/keymanager/refresher.py new file mode 100644 index 0000000..cd9db28 --- /dev/null +++ b/src/leap/bitmask/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 <http://www.gnu.org/licenses/>. + + +""" +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) + + + + |