mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-16 08:58:30 +01:00
149 lines
5 KiB
JavaScript
149 lines
5 KiB
JavaScript
/**
|
|
* @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.<br><br>RFC4493 defines AES-CMAC that uses AES encryption with a 128-bit key.<br>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 algo = args[1];
|
|
|
|
const info = (function() {
|
|
switch (algo) {
|
|
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;
|