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
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()))