From af6f9900647c36224130270dab385d323ff7534d Mon Sep 17 00:00:00 2001 From: Chris Wolfe Date: Wed, 18 Oct 2017 14:23:53 -0500 Subject: Add Multifernet.rotate method (#3979) * add rotate method * add some more tests for the failure modes * start adding some documentation for the rotate method * operate on a single token at a time, leave lists to the caller * add versionadded add versionadded, drop rotate from class doctest * give rotate a doctest * single level, not aligned * add changelog for mf.rotate * show that, once rotated, the old fernet instance can no longer decrypt the token * add the instead of just the how * update docs to reflect removal of ttl from rotate * update tests * refactor internal methods so that we can extract the timestamp * implement rotate * update wordlist (case sensitive?) * lints * consistent naming * get_token_data/get_unverified_token_data -> better name * doc changes * use the static method, do not treat as imethod * move up to MultiFernet docs * add to authors * alter wording * monkeypatch time to make it less possible for the test to pass simply due to calls occuring in less than one second * set the time after encryption to make sure that the time is preserved as part of re-encryption --- src/cryptography/fernet.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cryptography/fernet.py b/src/cryptography/fernet.py index 99eb10e5..1f33a12d 100644 --- a/src/cryptography/fernet.py +++ b/src/cryptography/fernet.py @@ -71,11 +71,14 @@ class Fernet(object): return base64.urlsafe_b64encode(basic_parts + hmac) def decrypt(self, token, ttl=None): + timestamp, data = Fernet._get_unverified_token_data(token) + return self._decrypt_data(data, timestamp, ttl) + + @staticmethod + def _get_unverified_token_data(token): if not isinstance(token, bytes): raise TypeError("token must be bytes.") - current_time = int(time.time()) - try: data = base64.urlsafe_b64decode(token) except (TypeError, binascii.Error): @@ -88,6 +91,10 @@ class Fernet(object): timestamp, = struct.unpack(">Q", data[1:9]) except struct.error: raise InvalidToken + return timestamp, data + + def _decrypt_data(self, data, timestamp, ttl): + current_time = int(time.time()) if ttl is not None: if timestamp + ttl < current_time: raise InvalidToken @@ -134,6 +141,20 @@ class MultiFernet(object): def encrypt(self, msg): return self._fernets[0].encrypt(msg) + def rotate(self, msg): + timestamp, data = Fernet._get_unverified_token_data(msg) + for f in self._fernets: + try: + p = f._decrypt_data(data, timestamp, None) + break + except InvalidToken: + pass + else: + raise InvalidToken + + iv = os.urandom(16) + return self._fernets[0]._encrypt_from_parts(p, timestamp, iv) + def decrypt(self, msg, ttl=None): for f in self._fernets: try: -- cgit v1.2.3