CyberChef/src/core/lib/XXTEA.mjs

175 lines
3.9 KiB
JavaScript

/**
* XXTEA library
*
* Encryption Algorithm Authors:
* David J. Wheeler
* Roger M. Needham
*
* @author Ma Bingyao [mabingyao@gmail.com]
* @author n1474335 [n1474335@gmail.com]
* @license MIT
*/
const DELTA = 0x9E3779B9;
/**
* Convert a buffer to a Uint8Array
* @param {Uint32Array} v
* @param {boolean} includeLength
* @returns {Uint8Array}
*/
function toUint8Array(v, includeLength) {
const length = v.length;
let n = length << 2;
if (includeLength) {
const m = v[length - 1];
n -= 4;
if ((m < n - 3) || (m > n)) {
return null;
}
n = m;
}
const bytes = new Uint8Array(n);
for (let i = 0; i < n; i++) {
bytes[i] = v[i >> 2] >> ((i & 3) << 3);
}
return bytes;
}
/**
* Convert a buffer to a Uint32Array
* @param {TypedArray} bs
* @param {boolean} includeLength
* @returns {Uint32Array}
*/
function toUint32Array(bs, includeLength) {
const length = bs.length;
let n = length >> 2;
if ((length & 3) !== 0) {
++n;
}
let v;
if (includeLength) {
v = new Uint32Array(n + 1);
v[n] = length;
} else {
v = new Uint32Array(n);
}
for (let i = 0; i < length; ++i) {
v[i >> 2] |= bs[i] << ((i & 3) << 3);
}
return v;
}
/**
* Mask an int to 32 bits
* @param {number} i
* @returns {number}
*/
function int32(i) {
return i & 0xFFFFFFFF;
}
/**
* MX function for data randomisation
* @param {number} sum
* @param {number} y
* @param {number} z
* @param {number} p
* @param {number} e
* @param {number} k
* @returns {number}
*/
function mx(sum, y, z, p, e, k) {
return ((z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4)) ^ ((sum ^ y) + (k[p & 3 ^ e] ^ z));
}
/**
* Ensure an array is a multiple of 16 bits
* @param {TypedArray} k
* @returns {TypedArray}
*/
function fixk(k) {
if (k.length < 16) {
const key = new Uint8Array(16);
key.set(k);
return key;
}
return k;
}
/**
* Performs XXTEA encryption on a Uint32Array
* @param {Uint32Array} v
* @param {Uint32Array} k
* @returns {Uint32Array}
*/
function encryptUint32Array(v, k) {
const length = v.length;
const n = length - 1;
let y, z, sum, e, p, q;
z = v[n];
sum = 0;
for (q = Math.floor(6 + 52 / length) | 0; q > 0; --q) {
sum = int32(sum + DELTA);
e = sum >>> 2 & 3;
for (p = 0; p < n; ++p) {
y = v[p + 1];
z = v[p] = int32(v[p] + mx(sum, y, z, p, e, k));
}
y = v[0];
z = v[n] = int32(v[n] + mx(sum, y, z, n, e, k));
}
return v;
}
/**
* Performs XXTEA decryption on a Uint32Array
* @param {Uint32Array} v
* @param {Uint32Array} k
* @returns {Uint32Array}
*/
function decryptUint32Array(v, k) {
const length = v.length;
const n = length - 1;
let y, z, sum, e, p;
y = v[0];
const q = Math.floor(6 + 52 / length);
for (sum = int32(q * DELTA); sum !== 0; sum = int32(sum - DELTA)) {
e = sum >>> 2 & 3;
for (p = n; p > 0; --p) {
z = v[p - 1];
y = v[p] = int32(v[p] - mx(sum, y, z, p, e, k));
}
z = v[n];
y = v[0] = int32(v[0] - mx(sum, y, z, 0, e, k));
}
return v;
}
/**
* Encrypt function
* @param {TypedArray} data
* @param {TypedArray} key
* @returns {Uint8Array}
*/
export function encrypt(data, key) {
if (data === undefined || data === null || data.length === 0) {
return data;
}
return toUint8Array(encryptUint32Array(toUint32Array(data, true), toUint32Array(fixk(key), false)), false);
}
/**
* Decrypt function
* @param {TypedArray} data
* @param {TypedArray} key
* @returns {Uint8Array}
*/
export function decrypt(data, key) {
if (data === undefined || data === null || data.length === 0) {
return data;
}
return toUint8Array(decryptUint32Array(toUint32Array(data, false), toUint32Array(fixk(key), false)), true);
}