mirror of
https://github.com/saymrwulf/cryptography.git
synced 2026-05-14 20:37:55 +00:00
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
This commit is contained in:
parent
33ae3cea99
commit
36ad98fd5e
4 changed files with 43 additions and 6 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue