diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 43d5dc4e1..79f3c19c7 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -365,6 +365,7 @@ "Compare SSDEEP hashes", "Compare CTPH hashes", "HMAC", + "CMAC", "Bcrypt", "Bcrypt compare", "Bcrypt parse", diff --git a/src/core/operations/CMAC.mjs b/src/core/operations/CMAC.mjs new file mode 100644 index 000000000..12c4080cf --- /dev/null +++ b/src/core/operations/CMAC.mjs @@ -0,0 +1,143 @@ +/** + * @author mikecat + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import forge from "node-forge"; +import { toHexFast } from "../lib/Hex.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * CMAC operation + */ +class CMAC extends Operation { + + /** + * CMAC constructor + */ + constructor() { + super(); + + this.name = "CMAC"; + this.module = "Crypto"; + this.description = "CMAC is a block-cipher based message authentication code algorithm.

RFC4493 defines AES-CMAC that uses AES encryption with a 128-bit key.
NIST SP 800-38B suggests usages of AES with other key lengths and Triple DES."; + this.infoURL = "https://wikipedia.org/wiki/CMAC"; + this.inputType = "ArrayBuffer"; + this.outputType = "string"; + this.args = [ + { + "name": "Key", + "type": "toggleString", + "value": "", + "toggleValues": ["Hex", "UTF8", "Latin1", "Base64"] + }, + { + "name": "Encryption algorithm", + "type": "option", + "value": ["AES", "Triple DES"] + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const key = Utils.convertToByteString(args[0].string, args[0].option); + const info = (function() { + switch (args[1]) { + case "AES": + if (key.length !== 16 && key.length !== 24 && key.length !== 32) { + throw new OperationError("the key for AES must be either 16, 24, or 32 bytes (currently " + key.length + " bytes)"); + } + return { + "algorithm": "AES-ECB", + "key": key, + "blockSize": 16, + "Rb": new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87]), + }; + case "Triple DES": + if (key.length !== 16 && key.length !== 24) { + throw new OperationError("the key for Triple DES must be 16 or 24 bytes (currently " + key.length + " bytes)"); + } + return { + "algorithm": "3DES-ECB", + "key": key.length === 16 ? key + key.substring(0, 8) : key, + "blockSize": 8, + "Rb": new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0x1b]), + }; + default: + throw new OperationError("undefined encryption algorithm"); + } + })(); + const xor = function(a, b, out) { + if (!out) out = new Uint8Array(a.length); + for (let i = 0; i < a.length; i++) { + out[i] = a[i] ^ b[i]; + } + return out; + }; + const leftShift1 = function(a) { + const out = new Uint8Array(a.length); + let carry = 0; + for (let i = a.length - 1; i >= 0; i--) { + out[i] = (a[i] << 1) | carry; + carry = a[i] >> 7; + } + return out; + }; + const cipher = forge.cipher.createCipher(info.algorithm, info.key); + const encrypt = function(a, out) { + if (!out) out = new Uint8Array(a.length); + cipher.start(); + cipher.update(forge.util.createBuffer(a)); + cipher.finish(); + const cipherText = cipher.output.getBytes(); + for (let i = 0; i < a.length; i++) { + out[i] = cipherText.charCodeAt(i); + } + return out; + }; + + const L = encrypt(new Uint8Array(info.blockSize)); + const K1 = leftShift1(L); + if (L[0] & 0x80) xor(K1, info.Rb, K1); + const K2 = leftShift1(K1); + if (K1[0] & 0x80) xor(K2, info.Rb, K2); + + const n = Math.ceil(input.byteLength / info.blockSize); + const lastBlock = (function() { + if (n === 0) { + const data = new Uint8Array(K2); + data[0] ^= 0x80; + return data; + } + const inputLast = new Uint8Array(input, info.blockSize * (n - 1)); + if (inputLast.length === info.blockSize) { + return xor(inputLast, K1, inputLast); + } else { + const data = new Uint8Array(info.blockSize); + data.set(inputLast, 0); + data[inputLast.length] = 0x80; + return xor(data, K2, data); + } + })(); + const X = new Uint8Array(info.blockSize); + const Y = new Uint8Array(info.blockSize); + for (let i = 0; i < n - 1; i++) { + xor(X, new Uint8Array(input, info.blockSize * i, info.blockSize), Y); + encrypt(Y, X); + } + xor(lastBlock, X, Y); + const T = encrypt(Y); + return toHexFast(T); + } + +} + +export default CMAC; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 19e709709..18a1b59f9 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -124,6 +124,7 @@ import "./tests/UnescapeString.mjs"; import "./tests/LS47.mjs"; import "./tests/LZString.mjs"; import "./tests/NTLM.mjs"; +import "./tests/CMAC.mjs"; // Cannot test operations that use the File type yet // import "./tests/SplitColourChannels.mjs"; diff --git a/tests/operations/tests/CMAC.mjs b/tests/operations/tests/CMAC.mjs new file mode 100644 index 000000000..92224968a --- /dev/null +++ b/tests/operations/tests/CMAC.mjs @@ -0,0 +1,314 @@ +/** + * @author mikecat + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +// values in "NIST's CSRC" testcases are taken from here: +// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values + +TestRegister.addTests([ + { + "name": "CMAC-AES128 NIST's CSRC Example #1", + "input": "", + "expectedOutput": "bb1d6929e95937287fa37d129b756746", + "recipeConfig": [ + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "2b7e151628aed2a6abf7158809cf4f3c"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES128 NIST's CSRC Example #2", + "input": "6bc1bee22e409f96e93d7e117393172a", + "expectedOutput": "070a16b46b4d4144f79bdd9dd04a287c", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "2b7e151628aed2a6abf7158809cf4f3c"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES128 NIST's CSRC Example #3", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a57", + "expectedOutput": "7d85449ea6ea19c823a7bf78837dfade", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "2b7e151628aed2a6abf7158809cf4f3c"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES128 NIST's CSRC Example #4", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "expectedOutput": "51f0bebf7e3b9d92fc49741779363cfe", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "2b7e151628aed2a6abf7158809cf4f3c"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES192 NIST's CSRC Example #1", + "input": "", + "expectedOutput": "d17ddf46adaacde531cac483de7a9367", + "recipeConfig": [ + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES192 NIST's CSRC Example #2", + "input": "6bc1bee22e409f96e93d7e117393172a", + "expectedOutput": "9e99a7bf31e710900662f65e617c5184", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES192 NIST's CSRC Example #3", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a57", + "expectedOutput": "3d75c194ed96070444a9fa7ec740ecf8", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES192 NIST's CSRC Example #4", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "expectedOutput": "a1d5df0eed790f794d77589659f39a11", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES256 NIST's CSRC Example #1", + "input": "", + "expectedOutput": "028962f61b7bf89efc6b551f4667d983", + "recipeConfig": [ + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES256 NIST's CSRC Example #2", + "input": "6bc1bee22e409f96e93d7e117393172a", + "expectedOutput": "28a7023f452e8f82bd4bf28d8c37c35c", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES256 NIST's CSRC Example #3", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a57", + "expectedOutput": "156727dc0878944a023c1fe03bad6d93", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"}, "AES"] + }, + ] + }, + { + "name": "CMAC-AES256 NIST's CSRC Example #4", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "expectedOutput": "e1992190549f6ed5696a2c056c315410", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"}, "AES"] + }, + ] + }, + { + "name": "CMAC-TDES (1) NIST's CSRC Sample #1", + "input": "", + "expectedOutput": "7db0d37df936c550", + "recipeConfig": [ + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "0123456789abcdef23456789abcdef01456789abcdef0123"}, "Triple DES"] + }, + ] + }, + { + "name": "CMAC-TDES (1) NIST's CSRC Sample #2", + "input": "6bc1bee22e409f96e93d7e117393172a", + "expectedOutput": "30239cf1f52e6609", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "0123456789abcdef23456789abcdef01456789abcdef0123"}, "Triple DES"] + }, + ] + }, + { + "name": "CMAC-TDES (1) NIST's CSRC Sample #3", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a57", + "expectedOutput": "6c9f3ee4923f6be2", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "0123456789abcdef23456789abcdef01456789abcdef0123"}, "Triple DES"] + }, + ] + }, + { + "name": "CMAC-TDES (1) NIST's CSRC Sample #4", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51", + "expectedOutput": "99429bd0bf7904e5", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "0123456789abcdef23456789abcdef01456789abcdef0123"}, "Triple DES"] + }, + ] + }, + { + "name": "CMAC-TDES (2) NIST's CSRC Sample #1", + "input": "", + "expectedOutput": "79ce52a7f786a960", + "recipeConfig": [ + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "0123456789abcdef23456789abcdef010123456789abcdef"}, "Triple DES"] + }, + ] + }, + { + "name": "CMAC-TDES (2) NIST's CSRC Sample #2", + "input": "6bc1bee22e409f96e93d7e117393172a", + "expectedOutput": "cc18a0b79af2413b", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "0123456789abcdef23456789abcdef010123456789abcdef"}, "Triple DES"] + }, + ] + }, + { + "name": "CMAC-TDES (2) NIST's CSRC Sample #3", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a57", + "expectedOutput": "c06d377ecd101969", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "0123456789abcdef23456789abcdef010123456789abcdef"}, "Triple DES"] + }, + ] + }, + { + "name": "CMAC-TDES (2) NIST's CSRC Sample #4", + "input": "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51", + "expectedOutput": "9cd33580f9b64dfb", + "recipeConfig": [ + { + "op": "From Hex", + "args": ["None"] + }, + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "0123456789abcdef23456789abcdef010123456789abcdef"}, "Triple DES"] + }, + ] + }, + { + "name": "CMAC-AES: invalid key length", + "input": "", + "expectedOutput": "the key for AES must be either 16, 24, or 32 bytes (currently 20 bytes)", + "recipeConfig": [ + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "00112233445566778899aabbccddeeff01234567"}, "AES"] + }, + ] + }, + { + "name": "CMAC-TDES: invalid key length", + "input": "", + "expectedOutput": "the key for Triple DES must be 16 or 24 bytes (currently 20 bytes)", + "recipeConfig": [ + { + "op": "CMAC", + "args": [{"option": "Hex", "string": "00112233445566778899aabbccddeeff01234567"}, "Triple DES"] + }, + ] + }, +]);