Fernet (Auto key)Fernet is a symmetric encryption method which makes sure that the message encrypted cannot be manipulated/read without the key. It uses URL safe encoding for the keys. Fernet uses 128-bit AES in CBC mode and PKCS7 padding, with HMAC using SHA256 for authentication. The IV is created from os.random(). Generate an example and use the decrypt page: [Fernet Decrypt] The key generated is a URL-safe base64-encoded key with 32 bytes. When the message is encrypted it contains the time it was generated in plaintext. If we use: decrypt(token,TTL) then an exception is raised is the token was created more that TTL seconds ago. |
Outline
The token has a version number, a time stamp, the IV, the cipher text and an HMAC signature:
- Version: 8 bits
- Timestamp: 64 bits (the number of seconds since between January 1, 1970 UTC and the time of the encryption).
- IV: 128 bits
- Ciphertext - variable length: Multiple of 128 bits
- HMAC: 256 bits
The token is then created in a Base-64 format:
Key: Ed_KChj5Qv2WQYRNBAXn5qwflq48b248LVDCKHMeO9M= Token gAAAAABWC882Iuv67vS758XNJGIrU24Qlw58_6RVgpyX3x4xg9QWG_DJPaSuj3404ULhhZJwGS HFaeqHU7jB3JyrQra2O__Q3Q== Current time: Wed Sep 30 13:01:58 2015 Token Details ============= Decoded data: 8000000000560bcf3622ebfaeef4bbe7c5cd24622b536e10970e7cffa455829c9 7df1e3183d4161bf0c93da4ae8f7e34e142e18592701921c569ea8753b8c1dc9cab42b6b63bffd0d d ======Analysis==== Version: 80 Date created: 00000000560bcf36 IV: 22ebfaeef4bbe7c5cd24622b536e1097 Cipher: 0e7cffa455829c97df1e3183d4161bf0 HMAC: c93da4ae8f7e34e142e18592701921c569ea8753b8c1dc9cab42b6b63bffd0dd ======Converted==== Time stamp: 1443614518 Date created: Wed Sep 30 13:01:58 2015 IV: 22ebfaeef4bbe7c5cd24622b536e1097 Decoded: password
Code
The following is some sample code:
# https://asecuritysite.com/encryption/fer # Based on code at https://cryptography.io/en/stable/_modules/cryptography/fernet/ import base64 import binascii import struct import time import six _MAX_CLOCK_SKEW = 60 from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.hmac import HMAC import sys test="password" if (len(sys.argv)>1): test=sys.argv[1] def decrypt(self, token, ttl=None): current_time = int(time.time()) print("Current time:\t",time.ctime(current_time)) print("\nToken Details") print("=============") if not isinstance(token, bytes): raise TypeError("token must be bytes.") try: data = base64.urlsafe_b64decode(token) except (TypeError, binascii.Error): raise InvalidToken print("Decoded data: ",binascii.hexlify(bytearray(data))) print("======Analysis====") print("Version:\t",binascii.hexlify(bytearray(data[0:1]))) print("Date created:\t",binascii.hexlify(bytearray(data[1:9]))) print("IV:\t\t",binascii.hexlify(bytearray(data[9:25]))) print("Cipher:\t\t",binascii.hexlify(bytearray(data[25:-32]))) print("HMAC:\t\t",binascii.hexlify(bytearray(data[-32:]))) print("======Converted====") if not data or six.indexbytes(data, 0) != 0x80: raise InvalidToken try: timestamp, = struct.unpack(">Q", data[1:9]) print("Time stamp:\t",timestamp) print("Date created:\t",time.ctime(timestamp)) except struct.error: raise InvalidToken if ttl is not None: if timestamp + ttl < current_time: raise InvalidToken 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 iv = data[9:25] print("IV:\t\t",binascii.hexlify(iv)) ciphertext = data[25:-32] decryptor = Cipher( algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend ).decryptor() plaintext_padded = decryptor.update(ciphertext) try: plaintext_padded += decryptor.finalize() except ValueError: raise InvalidToken unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() unpadded = unpadder.update(plaintext_padded) try: unpadded += unpadder.finalize() except ValueError: raise InvalidToken print("Decoded:\t",unpadded) return unpadded from cryptography.fernet import Fernet key = Fernet.generate_key() print("Key: ",key) f = Fernet(key) token = f.encrypt(test.encode()) print("Token: ", token) decrypt(f,token)
Presentation
The following is an outline presentation [slides]: