# Why Entropy is low for Python Fernet Encryption

I am using Fernet encryption from python cryptography.fernet and generated the key as below

    salt = os.urandom(16)
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=78643,
backend=default_backend()
)


Encrypted using:

fernet = Fernet(key)
fernet.encrypt(data)


The generated key is like

"rH8VbfuKPip-K9e46GZrkLt0x7NP-MushFlzWDvw0VE="


Algorithm:

fernet.encrypt(data) - Internal it used AES 128 CBC algorithm for encryption.


Encrypted Content:

"8hcg7KJMLFVjoXl7JUQL3PZf0iH3V5cNpdMjrLIzsO9Rvg2KKM0uDjjG9ugZZNKJUnymd8T70MBObGKPtrIiK"


Problem: Entropy of Encrypted content is very low, Shannon Entropy: 5.9, usually the entropy for encrypted is around 7.9.

Question: What make the encrypted content looks like Text? How to identify the content is encrypted? as entropy is very low.

Note the below method gives entropy around 7.9

key=b'\xcb\x98\xa8\xb6\x94jU\xe8\x92zdR\xa0\x1a\xae\xec'
aes = AES.new(key, AES.MODE_CTR, counter=Counter.new(128))
aes.encrypt(data)

• It's not possible to measure (Shannon) entropy of a given bytestring of unspecified origin. But I guess your problem is different. Hint: $5.9\lesssim\log_2(64)$. Suggestion: review the definition of (Shannon) entropy. What is the expected entropy per symbol for the symbols output by a Base64 encoder when fed with truly uniform random bytes?
– fgrieu
May 31 at 10:18
• Err, what do you do with a URL safe encryption key? Send through a browser? May 31 at 10:19
• Paul, No, the key is used for encryption May 31 at 10:23

You're simply confusing the entropy rate of a sequence with it's entire entropy content. And that depends on the sequence's encoding. The base64 version of a sequence is 33–36% larger than it might be if it were pure binary. And that consequently decreases the maximum possible entropy rate to your 5.9 bits/char.

We could jump into entropy measurements, but we can cut through to the chase with this line:salt = os.urandom(16). That's pretty much 128 bits of quite decent entropy packed into the key sequence. It easily fits into 32 characters (~21 characters really needed). Your actual password can then be a regular one (business requirements allowing) due to the presence of the key derivation function.

Following question edits:-

The cipher text is 'low' entropy as it's base64 encoded by default. It's a “Fernet token”. This is how it's meant to be with that module. Check out the documentation, especially for encrypt(data). Or here. It's a higher level API than just the AES encryption beneath. It also features SHA256 HMAC authentication and timestamping. All as base64 text. Thus ~5.9 bits/char.

I would appreciate any help with finding that answer which estimates the actual entropy of a Linux ChaCha sequence. I can't find it, but seem to recall that it was ~110 bits. I'll edit if found.

• May confuse with the word Base64 encoding, Let me rephrase it, The algorithm i have used is "fernet = Fernet(key) encrypted = fernet.encrypt(data)", but the encrypted content is looks like normal text "4TjhUUsbAbK5G9up3bxi8Tj4sGTcbijHGfAPG9A31cdEaSgykoqUdaXkgOeWGYJ7sVeVyEi4HXqtjBVPMxfTEflBM1QAaYiEp-0_pPF8v7QwhSLFQFI2". Due to that entropy of encrypted content is very low 5.9 bits/char. What makes the content to normal text? and how to identify the file is encrypted or not? Jun 1 at 3:37
• @user1661988 you have to compute the entropy on the raw encrypted data, not on some encoding (in ASCII) of it. Also, the token has some predictable info in its raw form in the brginning (version, timestamp), the entropy is really only about the last two fields cipher text/hmac. Jun 2 at 6:25