Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get an ValueError: Invalid padding bytes when I decrypt a string with an incorrect key using Python cryptography library?

When I decrypt my string with the correct password following this simple guide on the library's website I get a correct string response. When I change the final w to an e to get an invalid key, I get a padding bytes error like this stemming from:

lib/python3.8/site-packages/cryptography/hazmat/primitives/padding.py", line 101, in _byte_unpadding_check

Which is then followed by the cryptography.fernet.InvalidToken error I was expecting.

ValueError: Invalid padding bytes.

During handling of the above exception, another exception occurred:

cryptography.fernet.InvalidToken

I've read Stackoverflow and can only find one example of this but it didn't seem to apply when this is just the actual example from their site.

What am I missing?

from cryptography.fernet import Fernet
    
# print(Fernet.generate_key()) # used this to get the below
bytes_key = b'tsuG7oqhwyCsfUof35btDJA2eM7AdGf4tAM0o1zXOew='
    
# changed just one digit of key to test incorrect key
# bytes_key = b'tsuG7oqhwyCsfUof35btDJA2eM7AdGf4tAM0o1zXOee='
    
f = Fernet(bytes_key)
    
# encrypt a message
# token = f.encrypt(b"A really secret message. Not for prying eyes.")
# print(token) 
# b'gAAAAABiIOCx9Pp8HB9Asq0HUXb-t0SwX-W5ue7O3GJfuF9Rm4ue8I0Drk36NIyuZ0Mufknad5xkoL3091ZGOOS-QTqMbM-MeNrEC9jvmYx_Y0ojoSTaZWO9AXMJFZgdnvgPTB-wQIQd'
    
print(f.decrypt(b'gAAAAABiIOCx9Pp8HB9Asq0HUXb-t0SwX-W5ue7O3GJfuF9Rm4ue8I0Drk36NIyuZ0Mufknad5xkoL3091ZGOOS-QTqMbM-MeNrEC9jvmYx_Y0ojoSTaZWO9AXMJFZgdnvgPTB-wQIQd'))
print(f.decrypt(b'gAAAAABiIOCx9Pp8HB9Asq0HUXb-t0SwX-W5ue7O3GJfuF9Rm4ue8I0Drk36NIyuZ0Mufknad5xkoL3091ZGOOS-QTqMbM-MeNrEC9jvmYx_Y0ojoSTaZWO9AXMJFZgdnvgPTB-wQIQd').decode('utf-8'))
like image 557
Johnny John Boy Avatar asked Sep 02 '25 16:09

Johnny John Boy


1 Answers

Details about the structure of the Fernet key, the Fernet token and the algorithms involved can be found here.

The Fernet key consists of the concatenation of a 16 bytes signing key and a 16 bytes encryption key, Base64url encoded.

For the posted valid Fernet key is:

Fernet key (Base64url): tsuG7oqhwyCsfUof35btDJA2eM7AdGf4tAM0o1zXOew=
Signing key (hex):      b6cb86ee8aa1c320ac7d4a1fdf96ed0c
Encryption key (hex):   903678cec07467f8b40334a35cd739ec

and for the posted invalid key:

Fernet key (Base64url): tsuG7oqhwyCsfUof35btDJA2eM7AdGf4tAM0o1zXOee=
Signing key (hex):      b6cb86ee8aa1c320ac7d4a1fdf96ed0c
Encryption key (hex):   903678cec07467f8b40334a35cd739e7

Note that both encryption keys differ in the last byte (0xec vs 0xe7), i.e. your change has modified the encryption key!

The wrong key causes that the decryption generates a wrong plaintext with a wrong padding. It is not the length of the padding that is wrong, but the values of the padding bytes themselves.

Details: The plaintext used in the example:

A really secret message. Not for prying eyes.

consists of 45 bytes, i.e. the padding consists of 3 bytes, namely the byte sequence 0x030303, since PKCS#7 padding is applied. For PKCS7# padding all padding bytes have the same value and this value corresponds to the number of padding bytes, for details see PKCS#7. If the ciphertext is decrypted with the wrong key, the last three bytes are 0x07e3f2, which does not correspond to a valid PKCS#7 padding and generates the ValueError: Invalid padding bytes error message.
Note: If the padding is correct, it is automatically removed.

Similarly, an invalid signing key generates a corresponding error message InvalidSignature: Signature did not match digest.


The padding 0x07e3f2 can be determined as follows:

The structure of the token is: version (1 byte) || timestamp (8 bytes) || IV (16 bytes) || ciphertext || HMAC (32 bytes). Thus, from the token, the IV and ciphertext below can be derived:

IV:         f4fa7c1c1f40b2ad075176feb744b05f
ciphertext: e5b9b9eecedc625fb85f519b8b9ef08d03ae4dfa348cae67432e7e49da779c64a0bdf4f7564638e4be413a8c6ccf8c78

This allows decryption with AES/CBC without padding using the appropriate tools (e.g. here) and results in the following byte sequence for decryption with the wrong key:

6afb18a9791e407ce0eafd1d2c2ae1cef403e94903cf80e90193fafd0681e58f0e8fff0d3f3542901bced6fc8e07e3f2

with 0x07e3f2 as invalid padding bytes.

In contrast, if the correct key is supplied for decryption, the result is:

41207265616c6c7920736563726574206d6573736167652e204e6f7420666f7220707279696e6720657965732e030303

with the correct padding 0x030303.

like image 195
Topaco Avatar answered Sep 04 '25 05:09

Topaco