Compare commits

...

30 Commits

Author SHA1 Message Date
e218736 268e2fd445
Merge b2ec8facef into bbebba6481 2024-04-25 19:25:43 +01:00
n1474335 bbebba6481
Added pause after setting complex input to avoid race conditions 2024-04-25 18:10:59 +01:00
n1474335 f0a49fefa4
Extended time for autoBake to trigger in a test 2024-04-25 17:51:31 +01:00
n1474335 48f3bf9ea7
10.18.3 2024-04-24 18:09:21 +01:00
n1474335 b7a7eebc78
More test tweaks 2024-04-24 18:09:15 +01:00
n1474335 2e76e44a5a
Tweaked UI test 2024-04-24 17:40:16 +01:00
n1474335 718ce9ea11
10.18.2 2024-04-24 17:13:57 +01:00
n1474335 a79be1e3ef
Removed autoBakePause flag and statechange trigger in InputWaiter.set() as they are redundant. 2024-04-24 17:13:44 +01:00
n1474335 0a709acafe
Merge branch 'zb3-fix-testui-race-condition' 2024-04-24 16:43:38 +01:00
n1474335 29efd77eaf
Merge branch 'fix-testui-race-condition' of https://github.com/zb3/CyberChef into zb3-fix-testui-race-condition 2024-04-24 16:35:12 +01:00
n1474335 2d6ac8023e
10.18.1 2024-04-24 13:27:07 +01:00
n1474335 2f42f515b0
Updated chromedriver 2024-04-24 13:26:57 +01:00
n1474335 f304f0832b
10.18.0 2024-04-24 13:11:20 +01:00
n1474335 801f3a578d
Updated CHANGELOG 2024-04-24 13:11:16 +01:00
n1474335 0a353eeb37
Improved XXTEA operations. Added XXTEA Decrypt. 2024-04-24 13:09:17 +01:00
n1474335 2e2490ce47
10.17.1 2024-04-23 18:29:43 +01:00
n1474335 361a35b44c
Removed trailing spaces from RAKE 2024-04-23 18:29:04 +01:00
n1474335 e61d64f618
Wording and stats improvements 2024-04-23 18:28:08 +01:00
zb3 e3033173d7 Merge branch 'master' into fix-testui-race-condition 2024-04-06 13:33:59 +02:00
zb3 1adc2ff930 Make loadURIParams set input non-silently
Silent input changes might be overwritten due to the debounce logic present inside inputChange.
2024-04-05 18:52:50 +02:00
zb3 fc40580dce Avoid calling inputChange when setting encoding inside loadURIParams
Otherwise the debounce logic sometimes causes the input to be overriden by the previous value.
2024-04-05 18:48:45 +02:00
e218736 b2ec8facef
Merge branch 'master' into tooltips-keyboard-navigation 2024-02-27 10:21:18 +00:00
e218736 f75d07bfc7 formatting 2024-02-21 16:55:16 +00:00
e218736 a3c47299f9
Merge branch 'master' into tooltips-keyboard-navigation 2024-02-21 16:24:56 +00:00
e218736 d79d412959 formatting 2024-02-21 16:23:15 +00:00
e218736 4c31c16eef formatting 2024-02-21 16:13:56 +00:00
e218736 6fcf103760 statusbar popup keyboard navigation 2024-02-21 10:13:40 +00:00
e218736 85a3510454 open status bar elements on Enter 2024-02-14 14:38:20 +00:00
e218736 6543fee71a input/optput status bar elements with tooltips tabbable 2024-02-13 12:00:40 +00:00
e218736 ae3a97a00f enable tooltips trigger on focus 2024-02-12 09:04:31 +00:00
19 changed files with 497 additions and 300 deletions

View File

@ -13,6 +13,9 @@ All major and minor version changes will be documented in this file. Details of
## Details
### [10.18.0] - 2024-04-24
- Added 'XXTEA Encrypt' and 'XXTEA Decrypt' operations [@n1474335] | [0a353ee]
### [10.17.0] - 2024-04-13
- Fix unit test 'expectOutput' implementation [@zb3] | [#1783]
- Add accessibility labels for icons [@e218736] | [#1743]
@ -433,6 +436,7 @@ All major and minor version changes will be documented in this file. Details of
## [4.0.0] - 2016-11-28
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
[10.18.0]: https://github.com/gchq/CyberChef/releases/tag/v10.18.0
[10.17.0]: https://github.com/gchq/CyberChef/releases/tag/v10.17.0
[10.16.0]: https://github.com/gchq/CyberChef/releases/tag/v10.16.0
[10.15.0]: https://github.com/gchq/CyberChef/releases/tag/v10.15.0
@ -630,6 +634,7 @@ All major and minor version changes will be documented in this file. Details of
[31a7f83]: https://github.com/gchq/CyberChef/commit/31a7f83b82e78927f89689f323fcb9185144d6ff
[760eff4]: https://github.com/gchq/CyberChef/commit/760eff49b5307aaa3104c5e5b437ffe62299acd1
[65ffd8d]: https://github.com/gchq/CyberChef/commit/65ffd8d65d88eb369f6f61a5d1d0f807179bffb7
[0a353ee]: https://github.com/gchq/CyberChef/commit/0a353eeb378b9ca5d49e23c7dfc175ae07107b08
[#95]: https://github.com/gchq/CyberChef/pull/299
[#173]: https://github.com/gchq/CyberChef/pull/173

12
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "cyberchef",
"version": "10.17.0",
"version": "10.18.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cyberchef",
"version": "10.17.0",
"version": "10.18.3",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@ -115,7 +115,7 @@
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
"base64-loader": "^1.0.0",
"chromedriver": "^122.0.0",
"chromedriver": "^123.0.4",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"copy-webpack-plugin": "^12.0.2",
@ -4744,9 +4744,9 @@
}
},
"node_modules/chromedriver": {
"version": "122.0.6",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-122.0.6.tgz",
"integrity": "sha512-Q0r+QlUtiJWMQ5HdYaFa0CtBmLFq3n5JWfmq9mOC00UMBvWxku09gUkvBt457QnYfTM/XHqY/HTFOxHvATnTmA==",
"version": "123.0.4",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.4.tgz",
"integrity": "sha512-3Yi7y7q35kkSAOTbRisiww/SL2w+DqafDPAaUShpSuLMmPaOvHQR0i3bm2/33QBiQ8fUb1J/MzppzVL6IDqvhA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "10.17.0",
"version": "10.18.3",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@ -55,7 +55,7 @@
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
"base64-loader": "^1.0.0",
"chromedriver": "^122.0.0",
"chromedriver": "^123.0.4",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"copy-webpack-plugin": "^12.0.2",
@ -96,6 +96,7 @@
"@babel/polyfill": "^7.12.1",
"@blu3r4y/lzma": "^2.3.3",
"@wavesenterprise/crypto-gost-js": "^2.1.0-RC1",
"@xmldom/xmldom": "^0.8.0",
"argon2-browser": "^1.18.0",
"arrive": "^2.4.1",
"avsc": "^5.7.7",
@ -122,12 +123,12 @@
"escodegen": "^2.1.0",
"esprima": "^4.0.1",
"exif-parser": "^0.1.12",
"ieee754": "^1.1.13",
"fernet": "^0.3.2",
"file-saver": "^2.0.5",
"flat": "^6.0.1",
"geodesy": "1.1.3",
"highlight.js": "^11.9.0",
"ieee754": "^1.1.13",
"jimp": "^0.16.13",
"jquery": "3.7.1",
"js-crc": "^0.2.0",
@ -175,7 +176,6 @@
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"vkbeautify": "^0.99.3",
"@xmldom/xmldom": "^0.8.0",
"xpath": "0.0.34",
"xregexp": "^5.1.1",
"zlibjs": "^0.3.1"

View File

@ -117,6 +117,8 @@
"XOR Brute Force",
"Vigenère Encode",
"Vigenère Decode",
"XXTEA Encrypt",
"XXTEA Decrypt",
"To Morse Code",
"From Morse Code",
"Bacon Cipher Encode",
@ -155,8 +157,7 @@
"Typex",
"Lorenz",
"Colossus",
"SIGABA",
"XXTEA"
"SIGABA"
]
},
{

174
src/core/lib/XXTEA.mjs Normal file
View File

@ -0,0 +1,174 @@
/**
* 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);
}

View File

@ -102,7 +102,7 @@ class RAKE extends Operation {
// Remove duplicate phrases
phrases = phrases.unique();
// Generate word_degree_matrix and populate
const wordDegreeMatrix = Array(tokens.length).fill().map(() => Array(tokens.length).fill(0));
for (const phrase of phrases) {

View File

@ -1,182 +0,0 @@
/**
* @author devcydo [devcydo@gmail.com]
* @author Ma Bingyao [mabingyao@gmail.com]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import {toBase64} from "../lib/Base64.mjs";
import Utils from "../Utils.mjs";
/**
* XXTEA Encrypt operation
*/
class XXTEAEncrypt extends Operation {
/**
* XXTEAEncrypt constructor
*/
constructor() {
super();
this.name = "XXTEA";
this.module = "Default";
this.description = "Corrected Block TEA (often referred to as XXTEA) is a block cipher designed to correct weaknesses in the original Block TEA. XXTEA operates on variable-length blocks that are some arbitrary multiple of 32 bits in size (minimum 64 bits). The number of full cycles depends on the block size, but there are at least six (rising to 32 for small block sizes). The original Block TEA applies the XTEA round function to each word in the block and combines it additively with its leftmost neighbour. Slow diffusion rate of the decryption process was immediately exploited to break the cipher. Corrected Block TEA uses a more involved round function which makes use of both immediate neighbours in processing each word in the block.";
this.infoURL = "https://wikipedia.org/wiki/XXTEA";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Key",
"type": "string",
"value": "",
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let key = args[0];
if (input === undefined || input === null || input.length === 0) {
throw new OperationError("Invalid input length (0)");
}
if (key === undefined || key === null || key.length === 0) {
throw new OperationError("Invalid key length (0)");
}
input = Utils.convertToByteString(input, "utf8");
key = Utils.convertToByteString(key, "utf8");
input = this.convertToUint32Array(input, true);
key = this.fixLength(this.convertToUint32Array(key, false));
let encrypted = this.encryptUint32Array(input, key);
encrypted = toBase64(this.toBinaryString(encrypted, false));
return encrypted;
}
/**
* Convert Uint32Array to binary string
*
* @param {Uint32Array} v
* @param {Boolean} includeLength
* @returns {string}
*/
toBinaryString(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;
}
for (let i = 0; i < LENGTH; i++) {
v[i] = String.fromCharCode(
v[i] & 0xFF,
v[i] >>> 8 & 0xFF,
v[i] >>> 16 & 0xFF,
v[i] >>> 24 & 0xFF
);
}
const RESULT = v.join("");
if (includeLENGTH) {
return RESULT.substring(0, n);
}
return RESULT;
}
/**
* @param {number} sum
* @param {number} y
* @param {number} z
* @param {number} p
* @param {number} e
* @param {number} k
* @returns {number}
*/
mx(sum, y, z, p, e, k) {
return ((z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4)) ^ ((sum ^ y) + (k[p & 3 ^ e] ^ z));
}
/**
* Encrypt Uint32Array
*
* @param {Uint32Array} v
* @param {number} k
* @returns {Uint32Array}
*/
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 = (sum + 0x9E3779B9) & 0xFFFFFFFF;
e = sum >>> 2 & 3;
for (p = 0; p < N; ++p) {
y = v[p + 1];
z = v[p] = (v[p] + this.mx(sum, y, z, p, e, k)) & 0xFFFFFFFF;
}
y = v[0];
z = v[N] = (v[N] + this.mx(sum, y, z, N, e, k)) & 0xFFFFFFFF;
}
return v;
}
/**
* Fixes the Uint32Array lenght to 4
*
* @param {Uint32Array} k
* @returns {Uint32Array}
*/
fixLength(k) {
if (k.length < 4) {
k.length = 4;
}
return k;
}
/**
* Convert string to Uint32Array
*
* @param {string} bs
* @param {Boolean} includeLength
* @returns {Uint32Array}
*/
convertToUint32Array(bs, includeLength) {
const LENGTH = bs.length;
let n = LENGTH >> 2;
if ((LENGTH & 3) !== 0) {
++n;
}
let v;
if (includeLength) {
v = new Array(n + 1);
v[n] = LENGTH;
} else {
v = new Array(n);
}
for (let i = 0; i < LENGTH; ++i) {
v[i >> 2] |= bs.charCodeAt(i) << ((i & 3) << 3);
}
return v;
}
}
export default XXTEAEncrypt;

