What is a fernet key exactly?

562 views Asked by At

According to the documentation for the cryptography.fernet module, fernet keys are:

A URL-safe base64-encoded 32-byte key

Yet this doesn't work:

import secrets
from cryptography import fernet

f = fernet.Fernet(secrets.token_urlsafe(32))

failing with ValueError: Fernet key must be 32 url-safe base64-encoded bytes - however the documentation for token_urlsafe claims that it returns

a random URL-safe text string, containing nbytes random bytes. The text is Base64 encoded ...

Likewise, this doesn't work:

import base64
from cryptography import fernet

key = fernet.Fernet.generate_key()
print(base64.b64decode(key))

failing with: binascii.Error: Incorrect padding.

So what is a Fernet key and what is the right way to go about generating one from a pre-shared string?

1

There are 1 answers

3
Tom On

FWIW, it took ChatGPT a lot of attempts (and me telling it how to do it) to get this right. To generate a key from a pre-shared string:

key = "very sekrit key"
key += '=' * (32 - len(key))
key = base64.urlsafe_encode(key.encode("utf-8"))

This is not a good approach; Fernet uses the first 16 bytes of the key for signing and the second 16 bytes for encryption, so the above example ends up with an encryption key of "================".

The issue with secrets.token_urlsafe(32) is that, although it does produce a URL-safe base-64-encoded string, the result is not correctly padded for base-64 decoding. This can be rectified like this:

key = secrets.token_urlsafe(32)
key += '=' * (4 - len(key)%4)
cryptor = fernet.Fernet(key)

In this case, the padding is benign because it's not actually added to the key content, just to the base64-encoded key.

A better approach to generating a key from a pre-shared string is to use a key derivation function such as those in the kdf package.