Open Bug 1803105 Opened 3 years ago Updated 2 years ago

Counter rollover in AES-CTR is not implemented

Categories

(NSS :: Libraries, defect, P3)

Tracking

(firefox109 affected)

Tracking Status
firefox109 --- affected

People

(Reporter: jonasfj, Unassigned)

Details

(Whiteboard: [nss-triage])

AES-CTR operations says to use standard incrementing function, which will start counting from
zero when it rolls over. Both Chrome and Safari does counter rollovers correctly.

But it seems to not be handled in nss.

Whether this is addressed in NSS or the web crypto implementation is probably not important.
In chromium it's handled by calling into boringssl twice, in the webcrypto implementation.

This is demonstrated in the example below, which can also be verified in https://jsfiddle.net/ck3mfs92/

// Import a random key
let key = await window.crypto.subtle.importKey('jwk', {
  alg: "A128CTR",
  ext: true,
  k: "CchSwZcamSMV2fmTh2BUNQ",
  key_ops: ["encrypt", "decrypt"],
  kty: "oct"
}, {
  name: 'AES-CTR',
  length: 128,
}, true, ['encrypt', 'decrypt']);

// Create a counter that end with 255, such that more than one block will cause an overflow
// if the counter length is <= 8 bits
let ctr = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff]);

// Create data to be encrypted, at-least two blocks to trigger overflow
// In this example we just use zeros
let data = new Uint8Array(32); // 32 zero bytes

// Encrypt data, this should be deterministic.
// We limit the counter length to 4 bits, inorder to trigger a counter overflow.
let encryptedData = await window.crypto.subtle.encrypt({
  name: 'AES-CTR',
  counter: ctr,
  length: 4,
}, key, data);

// Print encrypted bytes as base64
const toBase64 = (data) => btoa(String.fromCharCode.apply(null, new Uint8Array(data)));
let encryptedDataBase64 = toBase64(encryptedData);
console.log('encryptedDataBase64: ' + encryptedDataBase64);
// This is the expected value as will be generated by Chrome and Safari
const expectedResult = 'HnadJbfAWwobUbUtdqXSY82RWUEQchseQfWqfK7JxQs=';
console.log('Expected: ' + expectedResult);

console.assert(encryptedDataBase64 == expectedResult);

I suppose that most reasonably sane users of AES-CTR are using a large counter length, hopefully around 64 bits (and not 4 as in my contrived example above). And hopefully they are using a random counter value, or at-least one that isn't 0xff... So in practice it's probably rare that anyone is affected by this. But it's nice not to have incompatibility issues between implementations :D

Assignee: nobody → nobody
Component: DOM: Web Crypto → Libraries
Product: Core → NSS
Summary: Counter rollover in AES-CTR does not work → Counter rollover in AES-CTR is not implemented
Version: Trunk → other

The severity field is not set for this bug.
:beurdouche, could you have a look please?

For more information, please visit auto_nag documentation.

Flags: needinfo?(bbeurdouche)
Severity: -- → S4
Flags: needinfo?(bbeurdouche)
Priority: -- → P3
Whiteboard: [nss-triage]
You need to log in before you can comment on or make changes to this bug.