From dd1dde0c5de1bda7f452ccc860993168d66f5e7c Mon Sep 17 00:00:00 2001 From: Medjedtxm Date: Sat, 31 Jan 2026 09:22:45 -0500 Subject: [PATCH] refactor: include edge case --- src/core/lib/RC6.mjs | 428 ++++++++++++--------- src/core/operations/RC6Decrypt.mjs | 5 - src/core/operations/RC6Encrypt.mjs | 5 - tests/operations/tests/RC6.mjs | 572 +++++------------------------ 4 files changed, 342 insertions(+), 668 deletions(-) diff --git a/src/core/lib/RC6.mjs b/src/core/lib/RC6.mjs index 4dd7d7e4..eaa6087c 100644 --- a/src/core/lib/RC6.mjs +++ b/src/core/lib/RC6.mjs @@ -1,11 +1,15 @@ /** * Complete implementation of RC6 block cipher encryption/decryption with - * ECB, CBC, CFB, OFB, CTR block modes. + * configurable word size (w), rounds (r), and key length (b). * * RC6 was an AES finalist designed by Ron Rivest, Matt Robshaw, Ray Sidney, and Yiqun Lisa Yin. * Reference: https://en.wikipedia.org/wiki/RC6 * Test Vectors: https://datatracker.ietf.org/doc/html/draft-krovetz-rc6-rc5-vectors-00 * + * The P and Q constants are derived from mathematical constants e (Euler's number) and + * φ (golden ratio) as specified in the IETF draft. Master 256-bit values are scaled to + * any word size. + * * @author Medjedtxm * @copyright Crown Copyright 2026 * @license Apache-2.0 @@ -13,70 +17,133 @@ import OperationError from "../errors/OperationError.mjs"; -/** Number of rounds (RC6-32/20/b for AES compatibility) */ -const NROUNDS = 20; - -/** Block size in bytes (128 bits = 4 words × 32 bits) */ -const BLOCKSIZE = 16; - -/** Magic constant P for w=32: Odd((e-2)*2^32) where e=2.71828... */ -const P32 = 0xB7E15163; - -/** Magic constant Q for w=32: Odd((phi-1)*2^32) where phi=1.61803... (golden ratio) */ -const Q32 = 0x9E3779B9; +/** + * Master P constant (256-bit) from IETF draft-krovetz-rc6-rc5-vectors-00 + * Derived from Odd((e-2) * 2^256) where e = 2.71828... + */ +const P_256 = 0xb7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfefn; /** - * Rotate left 32-bit value - * @param {number} x - Value to rotate - * @param {number} n - Rotation amount (only lower 5 bits used) - * @returns {number} - Rotated value as unsigned 32-bit + * Master Q constant (256-bit) from IETF draft-krovetz-rc6-rc5-vectors-00 + * Derived from Odd((φ-1) * 2^256) where φ = 1.61803... (golden ratio) */ -function ROL(x, n) { - n &= 0x1F; // Only use lower 5 bits (log2(32) = 5) - return ((x << n) | (x >>> (32 - n))) >>> 0; +const Q_256 = 0x9e3779b97f4a7c15f39cc0605cedc8341082276bf3a27251f86c6a11d0c18e95n; + +/** + * Get P constant for given word size by scaling the 256-bit master constant + * @param {number} w - Word size in bits + * @returns {bigint} - P constant for word size w + */ +function getP(w) { + return (P_256 >> BigInt(256 - w)) | 1n; // Ensure odd } /** - * Rotate right 32-bit value - * @param {number} x - Value to rotate - * @param {number} n - Rotation amount (only lower 5 bits used) - * @returns {number} - Rotated value as unsigned 32-bit + * Get Q constant for given word size by scaling the 256-bit master constant + * @param {number} w - Word size in bits + * @returns {bigint} - Q constant for word size w */ -function ROR(x, n) { - n &= 0x1F; - return ((x >>> n) | (x << (32 - n))) >>> 0; +function getQ(w) { + return (Q_256 >> BigInt(256 - w)) | 1n; // Ensure odd } /** - * Convert byte array to 32-bit word array (little-endian) + * Get block size in bytes for given word size + * Block size = 4 words = 4 * (w/8) bytes + * @param {number} w - Word size in bits + * @returns {number} - Block size in bytes + */ +export function getBlockSize(w) { + return 4 * (w / 8); +} + +/** + * Get recommended number of rounds for given word size + * @param {number} w - Word size in bits + * @returns {number} - Recommended rounds + */ +export function getDefaultRounds(w) { + if (w <= 16) return 16; + if (w <= 32) return 20; + if (w <= 64) return 24; + return 28; +} + +/** + * Create mask for w-bit word + * @param {number} w - Word size in bits + * @returns {bigint} - Mask with w bits set + */ +function wordMask(w) { + return (1n << BigInt(w)) - 1n; +} + +/** + * Rotate left for arbitrary word size using BigInt + * Uses lower lg(w) bits of n for rotation amount (RC6 spec) + * @param {bigint} x - Value to rotate + * @param {bigint} n - Rotation amount + * @param {number} w - Word size in bits + * @param {bigint} lgMask - Mask for lower lg(w) bits + * @returns {bigint} - Rotated value + */ +function ROL(x, n, w, lgMask) { + const mask = wordMask(w); + // Mask to lg(w) bits, then mod w for non-power-of-2 word sizes + // For power-of-2, (n & lgMask) < w always, so mod w is no-op + const shift = (n & lgMask) % BigInt(w); + return ((x << shift) | (x >> (BigInt(w) - shift))) & mask; +} + +/** + * Rotate right for arbitrary word size using BigInt + * Uses lower lg(w) bits of n for rotation amount (RC6 spec) + * @param {bigint} x - Value to rotate + * @param {bigint} n - Rotation amount + * @param {number} w - Word size in bits + * @param {bigint} lgMask - Mask for lower lg(w) bits + * @returns {bigint} - Rotated value + */ +function ROR(x, n, w, lgMask) { + const mask = wordMask(w); + // Mask to lg(w) bits, then mod w for non-power-of-2 word sizes + // For power-of-2, (n & lgMask) < w always, so mod w is no-op + const shift = (n & lgMask) % BigInt(w); + return ((x >> shift) | (x << (BigInt(w) - shift))) & mask; +} + +/** + * Convert byte array to word array (little-endian) using BigInt * @param {number[]} bytes - Input byte array - * @returns {number[]} - Array of 32-bit words + * @param {number} w - Word size in bits + * @returns {bigint[]} - Array of w-bit words as BigInt */ -function bytesToWords(bytes) { +function bytesToWords(bytes, w) { + const bytesPerWord = w / 8; const words = []; - for (let i = 0; i < bytes.length; i += 4) { - words.push( - ((bytes[i] || 0)) | - ((bytes[i + 1] || 0) << 8) | - ((bytes[i + 2] || 0) << 16) | - ((bytes[i + 3] || 0) << 24) - ); + for (let i = 0; i < bytes.length; i += bytesPerWord) { + let word = 0n; + for (let j = 0; j < bytesPerWord && (i + j) < bytes.length; j++) { + word |= BigInt(bytes[i + j] || 0) << BigInt(j * 8); + } + words.push(word); } return words; } /** - * Convert 32-bit word array to byte array (little-endian) - * @param {number[]} words - Array of 32-bit words + * Convert word array to byte array (little-endian) using BigInt + * @param {bigint[]} words - Array of words + * @param {number} w - Word size in bits * @returns {number[]} - Output byte array */ -function wordsToBytes(words) { +function wordsToBytes(words, w) { + const bytesPerWord = w / 8; const bytes = []; - for (const w of words) { - bytes.push(w & 0xFF); - bytes.push((w >>> 8) & 0xFF); - bytes.push((w >>> 16) & 0xFF); - bytes.push((w >>> 24) & 0xFF); + for (const word of words) { + for (let j = 0; j < bytesPerWord; j++) { + bytes.push(Number((word >> BigInt(j * 8)) & 0xFFn)); + } } return bytes; } @@ -84,42 +151,50 @@ function wordsToBytes(words) { /** * Generate round subkeys from user key * - * Algorithm from RC6 specification: - * 1. Initialise S[0..2r+3] using P and Q - * 2. Mix in the user key L[0..c-1] - * - * @param {number[]} key - User key as byte array (16, 24, or 32 bytes) - * @returns {number[]} - Array of 2r+4 = 44 subkeys + * @param {number[]} key - User key as byte array + * @param {number} rounds - Number of rounds + * @param {number} w - Word size in bits + * @returns {bigint[]} - Array of 2r+4 subkeys as BigInt */ -function generateSubkeys(key) { - const b = key.length; // Key length in bytes - const c = Math.max(Math.ceil(b / 4), 1); // Key length in words (at least 1) +function generateSubkeys(key, rounds, w) { + const bytesPerWord = w / 8; + const b = key.length; + const c = Math.max(Math.ceil(b / bytesPerWord), 1); - // Convert key bytes to words (little-endian), pad with zeros if needed + // Convert key bytes to words, pad with zeros if needed const paddedKey = [...key]; - while (paddedKey.length < c * 4) { + while (paddedKey.length < c * bytesPerWord) { paddedKey.push(0); } - const L = bytesToWords(paddedKey); + const L = bytesToWords(paddedKey, w); - // Number of subkeys: 2*r + 4 = 2*20 + 4 = 44 - const t = 2 * NROUNDS + 4; + // Number of subkeys: 2*r + 4 + const t = 2 * rounds + 4; + + // Get P and Q for this word size + const P = getP(w); + const Q = getQ(w); + const mask = wordMask(w); + + // lg(w) mask for rotation amounts (floor of log2(w), per RC6 spec) + const lgw = Math.floor(Math.log2(w)); + const lgMask = (1n << BigInt(lgw)) - 1n; // Initialise S array with magic constants const S = new Array(t); - S[0] = P32; + S[0] = P; for (let i = 1; i < t; i++) { - S[i] = (S[i - 1] + Q32) >>> 0; + S[i] = (S[i - 1] + Q) & mask; } // Mix key into S - let A = 0, B = 0; + let A = 0n, B = 0n; let i = 0, j = 0; const v = 3 * Math.max(c, t); for (let s = 0; s < v; s++) { - A = S[i] = ROL((S[i] + A + B) >>> 0, 3); - B = L[j] = ROL((L[j] + A + B) >>> 0, (A + B) >>> 0); + A = S[i] = ROL((S[i] + A + B) & mask, 3n, w, lgMask); + B = L[j] = ROL((L[j] + A + B) & mask, A + B, w, lgMask); i = (i + 1) % t; j = (j + 1) % c; } @@ -128,46 +203,39 @@ function generateSubkeys(key) { } /** - * Encrypt a single 128-bit block using RC6 + * Encrypt a single block using RC6 * - * Algorithm: - * B = B + S[0] - * D = D + S[1] - * for i = 1 to r do - * t = ROL(B * (2B + 1), log2(w)) - * u = ROL(D * (2D + 1), log2(w)) - * A = ROL(A ^ t, u) + S[2i] - * C = ROL(C ^ u, t) + S[2i + 1] - * (A, B, C, D) = (B, C, D, A) - * A = A + S[2r + 2] - * C = C + S[2r + 3] - * - * @param {number[]} block - 16-byte plaintext block - * @param {number[]} S - Subkeys array - * @returns {number[]} - 16-byte ciphertext block + * @param {number[]} block - Plaintext block (4*w/8 bytes) + * @param {bigint[]} S - Subkeys array + * @param {number} rounds - Number of rounds + * @param {number} w - Word size in bits + * @returns {number[]} - Ciphertext block */ -function encryptBlock(block, S) { - // Convert block to 4 words (A, B, C, D) in little-endian - let [A, B, C, D] = bytesToWords(block); +function encryptBlock(block, S, rounds, w) { + const mask = wordMask(w); + const lgw = BigInt(Math.floor(Math.log2(w))); + const lgMask = (1n << lgw) - 1n; + + // Convert block to 4 words (A, B, C, D) + let [A, B, C, D] = bytesToWords(block, w); // Pre-whitening - B = (B + S[0]) >>> 0; - D = (D + S[1]) >>> 0; + B = (B + S[0]) & mask; + D = (D + S[1]) & mask; // Main rounds - for (let i = 1; i <= NROUNDS; i++) { - // t = ROL(B * (2B + 1), 5) - // The multiplication B * (2B + 1) needs to be done in 32-bit - const t = ROL(Math.imul(B, (2 * B + 1) >>> 0) >>> 0, 5); + for (let i = 1; i <= rounds; i++) { + // t = ROL(B * (2B + 1), lg(w)) + const t = ROL((B * ((2n * B + 1n) & mask)) & mask, lgw, w, lgMask); - // u = ROL(D * (2D + 1), 5) - const u = ROL(Math.imul(D, (2 * D + 1) >>> 0) >>> 0, 5); + // u = ROL(D * (2D + 1), lg(w)) + const u = ROL((D * ((2n * D + 1n) & mask)) & mask, lgw, w, lgMask); // A = ROL(A ^ t, u) + S[2i] - A = (ROL(A ^ t, u) + S[2 * i]) >>> 0; + A = (ROL(A ^ t, u, w, lgMask) + S[2 * i]) & mask; // C = ROL(C ^ u, t) + S[2i + 1] - C = (ROL(C ^ u, t) + S[2 * i + 1]) >>> 0; + C = (ROL(C ^ u, t, w, lgMask) + S[2 * i + 1]) & mask; // Rotate registers: (A, B, C, D) = (B, C, D, A) const temp = A; @@ -178,42 +246,36 @@ function encryptBlock(block, S) { } // Post-whitening - A = (A + S[2 * NROUNDS + 2]) >>> 0; - C = (C + S[2 * NROUNDS + 3]) >>> 0; + A = (A + S[2 * rounds + 2]) & mask; + C = (C + S[2 * rounds + 3]) & mask; // Convert words back to bytes - return wordsToBytes([A, B, C, D]); + return wordsToBytes([A, B, C, D], w); } /** - * Decrypt a single 128-bit block using RC6 + * Decrypt a single block using RC6 * - * Algorithm (inverse of encryption): - * C = C - S[2r + 3] - * A = A - S[2r + 2] - * for i = r downto 1 do - * (A, B, C, D) = (D, A, B, C) - * u = ROL(D * (2D + 1), log2(w)) - * t = ROL(B * (2B + 1), log2(w)) - * C = ROR(C - S[2i + 1], t) ^ u - * A = ROR(A - S[2i], u) ^ t - * D = D - S[1] - * B = B - S[0] - * - * @param {number[]} block - 16-byte ciphertext block - * @param {number[]} S - Subkeys array - * @returns {number[]} - 16-byte plaintext block + * @param {number[]} block - Ciphertext block (4*w/8 bytes) + * @param {bigint[]} S - Subkeys array + * @param {number} rounds - Number of rounds + * @param {number} w - Word size in bits + * @returns {number[]} - Plaintext block */ -function decryptBlock(block, S) { - // Convert block to 4 words (A, B, C, D) in little-endian - let [A, B, C, D] = bytesToWords(block); +function decryptBlock(block, S, rounds, w) { + const mask = wordMask(w); + const lgw = BigInt(Math.floor(Math.log2(w))); + const lgMask = (1n << lgw) - 1n; + + // Convert block to 4 words (A, B, C, D) + let [A, B, C, D] = bytesToWords(block, w); // Reverse post-whitening - C = (C - S[2 * NROUNDS + 3]) >>> 0; - A = (A - S[2 * NROUNDS + 2]) >>> 0; + C = (C - S[2 * rounds + 3] + (1n << BigInt(w))) & mask; + A = (A - S[2 * rounds + 2] + (1n << BigInt(w))) & mask; // Main rounds in reverse - for (let i = NROUNDS; i >= 1; i--) { + for (let i = rounds; i >= 1; i--) { // Reverse rotate registers: (A, B, C, D) = (D, A, B, C) const temp = D; D = C; @@ -221,36 +283,36 @@ function decryptBlock(block, S) { B = A; A = temp; - // u = ROL(D * (2D + 1), 5) - const u = ROL(Math.imul(D, (2 * D + 1) >>> 0) >>> 0, 5); + // u = ROL(D * (2D + 1), lg(w)) + const u = ROL((D * ((2n * D + 1n) & mask)) & mask, lgw, w, lgMask); - // t = ROL(B * (2B + 1), 5) - const t = ROL(Math.imul(B, (2 * B + 1) >>> 0) >>> 0, 5); + // t = ROL(B * (2B + 1), lg(w)) + const t = ROL((B * ((2n * B + 1n) & mask)) & mask, lgw, w, lgMask); // C = ROR(C - S[2i + 1], t) ^ u - C = ROR((C - S[2 * i + 1]) >>> 0, t) ^ u; + C = ROR((C - S[2 * i + 1] + (1n << BigInt(w))) & mask, t, w, lgMask) ^ u; // A = ROR(A - S[2i], u) ^ t - A = ROR((A - S[2 * i]) >>> 0, u) ^ t; + A = ROR((A - S[2 * i] + (1n << BigInt(w))) & mask, u, w, lgMask) ^ t; } // Reverse pre-whitening - D = (D - S[1]) >>> 0; - B = (B - S[0]) >>> 0; + D = (D - S[1] + (1n << BigInt(w))) & mask; + B = (B - S[0] + (1n << BigInt(w))) & mask; // Convert words back to bytes - return wordsToBytes([A, B, C, D]); + return wordsToBytes([A, B, C, D], w); } /** - * XOR two 16-byte blocks + * XOR two blocks * @param {number[]} a - First block * @param {number[]} b - Second block * @returns {number[]} - XOR result */ function xorBlocks(a, b) { - const result = new Array(BLOCKSIZE); - for (let i = 0; i < BLOCKSIZE; i++) { + const result = new Array(a.length); + for (let i = 0; i < a.length; i++) { result[i] = a[i] ^ b[i]; } return result; @@ -258,12 +320,12 @@ function xorBlocks(a, b) { /** * Increment counter (little-endian) - * @param {number[]} counter - 16-byte counter + * @param {number[]} counter - Counter block * @returns {number[]} - Incremented counter */ function incrementCounter(counter) { const result = [...counter]; - for (let i = 0; i < BLOCKSIZE; i++) { + for (let i = 0; i < result.length; i++) { result[i]++; if (result[i] <= 255) break; result[i] = 0; @@ -379,22 +441,25 @@ function removePadding(message, padding, blockSize) { * Encrypt using RC6 cipher with specified block mode * * @param {number[]} message - Plaintext as byte array - * @param {number[]} key - Key (16, 24, or 32 bytes) - * @param {number[]} iv - IV (16 bytes, not used for ECB) + * @param {number[]} key - Key as byte array + * @param {number[]} iv - IV (block size bytes, not used for ECB) * @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR") * @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT") + * @param {number} rounds - Number of rounds (default: 20) + * @param {number} w - Word size in bits (default: 32) * @returns {number[]} - Ciphertext as byte array */ -export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") { +export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5", rounds = 20, w = 32) { + const blockSize = getBlockSize(w); const messageLength = message.length; if (messageLength === 0) return []; - const S = generateSubkeys(key); + const S = generateSubkeys(key, rounds, w); // Apply padding for ECB/CBC modes let paddedMessage; if (mode === "ECB" || mode === "CBC") { - paddedMessage = applyPadding(message, padding, BLOCKSIZE); + paddedMessage = applyPadding(message, padding, blockSize); } else { // Stream modes (CFB, OFB, CTR) don't need padding paddedMessage = [...message]; @@ -404,18 +469,18 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") { switch (mode) { case "ECB": - for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) { - const block = paddedMessage.slice(i, i + BLOCKSIZE); - cipherText.push(...encryptBlock(block, S)); + for (let i = 0; i < paddedMessage.length; i += blockSize) { + const block = paddedMessage.slice(i, i + blockSize); + cipherText.push(...encryptBlock(block, S, rounds, w)); } break; case "CBC": { let ivBlock = [...iv]; - for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) { - const block = paddedMessage.slice(i, i + BLOCKSIZE); + for (let i = 0; i < paddedMessage.length; i += blockSize) { + const block = paddedMessage.slice(i, i + blockSize); const xored = xorBlocks(block, ivBlock); - ivBlock = encryptBlock(xored, S); + ivBlock = encryptBlock(xored, S, rounds, w); cipherText.push(...ivBlock); } break; @@ -423,11 +488,11 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") { case "CFB": { let ivBlock = [...iv]; - for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) { - const encrypted = encryptBlock(ivBlock, S); - const block = paddedMessage.slice(i, i + BLOCKSIZE); - // Pad block if shorter than BLOCKSIZE - while (block.length < BLOCKSIZE) block.push(0); + for (let i = 0; i < paddedMessage.length; i += blockSize) { + const encrypted = encryptBlock(ivBlock, S, rounds, w); + const block = paddedMessage.slice(i, i + blockSize); + // Pad block if shorter than blockSize + while (block.length < blockSize) block.push(0); ivBlock = xorBlocks(encrypted, block); cipherText.push(...ivBlock); } @@ -436,11 +501,11 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") { case "OFB": { let ivBlock = [...iv]; - for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) { - ivBlock = encryptBlock(ivBlock, S); - const block = paddedMessage.slice(i, i + BLOCKSIZE); - // Pad block if shorter than BLOCKSIZE - while (block.length < BLOCKSIZE) block.push(0); + for (let i = 0; i < paddedMessage.length; i += blockSize) { + ivBlock = encryptBlock(ivBlock, S, rounds, w); + const block = paddedMessage.slice(i, i + blockSize); + // Pad block if shorter than blockSize + while (block.length < blockSize) block.push(0); cipherText.push(...xorBlocks(ivBlock, block)); } return cipherText.slice(0, messageLength); @@ -448,11 +513,11 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") { case "CTR": { let counter = [...iv]; - for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) { - const encrypted = encryptBlock(counter, S); - const block = paddedMessage.slice(i, i + BLOCKSIZE); - // Pad block if shorter than BLOCKSIZE - while (block.length < BLOCKSIZE) block.push(0); + for (let i = 0; i < paddedMessage.length; i += blockSize) { + const encrypted = encryptBlock(counter, S, rounds, w); + const block = paddedMessage.slice(i, i + blockSize); + // Pad block if shorter than blockSize + while (block.length < blockSize) block.push(0); cipherText.push(...xorBlocks(encrypted, block)); counter = incrementCounter(counter); } @@ -470,24 +535,27 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") { * Decrypt using RC6 cipher with specified block mode * * @param {number[]} cipherText - Ciphertext as byte array - * @param {number[]} key - Key (16, 24, or 32 bytes) - * @param {number[]} iv - IV (16 bytes, not used for ECB) + * @param {number[]} key - Key as byte array + * @param {number[]} iv - IV (block size bytes, not used for ECB) * @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR") * @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT") + * @param {number} rounds - Number of rounds (default: 20) + * @param {number} w - Word size in bits (default: 32) * @returns {number[]} - Plaintext as byte array */ -export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5") { +export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5", rounds = 20, w = 32) { + const blockSize = getBlockSize(w); const originalLength = cipherText.length; if (originalLength === 0) return []; - const S = generateSubkeys(key); + const S = generateSubkeys(key, rounds, w); if (mode === "ECB" || mode === "CBC") { - if ((originalLength % BLOCKSIZE) !== 0) - throw new OperationError(`Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of 16.`); + if ((originalLength % blockSize) !== 0) + throw new OperationError(`Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of ${blockSize}.`); } else { // Pad for stream modes - while ((cipherText.length % BLOCKSIZE) !== 0) + while ((cipherText.length % blockSize) !== 0) cipherText.push(0); } @@ -495,17 +563,17 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5") switch (mode) { case "ECB": - for (let i = 0; i < cipherText.length; i += BLOCKSIZE) { - const block = cipherText.slice(i, i + BLOCKSIZE); - plainText.push(...decryptBlock(block, S)); + for (let i = 0; i < cipherText.length; i += blockSize) { + const block = cipherText.slice(i, i + blockSize); + plainText.push(...decryptBlock(block, S, rounds, w)); } break; case "CBC": { let ivBlock = [...iv]; - for (let i = 0; i < cipherText.length; i += BLOCKSIZE) { - const block = cipherText.slice(i, i + BLOCKSIZE); - const decrypted = decryptBlock(block, S); + for (let i = 0; i < cipherText.length; i += blockSize) { + const block = cipherText.slice(i, i + blockSize); + const decrypted = decryptBlock(block, S, rounds, w); plainText.push(...xorBlocks(decrypted, ivBlock)); ivBlock = block; } @@ -514,9 +582,9 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5") case "CFB": { let ivBlock = [...iv]; - for (let i = 0; i < cipherText.length; i += BLOCKSIZE) { - const encrypted = encryptBlock(ivBlock, S); - const block = cipherText.slice(i, i + BLOCKSIZE); + for (let i = 0; i < cipherText.length; i += blockSize) { + const encrypted = encryptBlock(ivBlock, S, rounds, w); + const block = cipherText.slice(i, i + blockSize); plainText.push(...xorBlocks(encrypted, block)); ivBlock = block; } @@ -525,9 +593,9 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5") case "OFB": { let ivBlock = [...iv]; - for (let i = 0; i < cipherText.length; i += BLOCKSIZE) { - ivBlock = encryptBlock(ivBlock, S); - const block = cipherText.slice(i, i + BLOCKSIZE); + for (let i = 0; i < cipherText.length; i += blockSize) { + ivBlock = encryptBlock(ivBlock, S, rounds, w); + const block = cipherText.slice(i, i + blockSize); plainText.push(...xorBlocks(ivBlock, block)); } return plainText.slice(0, originalLength); @@ -535,9 +603,9 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5") case "CTR": { let counter = [...iv]; - for (let i = 0; i < cipherText.length; i += BLOCKSIZE) { - const encrypted = encryptBlock(counter, S); - const block = cipherText.slice(i, i + BLOCKSIZE); + for (let i = 0; i < cipherText.length; i += blockSize) { + const encrypted = encryptBlock(counter, S, rounds, w); + const block = cipherText.slice(i, i + blockSize); plainText.push(...xorBlocks(encrypted, block)); counter = incrementCounter(counter); } @@ -550,7 +618,7 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5") // Remove padding for ECB/CBC modes if (mode === "ECB" || mode === "CBC") { - return removePadding(plainText, padding, BLOCKSIZE); + return removePadding(plainText, padding, blockSize); } return plainText.slice(0, originalLength); diff --git a/src/core/operations/RC6Decrypt.mjs b/src/core/operations/RC6Decrypt.mjs index 5c088d0e..3185b632 100644 --- a/src/core/operations/RC6Decrypt.mjs +++ b/src/core/operations/RC6Decrypt.mjs @@ -95,11 +95,6 @@ class RC6Decrypt extends Operation { const blockSize = getBlockSize(wordSize); const defaultRounds = getDefaultRounds(wordSize); - if (key.length === 0) - throw new OperationError(`Invalid key length: ${key.length} bytes - -RC6 requires a key of at least 1 byte.`); - if (iv.length !== blockSize && iv.length !== 0 && mode !== "ECB") throw new OperationError(`Invalid IV length: ${iv.length} bytes diff --git a/src/core/operations/RC6Encrypt.mjs b/src/core/operations/RC6Encrypt.mjs index 990c33e8..d5ad6d7f 100644 --- a/src/core/operations/RC6Encrypt.mjs +++ b/src/core/operations/RC6Encrypt.mjs @@ -95,11 +95,6 @@ class RC6Encrypt extends Operation { const blockSize = getBlockSize(wordSize); const defaultRounds = getDefaultRounds(wordSize); - if (key.length === 0) - throw new OperationError(`Invalid key length: ${key.length} bytes - -RC6 requires a key of at least 1 byte.`); - if (iv.length !== blockSize && iv.length !== 0 && mode !== "ECB") throw new OperationError(`Invalid IV length: ${iv.length} bytes diff --git a/tests/operations/tests/RC6.mjs b/tests/operations/tests/RC6.mjs index bc0220af..b9159ce3 100644 --- a/tests/operations/tests/RC6.mjs +++ b/tests/operations/tests/RC6.mjs @@ -5,9 +5,6 @@ * "Test Vectors for RC6 and RC5" * https://datatracker.ietf.org/doc/html/draft-krovetz-rc6-rc5-vectors-00 * - * Supports all word sizes: 8, 16, 32, 64, 128 bits. - * Round-trip tests verify correct encryption/decryption behaviour. - * * @author Medjedtxm * @copyright Crown Copyright 2026 * @license Apache-2.0 @@ -17,8 +14,7 @@ import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ // ============================================================ - // IETF TEST VECTORS - RC6-8/12/4 (8-bit words, 12 rounds, 4-byte key) - // Block size: 4 bytes (32 bits) + // IETF TEST VECTORS - RC6-8/12/4 // ============================================================ { name: "RC6-8/12/4: IETF vector encrypt", @@ -52,8 +48,7 @@ TestRegister.addTests([ }, // ============================================================ - // IETF TEST VECTORS - RC6-16/16/8 (16-bit words, 16 rounds, 8-byte key) - // Block size: 8 bytes (64 bits) + // IETF TEST VECTORS - RC6-16/16/8 // ============================================================ { name: "RC6-16/16/8: IETF vector encrypt", @@ -87,8 +82,7 @@ TestRegister.addTests([ }, // ============================================================ - // IETF TEST VECTORS - RC6-32/20/16 (32-bit words, 20 rounds, 16-byte key) - // Block size: 16 bytes (128 bits) - Standard AES submission + // IETF TEST VECTORS - RC6-32/20/16 (AES standard) // ============================================================ { name: "RC6-32/20/16: IETF vector encrypt (AES standard)", @@ -122,8 +116,7 @@ TestRegister.addTests([ }, // ============================================================ - // IETF TEST VECTORS - RC6-64/24/24 (64-bit words, 24 rounds, 24-byte key) - // Block size: 32 bytes (256 bits) + // IETF TEST VECTORS - RC6-64/24/24 // ============================================================ { name: "RC6-64/24/24: IETF vector encrypt", @@ -157,8 +150,7 @@ TestRegister.addTests([ }, // ============================================================ - // IETF TEST VECTORS - RC6-128/28/32 (128-bit words, 28 rounds, 32-byte key) - // Block size: 64 bytes (512 bits) + // IETF TEST VECTORS - RC6-128/28/32 // ============================================================ { name: "RC6-128/28/32: IETF vector encrypt", @@ -192,7 +184,75 @@ TestRegister.addTests([ }, // ============================================================ - // ADDITIONAL RC6-32 TEST VECTORS (192-bit and 256-bit keys) + // IETF TEST VECTORS - RC6-24/4/0 (non-power-of-2) + // ============================================================ + { + name: "RC6-24/4/0: IETF non-standard vector encrypt (w=24, empty key)", + input: "000102030405060708090a0b", + expectedOutput: "0177982579be2ee3303269b9", + recipeConfig: [ + { + op: "RC6 Encrypt", + args: [ + { string: "", option: "Hex" }, + { string: "", option: "Hex" }, + "ECB", "Hex", "Hex", "NO", 24, 4 + ] + } + ] + }, + { + name: "RC6-24/4/0: IETF non-standard vector decrypt (w=24, empty key)", + input: "0177982579be2ee3303269b9", + expectedOutput: "000102030405060708090a0b", + recipeConfig: [ + { + op: "RC6 Decrypt", + args: [ + { string: "", option: "Hex" }, + { string: "", option: "Hex" }, + "ECB", "Hex", "Hex", "NO", 24, 4 + ] + } + ] + }, + + // ============================================================ + // IETF TEST VECTORS - RC6-80/4/12 (non-power-of-2) + // ============================================================ + { + name: "RC6-80/4/12: IETF non-standard vector encrypt (w=80)", + input: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627", + expectedOutput: "26d9d6128601d06dec3817d401f1c0ff715473543875da417c2116d1e87c919a49311b00b4e17962", + recipeConfig: [ + { + op: "RC6 Encrypt", + args: [ + { string: "000102030405060708090a0b", option: "Hex" }, + { string: "", option: "Hex" }, + "ECB", "Hex", "Hex", "NO", 80, 4 + ] + } + ] + }, + { + name: "RC6-80/4/12: IETF non-standard vector decrypt (w=80)", + input: "26d9d6128601d06dec3817d401f1c0ff715473543875da417c2116d1e87c919a49311b00b4e17962", + expectedOutput: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627", + recipeConfig: [ + { + op: "RC6 Decrypt", + args: [ + { string: "000102030405060708090a0b", option: "Hex" }, + { string: "", option: "Hex" }, + "ECB", "Hex", "Hex", "NO", 80, 4 + ] + } + ] + }, + + // ============================================================ + // ADDITIONAL KEY SIZE TESTS - RC6-32 (192-bit and 256-bit keys) // ============================================================ { name: "RC6-32/20/24: 192-bit key encrypt", @@ -209,21 +269,6 @@ TestRegister.addTests([ } ] }, - { - name: "RC6-32/20/24: 192-bit key decrypt", - input: "a68a14ff1342262a2bbd21f7966615eb", - expectedOutput: "000102030405060708090a0b0c0d0e0f", - recipeConfig: [ - { - op: "RC6 Decrypt", - args: [ - { string: "000102030405060708090a0b0c0d0e0f1011121314151617", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Hex", "NO", 32, 20 - ] - } - ] - }, { name: "RC6-32/20/32: 256-bit key encrypt", input: "000102030405060708090a0b0c0d0e0f", @@ -239,82 +284,10 @@ TestRegister.addTests([ } ] }, - { - name: "RC6-32/20/32: 256-bit key decrypt", - input: "921c3ecd43d9426a90089334d67aea2e", - expectedOutput: "000102030405060708090a0b0c0d0e0f", - recipeConfig: [ - { - op: "RC6 Decrypt", - args: [ - { string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Hex", "NO", 32, 20 - ] - } - ] - }, // ============================================================ - // ZERO KEY/PLAINTEXT TEST (RC6-32/20/16) + // ROUND-TRIP TESTS - One per word size to verify encrypt/decrypt // ============================================================ - { - name: "RC6-32/20: Zero Key/Plaintext encrypt", - input: "00000000000000000000000000000000", - expectedOutput: "8fc3a53656b1f778c129df4e9848a41e", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00000000000000000000000000000000", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Hex", "NO", 32, 20 - ] - } - ] - }, - { - name: "RC6-32/20: Zero Key/Plaintext decrypt", - input: "8fc3a53656b1f778c129df4e9848a41e", - expectedOutput: "00000000000000000000000000000000", - recipeConfig: [ - { - op: "RC6 Decrypt", - args: [ - { string: "00000000000000000000000000000000", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Hex", "NO", 32, 20 - ] - } - ] - }, - - // ============================================================ - // ROUND-TRIP TESTS - RC6-8 (8-bit words) - // ============================================================ - { - name: "RC6-8 Round-trip: ECB mode", - input: "Test", - expectedOutput: "Test", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 8, 12 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 8, 12 - ] - } - ] - }, { name: "RC6-8 Round-trip: CBC mode", input: "Hello World!", @@ -338,33 +311,6 @@ TestRegister.addTests([ } ] }, - - // ============================================================ - // ROUND-TRIP TESTS - RC6-16 (16-bit words) - // ============================================================ - { - name: "RC6-16 Round-trip: ECB mode", - input: "Testing!", - expectedOutput: "Testing!", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "0011223344556677", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 16, 16 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "0011223344556677", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 16, 16 - ] - } - ] - }, { name: "RC6-16 Round-trip: CBC mode", input: "The quick brown fox", @@ -388,35 +334,8 @@ TestRegister.addTests([ } ] }, - - // ============================================================ - // ROUND-TRIP TESTS - RC6-32 (32-bit words, Standard) - // ============================================================ { - name: "RC6-32 Round-trip: ECB 128-bit key", - input: "Hello World!!!!", - expectedOutput: "Hello World!!!!", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] - }, - { - name: "RC6-32 Round-trip: CBC 128-bit key", + name: "RC6-32 Round-trip: CBC mode", input: "The quick brown fox jumps over the lazy dog", expectedOutput: "The quick brown fox jumps over the lazy dog", recipeConfig: [ @@ -438,125 +357,6 @@ TestRegister.addTests([ } ] }, - { - name: "RC6-32 Round-trip: CFB mode", - input: "CFB mode test message", - expectedOutput: "CFB mode test message", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "ffeeddccbbaa99887766554433221100", option: "Hex" }, - "CFB", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "ffeeddccbbaa99887766554433221100", option: "Hex" }, - "CFB", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] - }, - { - name: "RC6-32 Round-trip: OFB mode", - input: "OFB mode test message", - expectedOutput: "OFB mode test message", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "aabbccddeeff00112233445566778899", option: "Hex" }, - "OFB", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "aabbccddeeff00112233445566778899", option: "Hex" }, - "OFB", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] - }, - { - name: "RC6-32 Round-trip: CTR mode", - input: "CTR mode test message", - expectedOutput: "CTR mode test message", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "00000000000000000000000000000001", option: "Hex" }, - "CTR", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "00000000000000000000000000000001", option: "Hex" }, - "CTR", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] - }, - { - name: "RC6-32 Round-trip: UTF8 key", - input: "Secret message", - expectedOutput: "Secret message", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "mypassword123456", option: "UTF8" }, - { string: "initialisevec123", option: "UTF8" }, - "CBC", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "mypassword123456", option: "UTF8" }, - { string: "initialisevec123", option: "UTF8" }, - "CBC", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] - }, - - // ============================================================ - // ROUND-TRIP TESTS - RC6-64 (64-bit words) - // ============================================================ - { - name: "RC6-64 Round-trip: ECB mode", - input: "Testing 64-bit word size!!!!!!!", - expectedOutput: "Testing 64-bit word size!!!!!!!", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "000102030405060708090a0b0c0d0e0f1011121314151617", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 64, 24 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "000102030405060708090a0b0c0d0e0f1011121314151617", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 64, 24 - ] - } - ] - }, { name: "RC6-64 Round-trip: CBC mode", input: "RC6 with 64-bit words is powerful!", @@ -580,10 +380,6 @@ TestRegister.addTests([ } ] }, - - // ============================================================ - // RC6-128 ROUND-TRIP TESTS (128-bit words, 64-byte block size) - // ============================================================ { name: "RC6-128 Round-trip: ECB mode", input: "RC6 with 128-bit words provides massive block size for testing purposes!", @@ -607,37 +403,41 @@ TestRegister.addTests([ } ] }, + + // ============================================================ + // STREAM MODES TEST - Verify CFB/OFB/CTR work correctly + // ============================================================ { - name: "RC6-128 Round-trip: CBC mode", - input: "RC6-128 with CBC mode needs a 64-byte IV for proper operation with large blocks!", - expectedOutput: "RC6-128 with CBC mode needs a 64-byte IV for proper operation with large blocks!", + name: "RC6-32 Round-trip: CTR mode", + input: "CTR mode test message", + expectedOutput: "CTR mode test message", recipeConfig: [ { op: "RC6 Encrypt", args: [ - { string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", option: "Hex" }, - { string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", option: "Hex" }, - "CBC", "Raw", "Hex", "PKCS5", 128, 28 + { string: "00112233445566778899aabbccddeeff", option: "Hex" }, + { string: "00000000000000000000000000000001", option: "Hex" }, + "CTR", "Raw", "Hex", "PKCS5", 32, 20 ] }, { op: "RC6 Decrypt", args: [ - { string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", option: "Hex" }, - { string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", option: "Hex" }, - "CBC", "Hex", "Raw", "PKCS5", 128, 28 + { string: "00112233445566778899aabbccddeeff", option: "Hex" }, + { string: "00000000000000000000000000000001", option: "Hex" }, + "CTR", "Hex", "Raw", "PKCS5", 32, 20 ] } ] }, // ============================================================ - // CUSTOM ROUND TESTS - Verify non-standard rounds work + // CUSTOM ROUNDS TEST - Verify non-standard round count works // ============================================================ { name: "RC6-32 Round-trip: Custom 8 rounds", - input: "Testing 8 rounds", - expectedOutput: "Testing 8 rounds", + input: "Testing custom rounds", + expectedOutput: "Testing custom rounds", recipeConfig: [ { op: "RC6 Encrypt", @@ -657,150 +457,12 @@ TestRegister.addTests([ } ] }, - { - name: "RC6-32 Round-trip: Custom 12 rounds", - input: "Testing 12 rounds", - expectedOutput: "Testing 12 rounds", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 32, 12 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 32, 12 - ] - } - ] - }, - { - name: "RC6-32 Round-trip: Custom 32 rounds", - input: "Testing 32 rounds", - expectedOutput: "Testing 32 rounds", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 32, 32 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 32, 32 - ] - } - ] - }, - { - name: "RC6-8 Round-trip: Custom 20 rounds", - input: "8-bit with 20 rounds", - expectedOutput: "8-bit with 20 rounds", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 8, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 8, 20 - ] - } - ] - }, - { - name: "RC6-16 Round-trip: Custom 24 rounds", - input: "16-bit with 24 rounds", - expectedOutput: "16-bit with 24 rounds", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "0011223344556677", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", "16", 24 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "0011223344556677", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", "16", 24 - ] - } - ] - }, // ============================================================ - // EDGE CASE TESTS - Various input lengths + // EDGE CASE TEST - Padding boundary // ============================================================ { - name: "RC6-32 Round-trip: 1 byte input", - input: "A", - expectedOutput: "A", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] - }, - { - name: "RC6-32 Round-trip: 15 byte input", - input: "123456789012345", - expectedOutput: "123456789012345", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] - }, - { - name: "RC6-32 Round-trip: 16 byte input (exact block)", + name: "RC6-32 Round-trip: Exact block size input", input: "1234567890123456", expectedOutput: "1234567890123456", recipeConfig: [ @@ -821,51 +483,5 @@ TestRegister.addTests([ ] } ] - }, - { - name: "RC6-32 Round-trip: 17 byte input", - input: "12345678901234567", - expectedOutput: "12345678901234567", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - { string: "", option: "Hex" }, - "ECB", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] - }, - { - name: "RC6-32 Round-trip: Binary data", - input: "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - expectedOutput: "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - recipeConfig: [ - { - op: "RC6 Encrypt", - args: [ - { string: "ffeeddccbbaa99887766554433221100", option: "Hex" }, - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - "CBC", "Raw", "Hex", "PKCS5", 32, 20 - ] - }, - { - op: "RC6 Decrypt", - args: [ - { string: "ffeeddccbbaa99887766554433221100", option: "Hex" }, - { string: "00112233445566778899aabbccddeeff", option: "Hex" }, - "CBC", "Hex", "Raw", "PKCS5", 32, 20 - ] - } - ] } ]);