79220755

Date: 2024-11-24 18:01:41
Score: 1
Natty:
Report link

Original Increment Function

NodeJS uses OpenSSL under the hood and the code for CTR mode can be found here: ctr128.c implementation An equivalent function in Node.js might look like this:

function ctr128Inc(counter) {
    let c = 1;
    let n = 16;

    do {
        n -= 1;
        c += counter[n];
        counter[n] = c & 0xFF;
        c = c >> 8;
    } while (n);
}

This function increments the counter by one block. To increment by multiple blocks, you might wrap it as follows:

function incrementIVOpenSSL(iv, increment) {
    for (let i = 0; i < increment; i++)
        ctr128Inc(iv)
}

However, this method is inefficient for large increments due to its linear time complexity and is practically unusable in real-world applications.

Readable and Efficient Version Using BigInt

Node.js introduces the BigInt type, which can handle arbitrarily large integers efficiently. We can utilize it to increment the IV by converting the IV buffer to a BigInt, performing the increment, and converting it back to a Buffer:

const IV_MAX = 0xffffffffffffffffffffffffffffffffn;
const IV_OVERFLOW_MODULO = IV_MAX + 1n;

function incrementIvByFullBlocks(originalIv: Buffer, fullBlocksToIncrement: bigint): Buffer {
    let ivBigInt = bufferToBigInt(originalIv);

    ivBigInt += fullBlocksToIncrement;

    if (ivBigInt > IV_MAX)
        ivBigInt %= IV_OVERFLOW_MODULO;

    return bigIntToBuffer(ivBigInt);
}

function bufferToBigInt(buffer: Buffer): bigint {
    const hexedBuffer = buffer.toString(`hex`);
    return BigInt(`0x${hexedBuffer}`);
}

function bigIntToBuffer(bigInt: bigint): Buffer {
    const hexedBigInt = bigInt.toString(16).padStart(32, `0`);
    return Buffer.from(hexedBigInt, `hex`);
}

Only this method isn't as fast as the one proposed by @youen. On my PC, for 100k iterations, @youn's method finishes in 15ms and BigInt version in 90ms. It is not a big difference though and BigInt version is by far more obvious for a reader.

Alternative Implementations and Performance Comparison

Another implementation can be found in the crypto-aes-ctr library. It performs the increment operation more quickly (~7ms for 100,000 iterations) but sacrifices readability. It also supports more edge cases, mostly connected with incrementing IV by very big numbers. Something that probably won't be the case in real-life scenarios for a very long time (until we switch to Petabytes drives). For a detailed comparison refer to my GitHub gist. The BigInt method and the OpenSSL-inspired function are the only ones passing all edge case tests, with the BigInt approach offering a good balance between performance and readability.

Introducing aes-ctr-concurrent

To simplify the process and enhance performance in concurrent environments, I've developed the aes-ctr-concurrent library, available on NPM. This library:

Reasons:
  • Contains signature (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @youen
  • User mentioned (0): @youn's
  • Low reputation (0.5):
Posted by: Rychu