I'm trying to decrypt some AES-CTR-256 data using the PyCrypto library. The ciphertext was produced by the Cryptocat multiparty chat Javascript code, which relies on the CryptoJS library. The IV scheme is as described in the Cryptocat Multiparty Protocol Specification:
The Initialization Vector (IV) is composed of 16 bytes: 12 bytes that are randomly generated and 4 bytes acting as the counter, incremented once per block.
(The 12 random bytes come before the 4 counter bytes.)
Here's my Python code:
import struct
import base64
import Crypto.Cipher.AES
def bytestring_to_int(s):
r = 0
for b in s:
r = r * 256 + ord(b)
return r
class IVCounter(object):
def __init__(self, prefix="", iv="\x00\x00\x00\x00"):
self.prefix = prefix
self.initial_value = iv
def increment(self, b):
if b == "\xff\xff\xff\xff":
raise ValueError("Reached the counter limit")
return struct.pack(">I", bytestring_to_int(b)+1)
def __call__(self):
self.initial_value = self.increment(self.initial_value)
n = base64.b64decode(self.prefix) + self.initial_value
return n
def decrypt_msg(key, msg, iv):
k = base64.b16decode(key.upper())
ctr = IVCounter(prefix=iv)
aes = Crypto.Cipher.AES.new(k, Crypto.Cipher.AES.MODE_CTR, counter=ctr)
plaintext = aes.decrypt(msg)
return plaintext
if __name__ == "__main__":
key = 'b1df40bc2e4a1d4e31c50574735e1c909aa3c8fda58eca09bf2681ce4d117e11'
msg = 'LwFUZbKzuarvPR6pmXM2AiYVD2iL0/Ww2gs/9OpcMy+MWasvvzA2UEmRM8dq4loB\ndfPaYOe65JqGQMWoLOTWo1TreBd9vmPUZt72nFs='
iv = 'gpG388l8rT02vBH4'
plaintext = decrypt_msg(key, msg, iv)
print plaintext
And this is how to do the same thing in Javascript:
- Install the CryptoCat extension
- Run CryptoCat
- Fire up the developer console (F12 in Chrome/Firefox)
- Run these lines of code
key = 'b1df40bc2e4a1d4e31c50574735e1c909aa3c8fda58eca09bf2681ce4d117e11';
msg = 'LwFUZbKzuarvPR6pmXM2AiYVD2iL0/Ww2gs/9OpcMy+MWasvvzA2UEmRM8dq4loB\ndfPaYOe65JqGQMWoLOTWo1TreBd9vmPUZt72nFs=';
iv = 'gpG388l8rT02vBH4';
opts = {mode: CryptoJS.mode.CTR, iv: CryptoJS.enc.Base64.parse(iv), padding: CryptoJS.pad.NoPadding};
CryptoJS.AES.decrypt(msg, CryptoJS.enc.Hex.parse(key), opts).toString(CryptoJS.enc.Utf8);
Expected output: "Hello, world!ImiAq7aVLlmZDM9RfhDQgPp0CrAyZE0lyzJ6HDq4VoUmIiKUg7i2xpTSPs28USU8"
. As expected, this works on Javascript.
However, the Python code outputs gibberish. repr(plaintext) gives:
'\x91I\xbd\n\xd5\x11\x0fkE\xaa\x04\x81V\xc9\x16;.\xe3\xd3#\x92\x85\xd2\x99\xaf;\xc5\xafI\xac\xb6\xbdT\xf4{l\x17\xa1`\x85\x13\xf2\x8e\x844\xac1OS\xad\x9eZ<\xea\xbb6\x9dS\xd5\xbc\xfd\xc4\r\xf94Y~\xaf\xf3\xe0I\xad\xa6.\xfa\x7f\xf8U\x16\x0e\x85\x82\x8c\x8e\x04\xcb,X\x8b\xf7\xef\xb2\xc2\xe3~\xf1\x80\x08L\x8b \x9f\xaf\x0e\x0b'
I'm not sure why this is happening. I'm pretty sure my IVCounter implementation matches the scheme that the JS code uses. Could it be that there is no Python equivalent of the CryptoJS NoPadding option? I'm stumped.
Thanks in advance for the help!