diff --git a/.gitignore b/.gitignore index b947077..c2658d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ node_modules/ -dist/ diff --git a/dist/aes-cmac.d.ts b/dist/aes-cmac.d.ts new file mode 100644 index 0000000..7399b43 --- /dev/null +++ b/dist/aes-cmac.d.ts @@ -0,0 +1,4 @@ +/// +export declare const generateSubkeys: (key: Buffer) => [Buffer, Buffer]; +export declare const generateAesCmac: (key: Buffer, message: Buffer) => Buffer; +export declare const verifyAesCmac: (aesCmac: Buffer, key: Buffer, message: Buffer) => boolean; diff --git a/dist/aes-cmac.js b/dist/aes-cmac.js new file mode 100644 index 0000000..1046c87 --- /dev/null +++ b/dist/aes-cmac.js @@ -0,0 +1,73 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.verifyAesCmac = exports.generateAesCmac = exports.generateSubkeys = void 0; +const crypto_1 = __importDefault(require("crypto")); +const buffer_utils_1 = require("./buffer-utils"); +const ZERO = Buffer.from("00000000000000000000000000000000", "hex"); +const RB = Buffer.from("00000000000000000000000000000087", "hex"); +const BLOCK_SIZE = 16; +const Ciphers = { + aes128: 16, + aes192: 24, + aes256: 32, +}; +const aes = (key, message) => { + const algorithm = Object.keys(Ciphers).find((cipherKey) => { + return (Ciphers[cipherKey] === key.length); + }); + if (!algorithm) { + throw new Error("Keys must be 128, 192, or 256 bits in length."); + } + const cipher = crypto_1.default.createCipheriv(algorithm, key, ZERO); + const result = cipher.update(message); + cipher.final(); + return result; +}; +const generateSubkeys = (key) => { + const l = aes(key, ZERO); + const k1 = l[0] & 0x80 ? buffer_utils_1.xor(buffer_utils_1.bitShiftLeft(l), RB) : buffer_utils_1.bitShiftLeft(l); + const k2 = k1[0] & 0x80 ? buffer_utils_1.xor(buffer_utils_1.bitShiftLeft(k1), RB) : buffer_utils_1.bitShiftLeft(k1); + return [k1, k2]; +}; +exports.generateSubkeys = generateSubkeys; +const generateAesCmac = (key, message) => { + const [k1, k2] = exports.generateSubkeys(key); + const blockCount = Math.ceil(message.length / BLOCK_SIZE); + const lastBlockCompleteFlag = blockCount === 0 ? false : message.length % BLOCK_SIZE === 0; + const lastBlockIndex = blockCount === 0 ? 0 : blockCount - 1; + const lastBlock = lastBlockCompleteFlag + ? buffer_utils_1.xor(getMessageBlock(message, lastBlockIndex), k1) + : buffer_utils_1.xor(getPaddedMessageBlock(message, lastBlockIndex), k2); + let x = Buffer.from("00000000000000000000000000000000", "hex"); + let y; + for (let index = 0; index < lastBlockIndex; index++) { + y = buffer_utils_1.xor(x, getMessageBlock(message, index)); + x = aes(key, y); + } + return aes(key, buffer_utils_1.xor(lastBlock, x)); +}; +exports.generateAesCmac = generateAesCmac; +const verifyAesCmac = (aesCmac, key, message) => { + const expected = exports.generateAesCmac(key, message); + return aesCmac.equals(expected); +}; +exports.verifyAesCmac = verifyAesCmac; +const getMessageBlock = (message, blockIndex) => { + const block = Buffer.alloc(BLOCK_SIZE); + const start = blockIndex * BLOCK_SIZE; + const end = start + BLOCK_SIZE; + message.copy(block, 0, start, end); + return block; +}; +const getPaddedMessageBlock = (message, blockIndex) => { + const block = Buffer.alloc(BLOCK_SIZE); + const start = blockIndex * BLOCK_SIZE; + const end = message.length; + block.fill(0); + message.copy(block, 0, start, end); + block[end - start] = 0x80; + return block; +}; diff --git a/dist/buffer-utils.d.ts b/dist/buffer-utils.d.ts new file mode 100644 index 0000000..4a074da --- /dev/null +++ b/dist/buffer-utils.d.ts @@ -0,0 +1,3 @@ +/// +export declare const bitShiftLeft: (buffer: Buffer) => Buffer; +export declare const xor: (buffer1: Buffer, buffer2: Buffer) => Buffer; diff --git a/dist/buffer-utils.js b/dist/buffer-utils.js new file mode 100644 index 0000000..fb7d666 --- /dev/null +++ b/dist/buffer-utils.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.xor = exports.bitShiftLeft = void 0; +const bitShiftLeft = (buffer) => Buffer.alloc(buffer.length).map((_, i) => { + const x = buffer[i] << 1; + return buffer[i + 1] & 0x80 ? x + 0x01 : x; +}); +exports.bitShiftLeft = bitShiftLeft; +const xor = (buffer1, buffer2) => { + const length = Math.min(buffer1.length, buffer2.length); + return Buffer.alloc(length).map((_, i) => buffer1[i] ^ buffer2[i]); +}; +exports.xor = xor; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..e61787a --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1 @@ +export { generateAesCmac } from "./aes-cmac"; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..a53dee6 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.generateAesCmac = void 0; +var aes_cmac_1 = require("./aes-cmac"); +Object.defineProperty(exports, "generateAesCmac", { enumerable: true, get: function () { return aes_cmac_1.generateAesCmac; } });