Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AES_128_CTR encryption by openssl and PyCrypto

Wondering the right way to convert a AES_128_CTR encryption by openssl to PyCrypto.

First, I did an encryption by openssl as following:

openssl enc -aes-128-ctr -in input.mp4 -out output.openssl.mp4 -K 7842f0a1ebc38f44e3e0c81943f68582 -iv d01f40dfc8ec8cd9

And then, I tried to do the same thing through PyCrypto:

from Crypto.Cipher import AES
from Crypto.Util import Counter
key = '7842f0a1ebc38f44e3e0c81943f68582'
iv = 'd01f40dfc8ec8cd9'

ctr_e = Counter.new(128, initial_value=int(iv, 16))
encryptor = AES.new(key.decode('hex'), AES.MODE_CTR, counter=ctr_e)

with open('output.pycrypto.mp4', 'wb') as fout:
    with open('input.mp4', 'rb') as fin:
        fout.write(encryptor.encrypt(fin.read()))

I assume they are supposed to be similar, but it is not:

diff output.openssl.mp4 output.pycrypto.mp4
Binary files output.openssl.mp4 and output.pycrypto.mp4 differ
like image 613
Drake Guan Avatar asked Nov 10 '14 01:11

Drake Guan


1 Answers

OpenSSL behaves as expected (fortunately, as documentation to this fact is missing for the command line) and uses the given IV as leftmost bytes of a big endian counter. In other words, the bytes given are the most significant part of the 16 byte counter. The code in the question uses the IV as initial counter value, i.e. it is interpreted as the least significant part of the counter.

Now it took me some time to fix the Python code as there are two problems with the Counter class I had to work around:

  • the size of the counter should be 64 bit instead of 128 bit if a prefix of 8 bytes is used; this is a design feature, but it may cause overflow if the amount of bits reserved for the counter is small (it's OK with the current setting of 64 bit though)
  • the default initial counter value is set to 1 while CTR mode always starts counting at 0; this is likely an off-by-one bug in the Counter design

So without further ado:

from Crypto.Cipher import AES
from Crypto.Util import Counter

key = '7842f0a1ebc38f44e3e0c81943f68582'.decode('hex')
iv = 'd01f40dfc8ec8cd9'.decode('hex')

ctr_e = Counter.new(64, prefix=iv, initial_value=0)
encryptor = AES.new(key, AES.MODE_CTR, counter=ctr_e)

with open('output.pycrypto.mp4', 'wb') as fout:
    with open('input.mp4', 'rb') as fin:
        fout.write(encryptor.encrypt(fin.read()))
like image 167
Maarten Bodewes Avatar answered Nov 11 '22 06:11

Maarten Bodewes