Совместим ли AES-CTR в CryptoJS с PyCrypto?

Я пытаюсь расшифровать некоторые данные AES-CTR-256 с помощью библиотеки PyCrypto. Зашифрованный текст был создан кодом Javascript многостороннего чата Cryptocat, который опирается на библиотеку CryptoJS. Схема IV описана в Спецификации многостороннего протокола Cryptocat:

Вектор инициализации (IV) состоит из 16 байтов: 12 байтов, которые генерируются случайным образом, и 4 байта, действующих как счетчик, увеличивающийся один раз на блок.

(12 случайных байтов идут перед 4 байтами счетчика.)

Вот мой код Python:

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

А вот как сделать то же самое в Javascript:

  1. Установите расширение CryptoCat.
  2. Запустить криптокот
  3. Запустите консоль разработчика (F12 в Chrome/Firefox)
  4. Запустите эти строки кода

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

Ожидаемый результат: "Hello, world!ImiAq7aVLlmZDM9RfhDQgPp0CrAyZE0lyzJ6HDq4VoUmIiKUg7i2xpTSPs28USU8". Как и ожидалось, это работает на Javascript.

Однако код Python выводит тарабарщину. repr(открытый текст) дает:

'\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'

Я не уверен, почему это происходит. Я почти уверен, что моя реализация IVCounter соответствует схеме, которую использует код JS. Может ли быть так, что нет Python-эквивалента опции CryptoJS NoPadding? Я в тупике.

Заранее спасибо за помощь!


person user2249400    schedule 05.04.2013    source источник
comment
Вам все еще нужно использовать SSL/TLS.   -  person rook    schedule 05.04.2013
comment
Привет, какое отношение SSL/TLS имеет к расшифровке AES-CTR? Или вы имеете в виду эту ветку комментариев? stackoverflow.com/a/15837593/2249400 Спасибо.   -  person user2249400    schedule 06.04.2013
comment
Я не эксперт по python, но разве начальное значение IV не заканчивается нулем или единицей? Может быть ошибка из-за одной ошибки.   -  person Maarten Bodewes    schedule 09.04.2013
comment
Спасибо за предложение, но оно заканчивается на 0: см. github.com/cryptocat/cryptocat/blob/master/src/chrome/js/ Я отредактировал свой скрипт Python, чтобы он заканчивался на 1, но вывод все равно был неверным.   -  person user2249400    schedule 09.04.2013
comment
@owlstead, ты прав. Виноват!   -  person user2249400    schedule 16.04.2013
comment
Рад, что вы исправили :)   -  person Maarten Bodewes    schedule 16.04.2013
comment
@owlstead, я заметил еще одну странную проблему с IV. Хотите посмотреть? Спасибо. stackoverflow.com/questions/16040367/   -  person user2249400    schedule 17.04.2013


Ответы (3)


Я думаю, вы хотите взглянуть на Почему я не могу расшифровать свое зашифрованное AES сообщение с помощью чужого дешифратора AES?

Способ, которым вы используете расшифровку Python, необходимо изменить в соответствии с ответом на этот вопрос.

person bubba    schedule 05.04.2013
comment
Спасибо, но добавляет ли CryptoJS соль в зашифрованный текст? Я просмотрел код, но не смог найти никаких признаков этого. - person user2249400; 05.04.2013
comment
Фактически, в спецификации не упоминается ни одна функция, которая создает ключ и IV из соли и пароля. Код Javascript от Cryptocat генерирует IV как случайную строку, а затем добавляет счетчик (\x00\x00\x00\x00, \x00\x00\x00\x01...). - person user2249400; 06.04.2013

Вот исправленный скрипт Python, который будет работать!

Редактировать: не совсем - раскрываются только первые 16 байтов открытого текста. Я буду работать над этим дальше.

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))
person user2249400    schedule 16.04.2013

Что-то не так с тем, как я увеличиваю счетчик. Очевидно, правильные значения счетчика применяются не в тех местах. Вот дополнительный вопрос, который касается проблемы. Странная проблема с режимом AES CTR с Python и Javascript

person user2249400    schedule 17.04.2013