ricmoo / aes-js

A pure JavaScript implementation of the AES block cipher and all common modes of operation for node.js or web browsers.
MIT License
1.45k stars 272 forks source link

How does the counter work? #67

Closed RokerHRO closed 5 years ago

RokerHRO commented 6 years ago

It looks to me that the Counter.prototype.increment() function increments all 16 bytes of the IV / Nonce (modulo 256), so after 256 AES blocks it is the same as at the beginning.

I'd expect that nonce is initialized with a random bytes and incremented like a 256-bit-integer for each AES block.

Am I right?

TotalTechGeek commented 6 years ago

tl;dr: No.


Longer explanation:

I am not the author, but I believe this is the code that you've called into question:

Counter.prototype.increment = function() {
        for (var i = 15; i >= 0; i--) {
            if (this._counter[i] === 255) {
                this._counter[i] = 0;
            } else {
                this._counter[i]++;
                break;
            }
        }
    }

Here it is with some annotations:

Counter.prototype.increment = function() {
        for (var i = 15; i >= 0; i--) { // iterate from the end of the IV to the beginning
            if (this._counter[i] === 255) { // if the max value has been reached, 
                this._counter[i] = 0; // set it to zero ("increment"), and continue incrementing.
            } else { // otherwise
                this._counter[i]++; // increment the value
                break; // and then stop incrementing
            }
        }
    }

As you can see, the break statement prevents it from going further.

So if you had a counter that looked like:
... 0, 255, 255, 255

Each iteration would look like (bold indicates focus): -> ... 0, 255, 255, 0 -> ... 0, 255, 0, 0 -> ... 0, 0, 0, 0 -> ... 1, 0, 0, 0 (break)

The code is slightly confusing at first glance, but it's a correct implementation.

ricmoo commented 6 years ago

Thanks @TotalTechGeek !

Exactly, it does the equivalent of "long addition", since JavaScript numbers are only compact for 53 bits of mantissa, and we require 128 bits (16 bytes). When you add 1, you must carry the overflow along until there is no longer any overflow.

Almost always, that for loop will break out of the first iteration. Only in the case of overflow will it continue, and only once for each overflow. So, 0xfe would exit the loop after 1 iteration. 0xff would require 2, as would 0xfeff. 0xffff would exit after 3 times.

Also, a counter should not be initialized to a "random" value, since both the encryption and decryption must agree on the IV. The application is required to synchronize these, somehow. For example, in a version 3 Ethereum JSON Secret Store, the IV is chosen randomly by the application, and included in the wallet.

Ruffio commented 6 years ago

@RokerHRO Has this question been answeared and this 'issue' be closed?

Ruffio commented 6 years ago

@ricmoo This issue/question should be closed due to the question has been answered and upon ping the originater has failed to reply within 10 days.

ricmoo commented 5 years ago

Closing this now, but if you have further questions, please feel free to re-open.

Thanks! :)