简体   繁体   English

正确使用随机数和计数器进行AES-CTR模式

[英]Using the nonce and counter correctly for AES-CTR mode

I understand that in AES Counter mode I need to use a 128 bit nonce. 据我所知,在AES计数器模式下,我需要使用128位随机数。 The naïve way to do that would be to use a random 128 bit nonce, but I'm not sure the algorithm will be able to increment the counter correctly if it's passed as all random bits. 这种天真的方法是使用一个随机的128位随机数,但我不确定如果它作为所有随机位传递,算法将能够正确递增计数器。 I thought the correct way to do it is to use a 96 bit nonce and also a 32 bit counter starting at 0, for example: 我认为正确的方法是使用96位nonce以及从0开始的32位计数器,例如:

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

Is this the correct way to do it with the CryptoJS library ? 这是使用CryptoJS库执行此操作的正确方法吗? Or what is the correct way? 或者正确的方法是什么?

I'm going to answer my own question as I went digging into the library code to see what it really does. 当我深入研究库代码以了解它的真正作用时,我将回答我自己的问题。

Summary: 摘要:

The answer is you can use either of two methods and it will work as expected: 答案是你可以使用两种方法中的任何一种,它将按预期工作:

1) Pass in a random nonce of 96 bits in length and the library itself will add the 32 bit counter automatically and increment it with every keystream block generated. 1)传入一个长度为96位的随机数,并且库本身将自动添加32位计数器,并在生成的每个密钥流块中递增它。 Eg 例如

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 in a random nonce of 96 bits in length and explicitly specify the 32 bit counter as well if you want to. 2)传入一个长度为96位的随机数,并在需要时明确指定32位计数器。 You can even specify a counter like 00000009 if you want to start encrypting/decrypting from the 9th block. 如果要从第9个块开始加密/解密,甚至可以指定像00000009这样的计数器。 Below is an example starting from counter 0: 以下是从计数器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 });

Explanation: 说明:

Using the code in the question with 32 bit counter of 00000000 , the relevant code is in this file mode-ctr.js : 使用32位计数器00000000的问题中的代码,相关代码在此文件模式-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;
}());

When running this code in a browser JS debugger using a breakpoint, it converts the nonceAndCounter into a WordArray consisting of 32 bit elements: 当使用断点在浏览器JS调试器中运行此代码时,它会将nonceAndCounter转换为由32位元素组成的WordArray:

[587320654, -142251766, 455884203, 0]

This is used to encrypt a block. 这用于加密块。 To encrypt the next block it runs this line: 要加密下一个块,它将运行此行:

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

Which evaluates to take the counter[3] element ie the integer 0 and increments it to: 其中计算取counter[3]元素,即整数0,并将其递增为:

[587320654, -142251766, 455884203, 1]

With subsequent blocks and nonces I can see... 随后的块和nonce我可以看到......

[587320654, -142251766, 455884203, 2]

[587320654, -142251766, 455884203, 3]

[587320654, -142251766, 455884203, 4]

And so on. 等等。 So it appears to be working correctly this way. 所以它似乎以这种方式正常工作。

Contrast this with how it works if you pass a 128 bit random nonce eg 如果你传递一个128位的随机数,例如,它与它的工作方式形成对比

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

This produces a nonce of: 这产生了一个nonce:

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

So it creates 5 array elements!? 所以它创建了5个数组元素!? Then the function increments the fourth element from -543577677 to -543577676 , then -543577675 , then -543577674 and so on. 然后该函数将第四个元素从-543577677-543577676 ,然后是-543577675 ,然后是-543577674 ,依此类推。 So it still works in a way, but but does not increment as nicely as starting from 0 and is perhaps more error prone. 所以它仍然在某种程度上起作用,但是从0开始并没有那么好地增加,并且可能更容易出错。

When I passed in just a 96 bit random nonce, the library automatically added the start counter as 0 to the end of the counter array and incremented it correctly for subsequent blocks. 当我只传入96位随机数时,库自动将起始计数器作为0添加到计数器数组的末尾,并为后续块正确递增。 eg 例如

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM