mirror of
https://github.com/gchq/CyberChef
synced 2024-12-28 21:43:21 +00:00
Improved XXTEA operations. Added XXTEA Decrypt.
This commit is contained in:
parent
2e2490ce47
commit
0a353eeb37
7 changed files with 308 additions and 225 deletions
|
@ -117,6 +117,8 @@
|
|||
"XOR Brute Force",
|
||||
"Vigenère Encode",
|
||||
"Vigenère Decode",
|
||||
"XXTEA Encrypt",
|
||||
"XXTEA Decrypt",
|
||||
"To Morse Code",
|
||||
"From Morse Code",
|
||||
"Bacon Cipher Encode",
|
||||
|
@ -155,8 +157,7 @@
|
|||
"Typex",
|
||||
"Lorenz",
|
||||
"Colossus",
|
||||
"SIGABA",
|
||||
"XXTEA"
|
||||
"SIGABA"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
174
src/core/lib/XXTEA.mjs
Normal file
174
src/core/lib/XXTEA.mjs
Normal file
|
@ -0,0 +1,174 @@
|
|||
/**
|
||||
* XXTEA library
|
||||
*
|
||||
* Encryption Algorithm Authors:
|
||||
* David J. Wheeler
|
||||
* Roger M. Needham
|
||||
*
|
||||
* @author Ma Bingyao [mabingyao@gmail.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const DELTA = 0x9E3779B9;
|
||||
|
||||
/**
|
||||
* Convert a buffer to a Uint8Array
|
||||
* @param {Uint32Array} v
|
||||
* @param {boolean} includeLength
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
function toUint8Array(v, includeLength) {
|
||||
const length = v.length;
|
||||
let n = length << 2;
|
||||
if (includeLength) {
|
||||
const m = v[length - 1];
|
||||
n -= 4;
|
||||
if ((m < n - 3) || (m > n)) {
|
||||
return null;
|
||||
}
|
||||
n = m;
|
||||
}
|
||||
const bytes = new Uint8Array(n);
|
||||
for (let i = 0; i < n; i++) {
|
||||
bytes[i] = v[i >> 2] >> ((i & 3) << 3);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a buffer to a Uint32Array
|
||||
* @param {TypedArray} bs
|
||||
* @param {boolean} includeLength
|
||||
* @returns {Uint32Array}
|
||||
*/
|
||||
function toUint32Array(bs, includeLength) {
|
||||
const length = bs.length;
|
||||
let n = length >> 2;
|
||||
if ((length & 3) !== 0) {
|
||||
++n;
|
||||
}
|
||||
let v;
|
||||
if (includeLength) {
|
||||
v = new Uint32Array(n + 1);
|
||||
v[n] = length;
|
||||
} else {
|
||||
v = new Uint32Array(n);
|
||||
}
|
||||
for (let i = 0; i < length; ++i) {
|
||||
v[i >> 2] |= bs[i] << ((i & 3) << 3);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mask an int to 32 bits
|
||||
* @param {number} i
|
||||
* @returns {number}
|
||||
*/
|
||||
function int32(i) {
|
||||
return i & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* MX function for data randomisation
|
||||
* @param {number} sum
|
||||
* @param {number} y
|
||||
* @param {number} z
|
||||
* @param {number} p
|
||||
* @param {number} e
|
||||
* @param {number} k
|
||||
* @returns {number}
|
||||
*/
|
||||
function mx(sum, y, z, p, e, k) {
|
||||
return ((z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4)) ^ ((sum ^ y) + (k[p & 3 ^ e] ^ z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure an array is a multiple of 16 bits
|
||||
* @param {TypedArray} k
|
||||
* @returns {TypedArray}
|
||||
*/
|
||||
function fixk(k) {
|
||||
if (k.length < 16) {
|
||||
const key = new Uint8Array(16);
|
||||
key.set(k);
|
||||
return key;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs XXTEA encryption on a Uint32Array
|
||||
* @param {Uint32Array} v
|
||||
* @param {Uint32Array} k
|
||||
* @returns {Uint32Array}
|
||||
*/
|
||||
function encryptUint32Array(v, k) {
|
||||
const length = v.length;
|
||||
const n = length - 1;
|
||||
let y, z, sum, e, p, q;
|
||||
z = v[n];
|
||||
sum = 0;
|
||||
for (q = Math.floor(6 + 52 / length) | 0; q > 0; --q) {
|
||||
sum = int32(sum + DELTA);
|
||||
e = sum >>> 2 & 3;
|
||||
for (p = 0; p < n; ++p) {
|
||||
y = v[p + 1];
|
||||
z = v[p] = int32(v[p] + mx(sum, y, z, p, e, k));
|
||||
}
|
||||
y = v[0];
|
||||
z = v[n] = int32(v[n] + mx(sum, y, z, n, e, k));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs XXTEA decryption on a Uint32Array
|
||||
* @param {Uint32Array} v
|
||||
* @param {Uint32Array} k
|
||||
* @returns {Uint32Array}
|
||||
*/
|
||||
function decryptUint32Array(v, k) {
|
||||
const length = v.length;
|
||||
const n = length - 1;
|
||||
let y, z, sum, e, p;
|
||||
y = v[0];
|
||||
const q = Math.floor(6 + 52 / length);
|
||||
for (sum = int32(q * DELTA); sum !== 0; sum = int32(sum - DELTA)) {
|
||||
e = sum >>> 2 & 3;
|
||||
for (p = n; p > 0; --p) {
|
||||
z = v[p - 1];
|
||||
y = v[p] = int32(v[p] - mx(sum, y, z, p, e, k));
|
||||
}
|
||||
z = v[n];
|
||||
y = v[0] = int32(v[0] - mx(sum, y, z, 0, e, k));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt function
|
||||
* @param {TypedArray} data
|
||||
* @param {TypedArray} key
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function encrypt(data, key) {
|
||||
if (data === undefined || data === null || data.length === 0) {
|
||||
return data;
|
||||
}
|
||||
return toUint8Array(encryptUint32Array(toUint32Array(data, true), toUint32Array(fixk(key), false)), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt function
|
||||
* @param {TypedArray} data
|
||||
* @param {TypedArray} key
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function decrypt(data, key) {
|
||||
if (data === undefined || data === null || data.length === 0) {
|
||||
return data;
|
||||
}
|
||||
return toUint8Array(decryptUint32Array(toUint32Array(data, false), toUint32Array(fixk(key), false)), true);
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
/**
|
||||
* @author devcydo [devcydo@gmail.com]
|
||||
* @author Ma Bingyao [mabingyao@gmail.com]
|
||||
* @copyright Crown Copyright 2022
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import {toBase64} from "../lib/Base64.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* XXTEA Encrypt operation
|
||||
*/
|
||||
class XXTEAEncrypt extends Operation {
|
||||
|
||||
/**
|
||||
* XXTEAEncrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "XXTEA";
|
||||
this.module = "Default";
|
||||
this.description = "Corrected Block TEA (often referred to as XXTEA) is a block cipher designed to correct weaknesses in the original Block TEA. XXTEA operates on variable-length blocks that are some arbitrary multiple of 32 bits in size (minimum 64 bits). The number of full cycles depends on the block size, but there are at least six (rising to 32 for small block sizes). The original Block TEA applies the XTEA round function to each word in the block and combines it additively with its leftmost neighbour. Slow diffusion rate of the decryption process was immediately exploited to break the cipher. Corrected Block TEA uses a more involved round function which makes use of both immediate neighbours in processing each word in the block.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/XXTEA";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Key",
|
||||
"type": "string",
|
||||
"value": "",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let key = args[0];
|
||||
|
||||
if (input === undefined || input === null || input.length === 0) {
|
||||
throw new OperationError("Invalid input length (0)");
|
||||
}
|
||||
|
||||
if (key === undefined || key === null || key.length === 0) {
|
||||
throw new OperationError("Invalid key length (0)");
|
||||
}
|
||||
|
||||
input = Utils.convertToByteString(input, "utf8");
|
||||
key = Utils.convertToByteString(key, "utf8");
|
||||
|
||||
input = this.convertToUint32Array(input, true);
|
||||
key = this.fixLength(this.convertToUint32Array(key, false));
|
||||
|
||||
let encrypted = this.encryptUint32Array(input, key);
|
||||
|
||||
encrypted = toBase64(this.toBinaryString(encrypted, false));
|
||||
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Uint32Array to binary string
|
||||
*
|
||||
* @param {Uint32Array} v
|
||||
* @param {Boolean} includeLength
|
||||
* @returns {string}
|
||||
*/
|
||||
toBinaryString(v, includeLENGTH) {
|
||||
const LENGTH = v.length;
|
||||
let n = LENGTH << 2;
|
||||
if (includeLENGTH) {
|
||||
const M = v[LENGTH - 1];
|
||||
n -= 4;
|
||||
if ((M < n - 3) || (M > n)) {
|
||||
return null;
|
||||
}
|
||||
n = M;
|
||||
}
|
||||
for (let i = 0; i < LENGTH; i++) {
|
||||
v[i] = String.fromCharCode(
|
||||
v[i] & 0xFF,
|
||||
v[i] >>> 8 & 0xFF,
|
||||
v[i] >>> 16 & 0xFF,
|
||||
v[i] >>> 24 & 0xFF
|
||||
);
|
||||
}
|
||||
const RESULT = v.join("");
|
||||
if (includeLENGTH) {
|
||||
return RESULT.substring(0, n);
|
||||
}
|
||||
return RESULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} sum
|
||||
* @param {number} y
|
||||
* @param {number} z
|
||||
* @param {number} p
|
||||
* @param {number} e
|
||||
* @param {number} k
|
||||
* @returns {number}
|
||||
*/
|
||||
mx(sum, y, z, p, e, k) {
|
||||
return ((z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4)) ^ ((sum ^ y) + (k[p & 3 ^ e] ^ z));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt Uint32Array
|
||||
*
|
||||
* @param {Uint32Array} v
|
||||
* @param {number} k
|
||||
* @returns {Uint32Array}
|
||||
*/
|
||||
encryptUint32Array(v, k) {
|
||||
const LENGTH = v.length;
|
||||
const N = LENGTH - 1;
|
||||
let y, z, sum, e, p, q;
|
||||
z = v[N];
|
||||
sum = 0;
|
||||
for (q = Math.floor(6 + 52 / LENGTH) | 0; q > 0; --q) {
|
||||
sum = (sum + 0x9E3779B9) & 0xFFFFFFFF;
|
||||
e = sum >>> 2 & 3;
|
||||
for (p = 0; p < N; ++p) {
|
||||
y = v[p + 1];
|
||||
z = v[p] = (v[p] + this.mx(sum, y, z, p, e, k)) & 0xFFFFFFFF;
|
||||
}
|
||||
y = v[0];
|
||||
z = v[N] = (v[N] + this.mx(sum, y, z, N, e, k)) & 0xFFFFFFFF;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the Uint32Array lenght to 4
|
||||
*
|
||||
* @param {Uint32Array} k
|
||||
* @returns {Uint32Array}
|
||||
*/
|
||||
fixLength(k) {
|
||||
if (k.length < 4) {
|
||||
k.length = 4;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to Uint32Array
|
||||
*
|
||||
* @param {string} bs
|
||||
* @param {Boolean} includeLength
|
||||
* @returns {Uint32Array}
|
||||
*/
|
||||
convertToUint32Array(bs, includeLength) {
|
||||
const LENGTH = bs.length;
|
||||
let n = LENGTH >> 2;
|
||||
if ((LENGTH & 3) !== 0) {
|
||||
++n;
|
||||
}
|
||||
let v;
|
||||
if (includeLength) {
|
||||
v = new Array(n + 1);
|
||||
v[n] = LENGTH;
|
||||
} else {
|
||||
v = new Array(n);
|
||||
}
|
||||
for (let i = 0; i < LENGTH; ++i) {
|
||||
v[i >> 2] |= bs.charCodeAt(i) << ((i & 3) << 3);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default XXTEAEncrypt;
|
57
src/core/operations/XXTEADecrypt.mjs
Normal file
57
src/core/operations/XXTEADecrypt.mjs
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* @author devcydo [devcydo@gmail.com]
|
||||
* @author Ma Bingyao [mabingyao@gmail.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2024
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import {decrypt} from "../lib/XXTEA.mjs";
|
||||
|
||||
/**
|
||||
* XXTEA Decrypt operation
|
||||
*/
|
||||
class XXTEADecrypt extends Operation {
|
||||
|
||||
/**
|
||||
* XXTEADecrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "XXTEA Decrypt";
|
||||
this.module = "Ciphers";
|
||||
this.description = "Corrected Block TEA (often referred to as XXTEA) is a block cipher designed to correct weaknesses in the original Block TEA. XXTEA operates on variable-length blocks that are some arbitrary multiple of 32 bits in size (minimum 64 bits). The number of full cycles depends on the block size, but there are at least six (rising to 32 for small block sizes). The original Block TEA applies the XTEA round function to each word in the block and combines it additively with its leftmost neighbour. Slow diffusion rate of the decryption process was immediately exploited to break the cipher. Corrected Block TEA uses a more involved round function which makes use of both immediate neighbours in processing each word in the block.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/XXTEA";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const key = new Uint8Array(Utils.convertToByteArray(args[0].string, args[0].option));
|
||||
try {
|
||||
return decrypt(new Uint8Array(input), key).buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError("Unable to decrypt using this key");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default XXTEADecrypt;
|
52
src/core/operations/XXTEAEncrypt.mjs
Normal file
52
src/core/operations/XXTEAEncrypt.mjs
Normal file
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* @author devcydo [devcydo@gmail.com]
|
||||
* @author Ma Bingyao [mabingyao@gmail.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2024
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {encrypt} from "../lib/XXTEA.mjs";
|
||||
|
||||
/**
|
||||
* XXTEA Encrypt operation
|
||||
*/
|
||||
class XXTEAEncrypt extends Operation {
|
||||
|
||||
/**
|
||||
* XXTEAEncrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "XXTEA Encrypt";
|
||||
this.module = "Ciphers";
|
||||
this.description = "Corrected Block TEA (often referred to as XXTEA) is a block cipher designed to correct weaknesses in the original Block TEA. XXTEA operates on variable-length blocks that are some arbitrary multiple of 32 bits in size (minimum 64 bits). The number of full cycles depends on the block size, but there are at least six (rising to 32 for small block sizes). The original Block TEA applies the XTEA round function to each word in the block and combines it additively with its leftmost neighbour. Slow diffusion rate of the decryption process was immediately exploited to break the cipher. Corrected Block TEA uses a more involved round function which makes use of both immediate neighbours in processing each word in the block.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/XXTEA";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const key = new Uint8Array(Utils.convertToByteArray(args[0].string, args[0].option));
|
||||
return encrypt(new Uint8Array(input), key).buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default XXTEAEncrypt;
|
|
@ -153,6 +153,7 @@ import "./tests/UnescapeString.mjs";
|
|||
import "./tests/Unicode.mjs";
|
||||
import "./tests/YARA.mjs";
|
||||
import "./tests/ParseCSR.mjs";
|
||||
import "./tests/XXTEA.mjs";
|
||||
|
||||
const testStatus = {
|
||||
allTestsPassing: true,
|
||||
|
|
|
@ -1,62 +1,42 @@
|
|||
/**
|
||||
* Base64 tests.
|
||||
* XXTEA tests.
|
||||
*
|
||||
* @author devcydo [devcydo@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2022
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2024
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "XXTEA",
|
||||
name: "XXTEA Encrypt and Decrypt",
|
||||
input: "Hello World! 你好,中国!",
|
||||
expectedOutput: "QncB1C0rHQoZ1eRiPM4dsZtRi9pNrp7sqvX76cFXvrrIHXL6",
|
||||
reecipeConfig: [
|
||||
expectedOutput: "Hello World! 你好,中国!",
|
||||
recipeConfig: [
|
||||
{
|
||||
args: "1234567890"
|
||||
"op": "XXTEA Encrypt",
|
||||
"args": [{ "option": "UTF8", "string": "1234567890" }]
|
||||
},
|
||||
{
|
||||
"op": "XXTEA Decrypt",
|
||||
"args": [{ "option": "UTF8", "string": "1234567890" }]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XXTEA",
|
||||
name: "XXTEA Encrypt",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "PbWjnbFmP8Apu2MKOGNbjeW/72IZLlLMS/g82ozLxwE=",
|
||||
reecipeConfig: [
|
||||
expectedOutput: "3db5a39db1663fc029bb630a38635b8de5bfef62192e52cc4bf83cda8ccbc701",
|
||||
recipeConfig: [
|
||||
{
|
||||
args: "1234567890"
|
||||
"op": "XXTEA Encrypt",
|
||||
"args": [{ "option": "UTF8", "string": "1234567890" }]
|
||||
},
|
||||
{
|
||||
"op": "To Hex",
|
||||
"args": ["None", 0]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XXTEA",
|
||||
input: "ნუ პანიკას",
|
||||
expectedOutput: "dHrOJ4ClIx6gH33NPSafYR2GG7UqsazY6Xfb0iekBY4=",
|
||||
reecipeConfig: [
|
||||
{
|
||||
args: "ll3kj209d2"
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XXTEA",
|
||||
input: "",
|
||||
expectedOutput: "Invalid input length (0)",
|
||||
reecipeConfig: [
|
||||
{
|
||||
args: "1234567890"
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XXTEA",
|
||||
input: "",
|
||||
expectedOutput: "Invalid input length (0)",
|
||||
reecipeConfig: [
|
||||
{
|
||||
args: ""
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
]);
|
||||
|
|
Loading…
Reference in a new issue