Skip to content

Commit 5c563c2

Browse files
authored
Merge pull request #1361 from devcydo/xxtea_encryption
2 parents 8647b50 + 1dfb231 commit 5c563c2

File tree

3 files changed

+246
-1
lines changed

3 files changed

+246
-1
lines changed

src/core/config/Categories.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@
155155
"Typex",
156156
"Lorenz",
157157
"Colossus",
158-
"SIGABA"
158+
"SIGABA",
159+
"XXTEA"
159160
]
160161
},
161162
{

src/core/operations/XXTEA.mjs

+182
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/**
2+
* @author devcydo [[email protected]]
3+
* @author Ma Bingyao [[email protected]]
4+
* @copyright Crown Copyright 2022
5+
* @license Apache-2.0
6+
*/
7+
8+
import Operation from "../Operation.mjs";
9+
import OperationError from "../errors/OperationError.mjs";
10+
import {toBase64} from "../lib/Base64.mjs";
11+
import Utils from "../Utils.mjs";
12+
13+
/**
14+
* XXTEA Encrypt operation
15+
*/
16+
class XXTEAEncrypt extends Operation {
17+
18+
/**
19+
* XXTEAEncrypt constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "XXTEA";
25+
this.module = "Default";
26+
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.";
27+
this.infoURL = "https://wikipedia.org/wiki/XXTEA";
28+
this.inputType = "string";
29+
this.outputType = "string";
30+
this.args = [
31+
{
32+
"name": "Key",
33+
"type": "string",
34+
"value": "",
35+
},
36+
];
37+
}
38+
39+
/**
40+
* @param {string} input
41+
* @param {Object[]} args
42+
* @returns {string}
43+
*/
44+
run(input, args) {
45+
let key = args[0];
46+
47+
if (input === undefined || input === null || input.length === 0) {
48+
throw new OperationError("Invalid input length (0)");
49+
}
50+
51+
if (key === undefined || key === null || key.length === 0) {
52+
throw new OperationError("Invalid key length (0)");
53+
}
54+
55+
input = Utils.convertToByteString(input, "utf8");
56+
key = Utils.convertToByteString(key, "utf8");
57+
58+
input = this.convertToUint32Array(input, true);
59+
key = this.fixLength(this.convertToUint32Array(key, false));
60+
61+
let encrypted = this.encryptUint32Array(input, key);
62+
63+
encrypted = toBase64(this.toBinaryString(encrypted, false));
64+
65+
return encrypted;
66+
}
67+
68+
/**
69+
* Convert Uint32Array to binary string
70+
*
71+
* @param {Uint32Array} v
72+
* @param {Boolean} includeLength
73+
* @returns {string}
74+
*/
75+
toBinaryString(v, includeLENGTH) {
76+
const LENGTH = v.length;
77+
let n = LENGTH << 2;
78+
if (includeLENGTH) {
79+
const M = v[LENGTH - 1];
80+
n -= 4;
81+
if ((M < n - 3) || (M > n)) {
82+
return null;
83+
}
84+
n = M;
85+
}
86+
for (let i = 0; i < LENGTH; i++) {
87+
v[i] = String.fromCharCode(
88+
v[i] & 0xFF,
89+
v[i] >>> 8 & 0xFF,
90+
v[i] >>> 16 & 0xFF,
91+
v[i] >>> 24 & 0xFF
92+
);
93+
}
94+
const RESULT = v.join("");
95+
if (includeLENGTH) {
96+
return RESULT.substring(0, n);
97+
}
98+
return RESULT;
99+
}
100+
101+
/**
102+
* @param {number} sum
103+
* @param {number} y
104+
* @param {number} z
105+
* @param {number} p
106+
* @param {number} e
107+
* @param {number} k
108+
* @returns {number}
109+
*/
110+
mx(sum, y, z, p, e, k) {
111+
return ((z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4)) ^ ((sum ^ y) + (k[p & 3 ^ e] ^ z));
112+
}
113+
114+
115+
/**
116+
* Encrypt Uint32Array
117+
*
118+
* @param {Uint32Array} v
119+
* @param {number} k
120+
* @returns {Uint32Array}
121+
*/
122+
encryptUint32Array(v, k) {
123+
const LENGTH = v.length;
124+
const N = LENGTH - 1;
125+
let y, z, sum, e, p, q;
126+
z = v[N];
127+
sum = 0;
128+
for (q = Math.floor(6 + 52 / LENGTH) | 0; q > 0; --q) {
129+
sum = (sum + 0x9E3779B9) & 0xFFFFFFFF;
130+
e = sum >>> 2 & 3;
131+
for (p = 0; p < N; ++p) {
132+
y = v[p + 1];
133+
z = v[p] = (v[p] + this.mx(sum, y, z, p, e, k)) & 0xFFFFFFFF;
134+
}
135+
y = v[0];
136+
z = v[N] = (v[N] + this.mx(sum, y, z, N, e, k)) & 0xFFFFFFFF;
137+
}
138+
return v;
139+
}
140+
141+
/**
142+
* Fixes the Uint32Array lenght to 4
143+
*
144+
* @param {Uint32Array} k
145+
* @returns {Uint32Array}
146+
*/
147+
fixLength(k) {
148+
if (k.length < 4) {
149+
k.length = 4;
150+
}
151+
return k;
152+
}
153+
154+
/**
155+
* Convert string to Uint32Array
156+
*
157+
* @param {string} bs
158+
* @param {Boolean} includeLength
159+
* @returns {Uint32Array}
160+
*/
161+
convertToUint32Array(bs, includeLength) {
162+
const LENGTH = bs.length;
163+
let n = LENGTH >> 2;
164+
if ((LENGTH & 3) !== 0) {
165+
++n;
166+
}
167+
let v;
168+
if (includeLength) {
169+
v = new Array(n + 1);
170+
v[n] = LENGTH;
171+
} else {
172+
v = new Array(n);
173+
}
174+
for (let i = 0; i < LENGTH; ++i) {
175+
v[i >> 2] |= bs.charCodeAt(i) << ((i & 3) << 3);
176+
}
177+
return v;
178+
}
179+
180+
}
181+
182+
export default XXTEAEncrypt;

tests/operations/tests/XXTEA.mjs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Base64 tests.
3+
*
4+
* @author devcydo [[email protected]]
5+
*
6+
* @copyright Crown Copyright 2022
7+
* @license Apache-2.0
8+
*/
9+
import TestRegister from "../../lib/TestRegister.mjs";
10+
11+
TestRegister.addTests([
12+
{
13+
name: "XXTEA",
14+
input: "Hello World! 你好,中国!",
15+
expectedOutput: "QncB1C0rHQoZ1eRiPM4dsZtRi9pNrp7sqvX76cFXvrrIHXL6",
16+
reecipeConfig: [
17+
{
18+
args: "1234567890"
19+
},
20+
],
21+
},
22+
{
23+
name: "XXTEA",
24+
input: "ნუ პანიკას",
25+
expectedOutput: "PbWjnbFmP8Apu2MKOGNbjeW/72IZLlLMS/g82ozLxwE=",
26+
reecipeConfig: [
27+
{
28+
args: "1234567890"
29+
},
30+
],
31+
},
32+
{
33+
name: "XXTEA",
34+
input: "ნუ პანიკას",
35+
expectedOutput: "dHrOJ4ClIx6gH33NPSafYR2GG7UqsazY6Xfb0iekBY4=",
36+
reecipeConfig: [
37+
{
38+
args: "ll3kj209d2"
39+
},
40+
],
41+
},
42+
{
43+
name: "XXTEA",
44+
input: "",
45+
expectedOutput: "Invalid input length (0)",
46+
reecipeConfig: [
47+
{
48+
args: "1234567890"
49+
},
50+
],
51+
},
52+
{
53+
name: "XXTEA",
54+
input: "",
55+
expectedOutput: "Invalid input length (0)",
56+
reecipeConfig: [
57+
{
58+
args: ""
59+
},
60+
],
61+
},
62+
]);

0 commit comments

Comments
 (0)