UNPKG

aes-ctr-concurrent

Version:

Native TypeScript Node.js library for AES-256-CTR, enabling concurrent encryption/decryption with precise IV offset handling down to any byte level, not just full blocks.

55 lines 2.71 kB
import crypto from "crypto"; import AesCtrConcurrentError from "./errors/aesCtrConcurrentError.js"; const aesBlockSizeInBytes = 16n; const IV_MAX = 0xffffffffffffffffffffffffffffffffn; const IV_OVERFLOW_MODULO = IV_MAX + 1n; export function createCipher(key, iv, startPositionInBytesNumberOrBigInt = 0n) { const startPositionInBytes = BigInt(startPositionInBytesNumberOrBigInt); return getCryptoStream(`cipher`, key, iv, startPositionInBytes); } export function createDecipher(key, iv, startPositionInBytesNumberOrBigInt = 0n) { const startPositionInBytes = BigInt(startPositionInBytesNumberOrBigInt); return getCryptoStream(`decipher`, key, iv, startPositionInBytes); } function getCryptoStream(cipherOrDecipher, key, iv, startPositionInBytes) { throwIfParametersAreInvalid(iv, key, startPositionInBytes); const fullAesBlocksIncrement = startPositionInBytes / aesBlockSizeInBytes; const incrementedIv = incrementIvByFullBlocks(iv, fullAesBlocksIncrement); const cipher = cipherOrDecipher === `cipher` ? crypto.createCipheriv(`aes-256-ctr`, key, incrementedIv) : crypto.createDecipheriv(`aes-256-ctr`, key, incrementedIv); moveCounterToCurrentStartPositionWithinCurrentBlock(cipher, startPositionInBytes); return cipher; } function throwIfParametersAreInvalid(iv, key, startPositionInBytes) { if (!Buffer.isBuffer(iv) || iv.length !== 16) throw new AesCtrConcurrentError(`IV is required to be 16 bytes long Buffer`); if (!Buffer.isBuffer(key) || key.length !== 32) throw new AesCtrConcurrentError(`Key is required to be 32 bytes long Buffer`); if (startPositionInBytes < 0n) throw new AesCtrConcurrentError(`Start position must be greater or equal to 0`); } // This method is exported only for test purpose export function incrementIvByFullBlocks(originalIv, fullBlocksToIncrement) { let ivBigInt = bufferToBigInt(originalIv); ivBigInt += fullBlocksToIncrement; if (ivBigInt > IV_MAX) ivBigInt %= IV_OVERFLOW_MODULO; return bigIntToBuffer(ivBigInt); } function bufferToBigInt(buffer) { const hexedBuffer = buffer.toString(`hex`); return BigInt(`0x${hexedBuffer}`); } function bigIntToBuffer(bigInt) { const hexedBigInt = bigInt.toString(16).padStart(32, `0`); return Buffer.from(hexedBigInt, `hex`); } function moveCounterToCurrentStartPositionWithinCurrentBlock(cipher, startPositionInBytes) { const currentBlockOffset = Number(startPositionInBytes % aesBlockSizeInBytes); if (currentBlockOffset === 0) return; const bytesToBeDiscarded = Buffer.alloc(currentBlockOffset); cipher.update(bytesToBeDiscarded); } //# sourceMappingURL=aesCtrConcurrent.js.map