diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2018-05-12 11:57:32 -0400 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2018-05-12 11:57:32 -0400 |
commit | 36ad98fd5e4b7358dc2aa903b6d51569bf19c5f8 (patch) | |
tree | b176b10478a5cfe302ca3ed7193fda5964c16d8b | |
parent | 33ae3cea990b307eafaa5f52232eba8315fd05fe (diff) | |
download | cryptography-36ad98fd5e4b7358dc2aa903b6d51569bf19c5f8.tar.gz cryptography-36ad98fd5e4b7358dc2aa903b6d51569bf19c5f8.tar.bz2 cryptography-36ad98fd5e4b7358dc2aa903b6d51569bf19c5f8.zip |
Add support for extracting timestamp from a Fernet token (#4229)
* Add API for retrieving the seconds-to-expiry for the token, given a TTL.
* Process PR feedback:
* Do compute the TTL, but just the age of the token. The caller
can decided what to do next.
* Factored out the HMAC signature verification to a separate function.
* Fixed a copy&paste mistake in the test cases
* Tests cleanup.
* `struct` no longer needed
* Document `def age()`
* typo in `age()` documentation
* token, not data
* remove test for TTL expiry that is already covered by the parameterized `test_invalid()`.
* let's call this extract_timestamp and just return timestamp
* review comments
* it's UNIX I know this
-rw-r--r-- | CHANGELOG.rst | 3 | ||||
-rw-r--r-- | docs/fernet.rst | 16 | ||||
-rw-r--r-- | src/cryptography/fernet.py | 21 | ||||
-rw-r--r-- | tests/test_fernet.py | 9 |
4 files changed, 43 insertions, 6 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6ab6b044..4cabaf7f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,9 @@ Changelog .. note:: This version is not yet released and is under active development. +* Added :meth:`~cryptography.fernet.Fernet.extract_timestamp` to get the + authenticated timestamp of a :doc:`Fernet </fernet>` token. + .. _v2-2-2: 2.2.2 - 2018-03-27 diff --git a/docs/fernet.rst b/docs/fernet.rst index a0ffe64f..2d7d2281 100644 --- a/docs/fernet.rst +++ b/docs/fernet.rst @@ -80,6 +80,22 @@ has support for implementing key rotation via :class:`MultiFernet`. :raises TypeError: This exception is raised if ``token`` is not ``bytes``. + .. method:: extract_timestamp(token) + + .. versionadded:: 2.3 + + Returns the timestamp for the token. The caller can then decide if + the token is about to expire and, for example, issue a new token. + + :param bytes token: The Fernet token. This is the result of calling + :meth:`encrypt`. + :returns int: The UNIX timestamp of the token. + :raises cryptography.fernet.InvalidToken: If the ``token``'s signature + is invalid this exception + is raised. + :raises TypeError: This exception is raised if ``token`` is not + ``bytes``. + .. class:: MultiFernet(fernets) diff --git a/src/cryptography/fernet.py b/src/cryptography/fernet.py index 1f33a12d..ac2dd0b6 100644 --- a/src/cryptography/fernet.py +++ b/src/cryptography/fernet.py @@ -74,6 +74,12 @@ class Fernet(object): timestamp, data = Fernet._get_unverified_token_data(token) return self._decrypt_data(data, timestamp, ttl) + def extract_timestamp(self, token): + timestamp, data = Fernet._get_unverified_token_data(token) + # Verify the token was not tampered with. + self._verify_signature(data) + return timestamp + @staticmethod def _get_unverified_token_data(token): if not isinstance(token, bytes): @@ -93,6 +99,14 @@ class Fernet(object): raise InvalidToken return timestamp, data + def _verify_signature(self, data): + h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) + h.update(data[:-32]) + try: + h.verify(data[-32:]) + except InvalidSignature: + raise InvalidToken + def _decrypt_data(self, data, timestamp, ttl): current_time = int(time.time()) if ttl is not None: @@ -102,12 +116,7 @@ class Fernet(object): if current_time + _MAX_CLOCK_SKEW < timestamp: raise InvalidToken - h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) - h.update(data[:-32]) - try: - h.verify(data[-32:]) - except InvalidSignature: - raise InvalidToken + self._verify_signature(data) iv = data[9:25] ciphertext = data[25:-32] diff --git a/tests/test_fernet.py b/tests/test_fernet.py index 6558d11b..75ecc356 100644 --- a/tests/test_fernet.py +++ b/tests/test_fernet.py @@ -122,6 +122,15 @@ class TestFernet(object): with pytest.raises(ValueError): Fernet(base64.urlsafe_b64encode(b"abc"), backend=backend) + def test_extract_timestamp(self, monkeypatch, backend): + f = Fernet(base64.urlsafe_b64encode(b"\x00" * 32), backend=backend) + current_time = 1526138327 + monkeypatch.setattr(time, "time", lambda: current_time) + token = f.encrypt(b'encrypt me') + assert f.extract_timestamp(token) == current_time + with pytest.raises(InvalidToken): + f.extract_timestamp(b"nonsensetoken") + @pytest.mark.requires_backend_interface(interface=CipherBackend) @pytest.mark.requires_backend_interface(interface=HMACBackend) |