Включить одноразовый номер и количество блоков в PyCrypto AES MODE_CTR

Некоторая справочная информация, вы можете пропустить эту часть для фактического вопроса

это мой третий вопрос по этой теме здесь, в stackoverflow. Чтобы быть полным, это другие вопросы AES с crypt-js и PyCrypto. и Сопоставьте де/шифрование AES в python и javascript. К сожалению, моя последняя попытка получила два отрицательных ответа на исходный вопрос. Проблема была в том, что даже я не знал, в чем был мой настоящий вопрос. Я просто копался, чтобы найти реальный вопрос, который я искал. Получив отзывы в комментариях и прочитав дополнительную информацию, я обновил свой вопрос. Я раскапываю правильный вопрос, я думаю. Но моя проблема больше не набирала просмотров после моих обновлений. Так что я очень надеюсь, что этот вопрос теперь стал более ясным и понятным - даже я теперь знаю, в чем моя проблема :D
Спасибо всем за то, что сделали stackoverflow этому классному сообществу - я часто находил здесь решения своих проблем. Пожалуйста, продолжайте оставлять отзывы на плохие вопросы, чтобы их можно было улучшить и обновить, что увеличивает эту огромную базу знаний и решений. И не стесняйтесь исправлять мою английскую грамматику и орфографию.

Проблема

AES в Javascript

У меня есть зашифрованная строка, которую я могу расшифровать с помощью этой Javascript-реализации AES 256 CTR. Режим

password = "myPassphrase"
ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
origtext = Aes.Ctr.decrypt(ciphertext, password, 256);
alert(origtext)

Это расшифровывает мою строку, и появляется окно предупреждения с This is a test Text.

AES с PyCrypto

Теперь я хочу расшифровать эту строку с помощью python и PyCrypto.

password = 'myPassphrase'
ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
ctr = Counter.new(nbits=128)
encryptor = AES.new(key, AES.MODE_CTR, counter=ctr)
origtext = encryptor.decrypt(base64.b64decode(ciphertext))
print origtext

Этот код не запускается. Я получаю ValueError: AES key must be either 16, 24, or 32 bytes long. Когда я понял, что мне нужно сделать в PyCrypto больше, чем просто вызвать метод расшифровки, я начал исследовать и пытаться выяснить, что я должен делать.

Расследование

Основные вещи, которые я понял в первую очередь, были:

Для парольной фразы они делают это:

for (var i=0; i<nBytes; i++) {
    pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
}

Затем я сделал это в питоне

l = 32
key = key + (chr(0)*(l-len(key)%l))

Но это не помогло. Я все еще получаю странную строку ? A???B??d9= ,?h????' со следующим кодом

l = 32
key = 'myPassphrase'
key = key + (chr(0)*(l-len(key)%l))
ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
ctr = Counter.new(nbits=128)
encryptor = AES.new(key, AES.MODE_CTR, counter=ctr)
origtext = encryptor.decrypt(base64.b64decode(ciphertext))
print origtext

Затем я прочитал больше о реализации Javascript, и там говорится

[...] В этой реализации начальный блок содержит одноразовый номер в первых 8 байтах и ​​количество блоков во вторых 8 байтах. [...]

Я думаю, что это может быть ключом к решению. Итак, я проверил, что происходит, когда я шифрую пустую строку в Javascript:

origtext = ""
var ciphertext =Aes.Ctr.encrypt(origtext, password, 256);
alert(ciphertext)

В окне предупреждения отображается /gEKb+N3Y08= (12 символов). Но почему 12? Разве это не должно быть 8+8 = 16 байт? В любом случае, я попробовал метод bruteforce для расшифровки Python, проверив расшифровку с помощью for i in xrange(0,20): и ciphertext[i:] или base64.b64decode(ciphertext)[i:]. Я знаю, что это очень неловкая попытка, но я становился все более и более отчаянным. И это тоже не сработало.

Будущие перспективы также заключаются в том, чтобы реализовать шифрование таким же образом.

Дополнительная информация

Зашифрованная строка изначально не была зашифрована с помощью этой реализации Javascript. другой источник. Я только что понял, что код Javascript работает правильно. Поэтому я утверждаю, что такая реализация является чем-то вроде "стандарта".

Вопрос

Что я могу сделать, чтобы шифрование и дешифрование строки с PyCrypto было таким же, как в реализации Javascript, чтобы я мог обмениваться данными между Javascript и Python? Я бы также переключился на другую криптобиблиотеку на питоне, если вы можете предложить другую. Кроме того, я рад любым советам и отзывам.

И я думаю, что все сводится к Как я могу включить одноразовый номер и количество блоков в зашифрованную строку? и Как я могу извлечь эту информацию для расшифровки?


