Как использовать режим CryptoJS CTR с переменным размером сообщения (счетчик одноразовых номеров и смещение)

При использовании cryptoJS для шифрования и дешифрования сообщений в AES (режим CTR) алгоритм AES использует значение nonce (Number Once), чтобы запутать блок потока и предотвратить атаки повторного воспроизведения. В cryptoJS, чтобы настроить одноразовый номер:

  const cli_verify = CryptoJS.AES.encrypt(CryptoJS.enc.Latin1.parse(originalString), key, {
    mode: CryptoJS.mode.CTR,
    iv: iv,
    padding: CryptoJS.pad.NoPadding
  });

Точно так же вы можете использовать тот же подход при расшифровке ваших сообщений:

  const decryptedString = CryptoJS.AES.decrypt(encryptedStringB64, key, {
  mode: CryptoJS.mode.CTR,
    iv: iv,
    padding: CryptoJS.pad.NoPadding
  }).toString(CryptoJS.enc.Latin1); 

НО: если вы делаете это, вам нужно отслеживать одноразовый номер самостоятельно. Это а) боль и б) действительно сложно, если вы начинаете иметь дело с сообщениями переменного размера (т.е. не кратного 16 байтам).

Я разобрал библиотеку AES, с которой разговариваю, и выяснил, как работают счетчик одноразовых номеров и смещение. По сути, если вы начнете отправлять очень маленькие сообщения, например, 4 байта, 4 байта будут расшифрованы с использованием первых 4 байтов потока, а смещение будет увеличено до 4, чтобы отслеживать это. Если затем шифруются 6 байтов, используются следующие 6 байтов потока (без увеличения одноразового номера), а смещение обновляется до 10... Если следующая передача составляет 143 байта, вы можете видеть, как это начинает усложняться. реализовать себя. Используются последние 6 байтов текущего потока, увеличивается одноразовый номер и так далее.

Мой вопрос: как заставить CryptoJS обрабатывать это для вас во время сеанса. Я пытался установить IV, заполнение и режим в первом вызове, а затем оставить его пустым в следующем, но это не работает. Это потрясающая библиотека, и я уверен, что она там есть. Просто непонятно, как его использовать.


person monkey    schedule 12.01.2020    source источник


Ответы (1)


Вместо этого вы можете использовать API прогрессивного шифрования, это правильно обновляет одноразовый номер/счетчик.

var key = CryptoJS.enc.Hex.parse("000102030405060708090a0b0c0d0e0f");
var iv = CryptoJS.enc.Hex.parse("101112131415161718191a1b1c1d1e1f");
    
// encrypt
var aesEncryptor = CryptoJS.algo.AES.createEncryptor(key, {
    mode: CryptoJS.mode.CTR,
    iv: iv,
    padding: CryptoJS.pad.NoPadding
});
var ciphertextPart1 = aesEncryptor.process("Message Part 1");
var ciphertextPart2 = aesEncryptor.process("Message Part 2");
var ciphertextPart3 = aesEncryptor.process("Message Part 3");
var ciphertextPart4 = aesEncryptor.finalize();
    ​
// decrypt
var aesDecryptor = CryptoJS.algo.AES.createDecryptor(key, {
    mode: CryptoJS.mode.CTR,
    iv: iv,
    padding: CryptoJS.pad.NoPadding
});    ​

var plaintextPart1 = aesDecryptor.process(ciphertextPart1);
var plaintextPart2 = aesDecryptor.process(ciphertextPart2);
var plaintextPart3 = aesDecryptor.process(ciphertextPart3);
var plaintextPart4 = aesDecryptor.process(ciphertextPart4);
var plaintextPart5 = aesDecryptor.finalize();

Согласно https://cryptojs.gitbook.io/docs/, откуда изначально был взят приведенный выше фрагмент. из.

Если вы обрабатываете входные данные, которые не соответствуют размеру блока (как в вашем случае), вы не получите весь вывод, пока не вызовете finalize.

Вы можете обнаружить, что вам нужно вызвать finalize, чтобы получить вывод из ввода, который не соответствует размеру блока, но все же хотите продолжать использовать шифратор/дешифратор. Вариант использования для этого - это когда одноразовый номер должен быть синхронизирован между клиентом и сервером. API не работает, если вы вызываете процесс после финализации, когда ввод и размер блока не совпадают. API-интерфейс клонирования также работает некорректно с несоответствием размера блока и ввода (что, по сути, и требуется в данном случае).

Однако, если вы отслеживаете все входные данные, отправленные и полученные во время сеанса, вы сможете восстановить сеанс после вызова finalize. Это можно сделать, воспроизведя предыдущие входные данные в новый шифратор/дешифратор, вызвав process перед вызовом finalize для получения окончательного вывода. В этом случае после вызова finalize вам нужно взять из вывода такое же количество байтов, сколько было во входе, поскольку шифратор/дешифратор может содержать какой-то предыдущий вывод, как в случае с несоответствием размера входного блока.

person samhjohnson    schedule 28.07.2020