Legorooj
Legorooj

Reputation: 2757

PyCryptoDome/cryptography inequality with AES-CFB in python

While running a test to make sure that the two different libraries give the same output, I found out that they don't with CFB. Code to replicate the problem is:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from Crypto.Cipher import AES

KEY = b'legoroojlegorooj'
IV = b'legoroojlegorooj'

aes = Cipher(algorithms.AES(KEY), modes.CFB(IV), default_backend()).encryptor()
output_data = aes.update(b'Feathers fall as fast as bowling balls.') + aes.finalize()
del aes


ctr = AES.new(KEY, AES.MODE_CFB, iv=IV)
output_data2 = ctr.encrypt(b'Feathers fall as fast as bowling balls.')

assert output_data == output_data2  # AssertionError

Any help sorting this out would be appreiciated.

This code works with modes.OFB(IV) and AES.MODE_OFB.

Upvotes: 2

Views: 1543

Answers (1)

Topaco
Topaco

Reputation: 49251

In CFB mode the size of the shift register must be specified, whereby the different libraries often use different default values. For distinction, the size of the shift register in bits is often appended to the CFB, i.e. CFB8 uses a shift register of 8 bits and CFB128 a shift register of 128 bits.

Cryptography has the two variants CFB8 and CFB128, where the latter is simply called CFB. PyCryptodome allows the setting in integer multiples of 8 bit by using the parameter segment_size with the default value 8 bit.

So in the current code Cryptography uses CFB128 and PyCryptodome uses CFB8 (its default value), which causes the different results.

The following combinations work:

  • PyCryptodome with segment_size=128 and Cryptography with CFB. Both correspond to CFB128:

    # CFB with a 128 bit shift-register
    # Output as hex-string: 63230751cc1efe25b980d9e707396a1a171cd413e6e77f1cd7a2d3deb2217255a36ae9cbf86c66
    ...
    aes = Cipher(algorithms.AES(KEY), modes.CFB(IV), default_backend()).encryptor()
    ...
    ctr = AES.new(KEY, AES.MODE_CFB, iv=IV, segment_size=128)
    ...
    
  • PyCryptodome with segment_size=8 (the default value) and Cryptography with CFB8. Both correspond to CFB8:

    # CFB with a 8 bit shift-register
    # Output as hex-string: 63d263889ffe94dd4740580067ee798da474c567b8b54319a5022650085c62674628f7c9e790c3
    ...
    aes = Cipher(algorithms.AES(KEY), modes.CFB8(IV), default_backend()).encryptor()
    ...
    ctr = AES.new(KEY, AES.MODE_CFB, iv=IV, segment_size=8)
    ...
    

Note, that (1) both Python libraries provide an identical result for the OFB mode, since both use OFB128. (2) CFB128 is faster than CFB8: In CFB8, AES encryption has to be called 16 times for each block vs. 1 time in CFB128.

Upvotes: 5

Related Questions