person samuirai    schedule 16.03.2012    source источник
comment
Вы не должны напрямую использовать строку символов в качестве ключа. Пароль не содержит достаточно энтропии для использования в качестве ключа. Если вы все равно используете строку в качестве ключа, вам лучше убедиться, что кодировка в байты одинакова на обеих платформах.   -  person Maarten Bodewes    schedule 17.03.2012
comment
Режим CTR создает ровно столько байтов, сколько вы ввели. В этом случае к нему добавляется NONCE. Таким образом, вы получаете только NONCE при шифровании пустой строки. Этот NONCE составляет 8 байт. 8 байт кодируются как ceil(8/3)*4 = 3 * 4 = 12 символов base 64 (последний символ = не содержит никакой информации, а предыдущий содержит только 4 бита фактических данных вместо обычных 6 ).   -  person Maarten Bodewes    schedule 17.03.2012
comment
AES всегда имеет размер блока 128 бит (хотя алгоритм Rijndael, на котором он основан, имеет больше возможностей). ключи AES могут иметь размер 128, 192 или 256 бит. Ни один из этих размеров не используется по умолчанию, хотя 128-битный AES, вероятно, используется большую часть времени (за ним следует AES-256).   -  person Maarten Bodewes    schedule 17.03.2012


Ответы (1)


Мы все еще имеем дело с кучей вопросов здесь.

Как извлечь одноразовый номер и счетчик для расшифровки?

Это легко. В реализации Javascript (которая в этом отношении не соответствует определенному стандарту) 8-байтовый одноразовый номер добавляется к зашифрованному результату. В Python вы извлекаете его с помощью:

import base64
from_js_bin = base64.decode(from_js)
nonce = from_js_bin[:8]
ciphertext = from_js_bin[8:]

Где from_js — это двоичная строка, которую вы получили.

Счетчик не может быть извлечен, поскольку реализация JS не передает его. Однако начальное значение равно (как это обычно бывает) 0.

Как использовать одноразовый номер и счетчик для расшифровки строки в Python?

Во-первых, необходимо установить, как одноразовый номер и счетчик комбинируются для получения блока счетчика. Похоже, что реализация JS соответствует стандарту NIST 800-38A, где левая половина — одноразовый номер, а правая половина — счетчик. Точнее, счетчик имеет формат с обратным порядком байтов (LSB — самый правый байт). Это также то, что показывает Википедия: AES CTR mode.

К сожалению, режим CTR плохо документирован в PyCrypto (хорошо известная проблема). По сути, параметр counter должен быть вызываемым объектом, который возвращает правильный 16-байтовый (для AES) блок счетчика для каждого последующего вызова. Crypto.Util.Counter делает это, но неясным образом.

Однако он существует только для целей производительности. Вы можете легко реализовать это самостоятельно следующим образом:

from Crypto.Cipher import AES
import struct

class MyCounter:

  def __init__(self, nonce):
    """Initialize the counter object.

    @nonce      An 8 byte binary string.
    """
    assert(len(nonce)==8)
    self.nonce = nonce
    self.cnt = 0

  def __call__(self):
    """Return the next 16 byte counter, as binary string."""
    righthalf = struct.pack('>Q',self.cnt)
    self.cnt += 1
    return self.nonce + righthalf

cipher_ctr = AES.new(key, mode=AES.MODE_CTR, counter=MyCounter(nonce))
plaintext = cipher_ctr.decrypt(ciphertext)

Какой длины ключ для AES?

Длина ключа для AES-128 составляет 16 байт. Длина ключа для AES-192 составляет 24 байта. Длина ключа для AES-256 составляет 32 байта. Каждый алгоритм отличается, но большая часть реализации является общей. Во всех случаях алгоритм работает с 16-байтовыми блоками данных. Для простоты придерживайтесь AES-128 (nBits=128).

Будет ли ваш код работать?

У меня такое чувство, что этого не произойдет, потому что способ вычисления ключа AES кажется неправильным. Код JS кодирует пароль в UTF-8 и шифрует его собой. В результате получается настоящий ключевой материал. Он имеет длину 16 байт, поэтому для AES-192 и -256 реализация копирует его часть сзади. Кроме того, открытый текст также кодируется UTF-8 перед шифрованием.

В общем, я предлагаю вам придерживаться такого подхода:

  1. Сделайте вашу реализацию JS воспроизводимой (сейчас шифрование зависит от текущего времени, которое довольно часто меняется ;-)).
  2. Выводить значение ключей и данных на каждом шаге (или использовать отладчик).
  3. Попробуйте воспроизвести тот же алгоритм в Python и тоже вывести значения.
  4. Исследуйте, где они начинают отличаться.

После того, как вы продублировали алгоритм шифрования в Python, расшифровка должна быть легкой.

person SquareRootOfTwentyThree    schedule 18.03.2012
comment
Большое спасибо за ваш подробный ответ. AES теперь более понятен для меня. Я нашел реализацию AES, которая отлично работает: wiki.birth-online.de/snippets /python/aes-rijndael - person samuirai; 20.03.2012
comment
Ссылка в этом комментарии давно битая. Было бы прекрасно, если бы кто-нибудь довел это до стандартов SO и предоставил информацию не по ссылке! - person David M. Perlman; 03.08.2017