View File

@ -0,0 +1,57 @@
/**
* @author devcydo [devcydo@gmail.com]
* @author Ma Bingyao [mabingyao@gmail.com]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import {decrypt} from "../lib/XXTEA.mjs";
/**
* XXTEA Decrypt operation
*/
class XXTEADecrypt extends Operation {
/**
* XXTEADecrypt constructor
*/
constructor() {
super();
this.name = "XXTEA Decrypt";
this.module = "Ciphers";
this.description = "Corrected Block TEA (often referred to as XXTEA) is a block cipher designed to correct weaknesses in the original Block TEA. XXTEA operates on variable-length blocks that are some arbitrary multiple of 32 bits in size (minimum 64 bits). The number of full cycles depends on the block size, but there are at least six (rising to 32 for small block sizes). The original Block TEA applies the XTEA round function to each word in the block and combines it additively with its leftmost neighbour. Slow diffusion rate of the decryption process was immediately exploited to break the cipher. Corrected Block TEA uses a more involved round function which makes use of both immediate neighbours in processing each word in the block.";
this.infoURL = "https://wikipedia.org/wiki/XXTEA";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = new Uint8Array(Utils.convertToByteArray(args[0].string, args[0].option));
try {
return decrypt(new Uint8Array(input), key).buffer;
} catch (err) {
throw new OperationError("Unable to decrypt using this key");
}
}
}
export default XXTEADecrypt;

View File

@ -0,0 +1,52 @@
/**
* @author devcydo [devcydo@gmail.com]
* @author Ma Bingyao [mabingyao@gmail.com]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {encrypt} from "../lib/XXTEA.mjs";
/**
* XXTEA Encrypt operation
*/
class XXTEAEncrypt extends Operation {
/**
* XXTEAEncrypt constructor
*/
constructor() {
super();
this.name = "XXTEA Encrypt";
this.module = "Ciphers";
this.description = "Corrected Block TEA (often referred to as XXTEA) is a block cipher designed to correct weaknesses in the original Block TEA. XXTEA operates on variable-length blocks that are some arbitrary multiple of 32 bits in size (minimum 64 bits). The number of full cycles depends on the block size, but there are at least six (rising to 32 for small block sizes). The original Block TEA applies the XTEA round function to each word in the block and combines it additively with its leftmost neighbour. Slow diffusion rate of the decryption process was immediately exploited to break the cipher. Corrected Block TEA uses a more involved round function which makes use of both immediate neighbours in processing each word in the block.";
this.infoURL = "https://wikipedia.org/wiki/XXTEA";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = new Uint8Array(Utils.convertToByteArray(args[0].string, args[0].option));
return encrypt(new Uint8Array(input), key).buffer;
}
}
export default XXTEAEncrypt;

View File

@ -39,7 +39,6 @@ class App {
this.baking = false;
this.autoBake_ = false;
this.autoBakePause = false;
this.progress = 0;
this.ingId = 0;
@ -155,11 +154,6 @@ class App {
* Runs Auto Bake if it is set.
*/
autoBake() {
// If autoBakePause is set, we are loading a full recipe (and potentially input), so there is no
// need to set the staleness indicator. Just exit and wait until auto bake is called after loading
// has completed.
if (this.autoBakePause) return false;
if (this.baking) {
this.manager.worker.cancelBakeForAutoBake();
this.baking = false;
@ -478,7 +472,6 @@ class App {
* @fires Manager#statechange
*/
loadURIParams(params=this.getURIParams()) {
this.autoBakePause = true;
this.uriParams = params;
// Read in recipe from URI params
@ -507,7 +500,7 @@ class App {
// Input Character Encoding
// Must be set before the input is loaded
if (this.uriParams.ienc) {
this.manager.input.chrEncChange(parseInt(this.uriParams.ienc, 10), true);
this.manager.input.chrEncChange(parseInt(this.uriParams.ienc, 10), true, true);
}
// Output Character Encoding
@ -545,7 +538,6 @@ class App {
this.manager.options.changeTheme(Utils.escapeHtml(this.uriParams.theme));
}
this.autoBakePause = false;
window.dispatchEvent(this.manager.statechange);
}
@ -579,10 +571,6 @@ class App {
setRecipeConfig(recipeConfig) {
document.getElementById("rec-list").innerHTML = null;
// Pause auto-bake while loading but don't modify `this.autoBake_`
// otherwise `manualBake` cannot trigger.
this.autoBakePause = true;
for (let i = 0; i < recipeConfig.length; i++) {
const item = this.manager.recipe.addOperation(recipeConfig[i].op);
@ -617,9 +605,6 @@ class App {
this.progress = 0;
}
// Unpause auto bake
this.autoBakePause = false;
}

View File

@ -184,10 +184,10 @@
<button type="button" aria-label="Hide arguments" class="btn btn-primary bmd-btn-icon" id="hide-icon" data-toggle="tooltip" title="Hide arguments" hide-args="false" data-help-title="Hiding every Operation's argument view in a Recipe" data-help="Clicking 'Hide arguments' will hide all the argument views for every Operation in the Recipe, to save space when you have too many Operation in your Recipe">
<i class="material-icons">keyboard_arrow_up</i>
</button>
<button type="button" aria-label="Save recipe" class="btn btn-primary bmd-btn-icon" id="save" data-toggle="tooltip" title="Save recipe" data-help-title="Saving a recipe" data-help="<p>Recipes can be represented in a few different formats and saved for use at a later date. You can either copy the Recipe configuration and save it somewhere offline for later use, or use your browser's local storage.</p><ul><li><b>Deep link:</b> The easiest way to share a CyberChef Recipe is to copy the deep link, either from the address bar (which is updated as the Recipe or Input changes), or from the 'Save recipe' pane. When you visit this link, the Recipe and Input should be populated from where you left off.</li><li><b>Chef format:</b> This custom format is designed to be compact and easily readable. It is the format used in CyberChef's URL, so it largely uses characters that do not have to be escaped in URL encoding, making it a little easier to understand what a CyberChef URL contains.</li><li><b>Clean JSON:</b> This JSON format uses whitespace and indentation in a way that makes the Recipe easy to read.</li><li><b>Compact JSON:</b> This is the most compact way that the Recipe can be represented in JSON.</li><li><b>Local storage:</b> Alternatively, you can enter a name into the 'Recipe name' field and save to your browser's local storage. The Recipe will then be available to load from the 'Load Recipe' pane as long as you are using the same browser profile. Be aware that if your browser profile is cleaned, you may lose this data.</li></ul>">
<button type="button" aria-label="Save recipe" class="btn btn-primary bmd-btn-icon" id="save" data-toggle="tooltip" title="Save recipe" data-help-title="Saving a recipe" data-help="<p>Recipes can be represented in a few different formats and saved for use at a later date. You can either copy the Recipe configuration and save it somewhere offline for later use, or use your browser's local storage.</p><ul><li><b>Deep link:</b> The easiest way to share a CyberChef Recipe is to copy the deep link, either from the address bar (which is updated as the Recipe or Input changes), or from the 'Save recipe' pane. When you visit this link, the Recipe and Input will be populated from where you left off.</li><li><b>Chef format:</b> This custom format is designed to be compact and easily readable. It is the format used in CyberChef's URL, so it largely uses characters that do not have to be escaped in URL encoding, making it a little easier to understand what a CyberChef URL contains.</li><li><b>Clean JSON:</b> This JSON format uses whitespace and indentation in a way that makes the Recipe easy to read.</li><li><b>Compact JSON:</b> This is the most compact way that the Recipe can be represented in JSON.</li><li><b>Local storage:</b> Alternatively, you can enter a name into the 'Recipe name' field and save to your browser's local storage. The Recipe will then be available to load from the 'Load Recipe' pane as long as you are using the same browser profile. Be aware that if your browser profile is cleaned, you may lose this data.</li></ul>">
<i class="material-icons" aria-hidden="true">save</i>
</button>
<button type="button" aria-label="Load recipe" class="btn btn-primary bmd-btn-icon" id="load" data-toggle="tooltip" title="Load recipe" data-help-title="Loading a recipe" data-help="<p>Saved recipes can be loaded using one of the following methods:</p><ul><li>If you have a CyberChef deep link, simply visit that link and the Recipe and Input should be populated automatically.</li><li>If you have a Recipe string in any of the accepted formats, paste it into the 'Load recipe' pane textbox and click 'Load'.</li><li>If you have saved a Recipe to your browser's local storage, it should be available in the dropdown menu in the 'Load recipe' pane. If it is not there, you may not be using the same browser profile, or your profile may have been cleared.</li></ul>">
<button type="button" aria-label="Load recipe" class="btn btn-primary bmd-btn-icon" id="load" data-toggle="tooltip" title="Load recipe" data-help-title="Loading a recipe" data-help="<p>Saved recipes can be loaded using one of the following methods:</p><ul><li>If you have a CyberChef deep link, simply visit that link and the Recipe and Input will be populated automatically.</li><li>If you have a Recipe string in any of the accepted formats, paste it into the 'Load recipe' pane textbox and click 'Load'.</li><li>If you have saved a Recipe to your browser's local storage, it should be available in the dropdown menu in the 'Load recipe' pane. If it is not there, you may not be using the same browser profile, or your profile may have been cleared.</li></ul>">
<i class="material-icons" aria-hidden="true">folder</i>
</button>
<button type="button" aria-label="Clear recipe" class="btn btn-primary bmd-btn-icon" id="clr-recipe" data-toggle="tooltip" title="Clear recipe" data-help-title="Clearing a recipe" data-help="Clicking the 'Clear recipe' button will remove all operations from the Recipe. It will not clear the Input, but it will trigger a Bake if Auto-bake is turned on, which will change the value of the Output.">
@ -275,7 +275,7 @@
</div>
</div>
<div id="output" class="split" data-help-title="Output pane" data-help="<p>This pane displays the results of the Recipe after it has processed your Input.</p><p>CyberChef does its best to represent data as accurately as possible to ensure you know exactly what you are working with. Non-printable characters are represented using control character pictures, for example a null byte (0x00) is displayed like this: <span title='Control character null' aria-label='Control character null' class='cm-specialChar'>␀</span>.</p><p>When copying these characters from the Output, the original byte value should be copied into your clipboard, rather than the control character picture itself.</p>">
<div id="output" class="split" data-help-title="Output pane" data-help="<p>This pane displays the results of the Recipe after it has processed your Input.</p><p>CyberChef does its best to represent data as accurately as possible to ensure you know exactly what you are working with. Non-printable characters are represented using control character pictures, for example a null byte (0x00) is displayed like this: <span title='Control character null' aria-label='Control character null' class='cm-specialChar'>␀</span>.</p><p>When copying these characters from the Output, the original byte value will be copied into your clipboard, rather than the control character picture itself.</p>">
<div class="title no-select">
<label for="output-text">Output</label>
<span class="pane-controls">
@ -286,7 +286,7 @@
<button type="button" aria-label="save" class="btn btn-primary bmd-btn-icon" id="save-to-file" data-toggle="tooltip" title="Save output to file" data-help-title="Saving output to a file" data-help="The currently active Output can be saved to a file. You will be asked to specify a filename. CyberChef will attempt to guess the correct file extension based on the data. If a file type cannot be detected, the extension defaults to '.dat' but can be changed manually.">
<i class="material-icons" aria-hidden="true">save</i>
</button>
<button type="button" aria-label="copy content" class="btn btn-primary bmd-btn-icon" id="copy-output" data-toggle="tooltip" title="Copy raw output to the clipboard" data-help-title="Copying raw output to the clipboard" data-help="<p>Data can be copied from the Output in the normal way by selecting text and copying it. This button provides a quick way of copying the entire output to the clipboard without having to select it. It directly copies the raw data rather than selecting text in the Output editor. Each method should have the same result, but the button may be more efficient for large Outputs as it does not require any DOM interaction.</p>">
<button type="button" aria-label="copy content" class="btn btn-primary bmd-btn-icon" id="copy-output" data-toggle="tooltip" title="Copy raw output to the clipboard" data-help-title="Copying raw output to the clipboard" data-help="<p>Data can be copied from the Output in the normal way by selecting text and copying it. This button provides a quick way of copying the entire output to the clipboard without having to select it. It directly copies the raw data rather than selecting text in the Output editor. Each method will have the same result, but the button may be more efficient for large Outputs as it does not require any DOM interaction.</p>">
<i class="material-icons" aria-hidden="true">content_copy</i>
</button>
<button type="button" aria-label="replace input with output" class="btn btn-primary bmd-btn-icon" id="switch" data-toggle="tooltip" title="Replace input with output" data-help-title="Replacing input with output" data-help="<p>This button moves the currently active Output data into the currently active Input tab, overwriting whatever data was already there.</p><p>The Input character encoding and EOL sequence will be changed to match the current Output values, so that the data is interpreted correctly.</p>">
@ -611,7 +611,7 @@
What sort of things can I do with CyberChef?
</a>
<div class="collapse" id="faq-examples">
<p>There are around 300 operations in CyberChef allowing you to carry out simple and complex tasks easily. Here are some examples:</p>
<p>There are <span class="num-ops">hundreds of</span> operations in CyberChef allowing you to carry out simple and complex tasks easily. Here are some examples:</p>
<ul>
<li><a href="#recipe=From_Base64('A-Za-z0-9%2B/%3D',true)&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1">Decode a Base64-encoded string</a></li>
<li><a href="#recipe=Translate_DateTime_Format('Standard%20date%20and%20time','DD/MM/YYYY%20HH:mm:ss','UTC','dddd%20Do%20MMMM%20YYYY%20HH:mm:ss%20Z%20z','Australia/Queensland')&input=MTUvMDYvMjAxNSAyMDo0NTowMA">Convert a date and time to a different time zone</a></li>
@ -682,7 +682,7 @@
<br>
<p>There are around 200 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>
<p>There are <span class="num-ops">hundreds of</span> useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>
<p>Its the Cyber Swiss Army Knife.</p>
</div>
<div role="tabpanel" class="tab-pane" id="keybindings" style="padding: 20px;">
@ -863,7 +863,7 @@
<h6>CyberChef v<%= htmlWebpackPlugin.options.version %></h6>
<ul>
<li>Build time: <%= htmlWebpackPlugin.options.compileTime %></li>
<li>The changelog for this version can be viewed <a href="https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md">here</a></li>
<li>The changelog for this version can be viewed <a href="https://github.com/gchq/CyberChef/blob/v<%= htmlWebpackPlugin.options.version %>/CHANGELOG.md">here</a></li>
<li>&copy; Crown Copyright 2016-<%= htmlWebpackPlugin.options.compileYear %></li>
<li>Released under the Apache Licence, Version 2.0</li>
<li>SHA256 hash: DOWNLOAD_HASH_PLACEHOLDER</li>

View File

@ -478,6 +478,11 @@
background-color: #ddd
}
/* Change color of dropup links on focus */
.cm-status-bar-select-content a:focus {
background-color: #ddd;
}
/* Change the background color of the dropup button when the dropup content is shown */
.cm-status-bar-select:hover .cm-status-bar-select-btn {
background-color: #f1f1f1;

View File

@ -53,9 +53,26 @@ class StatusBarPanel {
dom.appendChild(rhs);
// Event listeners
dom.querySelectorAll(".cm-status-bar-select-btn").forEach(
el => el.addEventListener("click", this.showDropUp.bind(this), false)
const eventHandler = this.showDropUp.bind(this);
dom.querySelectorAll(".cm-status-bar-select-btn").forEach((el) => {
el.addEventListener("click", eventHandler, false);
});
dom.querySelectorAll(".cm-status-bar-select-btn").forEach((el) => {
el.addEventListener("keydown", eventHandler, false);
});
const selectContent = dom.querySelectorAll(
".cm-status-bar-select-content"
);
selectContent.forEach((el) => {
const aTags = el.getElementsByTagName("a");
for (let i = 0; i < aTags.length; i++) {
aTags[i].addEventListener("keydown", arrowNav, false);
}
});
dom.querySelector(".eol-select").addEventListener("click", this.eolSelectClick.bind(this), false);
dom.querySelector(".chr-enc-select").addEventListener("click", this.chrEncSelectClick.bind(this), false);
dom.querySelector(".cm-status-bar-filter-input").addEventListener("keyup", this.chrEncFilter.bind(this), false);
@ -64,26 +81,30 @@ class StatusBarPanel {
}
/**
* Handler for dropup clicks
* Handler for dropup clicks/Enter press
* Shows/Hides the dropup
* @param {Event} e
*/
showDropUp(e) {
const el = e.target
.closest(".cm-status-bar-select")
.querySelector(".cm-status-bar-select-content");
const btn = e.target.closest(".cm-status-bar-select-btn");
if (e.type === "click" || e.key === "Enter"|| e.key === " ") {
const el = e.target
.closest(".cm-status-bar-select")
.querySelector(".cm-status-bar-select-content");
const btn = e.target.closest(".cm-status-bar-select-btn");
if (btn.classList.contains("disabled")) return;
if (btn.classList.contains("disabled")) return;
el.classList.add("show");
el.classList.add("show");
// Focus the filter input if present
const filter = el.querySelector(".cm-status-bar-filter-input");
if (filter) filter.focus();
// Focus the filter input if present
const filter = el.querySelector(".cm-status-bar-filter-input");
if (filter) filter.focus();
// Set up a listener to close the menu if the user clicks outside of it
hideOnClickOutside(el, e);
// Set up a listener to close the menu if the user clicks outside of it
hideOnClickOutside(el, e);
// Set up a listener to close the menu if the user presses key outside of it
hideOnMoveFocus(el, e);
}
}
/**
@ -209,6 +230,7 @@ class StatusBarPanel {
}
}
/**
* Sets the current EOL separator in the status bar
* @param {EditorState} state
@ -344,11 +366,11 @@ class StatusBarPanel {
*/
constructLHS() {
return `
<span data-toggle="tooltip" title="${this.label} length" data-help-title="${this.label} length" data-help="This number represents the number of characters in the ${this.label}.<br><br>The CRLF end of line separator is counted as two characters which impacts this value.">
<span data-toggle="tooltip" tabindex="0" title="${this.label} length" data-help-title="${this.label} length" data-help="This number represents the number of characters in the ${this.label}.<br><br>The CRLF end of line separator is counted as two characters which impacts this value.">
<i class="material-icons">abc</i>
<span class="stats-length-value"></span>
</span>
<span data-toggle="tooltip" title="Number of lines" data-help-title="Number of lines" data-help="This number represents the number of lines in the ${this.label}. Lines are separated by the End of Line Sequence which can be changed using the EOL selector at the far right of this status bar.">
<span data-toggle="tooltip" tabindex="0" title="Number of lines" data-help-title="Number of lines" data-help="This number represents the number of lines in the ${this.label}. Lines are separated by the End of Line Sequence which can be changed using the EOL selector at the far right of this status bar.">
<i class="material-icons">sort</i>
<span class="stats-lines-value"></span>
</span>
@ -386,18 +408,18 @@ class StatusBarPanel {
}
return `
<span class="baking-time-info" style="display: none" data-toggle="tooltip" data-html="true" title="Baking time" data-help-title="Baking time" data-help="The baking time is the total time between data being read from the input, processed, and then displayed in the output.<br><br>The 'Threading overhead' value accounts for the transfer of data between different processing threads, as well as some garbage collection. It is not included in the overall bake time displayed in the status bar as it is largely influenced by background operating system and browser activity which can fluctuate significantly.">
<span class="baking-time-info" style="display: none" data-toggle="tooltip" tabindex="0" data-html="true" title="Baking time" data-help-title="Baking time" data-help="The baking time is the total time between data being read from the input, processed, and then displayed in the output.<br><br>The 'Threading overhead' value accounts for the transfer of data between different processing threads, as well as some garbage collection. It is not included in the overall bake time displayed in the status bar as it is largely influenced by background operating system and browser activity which can fluctuate significantly.">
<i class="material-icons">schedule</i>
<span class="baking-time-value"></span>ms
</span>
<div class="cm-status-bar-select chr-enc-select" data-help-title="${this.label} character encoding" data-help="${chrEncHelpText}">
<span class="cm-status-bar-select-btn" data-toggle="tooltip" data-html="true" data-placement="left" title="${this.label} character encoding">
<span class="cm-status-bar-select-btn" data-toggle="tooltip" tabindex="0" data-html="true" data-placement="left" title="${this.label} character encoding">
<i class="material-icons">text_fields</i> <span class="chr-enc-value">Raw Bytes</span>
</span>
<div class="cm-status-bar-select-content">
<div class="cm-status-bar-select-scroll no-select">
<a href="#" draggable="false" data-val="0">Raw Bytes</a>
<a href="#" draggable="false" data-val="0" tabindex="0">Raw Bytes</a>
${chrEncOptions}
</div>
<div class="input-group cm-status-bar-filter-search">
@ -412,18 +434,18 @@ class StatusBarPanel {
</div>
<div class="cm-status-bar-select eol-select" data-help-title="${this.label} EOL sequence" data-help="${eolHelpText}">
<span class="cm-status-bar-select-btn" data-toggle="tooltip" data-html="true" data-placement="left" title="End of line sequence">
<span class="cm-status-bar-select-btn" data-toggle="tooltip" tabindex="0" data-html="true" data-placement="left" title="End of line sequence">
<i class="material-icons">keyboard_return</i> <span class="eol-value"></span>
</span>
<div class="cm-status-bar-select-content no-select">
<a href="#" draggable="false" data-val="LF">Line Feed, U+000A</a>
<a href="#" draggable="false" data-val="VT">Vertical Tab, U+000B</a>
<a href="#" draggable="false" data-val="FF">Form Feed, U+000C</a>
<a href="#" draggable="false" data-val="CR">Carriage Return, U+000D</a>
<a href="#" draggable="false" data-val="CRLF">CR+LF, U+000D U+000A</a>
<!-- <a href="#" draggable="false" data-val="NL">Next Line, U+0085</a> This causes problems. -->
<a href="#" draggable="false" data-val="LS">Line Separator, U+2028</a>
<a href="#" draggable="false" data-val="PS">Paragraph Separator, U+2029</a>
<div class="cm-status-bar-select-content no-select" tabindex="0">
<a href="#" draggable="false" data-val="LF" tabindex="0">Line Feed, U+000A</a>
<a href="#" draggable="false" data-val="VT" tabindex="0">Vertical Tab, U+000B</a>
<a href="#" draggable="false" data-val="FF" tabindex="0">Form Feed, U+000C</a>
<a href="#" draggable="false" data-val="CR" tabindex="0">Carriage Return, U+000D</a>
<a href="#" draggable="false" data-val="CRLF" tabindex="0">CR+LF, U+000D U+000A</a>
<!-- <a href="#" draggable="false" data-val="NL" tabindex="0">Next Line, U+0085</a> This causes problems. -->
<a href="#" draggable="false" data-val="LS" tabindex="0">Line Separator, U+2028</a>
<a href="#" draggable="false" data-val="PS" tabindex="0">Paragraph Separator, U+2029</a>
</div>
</div>`;
}
@ -458,14 +480,102 @@ function hideOnClickOutside(element, instantiatingEvent) {
}
}
const elementsWithKeyDownListeners = {};
/**
* Hides the specified element and removes the click listener for it
* Hides the provided element when a key press is made outside of it
* @param {Element} element
* @param {Event} instantiatingEvent
*/
function hideOnMoveFocus(element, instantiatingEvent) {
/**
* Handler for document keydown events
* Closes element if key press is outside it.
* @param {Event} event
*/
const outsidePressListener = (event) => {
// Don't trigger if we're pressing keys while inside the element, or if the element
// is not visible, or if this is the same click event that opened it.
if (
!element.contains(event.target) &&
event.timeStamp !== instantiatingEvent.timeStamp &&
event.key !== "ArrowUp"
) {
hideElement(element);
} else if (
event.key === "Escape" &&
event.timeStamp !== instantiatingEvent.timeStamp
) {
hideElement(element);
} else if (
event.key === "ArrowUp" ||
(event.key === "ArrowDown" &&
event.timeStamp !== instantiatingEvent.timeStamp)
) {
const menuItems = element.getElementsByTagName("a");
menuItems[0].focus();
console.log("ev target:", event.target);
console.log("element", element);
}
};
if (
!Object.prototype.hasOwnProperty.call(
elementsWithKeyDownListeners,
element
)
) {
elementsWithKeyDownListeners[element] = outsidePressListener;
document.addEventListener(
"keydown",
elementsWithKeyDownListeners[element],
false
);
}
}
/**
* Handler for menu item keydown events
* Moves focus to next/previous element based on arrow direction.
* @param {Event} event
*/
const arrowNav = (event) => {
const currentElement = event.target;
if (event.key === "ArrowDown") {
event.preventDefault();
event.stopPropagation();
const nextElement = currentElement.nextElementSibling;
if (nextElement === null) {
currentElement.parentElement.firstElementChild.focus();
} else {
nextElement.focus();
}
} else if (event.key === "ArrowUp") {
event.preventDefault();
event.stopPropagation();
const prevElement = currentElement.previousElementSibling;
if (prevElement === null) {
currentElement.parentElement.lastElementChild.focus();
} else {
prevElement.focus();
}
} else if (event.key === "Tab") {
event.preventDefault();
event.stopPropagation();
currentElement.parentElement.closest(".cm-status-bar-select-content").previousElementSibling.focus();
}
};
/**
* Hides the specified element and removes the click or keydown listener for it
* @param {Element} element
*/
function hideElement(element) {
element.classList.remove("show");
document.removeEventListener("click", elementsWithListeners[element], false);
document.removeEventListener("keydown", elementsWithKeyDownListeners[element], false);
delete elementsWithListeners[element];
delete elementsWithKeyDownListeners[element];
}

View File

@ -34,7 +34,12 @@ class ControlsWaiter {
animation: false,
container: "body",
boundary: "viewport",
trigger: "hover"
trigger: "hover focus"
});
// Set number of operations in various places in the DOM
document.querySelectorAll(".num-ops").forEach(el => {
el.innerHTML = Object.keys(this.app.operations).length;
});
}

View File

@ -215,13 +215,16 @@ class InputWaiter {
* Handler for Chr Enc change events
* Sets the input character encoding
* @param {number} chrEncVal
* @param {boolean} [manual=false]
* @param {boolean} [manual=false] - Flag to indicate the encoding was set by the user
* @param {boolean} [internal=false] - Flag to indicate this was set internally, i.e. by loading from URI
*/
chrEncChange(chrEncVal, manual=false) {
chrEncChange(chrEncVal, manual=false, internal=false) {
if (typeof chrEncVal !== "number") return;
this.inputChrEnc = chrEncVal;
this.encodingState = manual ? 2 : this.encodingState;
this.inputChange();
if (!internal) {
this.inputChange();
}
}
/**
@ -639,10 +642,6 @@ class InputWaiter {
const inputStr = toBase64(inputVal, "A-Za-z0-9+/");
this.app.updateURL(true, inputStr);
}
// Trigger a state change
if (!silent) window.dispatchEvent(this.manager.statechange);
}.bind(this));
}

View File

@ -174,16 +174,19 @@ module.exports = {
browser.waitForElementVisible("#stale-indicator");
// Enable previously disabled autobake
browser.expect.element("#auto-bake").to.not.be.selected;
browser.click("#auto-bake-label");
browser.expect.element("#auto-bake").to.be.selected.before(1000);
// Add content to the input
browser.pause(100);
browser.sendKeys("#input-text .cm-content", "1");
browser.waitForElementVisible("#output-loader");
browser.pause(500);
// Make another change while the previous input is being baked
browser.sendKeys("#input-text .cm-content", "2");
browser
.sendKeys("#input-text .cm-content", "2")
.waitForElementNotVisible("#stale-indicator")
.waitForElementNotVisible("#output-loader");
@ -192,6 +195,7 @@ module.exports = {
// Turn autobake off again
browser.click("#auto-bake-label");
browser.expect.element("#auto-bake").to.not.be.selected.before(1000);
},
"Special content": browser => {

View File

@ -39,6 +39,7 @@ function setInput(browser, input, type=true) {
browser.execute(text => {
window.app.setInput(text);
}, [input]);
browser.pause(100);
}
}

View File

@ -153,6 +153,7 @@ import "./tests/UnescapeString.mjs";
import "./tests/Unicode.mjs";
import "./tests/YARA.mjs";
import "./tests/ParseCSR.mjs";
import "./tests/XXTEA.mjs";
const testStatus = {
allTestsPassing: true,

View File

@ -1,62 +1,42 @@
/**
* Base64 tests.
* XXTEA tests.
*
* @author devcydo [devcydo@gmail.com]
*
* @copyright Crown Copyright 2022
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "XXTEA",
name: "XXTEA Encrypt and Decrypt",
input: "Hello World! 你好,中国!",
expectedOutput: "QncB1C0rHQoZ1eRiPM4dsZtRi9pNrp7sqvX76cFXvrrIHXL6",
reecipeConfig: [
expectedOutput: "Hello World! 你好,中国!",
recipeConfig: [
{
args: "1234567890"
"op": "XXTEA Encrypt",
"args": [{ "option": "UTF8", "string": "1234567890" }]
},
{
"op": "XXTEA Decrypt",
"args": [{ "option": "UTF8", "string": "1234567890" }]
}
],
},
{
name: "XXTEA",
name: "XXTEA Encrypt",
input: "ნუ პანიკას",
expectedOutput: "PbWjnbFmP8Apu2MKOGNbjeW/72IZLlLMS/g82ozLxwE=",
reecipeConfig: [
expectedOutput: "3db5a39db1663fc029bb630a38635b8de5bfef62192e52cc4bf83cda8ccbc701",
recipeConfig: [
{
args: "1234567890"
"op": "XXTEA Encrypt",
"args": [{ "option": "UTF8", "string": "1234567890" }]
},
],
},
{
name: "XXTEA",
input: "ნუ პანიკას",
expectedOutput: "dHrOJ4ClIx6gH33NPSafYR2GG7UqsazY6Xfb0iekBY4=",
reecipeConfig: [
{
args: "ll3kj209d2"
},
"op": "To Hex",
"args": ["None", 0]
}
],
},
{
name: "XXTEA",
input: "",
expectedOutput: "Invalid input length (0)",
reecipeConfig: [
{
args: "1234567890"
},
],
},
{
name: "XXTEA",
input: "",
expectedOutput: "Invalid input length (0)",
reecipeConfig: [
{
args: ""
},
],
},
}
]);