mirror of
https://github.com/gchq/CyberChef.git
synced 2026-03-17 04:11:51 -07:00
Merge 89c06670d2 into 4deaa0de20
This commit is contained in:
commit
9ca3c59458
8 changed files with 1481 additions and 0 deletions
|
|
@ -127,6 +127,10 @@
|
|||
"XOR Brute Force",
|
||||
"Vigenère Encode",
|
||||
"Vigenère Decode",
|
||||
"TEA Encrypt",
|
||||
"TEA Decrypt",
|
||||
"XTEA Encrypt",
|
||||
"XTEA Decrypt",
|
||||
"XXTEA Encrypt",
|
||||
"XXTEA Decrypt",
|
||||
"To Morse Code",
|
||||
|
|
|
|||
494
src/core/lib/TEA.mjs
Normal file
494
src/core/lib/TEA.mjs
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
/**
|
||||
* TEA and XTEA block cipher implementation.
|
||||
*
|
||||
* TEA (Tiny Encryption Algorithm) — Wheeler & Needham, 1994.
|
||||
* XTEA (Extended TEA) — Wheeler & Needham, 1997.
|
||||
*
|
||||
* Both operate on 64-bit blocks with 128-bit keys.
|
||||
* TEA uses 32 cycles (64 Feistel rounds).
|
||||
* XTEA uses 32 cycles (64 Feistel rounds) with improved key schedule.
|
||||
*
|
||||
* References:
|
||||
* https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
|
||||
* https://en.wikipedia.org/wiki/XTEA
|
||||
* https://www.cix.co.uk/~klockstone/teavect.htm
|
||||
*
|
||||
* @author Medjedtxm
|
||||
* @copyright Crown Copyright 2026
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/** TEA/XTEA constants */
|
||||
const DELTA = 0x9E3779B9;
|
||||
const BLOCK_SIZE = 8; // 64-bit block = 8 bytes
|
||||
const ROUNDS = 32; // 32 cycles
|
||||
|
||||
/**
|
||||
* Convert byte array to array of 32-bit unsigned integers (big-endian)
|
||||
* @param {number[]} bytes
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function bytesToUint32(bytes) {
|
||||
const words = [];
|
||||
for (let i = 0; i < bytes.length; i += 4) {
|
||||
words.push(
|
||||
((bytes[i] << 24) | (bytes[i + 1] << 16) |
|
||||
(bytes[i + 2] << 8) | bytes[i + 3]) >>> 0
|
||||
);
|
||||
}
|
||||
return words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert array of 32-bit unsigned integers to byte array (big-endian)
|
||||
* @param {number[]} words
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function uint32ToBytes(words) {
|
||||
const bytes = [];
|
||||
for (const w of words) {
|
||||
bytes.push((w >>> 24) & 0xFF);
|
||||
bytes.push((w >>> 16) & 0xFF);
|
||||
bytes.push((w >>> 8) & 0xFF);
|
||||
bytes.push(w & 0xFF);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* TEA encrypt a single 64-bit block
|
||||
* Reference: Wheeler & Needham, 1994
|
||||
*
|
||||
* @param {number[]} block - 8 bytes (plaintext)
|
||||
* @param {number[]} key - 16 bytes (128-bit key)
|
||||
* @returns {number[]} - 8 bytes (ciphertext)
|
||||
*/
|
||||
function teaEncryptBlock(block, key) {
|
||||
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++) {
|
||||
sum = (sum + DELTA) >>> 0;
|
||||
v0 = (v0 + ((((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >>> 5) + k[1])))) >>> 0;
|
||||
v1 = (v1 + ((((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >>> 5) + k[3])))) >>> 0;
|
||||
}
|
||||
|
||||
return uint32ToBytes([v0, v1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* TEA decrypt a single 64-bit block
|
||||
*
|
||||
* @param {number[]} block - 8 bytes (ciphertext)
|
||||
* @param {number[]} key - 16 bytes (128-bit key)
|
||||
* @returns {number[]} - 8 bytes (plaintext)
|
||||
*/
|
||||
function teaDecryptBlock(block, key) {
|
||||
const v = bytesToUint32(block);
|
||||
const k = bytesToUint32(key);
|
||||
let v0 = v[0], v1 = v[1];
|
||||
let sum = (DELTA * ROUNDS) >>> 0;
|
||||
|
||||
for (let i = 0; i < ROUNDS; i++) {
|
||||
v1 = (v1 - ((((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >>> 5) + k[3])))) >>> 0;
|
||||
v0 = (v0 - ((((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >>> 5) + k[1])))) >>> 0;
|
||||
sum = (sum - DELTA) >>> 0;
|
||||
}
|
||||
|
||||
return uint32ToBytes([v0, v1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* XTEA encrypt a single 64-bit block
|
||||
* Reference: Wheeler & Needham, 1997
|
||||
*
|
||||
* @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, 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++) {
|
||||
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;
|
||||
}
|
||||
|
||||
return uint32ToBytes([v0, v1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* XTEA decrypt a single 64-bit block
|
||||
*
|
||||
* @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, rounds) {
|
||||
const v = bytesToUint32(block);
|
||||
const k = bytesToUint32(key);
|
||||
let v0 = v[0], v1 = v[1];
|
||||
let sum = (DELTA * rounds) >>> 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return uint32ToBytes([v0, v1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* XOR two byte arrays of equal length
|
||||
* @param {number[]} a
|
||||
* @param {number[]} b
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function xorBlocks(a, b) {
|
||||
return a.map((byte, i) => byte ^ b[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment a byte array as a big-endian counter
|
||||
* @param {number[]} counter
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function incrementCounter(counter) {
|
||||
const result = [...counter];
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
result[i] = (result[i] + 1) & 0xFF;
|
||||
if (result[i] !== 0) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply padding to message
|
||||
* @param {number[]} message
|
||||
* @param {string} padding - "NO", "PKCS5", "ZERO", "RANDOM", "BIT"
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function applyPadding(message, padding) {
|
||||
const remainder = message.length % BLOCK_SIZE;
|
||||
if (remainder === 0 && padding !== "PKCS5") return [...message];
|
||||
|
||||
const nPadding = (remainder === 0 && padding === "PKCS5") ?
|
||||
BLOCK_SIZE :
|
||||
BLOCK_SIZE - remainder;
|
||||
|
||||
if (nPadding === 0) return [...message];
|
||||
|
||||
const padded = [...message];
|
||||
|
||||
switch (padding) {
|
||||
case "NO":
|
||||
throw new OperationError(
|
||||
`No padding requested but input length (${message.length} bytes) is not a multiple of ${BLOCK_SIZE} bytes.`
|
||||
);
|
||||
case "PKCS5":
|
||||
for (let i = 0; i < nPadding; i++) padded.push(nPadding);
|
||||
break;
|
||||
case "ZERO":
|
||||
for (let i = 0; i < nPadding; i++) padded.push(0);
|
||||
break;
|
||||
case "RANDOM":
|
||||
for (let i = 0; i < nPadding; i++) padded.push(Math.floor(Math.random() * 256));
|
||||
break;
|
||||
case "BIT":
|
||||
padded.push(0x80);
|
||||
for (let i = 1; i < nPadding; i++) padded.push(0);
|
||||
break;
|
||||
default:
|
||||
throw new OperationError(`Unknown padding type: ${padding}`);
|
||||
}
|
||||
|
||||
return padded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove padding from message
|
||||
* @param {number[]} message
|
||||
* @param {string} padding
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function removePadding(message, padding) {
|
||||
if (message.length === 0) return message;
|
||||
|
||||
switch (padding) {
|
||||
case "NO":
|
||||
case "ZERO":
|
||||
case "RANDOM":
|
||||
return message;
|
||||
|
||||
case "PKCS5": {
|
||||
const padByte = message[message.length - 1];
|
||||
if (padByte > 0 && padByte <= BLOCK_SIZE) {
|
||||
for (let i = 0; i < padByte; i++) {
|
||||
if (message[message.length - 1 - i] !== padByte) {
|
||||
throw new OperationError("Invalid PKCS#5 padding.");
|
||||
}
|
||||
}
|
||||
return message.slice(0, message.length - padByte);
|
||||
}
|
||||
throw new OperationError("Invalid PKCS#5 padding.");
|
||||
}
|
||||
|
||||
case "BIT": {
|
||||
for (let i = message.length - 1; i >= 0; i--) {
|
||||
if (message[i] === 0x80) return message.slice(0, i);
|
||||
if (message[i] !== 0) throw new OperationError("Invalid BIT padding.");
|
||||
}
|
||||
throw new OperationError("Invalid BIT padding.");
|
||||
}
|
||||
|
||||
default:
|
||||
throw new OperationError(`Unknown padding type: ${padding}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt with block cipher modes
|
||||
*
|
||||
* @param {number[]} message - Plaintext bytes
|
||||
* @param {number[]} key - 16-byte key
|
||||
* @param {number[]} iv - 8-byte IV (ignored for ECB)
|
||||
* @param {string} mode - "ECB", "CBC", "CFB", "OFB", "CTR"
|
||||
* @param {string} padding - "PKCS5", "NO", "ZERO", "RANDOM", "BIT"
|
||||
* @param {Function} encryptBlockFn - Block encrypt function
|
||||
* @returns {number[]} - Ciphertext bytes
|
||||
*/
|
||||
function encryptWithMode(message, key, iv, mode, padding, encryptBlockFn) {
|
||||
const messageLength = message.length;
|
||||
if (messageLength === 0) return [];
|
||||
|
||||
let data;
|
||||
if (mode === "ECB" || mode === "CBC") {
|
||||
data = applyPadding(message, padding);
|
||||
} else {
|
||||
data = [...message];
|
||||
}
|
||||
|
||||
const cipherText = [];
|
||||
|
||||
switch (mode) {
|
||||
case "ECB":
|
||||
for (let i = 0; i < data.length; i += BLOCK_SIZE) {
|
||||
cipherText.push(...encryptBlockFn(data.slice(i, i + BLOCK_SIZE), key));
|
||||
}
|
||||
break;
|
||||
|
||||
case "CBC": {
|
||||
let ivBlock = [...iv];
|
||||
for (let i = 0; i < data.length; i += BLOCK_SIZE) {
|
||||
const block = data.slice(i, i + BLOCK_SIZE);
|
||||
const xored = xorBlocks(block, ivBlock);
|
||||
ivBlock = encryptBlockFn(xored, key);
|
||||
cipherText.push(...ivBlock);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "CFB": {
|
||||
let ivBlock = [...iv];
|
||||
for (let i = 0; i < data.length; i += BLOCK_SIZE) {
|
||||
const encrypted = encryptBlockFn(ivBlock, key);
|
||||
const block = data.slice(i, i + BLOCK_SIZE);
|
||||
while (block.length < BLOCK_SIZE) block.push(0);
|
||||
ivBlock = xorBlocks(encrypted, block);
|
||||
cipherText.push(...ivBlock);
|
||||
}
|
||||
return cipherText.slice(0, messageLength);
|
||||
}
|
||||
|
||||
case "OFB": {
|
||||
let ivBlock = [...iv];
|
||||
for (let i = 0; i < data.length; i += BLOCK_SIZE) {
|
||||
ivBlock = encryptBlockFn(ivBlock, key);
|
||||
const block = data.slice(i, i + BLOCK_SIZE);
|
||||
while (block.length < BLOCK_SIZE) block.push(0);
|
||||
cipherText.push(...xorBlocks(ivBlock, block));
|
||||
}
|
||||
return cipherText.slice(0, messageLength);
|
||||
}
|
||||
|
||||
case "CTR": {
|
||||
let counter = [...iv];
|
||||
for (let i = 0; i < data.length; i += BLOCK_SIZE) {
|
||||
const encrypted = encryptBlockFn(counter, key);
|
||||
const block = data.slice(i, i + BLOCK_SIZE);
|
||||
while (block.length < BLOCK_SIZE) block.push(0);
|
||||
cipherText.push(...xorBlocks(encrypted, block));
|
||||
counter = incrementCounter(counter);
|
||||
}
|
||||
return cipherText.slice(0, messageLength);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new OperationError(`Invalid block cipher mode: ${mode}`);
|
||||
}
|
||||
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt with block cipher modes
|
||||
*
|
||||
* @param {number[]} cipherText - Ciphertext bytes
|
||||
* @param {number[]} key - 16-byte key
|
||||
* @param {number[]} iv - 8-byte IV (ignored for ECB)
|
||||
* @param {string} mode - "ECB", "CBC", "CFB", "OFB", "CTR"
|
||||
* @param {string} padding - "PKCS5", "NO", "ZERO", "RANDOM", "BIT"
|
||||
* @param {Function} encryptBlockFn - Block encrypt function (used for stream modes)
|
||||
* @param {Function} decryptBlockFn - Block decrypt function (used for ECB/CBC)
|
||||
* @returns {number[]} - Plaintext bytes
|
||||
*/
|
||||
function decryptWithMode(cipherText, key, iv, mode, padding, encryptBlockFn, decryptBlockFn) {
|
||||
const originalLength = cipherText.length;
|
||||
if (originalLength === 0) return [];
|
||||
|
||||
if (mode === "ECB" || mode === "CBC") {
|
||||
if ((originalLength % BLOCK_SIZE) !== 0)
|
||||
throw new OperationError(
|
||||
`Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of ${BLOCK_SIZE}.`
|
||||
);
|
||||
} else {
|
||||
while ((cipherText.length % BLOCK_SIZE) !== 0)
|
||||
cipherText.push(0);
|
||||
}
|
||||
|
||||
const plainText = [];
|
||||
|
||||
switch (mode) {
|
||||
case "ECB":
|
||||
for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
|
||||
plainText.push(...decryptBlockFn(cipherText.slice(i, i + BLOCK_SIZE), key));
|
||||
}
|
||||
break;
|
||||
|
||||
case "CBC": {
|
||||
let ivBlock = [...iv];
|
||||
for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
|
||||
const block = cipherText.slice(i, i + BLOCK_SIZE);
|
||||
const decrypted = decryptBlockFn(block, key);
|
||||
plainText.push(...xorBlocks(decrypted, ivBlock));
|
||||
ivBlock = block;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "CFB": {
|
||||
let ivBlock = [...iv];
|
||||
for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
|
||||
const encrypted = encryptBlockFn(ivBlock, key);
|
||||
const block = cipherText.slice(i, i + BLOCK_SIZE);
|
||||
plainText.push(...xorBlocks(encrypted, block));
|
||||
ivBlock = block;
|
||||
}
|
||||
return plainText.slice(0, originalLength);
|
||||
}
|
||||
|
||||
case "OFB": {
|
||||
let ivBlock = [...iv];
|
||||
for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
|
||||
ivBlock = encryptBlockFn(ivBlock, key);
|
||||
const block = cipherText.slice(i, i + BLOCK_SIZE);
|
||||
plainText.push(...xorBlocks(ivBlock, block));
|
||||
}
|
||||
return plainText.slice(0, originalLength);
|
||||
}
|
||||
|
||||
case "CTR": {
|
||||
let counter = [...iv];
|
||||
for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
|
||||
const encrypted = encryptBlockFn(counter, key);
|
||||
const block = cipherText.slice(i, i + BLOCK_SIZE);
|
||||
plainText.push(...xorBlocks(encrypted, block));
|
||||
counter = incrementCounter(counter);
|
||||
}
|
||||
return plainText.slice(0, originalLength);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new OperationError(`Invalid block cipher mode: ${mode}`);
|
||||
}
|
||||
|
||||
if (mode === "ECB" || mode === "CBC") {
|
||||
return removePadding(plainText, padding);
|
||||
}
|
||||
|
||||
return plainText.slice(0, originalLength);
|
||||
}
|
||||
|
||||
|
||||
// ==================== PUBLIC API ====================
|
||||
|
||||
/**
|
||||
* Encrypt using TEA cipher
|
||||
* @param {number[]} message - Plaintext bytes
|
||||
* @param {number[]} key - 16-byte key
|
||||
* @param {number[]} iv - 8-byte IV
|
||||
* @param {string} mode - Block cipher mode
|
||||
* @param {string} padding - Padding type
|
||||
* @returns {number[]} - Ciphertext bytes
|
||||
*/
|
||||
export function encryptTEA(message, key, iv, mode = "ECB", padding = "PKCS5") {
|
||||
return encryptWithMode(message, key, iv, mode, padding, teaEncryptBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt using TEA cipher
|
||||
* @param {number[]} cipherText - Ciphertext bytes
|
||||
* @param {number[]} key - 16-byte key
|
||||
* @param {number[]} iv - 8-byte IV
|
||||
* @param {string} mode - Block cipher mode
|
||||
* @param {string} padding - Padding type
|
||||
* @returns {number[]} - Plaintext bytes
|
||||
*/
|
||||
export function decryptTEA(cipherText, key, iv, mode = "ECB", padding = "PKCS5") {
|
||||
return decryptWithMode(cipherText, key, iv, mode, padding, teaEncryptBlock, teaDecryptBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt using XTEA cipher
|
||||
* @param {number[]} message - Plaintext bytes
|
||||
* @param {number[]} key - 16-byte key
|
||||
* @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", rounds = 32) {
|
||||
const encFn = (block, k) => xteaEncryptBlock(block, k, rounds);
|
||||
return encryptWithMode(message, key, iv, mode, padding, encFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt using XTEA cipher
|
||||
* @param {number[]} cipherText - Ciphertext bytes
|
||||
* @param {number[]} key - 16-byte key
|
||||
* @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", 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) */
|
||||
export const TEA_BLOCK_SIZE = BLOCK_SIZE;
|
||||
98
src/core/operations/TEADecrypt.mjs
Normal file
98
src/core/operations/TEADecrypt.mjs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* @author Medjedtxm
|
||||
* @copyright Crown Copyright 2026
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { toHex } from "../lib/Hex.mjs";
|
||||
import { decryptTEA, TEA_BLOCK_SIZE } from "../lib/TEA.mjs";
|
||||
|
||||
/**
|
||||
* TEA Decrypt operation
|
||||
*/
|
||||
class TEADecrypt extends Operation {
|
||||
|
||||
/**
|
||||
* TEADecrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "TEA Decrypt";
|
||||
this.module = "Ciphers";
|
||||
this.description = "TEA (Tiny Encryption Algorithm) is a block cipher designed by David Wheeler and Roger Needham in 1994. It operates on 64-bit blocks using a 128-bit key and performs 32 cycles (64 Feistel rounds) with the DELTA constant 0x9E3779B9 derived from the golden ratio.<br><br>TEA is notable for its simplicity and compact implementation, making it frequently encountered in malware analysis and CTF challenges. Despite its elegance, TEA has known weaknesses including equivalent keys and susceptibility to related-key attacks, leading to successors XTEA and XXTEA.<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.infoURL = "https://wikipedia.org/wiki/Tiny_Encryption_Algorithm";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "IV",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "ECB"]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
"type": "option",
|
||||
"value": ["Hex", "Raw"]
|
||||
},
|
||||
{
|
||||
"name": "Output",
|
||||
"type": "option",
|
||||
"value": ["Raw", "Hex"]
|
||||
},
|
||||
{
|
||||
"name": "Padding",
|
||||
"type": "option",
|
||||
"value": ["PKCS5", "NO", "ZERO", "RANDOM", "BIT"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
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;
|
||||
|
||||
if (key.length !== 16)
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
|
||||
TEA 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")
|
||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||
|
||||
TEA 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).`);
|
||||
|
||||
// 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 = decryptTEA(input, key, actualIv, mode, padding);
|
||||
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default TEADecrypt;
|
||||
98
src/core/operations/TEAEncrypt.mjs
Normal file
98
src/core/operations/TEAEncrypt.mjs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* @author Medjedtxm
|
||||
* @copyright Crown Copyright 2026
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { toHex } from "../lib/Hex.mjs";
|
||||
import { encryptTEA, TEA_BLOCK_SIZE } from "../lib/TEA.mjs";
|
||||
|
||||
/**
|
||||
* TEA Encrypt operation
|
||||
*/
|
||||
class TEAEncrypt extends Operation {
|
||||
|
||||
/**
|
||||
* TEAEncrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "TEA Encrypt";
|
||||
this.module = "Ciphers";
|
||||
this.description = "TEA (Tiny Encryption Algorithm) is a block cipher designed by David Wheeler and Roger Needham in 1994. It operates on 64-bit blocks using a 128-bit key and performs 32 cycles (64 Feistel rounds) with the DELTA constant 0x9E3779B9 derived from the golden ratio.<br><br>TEA is notable for its simplicity and compact implementation, making it frequently encountered in malware analysis and CTF challenges. Despite its elegance, TEA has known weaknesses including equivalent keys and susceptibility to related-key attacks, leading to successors XTEA and XXTEA.<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.infoURL = "https://wikipedia.org/wiki/Tiny_Encryption_Algorithm";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "IV",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "ECB"]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
"type": "option",
|
||||
"value": ["Raw", "Hex"]
|
||||
},
|
||||
{
|
||||
"name": "Output",
|
||||
"type": "option",
|
||||
"value": ["Hex", "Raw"]
|
||||
},
|
||||
{
|
||||
"name": "Padding",
|
||||
"type": "option",
|
||||
"value": ["PKCS5", "NO", "ZERO", "RANDOM", "BIT"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
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;
|
||||
|
||||
if (key.length !== 16)
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
|
||||
TEA 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")
|
||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||
|
||||
TEA 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).`);
|
||||
|
||||
// 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 = encryptTEA(input, key, actualIv, mode, padding);
|
||||
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default TEAEncrypt;
|
||||
110
src/core/operations/XTEADecrypt.mjs
Normal file
110
src/core/operations/XTEADecrypt.mjs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* @author Medjedtxm
|
||||
* @copyright Crown Copyright 2026
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { toHex } from "../lib/Hex.mjs";
|
||||
import { decryptXTEA, TEA_BLOCK_SIZE } from "../lib/TEA.mjs";
|
||||
|
||||
/**
|
||||
* XTEA Decrypt operation
|
||||
*/
|
||||
class XTEADecrypt extends Operation {
|
||||
|
||||
/**
|
||||
* XTEADecrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
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 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 & 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";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "IV",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "ECB"]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
"type": "option",
|
||||
"value": ["Hex", "Raw"]
|
||||
},
|
||||
{
|
||||
"name": "Output",
|
||||
"type": "option",
|
||||
"value": ["Raw", "Hex"]
|
||||
},
|
||||
{
|
||||
"name": "Padding",
|
||||
"type": "option",
|
||||
"value": ["PKCS5", "NO", "ZERO", "RANDOM", "BIT"]
|
||||
},
|
||||
{
|
||||
"name": "Rounds",
|
||||
"type": "number",
|
||||
"value": 32,
|
||||
"min": 1,
|
||||
"max": 255
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
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, rounds] = args;
|
||||
|
||||
if (key.length !== 16)
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
|
||||
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")
|
||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||
|
||||
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, rounds);
|
||||
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default XTEADecrypt;
|
||||
110
src/core/operations/XTEAEncrypt.mjs
Normal file
110
src/core/operations/XTEAEncrypt.mjs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* @author Medjedtxm
|
||||
* @copyright Crown Copyright 2026
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { toHex } from "../lib/Hex.mjs";
|
||||
import { encryptXTEA, TEA_BLOCK_SIZE } from "../lib/TEA.mjs";
|
||||
|
||||
/**
|
||||
* XTEA Encrypt operation
|
||||
*/
|
||||
class XTEAEncrypt extends Operation {
|
||||
|
||||
/**
|
||||
* XTEAEncrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
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 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 & 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";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Key",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "IV",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "ECB"]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
"type": "option",
|
||||
"value": ["Raw", "Hex"]
|
||||
},
|
||||
{
|
||||
"name": "Output",
|
||||
"type": "option",
|
||||
"value": ["Hex", "Raw"]
|
||||
},
|
||||
{
|
||||
"name": "Padding",
|
||||
"type": "option",
|
||||
"value": ["PKCS5", "NO", "ZERO", "RANDOM", "BIT"]
|
||||
},
|
||||
{
|
||||
"name": "Rounds",
|
||||
"type": "number",
|
||||
"value": 32,
|
||||
"min": 1,
|
||||
"max": 255
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
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, rounds] = args;
|
||||
|
||||
if (key.length !== 16)
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
|
||||
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")
|
||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||
|
||||
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, rounds);
|
||||
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default XTEAEncrypt;
|
||||
|
|
@ -186,6 +186,7 @@ import "./tests/JSONtoYAML.mjs";
|
|||
import "./tests/YARA.mjs";
|
||||
import "./tests/ParseCSR.mjs";
|
||||
import "./tests/XXTEA.mjs";
|
||||
import "./tests/TEA.mjs";
|
||||
|
||||
const testStatus = {
|
||||
allTestsPassing: true,
|
||||
|
|
|
|||
566
tests/operations/tests/TEA.mjs
Normal file
566
tests/operations/tests/TEA.mjs
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
/**
|
||||
* TEA and XTEA cipher tests.
|
||||
*
|
||||
* Test vectors sourced from:
|
||||
* - TEA: https://www.cix.co.uk/~klockstone/teavect.htm (Wheeler & Needham reference)
|
||||
* - XTEA: https://github.com/golang/crypto/blob/master/xtea/xtea_test.go (Go standard library)
|
||||
* Bouncy Castle XTEA test vectors (big-endian)
|
||||
*
|
||||
* @author Medjedtxm
|
||||
* @copyright Crown Copyright 2026
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
/**
|
||||
* TEA ECB Tests — Official test vectors from Wheeler & Needham
|
||||
*
|
||||
* From teavect.htm: TEA uses a fixed 32 cycles (64 Feistel rounds).
|
||||
* Row 1: plaintext 00000000 00000000, key 00000000 00000000 00000000 00000000
|
||||
* -> ciphertext 41ea3a0a 94baa940
|
||||
*/
|
||||
TestRegister.addTests([
|
||||
// ==================== TEA ECB TESTS ====================
|
||||
{
|
||||
name: "TEA Encrypt: ECB, all-zero key and plaintext",
|
||||
input: "0000000000000000",
|
||||
expectedOutput: "41ea3a0a94baa940",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "00000000000000000000000000000000"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "TEA Decrypt: ECB, all-zero key",
|
||||
input: "41ea3a0a94baa940",
|
||||
expectedOutput: "0000000000000000",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "00000000000000000000000000000000"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "TEA Encrypt then Decrypt: round-trip ECB",
|
||||
input: "48656c6c6f212121",
|
||||
expectedOutput: "48656c6c6f212121",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "TEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== TEA CBC TEST ====================
|
||||
{
|
||||
name: "TEA Encrypt then Decrypt: round-trip CBC with PKCS5",
|
||||
input: "Hello TEA cipher!",
|
||||
expectedOutput: "Hello TEA cipher!",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "0000000000000000"},
|
||||
"CBC", "Raw", "Hex", "PKCS5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "TEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "0000000000000000"},
|
||||
"CBC", "Hex", "Raw", "PKCS5"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== TEA CTR TEST ====================
|
||||
{
|
||||
name: "TEA Encrypt then Decrypt: round-trip CTR",
|
||||
input: "Short",
|
||||
expectedOutput: "Short",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
|
||||
{"option": "Hex", "string": "0102030405060708"},
|
||||
"CTR", "Raw", "Hex", "NO"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "TEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
|
||||
{"option": "Hex", "string": "0102030405060708"},
|
||||
"CTR", "Hex", "Raw", "NO"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== TEA CFB TEST ====================
|
||||
{
|
||||
name: "TEA Encrypt then Decrypt: round-trip CFB",
|
||||
input: "CFB mode testing with TEA",
|
||||
expectedOutput: "CFB mode testing with TEA",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "aabbccddeeff0011"},
|
||||
"CFB", "Raw", "Hex", "NO"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "TEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "aabbccddeeff0011"},
|
||||
"CFB", "Hex", "Raw", "NO"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== TEA OFB TEST ====================
|
||||
{
|
||||
name: "TEA Encrypt then Decrypt: round-trip OFB",
|
||||
input: "OFB mode testing with TEA",
|
||||
expectedOutput: "OFB mode testing with TEA",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "1122334455667788"},
|
||||
"OFB", "Raw", "Hex", "NO"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "TEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "1122334455667788"},
|
||||
"OFB", "Hex", "Raw", "NO"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== XTEA ECB TESTS ====================
|
||||
// Go standard library + Bouncy Castle test vectors (big-endian)
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, sequential key, 'ABCDEFGH'",
|
||||
input: "4142434445464748",
|
||||
expectedOutput: "497df3d072612cb5",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Decrypt: ECB, sequential key",
|
||||
input: "497df3d072612cb5",
|
||||
expectedOutput: "4142434445464748",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, sequential key, 'AAAAAAAA'",
|
||||
input: "4141414141414141",
|
||||
expectedOutput: "e78f2d13744341d8",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, sequential key, plaintext 5a5b6e27",
|
||||
input: "5a5b6e278948d77f",
|
||||
expectedOutput: "4141414141414141",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "000102030405060708090a0b0c0d0e0f"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, all-zero key, 'ABCDEFGH'",
|
||||
input: "4142434445464748",
|
||||
expectedOutput: "a0390589f8b8efa5",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "00000000000000000000000000000000"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, all-zero key, 'AAAAAAAA'",
|
||||
input: "4141414141414141",
|
||||
expectedOutput: "ed23375a821a8c2d",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "00000000000000000000000000000000"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, all-zero key, all-zero plaintext",
|
||||
input: "0000000000000000",
|
||||
expectedOutput: "dee9d4d8f7131ed9",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "00000000000000000000000000000000"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, all-zero key, sequential plaintext",
|
||||
input: "0102030405060708",
|
||||
expectedOutput: "065c1b8975c6a816",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "00000000000000000000000000000000"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, pattern key, all-zero plaintext",
|
||||
input: "0000000000000000",
|
||||
expectedOutput: "1ff9a0261ac64264",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456712345678234567893456789a"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: ECB, pattern key, sequential plaintext",
|
||||
input: "0102030405060708",
|
||||
expectedOutput: "8c67155b2ef91ead",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456712345678234567893456789a"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Decrypt: ECB, pattern key, sequential ciphertext",
|
||||
input: "8c67155b2ef91ead",
|
||||
expectedOutput: "0102030405060708",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456712345678234567893456789a"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== XTEA CBC TEST ====================
|
||||
{
|
||||
name: "XTEA Encrypt then Decrypt: round-trip CBC with PKCS5",
|
||||
input: "Hello XTEA cipher!",
|
||||
expectedOutput: "Hello XTEA cipher!",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "fedcba9876543210"},
|
||||
"CBC", "Raw", "Hex", "PKCS5", 32
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "XTEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "fedcba9876543210"},
|
||||
"CBC", "Hex", "Raw", "PKCS5", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== XTEA OFB TEST ====================
|
||||
{
|
||||
name: "XTEA Encrypt then Decrypt: round-trip OFB",
|
||||
input: "Stream mode test",
|
||||
expectedOutput: "Stream mode test",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
|
||||
{"option": "Hex", "string": "0102030405060708"},
|
||||
"OFB", "Raw", "Hex", "NO", 32
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "XTEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
|
||||
{"option": "Hex", "string": "0102030405060708"},
|
||||
"OFB", "Hex", "Raw", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== XTEA CTR TEST ====================
|
||||
{
|
||||
name: "XTEA Encrypt then Decrypt: round-trip CTR",
|
||||
input: "CTR mode with XTEA",
|
||||
expectedOutput: "CTR mode with XTEA",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
|
||||
{"option": "Hex", "string": "0000000000000001"},
|
||||
"CTR", "Raw", "Hex", "NO", 32
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "XTEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeef"},
|
||||
{"option": "Hex", "string": "0000000000000001"},
|
||||
"CTR", "Hex", "Raw", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== XTEA CFB TEST ====================
|
||||
{
|
||||
name: "XTEA Encrypt then Decrypt: round-trip CFB",
|
||||
input: "CFB mode with XTEA cipher",
|
||||
expectedOutput: "CFB mode with XTEA cipher",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "aabbccddeeff0011"},
|
||||
"CFB", "Raw", "Hex", "NO", 32
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "XTEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": "aabbccddeeff0011"},
|
||||
"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
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== EDGE CASES ====================
|
||||
{
|
||||
name: "TEA Encrypt: empty input returns empty",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "00000000000000000000000000000000"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt: empty input returns empty",
|
||||
input: "",
|
||||
expectedOutput: "",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "00000000000000000000000000000000"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Hex", "NO", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
// ==================== MULTI-BLOCK ECB TESTS ====================
|
||||
{
|
||||
name: "TEA Encrypt then Decrypt: multi-block ECB with PKCS5",
|
||||
input: "This is a longer message that spans multiple TEA blocks!",
|
||||
expectedOutput: "This is a longer message that spans multiple TEA blocks!",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "TEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Raw", "Hex", "PKCS5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "TEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Raw", "PKCS5"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "XTEA Encrypt then Decrypt: multi-block ECB with PKCS5",
|
||||
input: "This is a longer message that spans multiple XTEA blocks!",
|
||||
expectedOutput: "This is a longer message that spans multiple XTEA blocks!",
|
||||
recipeConfig: [
|
||||
{
|
||||
"op": "XTEA Encrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Raw", "Hex", "PKCS5", 32
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "XTEA Decrypt",
|
||||
"args": [
|
||||
{"option": "Hex", "string": "0123456789abcdef0123456789abcdef"},
|
||||
{"option": "Hex", "string": ""},
|
||||
"ECB", "Hex", "Raw", "PKCS5", 32
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
]);
|
||||
Loading…
Add table
Add a link
Reference in a new issue