summaryrefslogtreecommitdiff
path: root/src/leap/keymanager/refresher.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/keymanager/refresher.py')
-rw-r--r--src/leap/keymanager/refresher.py132
1 files changed, 132 insertions, 0 deletions
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 <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)
+
+
+
+