feat: add configurable rounds for XTEA

Add a Rounds parameter (1-255, default 32) to XTEA Encrypt and Decrypt
operations, matching the reference implementation by Wheeler & Needham
which accepts a configurable round count. Follows the same convention as
RC6 for argument ordering (Rounds after Padding).
This commit is contained in:
Medjedtxm 2026-03-07 10:20:08 -05:00
parent 9f0f7882b7
commit 89c06670d2
4 changed files with 109 additions and 38 deletions

View File

@ -108,15 +108,16 @@ function teaDecryptBlock(block, key) {
*
* @param {number[]} block - 8 bytes (plaintext)
* @param {number[]} key - 16 bytes (128-bit key)
* @param {number} rounds - Number of rounds (default 32)
* @returns {number[]} - 8 bytes (ciphertext)
*/
function xteaEncryptBlock(block, key) {
function xteaEncryptBlock(block, key, rounds) {
const v = bytesToUint32(block);
const k = bytesToUint32(key);
let v0 = v[0], v1 = v[1];
let sum = 0;
for (let i = 0; i < ROUNDS; i++) {
for (let i = 0; i < rounds; i++) {
v0 = (v0 + ((((v1 << 4) ^ (v1 >>> 5)) + v1) ^ (sum + k[sum & 3]))) >>> 0;
sum = (sum + DELTA) >>> 0;
v1 = (v1 + ((((v0 << 4) ^ (v0 >>> 5)) + v0) ^ (sum + k[(sum >>> 11) & 3]))) >>> 0;
@ -130,15 +131,16 @@ function xteaEncryptBlock(block, key) {
*
* @param {number[]} block - 8 bytes (ciphertext)
* @param {number[]} key - 16 bytes (128-bit key)
* @param {number} rounds - Number of rounds (default 32)
* @returns {number[]} - 8 bytes (plaintext)
*/
function xteaDecryptBlock(block, key) {
function xteaDecryptBlock(block, key, rounds) {
const v = bytesToUint32(block);
const k = bytesToUint32(key);
let v0 = v[0], v1 = v[1];
let sum = (DELTA * ROUNDS) >>> 0;
let sum = (DELTA * rounds) >>> 0;
for (let i = 0; i < ROUNDS; i++) {
for (let i = 0; i < rounds; i++) {
v1 = (v1 - ((((v0 << 4) ^ (v0 >>> 5)) + v0) ^ (sum + k[(sum >>> 11) & 3]))) >>> 0;
sum = (sum - DELTA) >>> 0;
v0 = (v0 - ((((v1 << 4) ^ (v1 >>> 5)) + v1) ^ (sum + k[sum & 3]))) >>> 0;
@ -464,10 +466,12 @@ export function decryptTEA(cipherText, key, iv, mode = "ECB", padding = "PKCS5")
* @param {number[]} iv - 8-byte IV
* @param {string} mode - Block cipher mode
* @param {string} padding - Padding type
* @param {number} rounds - Number of rounds (default 32)
* @returns {number[]} - Ciphertext bytes
*/
export function encryptXTEA(message, key, iv, mode = "ECB", padding = "PKCS5") {
return encryptWithMode(message, key, iv, mode, padding, xteaEncryptBlock);
export function encryptXTEA(message, key, iv, mode = "ECB", padding = "PKCS5", rounds = 32) {
const encFn = (block, k) => xteaEncryptBlock(block, k, rounds);
return encryptWithMode(message, key, iv, mode, padding, encFn);
}
/**
@ -477,10 +481,13 @@ export function encryptXTEA(message, key, iv, mode = "ECB", padding = "PKCS5") {
* @param {number[]} iv - 8-byte IV
* @param {string} mode - Block cipher mode
* @param {string} padding - Padding type
* @param {number} rounds - Number of rounds (default 32)
* @returns {number[]} - Plaintext bytes
*/
export function decryptXTEA(cipherText, key, iv, mode = "ECB", padding = "PKCS5") {
return decryptWithMode(cipherText, key, iv, mode, padding, xteaEncryptBlock, xteaDecryptBlock);
export function decryptXTEA(cipherText, key, iv, mode = "ECB", padding = "PKCS5", rounds = 32) {
const encFn = (block, k) => xteaEncryptBlock(block, k, rounds);
const decFn = (block, k) => xteaDecryptBlock(block, k, rounds);
return decryptWithMode(cipherText, key, iv, mode, padding, encFn, decFn);
}
/** Block size in bytes (exported for operation validation) */

View File

@ -23,7 +23,7 @@ class XTEADecrypt extends Operation {
this.name = "XTEA Decrypt";
this.module = "Ciphers";
this.description = "XTEA (eXtended Tiny Encryption Algorithm) is a block cipher designed by David Wheeler and Roger Needham in 1997 as a successor to TEA, correcting several weaknesses identified in the original algorithm. It operates on 64-bit blocks using a 128-bit key and performs 32 cycles (64 Feistel rounds) with an improved key schedule that uses sum-dependent key word selection to resist related-key attacks.<br><br>XTEA retains the simplicity and compact implementation of TEA whilst providing significantly improved security. It is frequently encountered in malware analysis and CTF challenges due to its straightforward implementation.<br><br><b>Key:</b> Must be exactly 16 bytes (128 bits).<br><br><b>IV:</b> The Initialisation Vector should be 8 bytes (64 bits). If not entered, it will default to null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, the PKCS#5 padding scheme is used.";
this.description = "XTEA (eXtended Tiny Encryption Algorithm) is a block cipher designed by David Wheeler and Roger Needham in 1997 as a successor to TEA, correcting several weaknesses identified in the original algorithm. It operates on 64-bit blocks using a 128-bit key with an improved key schedule that uses sum-dependent key word selection to resist related-key attacks.<br><br>XTEA retains the simplicity and compact implementation of TEA whilst providing significantly improved security. It is frequently encountered in malware analysis and CTF challenges due to its straightforward implementation.<br><br><b>Key:</b> Must be exactly 16 bytes (128 bits).<br><br><b>IV:</b> The Initialisation Vector should be 8 bytes (64 bits). If not entered, it will default to null bytes.<br><br><b>Rounds:</b> The recommended number of rounds is 32 (default). The reference implementation by Wheeler &amp; Needham accepts a configurable round count.<br><br><b>Padding:</b> In CBC and ECB mode, the PKCS#5 padding scheme is used.";
this.infoURL = "https://wikipedia.org/wiki/XTEA";
this.inputType = "string";
this.outputType = "string";
@ -59,6 +59,13 @@ class XTEADecrypt extends Operation {
"name": "Padding",
"type": "option",
"value": ["PKCS5", "NO", "ZERO", "RANDOM", "BIT"]
},
{
"name": "Rounds",
"type": "number",
"value": 32,
"min": 1,
"max": 255
}
];
}
@ -71,12 +78,12 @@ class XTEADecrypt extends Operation {
run(input, args) {
const key = Utils.convertToByteArray(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
[,, mode, inputType, outputType, padding] = args;
[,, mode, inputType, outputType, padding, rounds] = args;
if (key.length !== 16)
throw new OperationError(`Invalid key length: ${key.length} bytes
XTEA uses a key length of 16 bytes (128 bits).
XTEA requires a key length of 16 bytes (128 bits).
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
if (iv.length !== TEA_BLOCK_SIZE && iv.length !== 0 && mode !== "ECB")
@ -85,11 +92,16 @@ Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
XTEA uses an IV length of ${TEA_BLOCK_SIZE} bytes (${TEA_BLOCK_SIZE * 8} bits).
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
if (!Number.isInteger(rounds) || rounds < 1 || rounds > 255)
throw new OperationError(`Invalid number of rounds: ${rounds}
Rounds must be an integer between 1 and 255. Standard XTEA uses 32 rounds.`);
// Default IV to null bytes if empty (like AES)
const actualIv = iv.length === 0 ? new Array(TEA_BLOCK_SIZE).fill(0) : iv;
input = Utils.convertToByteArray(input, inputType);
const output = decryptXTEA(input, key, actualIv, mode, padding);
const output = decryptXTEA(input, key, actualIv, mode, padding, rounds);
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
}

View File

@ -23,7 +23,7 @@ class XTEAEncrypt extends Operation {
this.name = "XTEA Encrypt";
this.module = "Ciphers";
this.description = "XTEA (eXtended Tiny Encryption Algorithm) is a block cipher designed by David Wheeler and Roger Needham in 1997 as a successor to TEA, correcting several weaknesses identified in the original algorithm. It operates on 64-bit blocks using a 128-bit key and performs 32 cycles (64 Feistel rounds) with an improved key schedule that uses sum-dependent key word selection to resist related-key attacks.<br><br>XTEA retains the simplicity and compact implementation of TEA whilst providing significantly improved security. It is frequently encountered in malware analysis and CTF challenges due to its straightforward implementation.<br><br><b>Key:</b> Must be exactly 16 bytes (128 bits).<br><br><b>IV:</b> The Initialisation Vector should be 8 bytes (64 bits). If not entered, it will default to null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, the PKCS#5 padding scheme is used.";
this.description = "XTEA (eXtended Tiny Encryption Algorithm) is a block cipher designed by David Wheeler and Roger Needham in 1997 as a successor to TEA, correcting several weaknesses identified in the original algorithm. It operates on 64-bit blocks using a 128-bit key with an improved key schedule that uses sum-dependent key word selection to resist related-key attacks.<br><br>XTEA retains the simplicity and compact implementation of TEA whilst providing significantly improved security. It is frequently encountered in malware analysis and CTF challenges due to its straightforward implementation.<br><br><b>Key:</b> Must be exactly 16 bytes (128 bits).<br><br><b>IV:</b> The Initialisation Vector should be 8 bytes (64 bits). If not entered, it will default to null bytes.<br><br><b>Rounds:</b> The recommended number of rounds is 32 (default). The reference implementation by Wheeler &amp; Needham accepts a configurable round count.<br><br><b>Padding:</b> In CBC and ECB mode, the PKCS#5 padding scheme is used.";
this.infoURL = "https://wikipedia.org/wiki/XTEA";
this.inputType = "string";
this.outputType = "string";
@ -59,6 +59,13 @@ class XTEAEncrypt extends Operation {
"name": "Padding",
"type": "option",
"value": ["PKCS5", "NO", "ZERO", "RANDOM", "BIT"]
},
{
"name": "Rounds",
"type": "number",
"value": 32,
"min": 1,
"max": 255
}
];
}
@ -71,7 +78,7 @@ class XTEAEncrypt extends Operation {
run(input, args) {
const key = Utils.convertToByteArray(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
[,, mode, inputType, outputType, padding] = args;
[,, mode, inputType, outputType, padding, rounds] = args;
if (key.length !== 16)
throw new OperationError(`Invalid key length: ${key.length} bytes
@ -85,11 +92,16 @@ Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
XTEA uses an IV length of ${TEA_BLOCK_SIZE} bytes (${TEA_BLOCK_SIZE * 8} bits).
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
if (!Number.isInteger(rounds) || rounds < 1 || rounds > 255)
throw new OperationError(`Invalid number of rounds: ${rounds}
Rounds must be an integer between 1 and 255. Standard XTEA uses 32 rounds.`);
// Default IV to null bytes if empty (like AES)
const actualIv = iv.length === 0 ? new Array(TEA_BLOCK_SIZE).fill(0) : iv;
input = Utils.convertToByteArray(input, inputType);
const output = encryptXTEA(input, key, actualIv, mode, padding);
const output = encryptXTEA(input, key, actualIv, mode, padding, rounds);
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
}

View File

@ -188,7 +188,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -203,7 +203,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -218,7 +218,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -233,7 +233,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -248,7 +248,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "00000000000000000000000000000000"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -263,7 +263,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "00000000000000000000000000000000"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -278,7 +278,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "00000000000000000000000000000000"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -293,7 +293,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "00000000000000000000000000000000"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -308,7 +308,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456712345678234567893456789a"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -323,7 +323,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456712345678234567893456789a"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -338,7 +338,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456712345678234567893456789a"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -355,7 +355,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
{"option": "Hex", "string": "fedcba9876543210"},
"CBC", "Raw", "Hex", "PKCS5"
"CBC", "Raw", "Hex", "PKCS5", 32
]
},
{
@ -363,7 +363,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
{"option": "Hex", "string": "fedcba9876543210"},
"CBC", "Hex", "Raw", "PKCS5"
"CBC", "Hex", "Raw", "PKCS5", 32
]
}
],
@ -380,7 +380,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
{"option": "Hex", "string": "0102030405060708"},
"OFB", "Raw", "Hex", "NO"
"OFB", "Raw", "Hex", "NO", 32
]
},
{
@ -388,7 +388,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
{"option": "Hex", "string": "0102030405060708"},
"OFB", "Hex", "Raw", "NO"
"OFB", "Hex", "Raw", "NO", 32
]
}
],
@ -405,7 +405,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
{"option": "Hex", "string": "0000000000000001"},
"CTR", "Raw", "Hex", "NO"
"CTR", "Raw", "Hex", "NO", 32
]
},
{
@ -413,7 +413,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
{"option": "Hex", "string": "0000000000000001"},
"CTR", "Hex", "Raw", "NO"
"CTR", "Hex", "Raw", "NO", 32
]
}
],
@ -430,7 +430,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
{"option": "Hex", "string": "aabbccddeeff0011"},
"CFB", "Raw", "Hex", "NO"
"CFB", "Raw", "Hex", "NO", 32
]
},
{
@ -438,7 +438,47 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
{"option": "Hex", "string": "aabbccddeeff0011"},
"CFB", "Hex", "Raw", "NO"
"CFB", "Hex", "Raw", "NO", 32
]
}
],
},
// ==================== XTEA NON-DEFAULT ROUNDS TEST ====================
{
name: "XTEA Encrypt then Decrypt: round-trip ECB with 16 rounds",
input: "4142434445464748",
expectedOutput: "4142434445464748",
recipeConfig: [
{
"op": "XTEA Encrypt",
"args": [
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO", 16
]
},
{
"op": "XTEA Decrypt",
"args": [
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO", 16
]
}
],
},
{
name: "XTEA Encrypt: ECB, 16 rounds differs from 32 rounds",
input: "4142434445464748",
expectedOutput: "497df3d072612cb5",
recipeConfig: [
{
"op": "XTEA Encrypt",
"args": [
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -470,7 +510,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "00000000000000000000000000000000"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Hex", "NO"
"ECB", "Hex", "Hex", "NO", 32
]
}
],
@ -510,7 +550,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
{"option": "Hex", "string": ""},
"ECB", "Raw", "Hex", "PKCS5"
"ECB", "Raw", "Hex", "PKCS5", 32
]
},
{
@ -518,7 +558,7 @@ TestRegister.addTests([
"args": [
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
{"option": "Hex", "string": ""},
"ECB", "Hex", "Raw", "PKCS5"
"ECB", "Hex", "Raw", "PKCS5", 32
]
}
],