2015-04-02 4 views
2

Я понимаю, что в режиме AES Counter мне нужно использовать 128-битное nonce. Наивный способ сделать это - использовать случайное 128-битное nonce, но я не уверен, что алгоритм будет способен правильно увеличивать счетчик, если он передается как все случайные биты. Я думал, что правильный способ сделать это состоит в использовании 96 битную одноразового номера, а также 32-битный счетчик, начиная с 0, например:Правильное использование счетчика и счетчика для режима AES-CTR

var key = CryptoJS.enc.Hex.parse('01ab23cd45ef67089a1b2c3d4e5f6a7b'); // 128 bits/16 bytes 
var nonce = '2301cd4ef785690a1b2c3dab'; // 96 bits/12 bytes 
var counter = '00000000'; // 32 bits/4 bytes 
var nonceAndCounter = nonce + counter; 
    nonceAndCounter = CryptoJS.enc.Hex.parse(nonceAndCounter); 
var plaintext = 'The quick brown fox jumps over the lazy dog.'; 

var encryption = CryptoJS.AES.encrypt(plaintext, key, { iv: nonceAndCounter, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding }); 
var ciphertext = encryption.ciphertext.toString(CryptoJS.enc.Hex); 

Является ли это правильный способ сделать это с CryptoJS library? Или что это правильный путь?

ответ

3

Я собираюсь ответить на свой вопрос, когда я вошел в библиотечный код, чтобы узнать, что он на самом деле делает.

Резюме:

Ответ Вы можете использовать любой из двух методов, и он будет работать, как ожидалось:

1) Pass в случайном нонса 96 бит в длину и сама библиотека автоматически добавит 32-битный счетчик и увеличит его с каждым генерируемым блоком ключей. Например.

var nonce = CryptoJS.enc.Hex.parse('2301cd4ef785690a1b2c3dab'); // 12 Bytes 
var encryption = CryptoJS.AES.encrypt(plaintext, key, { iv: nonce, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding }); 

2) Pass в случайном нонса 96 битов в длину и явно указать 32 битный счетчик, а если вы хотите. Вы даже можете указать счетчик, например 00000009, если вы хотите начать шифрование/дешифрование с 9-го блока. Ниже приведен пример, начиная с счетчика 0:

var nonce = '2301cd4ef785690a1b2c3dab'; // 12 Bytes 
var counter = '00000000';    // 4 Bytes, start at counter 0 
var nonceAndCounter = CryptoJS.enc.Hex.parse(nonce + counter); // 16 Bytes 
var encryption = CryptoJS.AES.encrypt(plaintext, key, { iv: nonceAndCounter, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding }); 

Пояснение:

Используя код в вопросе с 32 бит счетчика 00000000, соответствующий код в этом файле mode-ctr.js:

/** 
* Counter block mode. 
*/ 
CryptoJS.mode.CTR = (function() { 
    var CTR = CryptoJS.lib.BlockCipherMode.extend(); 

    var Encryptor = CTR.Encryptor = CTR.extend({ 
     processBlock: function (words, offset) { 
      // Shortcuts 
      var cipher = this._cipher 
      var blockSize = cipher.blockSize; 
      var iv = this._iv; 
      var counter = this._counter; 

      // Generate keystream 
      if (iv) { 
       counter = this._counter = iv.slice(0); 

       // Remove IV for subsequent blocks 
       this._iv = undefined; 
      } 
      var keystream = counter.slice(0); 
      cipher.encryptBlock(keystream, 0); 

      // Increment counter 
      counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0 

      // Encrypt 
      for (var i = 0; i < blockSize; i++) { 
       words[offset + i] ^= keystream[i]; 
      } 
     } 
    }); 

    CTR.Decryptor = Encryptor; 

    return CTR; 
}()); 

При запуске этого кода в браузере JS-отладчик, использующий точку останова, он преобразует nonceAndCounter в WordArray cons ствовавшие 32 битовых элементов:

[587320654, -142251766, 455884203, 0]

Это используется для шифрования блока. Для шифрования следующего блока он запускает эту строку:

counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0

который оценивает взять counter[3] элемент т.е. целое число 0 и увеличивает его:

[587320654, -142251766, 455884203, 1]

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

[587320654, -142251766, 455884203, 2]

[587320654, -142251766, 455884203, 3]

[587320654, -142251766, 455884203, 4]

И так далее. Таким образом, похоже, что он работает правильно таким образом.

Сравните это с тем, как это работает, если вы передаете 128-битное случайное исключение, например.

var nonceAndCounter = CryptoJS.enc.Hex.parse('2301cd4ef785690a1b2c3dabdf99a9b3');

Это производит случайное слово из:

[587320654, -142251766, 455884203, -543577677, 0]

Так что создает 5 элементов массива !? Затем функция увеличивает четвертый элемент от -543577677 до -543577676, затем -543577675, затем -543577674 и так далее. Таким образом, он все еще работает в некотором роде, но не увеличивается так же хорошо, как начиная с 0 и, возможно, более подвержен ошибкам.

Когда я прошел только 96-разрядное случайное исключение, библиотека автоматически добавила счетчик начала как 0 в конец массива счетчиков и правильно увеличила его для последующих блоков. например

[587320654, -142251766, 455884203, 0] 
[587320654, -142251766, 455884203, 1] 
[587320654, -142251766, 455884203, 2] 
+0

Итак, вам явно не нужно устанавливать счетчик, потому что CryptoJS автоматически добавит его для вас. Вероятно, он проверяет, является ли последнее слово уже 0. Кроме того, режим CTR не имеет IV, он называется nonce. –

+0

«Кроме того, режим CTR не имеет IV, он называется nonce». Отметил благодарности. «Таким образом, вам явно не нужно устанавливать счетчик, потому что CryptoJS автоматически добавит его для вас». Да, я протестировал это, просто передав 96-битное nonce, и он автоматически добавляет начальный счетчик как 0 в конец массива 'counter', например. '[587320654, -142251766, 455884203, 0]' и правильно его исправить. Большое спасибо за ответ Artjom. Извините, я пометил вас, потому что увидел, что ваше имя связано с рядом других ответов CryptoJS, и я подумал, что вы хорошо разбираетесь в библиотеке. – seatosum

+0

Добавил резюме к началу, спасибо Artjom! – seatosum