pythonencryptionaespycryptocbc-mode

AES get different results by using the same key and iv to encrypt the same plaintext repeatedly


Here is my code:

from Crypto.Cipher import AES
import binascii

def encrypt(secret_key, sign, raw):
    key = md5(secret_key).hexdigest()[::-2]
    iv = md5(sign).hexdigest()[::-2]
    raw += (16 - len(raw) % 16) * '\0'
    generator = AES.new(key, AES.MODE_CBC, IV=iv)

    #***********************************************
    #Problems occur at here ! 
    #If I execute "generator.encrypt(raw)"
    #The results are not same every time

    print generator.encrypt(raw) # result_1
    print generator.encrypt(raw) # result_2
    print generator.encrypt(raw) # result_3

    #***********************************************

    return binascii.b2a_hex(generator.encrypt(raw))

I will get the different results when every time I execute "generator.encrypt(raw)" It is very confuse to me because I used the same KEY and IV.

I want to establish a API-SYSTEM, I need others post their cryptographic data no matter which language they used, just want to get the same result in AES.

How can I get the stable result in using AES?

I mean I want to get the same result when I using the same KEY and IV to encrypt the same plaintext.


Solution

  • The AES implementation in pycrypto is stateful for CBC mode. This statefulness can be simulated by the IV value. Let's assume that raw is shorter than 16 bytes for simplicity.

    In that case the code

    raw += (16 - len(raw) % 16) * '\0' # zero padding
    generator = AES.new(key, AES.MODE_CBC, IV=iv)
    
    print generator.encrypt(raw) # result_1
    print generator.encrypt(raw) # result_2
    print generator.encrypt(raw) # result_3
    

    is equivalent to

    raw += (16 - len(raw) % 16) * '\0' # zero padding
    generator = AES.new(key, AES.MODE_CBC, IV=iv)
    ct1 = generator.encrypt(raw) 
    print ct1 # result_1
    
    generator = AES.new(key, AES.MODE_CBC, IV=ct1)
    ct2 = generator.encrypt(raw) 
    print ct2 # result_2
    
    generator = AES.new(key, AES.MODE_CBC, IV=ct2)
    ct3 = generator.encrypt(raw) 
    print ct3 # result_3
    

    The reason is that the IV is advanced internally according to the definition of the CBC mode. It means that the IV is set to the last full ciphertext block.

    If raw is assumed to be any length, then the following would be equivalent where only the last block of the ciphertext is used as the IV for the next encryption:

    raw += (16 - len(raw) % 16) * '\0' # zero padding
    generator = AES.new(key, AES.MODE_CBC, IV=iv)
    ct1 = generator.encrypt(raw) 
    print ct1 # result_1
    
    generator = AES.new(key, AES.MODE_CBC, IV=ct1[-16:])
    ct2 = generator.encrypt(raw) 
    print ct2 # result_2
    
    generator = AES.new(key, AES.MODE_CBC, IV=ct2[-16:])
    ct3 = generator.encrypt(raw) 
    print ct3 # result_3
    

    If you don't want that, then you need to initialize the generator with the original IV.