From 3ea12a2e1be11805837dad8b620b73662f5fa8ce Mon Sep 17 00:00:00 2001 From: Luis Martinez Date: Sat, 28 May 2022 00:17:59 -0500 Subject: [PATCH 1/5] xxtea encryption added --- src/core/config/Categories.json | 3 +- src/core/operations/XXTEA.mjs | 182 ++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 src/core/operations/XXTEA.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 9540f8a37..a55d5ddd4 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -123,7 +123,8 @@ "Typex", "Lorenz", "Colossus", - "SIGABA" + "SIGABA", + "XXTEA" ] }, { diff --git a/src/core/operations/XXTEA.mjs b/src/core/operations/XXTEA.mjs new file mode 100644 index 000000000..e8264c4db --- /dev/null +++ b/src/core/operations/XXTEA.mjs @@ -0,0 +1,182 @@ +/** + * @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; From 19423cc4372ed5d901d65afb21b8620ca188d881 Mon Sep 17 00:00:00 2001 From: Luis Martinez Date: Sat, 28 May 2022 00:20:51 -0500 Subject: [PATCH 2/5] xxtea encryption added --- src/core/operations/XXTEA.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/XXTEA.mjs b/src/core/operations/XXTEA.mjs index e8264c4db..4fa0706dc 100644 --- a/src/core/operations/XXTEA.mjs +++ b/src/core/operations/XXTEA.mjs @@ -98,7 +98,7 @@ class XXTEAEncrypt extends Operation { return RESULT; } - /** + /** * @param {number} sum * @param {number} y * @param {number} z From 893b84d0426754d0b21647ef25564f6d9b19f95e Mon Sep 17 00:00:00 2001 From: Luis Martinez Date: Sat, 28 May 2022 00:17:59 -0500 Subject: [PATCH 3/5] xxtea encryption added --- src/core/config/Categories.json | 3 +- src/core/operations/XXTEA.mjs | 182 ++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 src/core/operations/XXTEA.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 8ac60048a..26e569055 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -131,7 +131,8 @@ "Typex", "Lorenz", "Colossus", - "SIGABA" + "SIGABA", + "XXTEA" ] }, { diff --git a/src/core/operations/XXTEA.mjs b/src/core/operations/XXTEA.mjs new file mode 100644 index 000000000..e8264c4db --- /dev/null +++ b/src/core/operations/XXTEA.mjs @@ -0,0 +1,182 @@ +/** + * @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; From 653af6a3005f872d98ca0d59f0aa118e48f88bb7 Mon Sep 17 00:00:00 2001 From: Luis Martinez Date: Sat, 28 May 2022 00:20:51 -0500 Subject: [PATCH 4/5] xxtea encryption added --- src/core/operations/XXTEA.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/XXTEA.mjs b/src/core/operations/XXTEA.mjs index e8264c4db..4fa0706dc 100644 --- a/src/core/operations/XXTEA.mjs +++ b/src/core/operations/XXTEA.mjs @@ -98,7 +98,7 @@ class XXTEAEncrypt extends Operation { return RESULT; } - /** + /** * @param {number} sum * @param {number} y * @param {number} z From c14098a27c47bf345f1f119b970248fd573fa9d6 Mon Sep 17 00:00:00 2001 From: Luis Martinez Date: Mon, 11 Jul 2022 19:38:59 -0500 Subject: [PATCH 5/5] tests added and XXTEA not working correctly fixed --- src/core/operations/XXTEA.mjs | 6 ++-- tests/operations/tests/XXTEA.mjs | 62 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 tests/operations/tests/XXTEA.mjs diff --git a/src/core/operations/XXTEA.mjs b/src/core/operations/XXTEA.mjs index 4fa0706dc..1a0a43687 100644 --- a/src/core/operations/XXTEA.mjs +++ b/src/core/operations/XXTEA.mjs @@ -73,7 +73,7 @@ class XXTEAEncrypt extends Operation { * @returns {string} */ toBinaryString(v, includeLENGTH) { - const LENGTH = v.LENGTH; + const LENGTH = v.length; let n = LENGTH << 2; if (includeLENGTH) { const M = v[LENGTH - 1]; @@ -120,7 +120,7 @@ class XXTEAEncrypt extends Operation { * @returns {Uint32Array} */ encryptUint32Array(v, k) { - const LENGTH = v.LENGTH; + const LENGTH = v.length; const N = LENGTH - 1; let y, z, sum, e, p, q; z = v[N]; @@ -159,7 +159,7 @@ class XXTEAEncrypt extends Operation { * @returns {Uint32Array} */ convertToUint32Array(bs, includeLength) { - const LENGTH = bs.LENGTH; + const LENGTH = bs.length; let n = LENGTH >> 2; if ((LENGTH & 3) !== 0) { ++n; diff --git a/tests/operations/tests/XXTEA.mjs b/tests/operations/tests/XXTEA.mjs new file mode 100644 index 000000000..4787f086c --- /dev/null +++ b/tests/operations/tests/XXTEA.mjs @@ -0,0 +1,62 @@ +/** + * Base64 tests. + * + * @author devcydo [devcydo@gmail.com] + * + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "XXTEA", + input: "Hello World! 你好,中国!", + expectedOutput: "QncB1C0rHQoZ1eRiPM4dsZtRi9pNrp7sqvX76cFXvrrIHXL6", + reecipeConfig: [ + { + args: "1234567890" + }, + ], + }, + { + name: "XXTEA", + input: "ნუ პანიკას", + expectedOutput: "PbWjnbFmP8Apu2MKOGNbjeW/72IZLlLMS/g82ozLxwE=", + reecipeConfig: [ + { + args: "1234567890" + }, + ], + }, + { + 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: "" + }, + ], + }, +]);