问题
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!
回答1:
I think you want to look at How come I can't decrypted my AES encrypted message on someone elses AES decryptor?
The way you are using the python decrypt needs to be modified as per the answer to that quesiton.
回答2:
Here is the corrected Python script that will work!
Edit: not really- only the first 16 bytes of the plaintext are revealed. I will work on this further.
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
self.first = True
def increment(self, b):
if b == "\xff\xff\xff\xff":
raise ValueError("Reached the counter limit")
if self.first:
return struct.pack(">I", bytestring_to_int(b))
else:
self.first = False
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(base64.b64decode(msg))
return plaintext
if __name__ == "__main__":
key = 'b1df40bc2e4a1d4e31c50574735e1c909aa3c8fda58eca09bf2681ce4d117e11'
msg = 'LwFUZbKzuarvPR6pmXM2AiYVD2iL0/Ww2gs/9OpcMy+MWasvvzA2UEmRM8dq4loB\ndfPaYOe65JqGQMWoLOTWo1TreBd9vmPUZt72nFs='
iv = 'gpG388l8rT02vBH4'
print decrypt_msg(key, msg, iv)
print "Decrypted message:", repr(decrypt_msg(key, msg, iv))
回答3:
There's something wrong with the way I'm incrementing the counter. Apparently the right counter values are applied to the wrong places. Here's a follow-up question that addresses the issue. Strange issue with AES CTR mode with Python and Javascript
来源:https://stackoverflow.com/questions/15837463/is-aes-ctr-in-cryptojs-compatible-with-pycrypto