refactor: include edge case
This commit is contained in:
parent
9f107f4d66
commit
dd1dde0c5d
@ -1,11 +1,15 @@
|
|||||||
/**
|
/**
|
||||||
* Complete implementation of RC6 block cipher encryption/decryption with
|
* 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.
|
* RC6 was an AES finalist designed by Ron Rivest, Matt Robshaw, Ray Sidney, and Yiqun Lisa Yin.
|
||||||
* Reference: https://en.wikipedia.org/wiki/RC6
|
* Reference: https://en.wikipedia.org/wiki/RC6
|
||||||
* Test Vectors: https://datatracker.ietf.org/doc/html/draft-krovetz-rc6-rc5-vectors-00
|
* 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
|
* @author Medjedtxm
|
||||||
* @copyright Crown Copyright 2026
|
* @copyright Crown Copyright 2026
|
||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
@ -13,70 +17,133 @@
|
|||||||
|
|
||||||
import OperationError from "../errors/OperationError.mjs";
|
import OperationError from "../errors/OperationError.mjs";
|
||||||
|
|
||||||
/** Number of rounds (RC6-32/20/b for AES compatibility) */
|
/**
|
||||||
const NROUNDS = 20;
|
* Master P constant (256-bit) from IETF draft-krovetz-rc6-rc5-vectors-00
|
||||||
|
* Derived from Odd((e-2) * 2^256) where e = 2.71828...
|
||||||
/** Block size in bytes (128 bits = 4 words × 32 bits) */
|
*/
|
||||||
const BLOCKSIZE = 16;
|
const P_256 = 0xb7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfefn;
|
||||||
|
|
||||||
/** 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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotate left 32-bit value
|
* Master Q constant (256-bit) from IETF draft-krovetz-rc6-rc5-vectors-00
|
||||||
* @param {number} x - Value to rotate
|
* Derived from Odd((φ-1) * 2^256) where φ = 1.61803... (golden ratio)
|
||||||
* @param {number} n - Rotation amount (only lower 5 bits used)
|
|
||||||
* @returns {number} - Rotated value as unsigned 32-bit
|
|
||||||
*/
|
*/
|
||||||
function ROL(x, n) {
|
const Q_256 = 0x9e3779b97f4a7c15f39cc0605cedc8341082276bf3a27251f86c6a11d0c18e95n;
|
||||||
n &= 0x1F; // Only use lower 5 bits (log2(32) = 5)
|
|
||||||
return ((x << n) | (x >>> (32 - n))) >>> 0;
|
/**
|
||||||
|
* 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
|
* Get Q constant for given word size by scaling the 256-bit master constant
|
||||||
* @param {number} x - Value to rotate
|
* @param {number} w - Word size in bits
|
||||||
* @param {number} n - Rotation amount (only lower 5 bits used)
|
* @returns {bigint} - Q constant for word size w
|
||||||
* @returns {number} - Rotated value as unsigned 32-bit
|
|
||||||
*/
|
*/
|
||||||
function ROR(x, n) {
|
function getQ(w) {
|
||||||
n &= 0x1F;
|
return (Q_256 >> BigInt(256 - w)) | 1n; // Ensure odd
|
||||||
return ((x >>> n) | (x << (32 - n))) >>> 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* @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 = [];
|
const words = [];
|
||||||
for (let i = 0; i < bytes.length; i += 4) {
|
for (let i = 0; i < bytes.length; i += bytesPerWord) {
|
||||||
words.push(
|
let word = 0n;
|
||||||
((bytes[i] || 0)) |
|
for (let j = 0; j < bytesPerWord && (i + j) < bytes.length; j++) {
|
||||||
((bytes[i + 1] || 0) << 8) |
|
word |= BigInt(bytes[i + j] || 0) << BigInt(j * 8);
|
||||||
((bytes[i + 2] || 0) << 16) |
|
}
|
||||||
((bytes[i + 3] || 0) << 24)
|
words.push(word);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return words;
|
return words;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert 32-bit word array to byte array (little-endian)
|
* Convert word array to byte array (little-endian) using BigInt
|
||||||
* @param {number[]} words - Array of 32-bit words
|
* @param {bigint[]} words - Array of words
|
||||||
|
* @param {number} w - Word size in bits
|
||||||
* @returns {number[]} - Output byte array
|
* @returns {number[]} - Output byte array
|
||||||
*/
|
*/
|
||||||
function wordsToBytes(words) {
|
function wordsToBytes(words, w) {
|
||||||
|
const bytesPerWord = w / 8;
|
||||||
const bytes = [];
|
const bytes = [];
|
||||||
for (const w of words) {
|
for (const word of words) {
|
||||||
bytes.push(w & 0xFF);
|
for (let j = 0; j < bytesPerWord; j++) {
|
||||||
bytes.push((w >>> 8) & 0xFF);
|
bytes.push(Number((word >> BigInt(j * 8)) & 0xFFn));
|
||||||
bytes.push((w >>> 16) & 0xFF);
|
}
|
||||||
bytes.push((w >>> 24) & 0xFF);
|
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
@ -84,42 +151,50 @@ function wordsToBytes(words) {
|
|||||||
/**
|
/**
|
||||||
* Generate round subkeys from user key
|
* Generate round subkeys from user key
|
||||||
*
|
*
|
||||||
* Algorithm from RC6 specification:
|
* @param {number[]} key - User key as byte array
|
||||||
* 1. Initialise S[0..2r+3] using P and Q
|
* @param {number} rounds - Number of rounds
|
||||||
* 2. Mix in the user key L[0..c-1]
|
* @param {number} w - Word size in bits
|
||||||
*
|
* @returns {bigint[]} - Array of 2r+4 subkeys as BigInt
|
||||||
* @param {number[]} key - User key as byte array (16, 24, or 32 bytes)
|
|
||||||
* @returns {number[]} - Array of 2r+4 = 44 subkeys
|
|
||||||
*/
|
*/
|
||||||
function generateSubkeys(key) {
|
function generateSubkeys(key, rounds, w) {
|
||||||
const b = key.length; // Key length in bytes
|
const bytesPerWord = w / 8;
|
||||||
const c = Math.max(Math.ceil(b / 4), 1); // Key length in words (at least 1)
|
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];
|
const paddedKey = [...key];
|
||||||
while (paddedKey.length < c * 4) {
|
while (paddedKey.length < c * bytesPerWord) {
|
||||||
paddedKey.push(0);
|
paddedKey.push(0);
|
||||||
}
|
}
|
||||||
const L = bytesToWords(paddedKey);
|
const L = bytesToWords(paddedKey, w);
|
||||||
|
|
||||||
// Number of subkeys: 2*r + 4 = 2*20 + 4 = 44
|
// Number of subkeys: 2*r + 4
|
||||||
const t = 2 * NROUNDS + 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
|
// Initialise S array with magic constants
|
||||||
const S = new Array(t);
|
const S = new Array(t);
|
||||||
S[0] = P32;
|
S[0] = P;
|
||||||
for (let i = 1; i < t; i++) {
|
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
|
// Mix key into S
|
||||||
let A = 0, B = 0;
|
let A = 0n, B = 0n;
|
||||||
let i = 0, j = 0;
|
let i = 0, j = 0;
|
||||||
const v = 3 * Math.max(c, t);
|
const v = 3 * Math.max(c, t);
|
||||||
|
|
||||||
for (let s = 0; s < v; s++) {
|
for (let s = 0; s < v; s++) {
|
||||||
A = S[i] = ROL((S[i] + A + B) >>> 0, 3);
|
A = S[i] = ROL((S[i] + A + B) & mask, 3n, w, lgMask);
|
||||||
B = L[j] = ROL((L[j] + A + B) >>> 0, (A + B) >>> 0);
|
B = L[j] = ROL((L[j] + A + B) & mask, A + B, w, lgMask);
|
||||||
i = (i + 1) % t;
|
i = (i + 1) % t;
|
||||||
j = (j + 1) % c;
|
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:
|
* @param {number[]} block - Plaintext block (4*w/8 bytes)
|
||||||
* B = B + S[0]
|
* @param {bigint[]} S - Subkeys array
|
||||||
* D = D + S[1]
|
* @param {number} rounds - Number of rounds
|
||||||
* for i = 1 to r do
|
* @param {number} w - Word size in bits
|
||||||
* t = ROL(B * (2B + 1), log2(w))
|
* @returns {number[]} - Ciphertext block
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
function encryptBlock(block, S) {
|
function encryptBlock(block, S, rounds, w) {
|
||||||
// Convert block to 4 words (A, B, C, D) in little-endian
|
const mask = wordMask(w);
|
||||||
let [A, B, C, D] = bytesToWords(block);
|
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
|
// Pre-whitening
|
||||||
B = (B + S[0]) >>> 0;
|
B = (B + S[0]) & mask;
|
||||||
D = (D + S[1]) >>> 0;
|
D = (D + S[1]) & mask;
|
||||||
|
|
||||||
// Main rounds
|
// Main rounds
|
||||||
for (let i = 1; i <= NROUNDS; i++) {
|
for (let i = 1; i <= rounds; i++) {
|
||||||
// t = ROL(B * (2B + 1), 5)
|
// t = ROL(B * (2B + 1), lg(w))
|
||||||
// The multiplication B * (2B + 1) needs to be done in 32-bit
|
const t = ROL((B * ((2n * B + 1n) & mask)) & mask, lgw, w, lgMask);
|
||||||
const t = ROL(Math.imul(B, (2 * B + 1) >>> 0) >>> 0, 5);
|
|
||||||
|
|
||||||
// u = ROL(D * (2D + 1), 5)
|
// u = ROL(D * (2D + 1), lg(w))
|
||||||
const u = ROL(Math.imul(D, (2 * D + 1) >>> 0) >>> 0, 5);
|
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[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[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)
|
// Rotate registers: (A, B, C, D) = (B, C, D, A)
|
||||||
const temp = A;
|
const temp = A;
|
||||||
@ -178,42 +246,36 @@ function encryptBlock(block, S) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Post-whitening
|
// Post-whitening
|
||||||
A = (A + S[2 * NROUNDS + 2]) >>> 0;
|
A = (A + S[2 * rounds + 2]) & mask;
|
||||||
C = (C + S[2 * NROUNDS + 3]) >>> 0;
|
C = (C + S[2 * rounds + 3]) & mask;
|
||||||
|
|
||||||
// Convert words back to bytes
|
// 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):
|
* @param {number[]} block - Ciphertext block (4*w/8 bytes)
|
||||||
* C = C - S[2r + 3]
|
* @param {bigint[]} S - Subkeys array
|
||||||
* A = A - S[2r + 2]
|
* @param {number} rounds - Number of rounds
|
||||||
* for i = r downto 1 do
|
* @param {number} w - Word size in bits
|
||||||
* (A, B, C, D) = (D, A, B, C)
|
* @returns {number[]} - Plaintext block
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
function decryptBlock(block, S) {
|
function decryptBlock(block, S, rounds, w) {
|
||||||
// Convert block to 4 words (A, B, C, D) in little-endian
|
const mask = wordMask(w);
|
||||||
let [A, B, C, D] = bytesToWords(block);
|
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
|
// Reverse post-whitening
|
||||||
C = (C - S[2 * NROUNDS + 3]) >>> 0;
|
C = (C - S[2 * rounds + 3] + (1n << BigInt(w))) & mask;
|
||||||
A = (A - S[2 * NROUNDS + 2]) >>> 0;
|
A = (A - S[2 * rounds + 2] + (1n << BigInt(w))) & mask;
|
||||||
|
|
||||||
// Main rounds in reverse
|
// 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)
|
// Reverse rotate registers: (A, B, C, D) = (D, A, B, C)
|
||||||
const temp = D;
|
const temp = D;
|
||||||
D = C;
|
D = C;
|
||||||
@ -221,36 +283,36 @@ function decryptBlock(block, S) {
|
|||||||
B = A;
|
B = A;
|
||||||
A = temp;
|
A = temp;
|
||||||
|
|
||||||
// u = ROL(D * (2D + 1), 5)
|
// u = ROL(D * (2D + 1), lg(w))
|
||||||
const u = ROL(Math.imul(D, (2 * D + 1) >>> 0) >>> 0, 5);
|
const u = ROL((D * ((2n * D + 1n) & mask)) & mask, lgw, w, lgMask);
|
||||||
|
|
||||||
// t = ROL(B * (2B + 1), 5)
|
// t = ROL(B * (2B + 1), lg(w))
|
||||||
const t = ROL(Math.imul(B, (2 * B + 1) >>> 0) >>> 0, 5);
|
const t = ROL((B * ((2n * B + 1n) & mask)) & mask, lgw, w, lgMask);
|
||||||
|
|
||||||
// C = ROR(C - S[2i + 1], t) ^ u
|
// 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[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
|
// Reverse pre-whitening
|
||||||
D = (D - S[1]) >>> 0;
|
D = (D - S[1] + (1n << BigInt(w))) & mask;
|
||||||
B = (B - S[0]) >>> 0;
|
B = (B - S[0] + (1n << BigInt(w))) & mask;
|
||||||
|
|
||||||
// Convert words back to bytes
|
// 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[]} a - First block
|
||||||
* @param {number[]} b - Second block
|
* @param {number[]} b - Second block
|
||||||
* @returns {number[]} - XOR result
|
* @returns {number[]} - XOR result
|
||||||
*/
|
*/
|
||||||
function xorBlocks(a, b) {
|
function xorBlocks(a, b) {
|
||||||
const result = new Array(BLOCKSIZE);
|
const result = new Array(a.length);
|
||||||
for (let i = 0; i < BLOCKSIZE; i++) {
|
for (let i = 0; i < a.length; i++) {
|
||||||
result[i] = a[i] ^ b[i];
|
result[i] = a[i] ^ b[i];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -258,12 +320,12 @@ function xorBlocks(a, b) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment counter (little-endian)
|
* Increment counter (little-endian)
|
||||||
* @param {number[]} counter - 16-byte counter
|
* @param {number[]} counter - Counter block
|
||||||
* @returns {number[]} - Incremented counter
|
* @returns {number[]} - Incremented counter
|
||||||
*/
|
*/
|
||||||
function incrementCounter(counter) {
|
function incrementCounter(counter) {
|
||||||
const result = [...counter];
|
const result = [...counter];
|
||||||
for (let i = 0; i < BLOCKSIZE; i++) {
|
for (let i = 0; i < result.length; i++) {
|
||||||
result[i]++;
|
result[i]++;
|
||||||
if (result[i] <= 255) break;
|
if (result[i] <= 255) break;
|
||||||
result[i] = 0;
|
result[i] = 0;
|
||||||
@ -379,22 +441,25 @@ function removePadding(message, padding, blockSize) {
|
|||||||
* Encrypt using RC6 cipher with specified block mode
|
* Encrypt using RC6 cipher with specified block mode
|
||||||
*
|
*
|
||||||
* @param {number[]} message - Plaintext as byte array
|
* @param {number[]} message - Plaintext as byte array
|
||||||
* @param {number[]} key - Key (16, 24, or 32 bytes)
|
* @param {number[]} key - Key as byte array
|
||||||
* @param {number[]} iv - IV (16 bytes, not used for ECB)
|
* @param {number[]} iv - IV (block size bytes, not used for ECB)
|
||||||
* @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR")
|
* @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR")
|
||||||
* @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT")
|
* @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
|
* @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;
|
const messageLength = message.length;
|
||||||
if (messageLength === 0) return [];
|
if (messageLength === 0) return [];
|
||||||
|
|
||||||
const S = generateSubkeys(key);
|
const S = generateSubkeys(key, rounds, w);
|
||||||
|
|
||||||
// Apply padding for ECB/CBC modes
|
// Apply padding for ECB/CBC modes
|
||||||
let paddedMessage;
|
let paddedMessage;
|
||||||
if (mode === "ECB" || mode === "CBC") {
|
if (mode === "ECB" || mode === "CBC") {
|
||||||
paddedMessage = applyPadding(message, padding, BLOCKSIZE);
|
paddedMessage = applyPadding(message, padding, blockSize);
|
||||||
} else {
|
} else {
|
||||||
// Stream modes (CFB, OFB, CTR) don't need padding
|
// Stream modes (CFB, OFB, CTR) don't need padding
|
||||||
paddedMessage = [...message];
|
paddedMessage = [...message];
|
||||||
@ -404,18 +469,18 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") {
|
|||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "ECB":
|
case "ECB":
|
||||||
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
for (let i = 0; i < paddedMessage.length; i += blockSize) {
|
||||||
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
const block = paddedMessage.slice(i, i + blockSize);
|
||||||
cipherText.push(...encryptBlock(block, S));
|
cipherText.push(...encryptBlock(block, S, rounds, w));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "CBC": {
|
case "CBC": {
|
||||||
let ivBlock = [...iv];
|
let ivBlock = [...iv];
|
||||||
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
for (let i = 0; i < paddedMessage.length; i += blockSize) {
|
||||||
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
const block = paddedMessage.slice(i, i + blockSize);
|
||||||
const xored = xorBlocks(block, ivBlock);
|
const xored = xorBlocks(block, ivBlock);
|
||||||
ivBlock = encryptBlock(xored, S);
|
ivBlock = encryptBlock(xored, S, rounds, w);
|
||||||
cipherText.push(...ivBlock);
|
cipherText.push(...ivBlock);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -423,11 +488,11 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") {
|
|||||||
|
|
||||||
case "CFB": {
|
case "CFB": {
|
||||||
let ivBlock = [...iv];
|
let ivBlock = [...iv];
|
||||||
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
for (let i = 0; i < paddedMessage.length; i += blockSize) {
|
||||||
const encrypted = encryptBlock(ivBlock, S);
|
const encrypted = encryptBlock(ivBlock, S, rounds, w);
|
||||||
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
const block = paddedMessage.slice(i, i + blockSize);
|
||||||
// Pad block if shorter than BLOCKSIZE
|
// Pad block if shorter than blockSize
|
||||||
while (block.length < BLOCKSIZE) block.push(0);
|
while (block.length < blockSize) block.push(0);
|
||||||
ivBlock = xorBlocks(encrypted, block);
|
ivBlock = xorBlocks(encrypted, block);
|
||||||
cipherText.push(...ivBlock);
|
cipherText.push(...ivBlock);
|
||||||
}
|
}
|
||||||
@ -436,11 +501,11 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") {
|
|||||||
|
|
||||||
case "OFB": {
|
case "OFB": {
|
||||||
let ivBlock = [...iv];
|
let ivBlock = [...iv];
|
||||||
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
for (let i = 0; i < paddedMessage.length; i += blockSize) {
|
||||||
ivBlock = encryptBlock(ivBlock, S);
|
ivBlock = encryptBlock(ivBlock, S, rounds, w);
|
||||||
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
const block = paddedMessage.slice(i, i + blockSize);
|
||||||
// Pad block if shorter than BLOCKSIZE
|
// Pad block if shorter than blockSize
|
||||||
while (block.length < BLOCKSIZE) block.push(0);
|
while (block.length < blockSize) block.push(0);
|
||||||
cipherText.push(...xorBlocks(ivBlock, block));
|
cipherText.push(...xorBlocks(ivBlock, block));
|
||||||
}
|
}
|
||||||
return cipherText.slice(0, messageLength);
|
return cipherText.slice(0, messageLength);
|
||||||
@ -448,11 +513,11 @@ export function encryptRC6(message, key, iv, mode = "ECB", padding = "PKCS5") {
|
|||||||
|
|
||||||
case "CTR": {
|
case "CTR": {
|
||||||
let counter = [...iv];
|
let counter = [...iv];
|
||||||
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
for (let i = 0; i < paddedMessage.length; i += blockSize) {
|
||||||
const encrypted = encryptBlock(counter, S);
|
const encrypted = encryptBlock(counter, S, rounds, w);
|
||||||
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
const block = paddedMessage.slice(i, i + blockSize);
|
||||||
// Pad block if shorter than BLOCKSIZE
|
// Pad block if shorter than blockSize
|
||||||
while (block.length < BLOCKSIZE) block.push(0);
|
while (block.length < blockSize) block.push(0);
|
||||||
cipherText.push(...xorBlocks(encrypted, block));
|
cipherText.push(...xorBlocks(encrypted, block));
|
||||||
counter = incrementCounter(counter);
|
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
|
* Decrypt using RC6 cipher with specified block mode
|
||||||
*
|
*
|
||||||
* @param {number[]} cipherText - Ciphertext as byte array
|
* @param {number[]} cipherText - Ciphertext as byte array
|
||||||
* @param {number[]} key - Key (16, 24, or 32 bytes)
|
* @param {number[]} key - Key as byte array
|
||||||
* @param {number[]} iv - IV (16 bytes, not used for ECB)
|
* @param {number[]} iv - IV (block size bytes, not used for ECB)
|
||||||
* @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR")
|
* @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR")
|
||||||
* @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT")
|
* @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
|
* @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;
|
const originalLength = cipherText.length;
|
||||||
if (originalLength === 0) return [];
|
if (originalLength === 0) return [];
|
||||||
|
|
||||||
const S = generateSubkeys(key);
|
const S = generateSubkeys(key, rounds, w);
|
||||||
|
|
||||||
if (mode === "ECB" || mode === "CBC") {
|
if (mode === "ECB" || mode === "CBC") {
|
||||||
if ((originalLength % BLOCKSIZE) !== 0)
|
if ((originalLength % blockSize) !== 0)
|
||||||
throw new OperationError(`Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of 16.`);
|
throw new OperationError(`Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of ${blockSize}.`);
|
||||||
} else {
|
} else {
|
||||||
// Pad for stream modes
|
// Pad for stream modes
|
||||||
while ((cipherText.length % BLOCKSIZE) !== 0)
|
while ((cipherText.length % blockSize) !== 0)
|
||||||
cipherText.push(0);
|
cipherText.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,17 +563,17 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5")
|
|||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "ECB":
|
case "ECB":
|
||||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
for (let i = 0; i < cipherText.length; i += blockSize) {
|
||||||
const block = cipherText.slice(i, i + BLOCKSIZE);
|
const block = cipherText.slice(i, i + blockSize);
|
||||||
plainText.push(...decryptBlock(block, S));
|
plainText.push(...decryptBlock(block, S, rounds, w));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "CBC": {
|
case "CBC": {
|
||||||
let ivBlock = [...iv];
|
let ivBlock = [...iv];
|
||||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
for (let i = 0; i < cipherText.length; i += blockSize) {
|
||||||
const block = cipherText.slice(i, i + BLOCKSIZE);
|
const block = cipherText.slice(i, i + blockSize);
|
||||||
const decrypted = decryptBlock(block, S);
|
const decrypted = decryptBlock(block, S, rounds, w);
|
||||||
plainText.push(...xorBlocks(decrypted, ivBlock));
|
plainText.push(...xorBlocks(decrypted, ivBlock));
|
||||||
ivBlock = block;
|
ivBlock = block;
|
||||||
}
|
}
|
||||||
@ -514,9 +582,9 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5")
|
|||||||
|
|
||||||
case "CFB": {
|
case "CFB": {
|
||||||
let ivBlock = [...iv];
|
let ivBlock = [...iv];
|
||||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
for (let i = 0; i < cipherText.length; i += blockSize) {
|
||||||
const encrypted = encryptBlock(ivBlock, S);
|
const encrypted = encryptBlock(ivBlock, S, rounds, w);
|
||||||
const block = cipherText.slice(i, i + BLOCKSIZE);
|
const block = cipherText.slice(i, i + blockSize);
|
||||||
plainText.push(...xorBlocks(encrypted, block));
|
plainText.push(...xorBlocks(encrypted, block));
|
||||||
ivBlock = block;
|
ivBlock = block;
|
||||||
}
|
}
|
||||||
@ -525,9 +593,9 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5")
|
|||||||
|
|
||||||
case "OFB": {
|
case "OFB": {
|
||||||
let ivBlock = [...iv];
|
let ivBlock = [...iv];
|
||||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
for (let i = 0; i < cipherText.length; i += blockSize) {
|
||||||
ivBlock = encryptBlock(ivBlock, S);
|
ivBlock = encryptBlock(ivBlock, S, rounds, w);
|
||||||
const block = cipherText.slice(i, i + BLOCKSIZE);
|
const block = cipherText.slice(i, i + blockSize);
|
||||||
plainText.push(...xorBlocks(ivBlock, block));
|
plainText.push(...xorBlocks(ivBlock, block));
|
||||||
}
|
}
|
||||||
return plainText.slice(0, originalLength);
|
return plainText.slice(0, originalLength);
|
||||||
@ -535,9 +603,9 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5")
|
|||||||
|
|
||||||
case "CTR": {
|
case "CTR": {
|
||||||
let counter = [...iv];
|
let counter = [...iv];
|
||||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
for (let i = 0; i < cipherText.length; i += blockSize) {
|
||||||
const encrypted = encryptBlock(counter, S);
|
const encrypted = encryptBlock(counter, S, rounds, w);
|
||||||
const block = cipherText.slice(i, i + BLOCKSIZE);
|
const block = cipherText.slice(i, i + blockSize);
|
||||||
plainText.push(...xorBlocks(encrypted, block));
|
plainText.push(...xorBlocks(encrypted, block));
|
||||||
counter = incrementCounter(counter);
|
counter = incrementCounter(counter);
|
||||||
}
|
}
|
||||||
@ -550,7 +618,7 @@ export function decryptRC6(cipherText, key, iv, mode = "ECB", padding = "PKCS5")
|
|||||||
|
|
||||||
// Remove padding for ECB/CBC modes
|
// Remove padding for ECB/CBC modes
|
||||||
if (mode === "ECB" || mode === "CBC") {
|
if (mode === "ECB" || mode === "CBC") {
|
||||||
return removePadding(plainText, padding, BLOCKSIZE);
|
return removePadding(plainText, padding, blockSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
return plainText.slice(0, originalLength);
|
return plainText.slice(0, originalLength);
|
||||||
|
|||||||
@ -95,11 +95,6 @@ class RC6Decrypt extends Operation {
|
|||||||
const blockSize = getBlockSize(wordSize);
|
const blockSize = getBlockSize(wordSize);
|
||||||
const defaultRounds = getDefaultRounds(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")
|
if (iv.length !== blockSize && iv.length !== 0 && mode !== "ECB")
|
||||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||||
|
|
||||||
|
|||||||
@ -95,11 +95,6 @@ class RC6Encrypt extends Operation {
|
|||||||
const blockSize = getBlockSize(wordSize);
|
const blockSize = getBlockSize(wordSize);
|
||||||
const defaultRounds = getDefaultRounds(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")
|
if (iv.length !== blockSize && iv.length !== 0 && mode !== "ECB")
|
||||||
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
||||||
|
|
||||||
|
|||||||
@ -5,9 +5,6 @@
|
|||||||
* "Test Vectors for RC6 and RC5"
|
* "Test Vectors for RC6 and RC5"
|
||||||
* https://datatracker.ietf.org/doc/html/draft-krovetz-rc6-rc5-vectors-00
|
* 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
|
* @author Medjedtxm
|
||||||
* @copyright Crown Copyright 2026
|
* @copyright Crown Copyright 2026
|
||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
@ -17,8 +14,7 @@ import TestRegister from "../../lib/TestRegister.mjs";
|
|||||||
|
|
||||||
TestRegister.addTests([
|
TestRegister.addTests([
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// IETF TEST VECTORS - RC6-8/12/4 (8-bit words, 12 rounds, 4-byte key)
|
// IETF TEST VECTORS - RC6-8/12/4
|
||||||
// Block size: 4 bytes (32 bits)
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
{
|
{
|
||||||
name: "RC6-8/12/4: IETF vector encrypt",
|
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)
|
// IETF TEST VECTORS - RC6-16/16/8
|
||||||
// Block size: 8 bytes (64 bits)
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
{
|
{
|
||||||
name: "RC6-16/16/8: IETF vector encrypt",
|
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)
|
// IETF TEST VECTORS - RC6-32/20/16 (AES standard)
|
||||||
// Block size: 16 bytes (128 bits) - Standard AES submission
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
{
|
{
|
||||||
name: "RC6-32/20/16: IETF vector encrypt (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)
|
// IETF TEST VECTORS - RC6-64/24/24
|
||||||
// Block size: 32 bytes (256 bits)
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
{
|
{
|
||||||
name: "RC6-64/24/24: IETF vector encrypt",
|
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)
|
// IETF TEST VECTORS - RC6-128/28/32
|
||||||
// Block size: 64 bytes (512 bits)
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
{
|
{
|
||||||
name: "RC6-128/28/32: IETF vector encrypt",
|
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",
|
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",
|
name: "RC6-32/20/32: 256-bit key encrypt",
|
||||||
input: "000102030405060708090a0b0c0d0e0f",
|
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",
|
name: "RC6-8 Round-trip: CBC mode",
|
||||||
input: "Hello World!",
|
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",
|
name: "RC6-16 Round-trip: CBC mode",
|
||||||
input: "The quick brown fox",
|
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",
|
name: "RC6-32 Round-trip: CBC mode",
|
||||||
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",
|
|
||||||
input: "The quick brown fox jumps over the lazy dog",
|
input: "The quick brown fox jumps over the lazy dog",
|
||||||
expectedOutput: "The quick brown fox jumps over the lazy dog",
|
expectedOutput: "The quick brown fox jumps over the lazy dog",
|
||||||
recipeConfig: [
|
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",
|
name: "RC6-64 Round-trip: CBC mode",
|
||||||
input: "RC6 with 64-bit words is powerful!",
|
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",
|
name: "RC6-128 Round-trip: ECB mode",
|
||||||
input: "RC6 with 128-bit words provides massive block size for testing purposes!",
|
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",
|
name: "RC6-32 Round-trip: CTR mode",
|
||||||
input: "RC6-128 with CBC mode needs a 64-byte IV for proper operation with large blocks!",
|
input: "CTR mode test message",
|
||||||
expectedOutput: "RC6-128 with CBC mode needs a 64-byte IV for proper operation with large blocks!",
|
expectedOutput: "CTR mode test message",
|
||||||
recipeConfig: [
|
recipeConfig: [
|
||||||
{
|
{
|
||||||
op: "RC6 Encrypt",
|
op: "RC6 Encrypt",
|
||||||
args: [
|
args: [
|
||||||
{ string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", option: "Hex" },
|
{ string: "00112233445566778899aabbccddeeff", option: "Hex" },
|
||||||
{ string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", option: "Hex" },
|
{ string: "00000000000000000000000000000001", option: "Hex" },
|
||||||
"CBC", "Raw", "Hex", "PKCS5", 128, 28
|
"CTR", "Raw", "Hex", "PKCS5", 32, 20
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: "RC6 Decrypt",
|
op: "RC6 Decrypt",
|
||||||
args: [
|
args: [
|
||||||
{ string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", option: "Hex" },
|
{ string: "00112233445566778899aabbccddeeff", option: "Hex" },
|
||||||
{ string: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", option: "Hex" },
|
{ string: "00000000000000000000000000000001", option: "Hex" },
|
||||||
"CBC", "Hex", "Raw", "PKCS5", 128, 28
|
"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",
|
name: "RC6-32 Round-trip: Custom 8 rounds",
|
||||||
input: "Testing 8 rounds",
|
input: "Testing custom rounds",
|
||||||
expectedOutput: "Testing 8 rounds",
|
expectedOutput: "Testing custom rounds",
|
||||||
recipeConfig: [
|
recipeConfig: [
|
||||||
{
|
{
|
||||||
op: "RC6 Encrypt",
|
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",
|
name: "RC6-32 Round-trip: Exact block size 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)",
|
|
||||||
input: "1234567890123456",
|
input: "1234567890123456",
|
||||||
expectedOutput: "1234567890123456",
|
expectedOutput: "1234567890123456",
|
||||||
recipeConfig: [
|
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
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user