Merge branch 'gchq:master' into leon/node-updates

This commit is contained in:
Leon Zandman 2026-03-07 10:14:51 +01:00 committed by GitHub
commit f9b7389511
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1080 additions and 82 deletions

82
package-lock.json generated
View File

@ -52,14 +52,14 @@
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"ieee754": "^1.2.1", "ieee754": "^1.2.1",
"jimp": "^1.6.0", "jimp": "^1.6.0",
"jq-web": "^0.6.2", "jq-wasm": "^1.1.0-jq-1.8.1",
"jquery": "3.7.1", "jquery": "3.7.1",
"js-sha3": "^0.9.3", "js-sha3": "^0.9.3",
"jsesc": "^3.1.0", "jsesc": "^3.1.0",
"json5": "^2.2.3", "json5": "^2.2.3",
"jsonata": "^2.1.0", "jsonata": "^2.1.0",
"jsonpath-plus": "^10.4.0", "jsonpath-plus": "^10.4.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "9.0.0",
"jsqr": "^1.4.0", "jsqr": "^1.4.0",
"jsrsasign": "^11.1.1", "jsrsasign": "^11.1.1",
"kbpgp": "^2.1.17", "kbpgp": "^2.1.17",
@ -12442,11 +12442,11 @@
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/jq-web": { "node_modules/jq-wasm": {
"version": "0.6.2", "version": "1.1.0-jq-1.8.1",
"resolved": "https://registry.npmjs.org/jq-web/-/jq-web-0.6.2.tgz", "resolved": "https://registry.npmjs.org/jq-wasm/-/jq-wasm-1.1.0-jq-1.8.1.tgz",
"integrity": "sha512-+7XvjBYwTx4vP5PYkf6Q6orubO/v+UgMU6By1GritrmShr9QpT3UKa4ANzXWQfhdqtBnQYXsm7ZNbdIHT6tYpQ==", "integrity": "sha512-lWfu34lpDFIygOYcL5TzxhZIApDR9iR5XywcVoyUAZ6jlQrj8HKHOKeCcHgUm2dE9RVdbP3eqNAKGLuj+k4seQ==",
"license": "ISC" "license": "MIT"
}, },
"node_modules/jquery": { "node_modules/jquery": {
"version": "3.7.1", "version": "3.7.1",
@ -12627,34 +12627,31 @@
} }
}, },
"node_modules/jsonwebtoken": { "node_modules/jsonwebtoken": {
"version": "8.5.1", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"jws": "^3.2.2", "jws": "^3.2.2",
"lodash.includes": "^4.3.0", "lodash": "^4.17.21",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1", "ms": "^2.1.1",
"semver": "^5.6.0" "semver": "^7.3.8"
}, },
"engines": { "engines": {
"node": ">=4", "node": ">=12",
"npm": ">=1.4.28" "npm": ">=6"
} }
}, },
"node_modules/jsonwebtoken/node_modules/semver": { "node_modules/jsonwebtoken/node_modules/semver": {
"version": "5.7.2", "version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver" "semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
} }
}, },
"node_modules/jsqr": { "node_modules/jsqr": {
@ -12965,18 +12962,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
"license": "MIT"
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isfinite": { "node_modules/lodash.isfinite": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz",
@ -12984,28 +12969,11 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
"license": "MIT"
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
"license": "MIT"
},
"node_modules/lodash.isplainobject": { "node_modules/lodash.isplainobject": {
"version": "4.0.6", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"license": "MIT" "dev": true,
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
@ -13015,12 +12983,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
"node_modules/lodash.union": { "node_modules/lodash.union": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",

View File

@ -134,14 +134,14 @@
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"ieee754": "^1.2.1", "ieee754": "^1.2.1",
"jimp": "^1.6.0", "jimp": "^1.6.0",
"jq-web": "^0.6.2", "jq-wasm": "^1.1.0-jq-1.8.1",
"jquery": "3.7.1", "jquery": "3.7.1",
"js-sha3": "^0.9.3", "js-sha3": "^0.9.3",
"jsesc": "^3.1.0", "jsesc": "^3.1.0",
"json5": "^2.2.3", "json5": "^2.2.3",
"jsonata": "^2.1.0", "jsonata": "^2.1.0",
"jsonpath-plus": "^10.4.0", "jsonpath-plus": "^10.4.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "9.0.0",
"jsqr": "^1.4.0", "jsqr": "^1.4.0",
"jsrsasign": "^11.1.1", "jsrsasign": "^11.1.1",
"kbpgp": "^2.1.17", "kbpgp": "^2.1.17",

View File

@ -164,7 +164,10 @@
"Typex", "Typex",
"Lorenz", "Lorenz",
"Colossus", "Colossus",
"SIGABA" "SIGABA",
"Flask Session Decode",
"Flask Session Sign",
"Flask Session Verify"
] ]
}, },
{ {
@ -222,6 +225,9 @@
"Subtract", "Subtract",
"Multiply", "Multiply",
"Divide", "Divide",
"Modular Exponentiation",
"Modular Inverse",
"Extended GCD",
"Mean", "Mean",
"Median", "Median",
"Standard Deviation", "Standard Deviation",
@ -551,6 +557,7 @@
"P-list Viewer", "P-list Viewer",
"Disassemble x86", "Disassemble x86",
"Pseudo-Random Number Generator", "Pseudo-Random Number Generator",
"Pseudo-Random Integer Generator",
"Generate De Bruijn Sequence", "Generate De Bruijn Sequence",
"Generate UUID", "Generate UUID",
"Analyse UUID", "Analyse UUID",

View File

@ -0,0 +1,73 @@
/**
* @author p-leriche [philip.leriche@cantab.net]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
/**
* Number theory utilities used by cryptographic operations.
*
* Currently provides:
* - parseBigInt
* - Extended Euclidean Algorithm
* - Modular Exponentiation
*
* Additional algorithms may be added as required.
*/
/**
* parseBigInt helper operation
*/
export function parseBigInt(value, param) {
const v = (value ?? "").trim();
if (/^0x[0-9a-f]+$/i.test(v)) return BigInt(v);
if (/^[+-]?[0-9]+$/.test(v)) return BigInt(v);
throw new OperationError(param + " must be decimal or hex (0x...)");
}
/**
* Extended Euclidean Algorithm
*
* Returns [g, x, y] such that:
* a*x + b*y = g = gcd(a, b)
*
* (Uses an iterative algorithm to avoid possible stack overflow)
*/
export function egcd(a, b) {
let oldR = a, r = b;
let oldS = 1n, s = 0n;
let oldT = 0n, t = 1n;
while (r !== 0n) {
const quotient = oldR / r;
[oldR, r] = [r, oldR - quotient * r];
[oldS, s] = [s, oldS - quotient * s];
[oldT, t] = [t, oldT - quotient * t];
}
// oldR is the gcd
// oldS and oldT are the Bézout coefficients
return [oldR, oldS, oldT];
}
/**
* Modular exponentiation
*/
export function modPow(base, exponent, modulus) {
let result = 1n;
base %= modulus;
while (exponent > 0n) {
if (exponent & 1n) {
result = (result * base) % modulus;
}
base = (base * base) % modulus;
exponent >>= 1n;
}
return result;
}

View File

@ -0,0 +1,80 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { fromBase64 } from "../lib/Base64.mjs";
/**
* Flask Session Decode operation
*/
class FlaskSessionDecode extends Operation {
/**
* FlaskSessionDecode constructor
*/
constructor() {
super();
this.name = "Flask Session Decode";
this.module = "Crypto";
this.description = "Decodes the payload of a Flask session cookie (itsdangerous) into JSON.";
this.inputType = "string";
this.outputType = "JSON";
this.args = [
{
name: "View TimeStamp",
type: "boolean",
value: false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {Object[]}
*/
run(input, args) {
input = input.trim();
const parts = input.split(".");
if (parts.length !== 3) {
throw new OperationError("Invalid Flask token format. Expected payload.timestamp.signature");
}
const payloadB64 = parts[0];
const time = parts[1];
const timeB64 = time.replace(/-/g, "+").replace(/_/g, "/");
const binary = fromBase64(timeB64);
const bytes = new Uint8Array(4);
for (let i = 0; i < 4; i++) {
bytes[i] = binary.charCodeAt(i);
}
const view = new DataView(bytes.buffer);
const timestamp = view.getInt32(0, false);
const base64 = payloadB64.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
let payloadJson;
try {
payloadJson = fromBase64(padded);
} catch (e) {
throw new OperationError("Invalid Base64 payload");
}
try {
let data = JSON.parse(payloadJson);
if (args[0]) {
data = {payload: data, timestamp: timestamp};
}
return data;
} catch (e) {
throw new OperationError("Unable to decode JSON payload: " + e.message);
}
}
}
export default FlaskSessionDecode;

View File

@ -0,0 +1,89 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import CryptoApi from "crypto-api/src/crypto-api.mjs";
import Utils from "../Utils.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Flask Session Sign operation
*/
class FlaskSessionSign extends Operation {
/**
* FlaskSessionSign constructor
*/
constructor() {
super();
this.name = "Flask Session Sign";
this.module = "Crypto";
this.description = "Signs a JSON payload to produce a Flask session cookie (itsdangerous HMAC).";
this.inputType = "JSON";
this.outputType = "string";
this.args = [
{
name: "Key",
type: "toggleString",
value: "",
toggleValues: ["Hex", "Decimal", "Binary", "Base64", "UTF8", "Latin1"]
},
{
name: "Salt",
type: "toggleString",
value: "cookie-session",
toggleValues: ["UTF8", "Hex", "Decimal", "Binary", "Base64", "Latin1"]
},
{
name: "Algorithm",
type: "option",
value: ["sha1", "sha256"],
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!args[0].string) {
throw new OperationError("Secret key required");
}
const key = Utils.convertToByteString(args[0].string, args[0].option);
const salt = Utils.convertToByteString(args[1].string || "cookie-session", args[1].option);
const algorithm = args[2] || "sha1";
const payloadB64 = toBase64(Utils.strToByteArray(JSON.stringify(input)));
const payload = payloadB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
const derivedKey = CryptoApi.getHmac(key, CryptoApi.getHasher(algorithm));
derivedKey.update(salt);
const currentTimeStamp = Math.ceil(Date.now() / 1000);
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setInt32(0, currentTimeStamp, false);
const bytes = new Uint8Array(buffer);
let binary = "";
bytes.forEach(b => binary += String.fromCharCode(b));
const timeB64 = toBase64(Utils.strToByteArray(binary));
const time = timeB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
const data = Utils.convertToByteString(payload + "." + time, "utf8");
const sign = CryptoApi.getHmac(derivedKey.finalize(), CryptoApi.getHasher(algorithm));
sign.update(data);
const signB64 = toBase64(sign.finalize());
const sign64 = signB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
return payload + "." + time + "." + sign64;
}
}
export default FlaskSessionSign;

View File

@ -0,0 +1,136 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import CryptoApi from "crypto-api/src/crypto-api.mjs";
import Utils from "../Utils.mjs";
import { toBase64, fromBase64 } from "../lib/Base64.mjs";
/**
* Flask Session Verify operation
*/
class FlaskSessionVerify extends Operation {
/**
* FlaskSessionVerify constructor
*/
constructor() {
super();
this.name = "Flask Session Verify";
this.module = "Crypto";
this.description = "Verifies the HMAC signature of a Flask session cookie (itsdangerous) generated.";
this.inputType = "string";
this.outputType = "JSON";
this.args = [
{
name: "Key",
type: "toggleString",
value: "",
toggleValues: ["Hex", "Decimal", "Binary", "Base64", "UTF8", "Latin1"]
},
{
name: "Salt",
type: "toggleString",
value: "cookie-session",
toggleValues: ["UTF8", "Hex", "Decimal", "Binary", "Base64", "Latin1"]
},
{
name: "Algorithm",
type: "option",
value: ["sha1", "sha256"],
},
{
name: "View TimeStamp",
type: "boolean",
value: true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!args[0].string) {
throw new OperationError("Secret key required");
}
const key = Utils.convertToByteString(args[0].string, args[0].option);
const salt = Utils.convertToByteString(args[1].string || "cookie-session", args[1].option);
const algorithm = args[2] || "sha1";
input = input.trim();
const parts = input.split(".");
if (parts.length !== 3) {
throw new OperationError("Invalid Flask token format. Expected payload.timestamp.signature");
}
const data = Utils.convertToByteString(parts[0] + "." + parts[1], "utf8");
const derivedKey = CryptoApi.getHmac(key, CryptoApi.getHasher(algorithm));
derivedKey.update(salt);
const sign = CryptoApi.getHmac(derivedKey.finalize(), CryptoApi.getHasher(algorithm));
sign.update(data);
const payloadB64 = parts[0];
const base64 = payloadB64.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
const time = parts[1];
const timeB64 = time.replace(/-/g, "+").replace(/_/g, "/");
const binary = fromBase64(timeB64);
const bytes = new Uint8Array(4);
for (let i = 0; i < 4; i++) {
bytes[i] = binary.charCodeAt(i);
}
const view = new DataView(bytes.buffer);
const timestamp = view.getInt32(0, false);
let payloadJson;
try {
payloadJson = fromBase64(padded);
} catch (e) {
throw new OperationError("Invalid Base64 payload");
}
const signB64 = toBase64(sign.finalize());
const sign64 = signB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
if (sign64 !== parts[2]) {
throw new OperationError("Invalid signature!");
}
try {
const decoded = JSON.parse(payloadJson);
if (!args[3]) {
return {
valid: true,
payload: decoded,
};
} else {
return {
valid: true,
payload: decoded,
timestamp: timestamp
};
}
} catch (e) {
throw new OperationError("Unable to decode JSON payload: " + e.message);
}
}
}
export default FlaskSessionVerify;

View File

@ -6,7 +6,7 @@
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import jq from "jq-web"; import * as jq from "jq-wasm";
/** /**
* jq operation * jq operation
@ -40,16 +40,15 @@ class Jq extends Operation {
* @returns {string} * @returns {string}
*/ */
run(input, args) { run(input, args) {
return (async () => {
const [query] = args; const [query] = args;
let result;
try { try {
result = jq.json(input, query); const result = await jq.json(input, query);
return JSON.stringify(result);
} catch (err) { } catch (err) {
throw new OperationError(`Invalid jq expression: ${err.message}`); throw new OperationError(`Invalid jq expression: ${err.message}`);
} }
})();
return JSON.stringify(result);
} }
} }

View File

@ -0,0 +1,164 @@
/**
* @author cktgh [chankaitung@gmail.com]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import forge from "node-forge";
import Utils, { isWorkerEnvironment } from "../Utils.mjs";
import { DELIM_OPTIONS } from "../lib/Delim.mjs";
/**
* Pseudo-Random Integer Generator operation
*/
class PseudoRandomIntegerGenerator extends Operation {
// in theory 2**53 is the max range, but we use Number.MAX_SAFE_INTEGER (2**53 - 1) as it is more consistent.
static MAX_RANGE = Number.MAX_SAFE_INTEGER;
// arbitrary choice
static BUFFER_SIZE = 1024;
/**
* PseudoRandomIntegerGenerator constructor
*/
constructor() {
super();
this.name = "Pseudo-Random Integer Generator";
this.module = "Ciphers";
this.description = "A cryptographically-secure pseudo-random number generator (PRNG).<br><br>Generates random integers within a specified range using the browser's built-in <code>crypto.getRandomValues()</code> method if available.<br><br>The supported range of integers is from <code>-(2^53 - 1)</code> to <code>(2^53 - 1)</code>.";
this.infoURL = "https://wikipedia.org/wiki/Pseudorandom_number_generator";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Number of Integers",
"type": "number",
"value": 1,
"min": 1
},
{
"name": "Min Value",
"type": "number",
"value": 0,
"min": Number.MIN_SAFE_INTEGER,
"max": Number.MAX_SAFE_INTEGER
},
{
"name": "Max Value",
"type": "number",
"value": 99,
"min": Number.MIN_SAFE_INTEGER,
"max": Number.MAX_SAFE_INTEGER
},
{
"name": "Delimiter",
"type": "option",
"value": DELIM_OPTIONS
},
{
"name": "Output",
"type": "option",
"value": ["Raw", "Hex", "Decimal"]
}
];
// not using BigUint64Array to avoid BigInt handling overhead
this.randomBuffer = new Uint32Array(PseudoRandomIntegerGenerator.BUFFER_SIZE);
this.randomBufferOffset = PseudoRandomIntegerGenerator.BUFFER_SIZE;
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [numInts, minInt, maxInt, delimiter, outputType] = args;
if (minInt === null || maxInt === null) return "";
const min = Math.ceil(minInt);
const max = Math.floor(maxInt);
const delim = Utils.charRep(delimiter || "Space");
if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max)) {
throw new OperationError("Min and Max must be between `-(2^53 - 1)` and `2^53 - 1`.");
}
if (min > max) {
throw new OperationError("Min cannot be larger than Max.");
}
const range = max - min + 1; // inclusive range
if (range > PseudoRandomIntegerGenerator.MAX_RANGE) {
throw new OperationError("Range between Min and Max cannot be larger than `2^53`");
}
// as large as possible while divisible by range
const rejectionThreshold = PseudoRandomIntegerGenerator.MAX_RANGE - (PseudoRandomIntegerGenerator.MAX_RANGE % range);
const output = [];
for (let i = 0; i < numInts; i++) {
const result = this._generateRandomValue(rejectionThreshold);
const intValue = min + (result % range);
switch (outputType) {
case "Hex":
output.push(intValue.toString(16));
break;
case "Decimal":
output.push(intValue.toString(10));
break;
case "Raw":
default:
output.push(Utils.chr(intValue));
}
}
if (outputType === "Raw") {
return output.join("");
}
return output.join(delim);
}
/**
* Generate a random value, result will be less than the rejection threshold (exclusive).
*
* @param {number} rejectionThreshold
* @returns {number}
*/
_generateRandomValue(rejectionThreshold) {
let result;
do {
if (this.randomBufferOffset + 2 > this.randomBuffer.length) {
this._resetRandomBuffer();
}
// stitching a 53 bit number; not using BigUint64Array to avoid BigInt handling overhead
result = (this.randomBuffer[this.randomBufferOffset++] & 0x1f_ffff) * 0x1_0000_0000 +
this.randomBuffer[this.randomBufferOffset++];
} while (result >= rejectionThreshold);
return result;
}
/**
* Fill random buffer with new random values and rseet the offset.
*/
_resetRandomBuffer() {
if (isWorkerEnvironment() && self.crypto) {
self.crypto.getRandomValues(this.randomBuffer);
} else {
const bytes = forge.random.getBytesSync(this.randomBuffer.length * 4);
for (let j = 0; j < this.randomBuffer.length; j++) {
this.randomBuffer[j] = (bytes.charCodeAt(j * 4) << 24) |
(bytes.charCodeAt(j * 4 + 1) << 16) |
(bytes.charCodeAt(j * 4 + 2) << 8) |
bytes.charCodeAt(j * 4 + 3);
}
}
this.randomBufferOffset = 0;
}
}
export default PseudoRandomIntegerGenerator;

View File

@ -249,6 +249,13 @@ optgroup {
} }
/* Bootstrap form inside CodeMirror editor */
.cm-panel > .bmd-form-group {
padding-top: 0;
}
/* CodeMirror */ /* CodeMirror */
.ͼ2 .cm-specialChar, .ͼ2 .cm-specialChar,

View File

@ -50,6 +50,14 @@ module.exports = {
testOp(browser, "Analyse hash", "0123456789abcdef", /CRC-64/); testOp(browser, "Analyse hash", "0123456789abcdef", /CRC-64/);
testOp(browser, "Atbash Cipher", "test input", "gvhg rmkfg"); testOp(browser, "Atbash Cipher", "test input", "gvhg rmkfg");
// testOp(browser, "Avro to JSON", "test input", "test_output"); // testOp(browser, "Avro to JSON", "test input", "test_output");
testOp(browser,
[
"From Hex", "Avro to JSON"
],
"4f626a0104166176726f2e736368656d6196017b2274797065223a227265636f7264222c226e616d65223a22736d616c6c222c226669656c6473223a5b7b226e616d65223a226e616d65222c2274797065223a22737472696e67227d5d7d146176726f2e636f646563086e756c6c004e0247632e3702e5b75cdab9a62f1541020e0c6d796e616d654e0247632e3702e5b75cdab9a62f1541",
'{"name":"myname"}\n',
[[], [false]]
);
testOp(browser, "BLAKE2b", "test input", "33ebdc8f38177f3f3f334eeb117a84e11f061bbca4db6b8923e5cec85103f59f415551a5d5a933fdb6305dc7bf84671c2540b463dbfa08ee1895cfaa5bd780b5", ["512", "Hex", { "option": "UTF8", "string": "pass" }]); testOp(browser, "BLAKE2b", "test input", "33ebdc8f38177f3f3f334eeb117a84e11f061bbca4db6b8923e5cec85103f59f415551a5d5a933fdb6305dc7bf84671c2540b463dbfa08ee1895cfaa5bd780b5", ["512", "Hex", { "option": "UTF8", "string": "pass" }]);
testOp(browser, "BLAKE2s", "test input", "defe73d61dfa6e5807e4f9643e159a09ccda6be3c26dcd65f8a9bb38bfc973a7", ["256", "Hex", { "option": "UTF8", "string": "pass" }]); testOp(browser, "BLAKE2s", "test input", "defe73d61dfa6e5807e4f9643e159a09ccda6be3c26dcd65f8a9bb38bfc973a7", ["256", "Hex", { "option": "UTF8", "string": "pass" }]);
testOp(browser, "BSON deserialise", "\u0011\u0000\u0000\u0000\u0002a\u0000\u0005\u0000\u0000\u0000test\u0000\u0000", '{\u000A "a": "test"\u000A}'); testOp(browser, "BSON deserialise", "\u0011\u0000\u0000\u0000\u0002a\u0000\u0005\u0000\u0000\u0000test\u0000\u0000", '{\u000A "a": "test"\u000A}');
@ -206,6 +214,7 @@ module.exports = {
testOpHtml(browser, "Index of Coincidence", "test input", "", /Index of Coincidence: 0.08333333333333333/); testOpHtml(browser, "Index of Coincidence", "test input", "", /Index of Coincidence: 0.08333333333333333/);
testOpImage(browser, "Invert Image", "files/Hitchhikers_Guide.jpeg"); testOpImage(browser, "Invert Image", "files/Hitchhikers_Guide.jpeg");
// testOp(browser, "JPath expression", "test input", "test_output"); // testOp(browser, "JPath expression", "test input", "test_output");
testOp(browser, "Jq", '{"a":{"b":1}}', '{"b":1}', [".a"]);
testOpHtml(browser, "JSON Beautify", "{a:1}", ".json-dict .json-literal", "1"); testOpHtml(browser, "JSON Beautify", "{a:1}", ".json-dict .json-literal", "1");
// testOp(browser, "JSON Minify", "test input", "test_output"); // testOp(browser, "JSON Minify", "test input", "test_output");
// testOp(browser, "JSON to CSV", "test input", "test_output"); // testOp(browser, "JSON to CSV", "test input", "test_output");

View File

@ -23,6 +23,7 @@ import "./tests/Dish.mjs";
import "./tests/NodeDish.mjs"; import "./tests/NodeDish.mjs";
import "./tests/Utils.mjs"; import "./tests/Utils.mjs";
import "./tests/Categories.mjs"; import "./tests/Categories.mjs";
import "./tests/lib/BigIntUtils.mjs";
const testStatus = { const testStatus = {
allTestsPassing: true, allTestsPassing: true,

View File

@ -0,0 +1,150 @@
import TestRegister from "../../../lib/TestRegister.mjs";
import { parseBigInt, egcd, modPow } from "../../../../src/core/lib/BigIntUtils.mjs";
import it from "../../assertionHandler.mjs";
import assert from "assert";
TestRegister.addApiTests([
// ===== parseBigInt tests =====
it("BigIntUtils: parseBigInt - decimal number", () => {
const value = parseBigInt("1", "test value");
assert.deepStrictEqual(value, BigInt("1"));
}),
it("BigIntUtils: parseBigInt - large decimal", () => {
const value = parseBigInt("123456789012345678901234567890", "test value");
assert.deepStrictEqual(value, BigInt("123456789012345678901234567890"));
}),
it("BigIntUtils: parseBigInt - hexadecimal lowercase", () => {
const value = parseBigInt("0xff", "test value");
assert.deepStrictEqual(value, BigInt("255"));
}),
it("BigIntUtils: parseBigInt - hexadecimal uppercase", () => {
const value = parseBigInt("0xFF", "test value");
assert.deepStrictEqual(value, BigInt("255"));
}),
it("BigIntUtils: parseBigInt - large hexadecimal", () => {
const value = parseBigInt("0x123456789ABCDEF", "test value");
assert.deepStrictEqual(value, BigInt("0x123456789ABCDEF"));
}),
it("BigIntUtils: parseBigInt - whitespace trimming", () => {
const value = parseBigInt(" 42 ", "test value");
assert.deepStrictEqual(value, BigInt("42"));
}),
it("BigIntUtils: parseBigInt - invalid input (text)", () => {
assert.throws(() => parseBigInt("test", "test value"), {
name: "Error",
message: "test value must be decimal or hex (0x...)"
});
}),
it("BigIntUtils: parseBigInt - invalid input (hex without prefix)", () => {
assert.throws(() => parseBigInt("FF", "test value"), {
name: "Error",
message: "test value must be decimal or hex (0x...)"
});
}),
it("BigIntUtils: parseBigInt - invalid input (mixed)", () => {
assert.throws(() => parseBigInt("12abc", "test value"), {
name: "Error",
message: "test value must be decimal or hex (0x...)"
});
}),
// ===== egcd tests =====
it("BigIntUtils: egcd - basic coprime", () => {
const a = BigInt("36");
const b = BigInt("48");
const gcd = BigInt("12");
const bezout1 = BigInt("-1");
const bezout2 = BigInt("1");
assert.deepStrictEqual(egcd(a, b), [gcd, bezout1, bezout2]);
}),
it("BigIntUtils: egcd - coprime numbers", () => {
const [g, x, y] = egcd(BigInt("3"), BigInt("11"));
assert.strictEqual(g, BigInt("1"));
// Verify Bézout identity: a*x + b*y = gcd
assert.strictEqual(BigInt("3") * x + BigInt("11") * y, g);
}),
it("BigIntUtils: egcd - non-coprime numbers", () => {
const [g, x, y] = egcd(BigInt("240"), BigInt("46"));
assert.strictEqual(g, BigInt("2"));
// Verify Bézout identity
assert.strictEqual(BigInt("240") * x + BigInt("46") * y, g);
}),
it("BigIntUtils: egcd - with zero", () => {
const [g, x, y] = egcd(BigInt("17"), BigInt("0"));
assert.strictEqual(g, BigInt("17"));
assert.strictEqual(x, BigInt("1"));
assert.strictEqual(y, BigInt("0"));
}),
it("BigIntUtils: egcd - identical numbers", () => {
const [g, x, y] = egcd(BigInt("42"), BigInt("42"));
assert.strictEqual(g, BigInt("42"));
// Verify Bézout identity
assert.strictEqual(BigInt("42") * x + BigInt("42") * y, g);
}),
it("BigIntUtils: egcd - large numbers", () => {
const a = BigInt("123456789012345678901234567890");
const b = BigInt("987654321098765432109876543210");
const [g, x, y] = egcd(a, b);
// Verify Bézout identity
assert.strictEqual(a * x + b * y, g);
}),
// ===== modPow tests =====
it("BigIntUtils: modPow - basic", () => {
// 2^10 mod 1000 = 1024 mod 1000 = 24
const result = modPow(BigInt("2"), BigInt("10"), BigInt("1000"));
assert.strictEqual(result, BigInt("24"));
}),
it("BigIntUtils: modPow - RSA-like example", () => {
// Common RSA public exponent
const base = BigInt("123456789");
const exp = BigInt("65537");
const mod = BigInt("999999999999");
const result = modPow(base, exp, mod);
// Result should be less than modulus
assert(result < mod);
assert(result >= BigInt("0"));
}),
it("BigIntUtils: modPow - exponent zero", () => {
// Any number^0 = 1
const result = modPow(BigInt("999"), BigInt("0"), BigInt("100"));
assert.strictEqual(result, BigInt("1"));
}),
it("BigIntUtils: modPow - base zero", () => {
// 0^n = 0
const result = modPow(BigInt("0"), BigInt("5"), BigInt("100"));
assert.strictEqual(result, BigInt("0"));
}),
it("BigIntUtils: modPow - large exponent", () => {
// Test with very large exponent (efficient algorithm should handle this)
const result = modPow(BigInt("3"), BigInt("1000000"), BigInt("1000000007"));
assert(result >= BigInt("0"));
assert(result < BigInt("1000000007"));
}),
it("BigIntUtils: modPow - modular inverse verification", () => {
// If a*x . 1 (mod m), then modPow(a, 1, m) * x . 1 (mod m)
const a = BigInt("3");
const m = BigInt("11");
const x = BigInt("4"); // inverse of 3 mod 11
const result = modPow(a, BigInt("1"), m) * x % m;
assert.strictEqual(result, BigInt("1"));
}),
]);

View File

@ -0,0 +1,246 @@
/**
* Flask Session tests
*
* @author ThePlayer372-FR []
*
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const validTokenSha1 = "eyJyb2xlIjoic3VwZXJ1c2VyIiwidXNlciI6ImFkbWluIn0.aZ-KEw.E_x6bOhA4GU9t72pMinJUjN-O3I";
const validTokenSha256 = "eyJyb2xlIjoic3VwZXJ1c2VyIiwidXNlciI6ImFkbWluIn0.aab3Ew.Jsx2DOx_H9anZg0YcvhsASxQ11897EFHeQfS2oja4y8";
const validKey = "mysecretkey";
const wrongKey = "notTheKey";
const outputObject = {
user: "admin",
role: "superuser",
};
const outputVerify = {
valid: true,
payload: outputObject,
};
TestRegister.addTests([
{
name: "Flask Session: Decode",
input: validTokenSha1,
expectedOutput: outputObject,
recipeConfig: [
{
op: "Flask Session Decode",
args: [
false
],
}
]
},
{
name: "Flask Session: Verify Sha1",
input: validTokenSha1,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256",
input: validTokenSha256,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Sign Sha1",
input: outputObject,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Sign",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1"
]
},
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Sign Sha256",
input: outputObject,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Sign",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256"
]
},
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha1 Wrong Key",
input: validTokenSha1,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: wrongKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256 Wrong Key",
input: validTokenSha256,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: wrongKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha1 Wrong Salt",
input: validTokenSha1,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "notTheSalt",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256 Wrong Salt",
input: validTokenSha256,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "notTheSalt",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
]);

View File

@ -15,7 +15,7 @@ const inputObject = JSON.stringify({
}, null, 4); }, null, 4);
const hsKey = "secret_cat"; const hsKey = "secret_cat";
const rsKey = `-----BEGIN RSA PRIVATE KEY----- const rsKey1024 = `-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw
33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW 33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW
+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB +jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB
@ -30,11 +30,52 @@ fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523
Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP
FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw== FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==
-----END RSA PRIVATE KEY-----`; -----END RSA PRIVATE KEY-----`;
const esKey = `-----BEGIN PRIVATE KEY----- const rsKey2048 = `-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAk0VOoksAblwP82DALTG6xGC86Hfho3nChbcPGWyqn+ScfHBF
cg3SeKyy6aWCyLcKfNwE5cPYzuYvVBsZyIrdfFOuV90D/aRYbuw6UkKR3cmmy9qE
qvu05dogvc0BcmkwbC37Q8JnsZBRcosoLGgTFxcK+LXdsG7DukajpsGesxQjOLb2
1jnx+ypzx74xvj7grqlXkxeDKr22q7QkO3A1ApoOuJRAU+SjEEZmqdXzRery2RWx
hkWbCXuQw4PnW5Lh3Wwabnu7XKVIa6wJa1pqL2IAxmlZ0bvGTfjtO5ggNfgJk5V4
bGSOXnsplpG71AWMrK2q6NqHjFIE1szEycUKrwIDAQABAoIBAAivyt6Zy/G2g8kC
852hfvcRubLV92eRdAmNGFqTOqaUcS00i3QZyp4MRGqxtOV/88y/nEOtP1RHkZJw
HXTjHq4JsDvwhnQR8JbCX6z1zkLQdS01u3jrwJTaPpooxdATfPlfO6CYjqM+SapB
o7dS1ZAZb4U8vPx+MWoDEVNxvO7/xyqho1Oc4H9MwqQUiyG2WfIoqxLSrBYcambv
RmySwTIpgQZTr61EeWf/0eWpV0iEYbSnkB/VaKW+5tg4gCjPgy5v6/LQ0u/pzlYz
ayCL3xN2rp0tigXsiiWz3cM5gDsnatK4nVNRs9y3JSZpWpI236ZfZjs8Lts+WBUw
hAEoE9kCgYEAyEIGD1A7R/t5EYk5HhHDH5tGdyxejAcQL5AIz0YnTZU8Iixyc7FR
uDmAMiuKIcJY/nUlxZjSxNc3MkOfZNggQvf9ONrt+ftQ1yyTjv+019NfU4w4d0Ep
LNaiAHgaPKimBUZjYXbLgiMXj/1pBaQmgUYTK/VlO3PVdowxxzxMYlMCgYEAvEOG
GrhVaQV1nAYx86BgZ3wn90hBFXZWGaN+eXUmyrast93Ih3TCSgQDKPuN3pdv/TIe
cpQv/BxEMpW+6d5Z1NP3GbrLpaZUiUNk8fqw1S3pmD5aWZrYIUaNukAyOxnZVgjv
EWD9QTpI663gODaeZZTkDYiRNzTzGOg5HtzporUCgYBBOphEtqqImNXnq13qeHip
O+eo+8/UJpzUEUN9WGmG8NxEeVvSaWin7DrgnKQCuQ5J3Biwk0XcDgoRmks6Ctf/
WE2oDk/DxGOhowhxZMMgJd6AFUVzOstRqpvcMULCjWB+iV3nqk1Bl3KeWTmzN7O/
Gfc2s1kFE4btdV7lebObtwKBgE3rkLS8eLVYCh6Cvef9CAms7Im/wRhV+zrvXWh9
4YljZEdRpy7RV5z03i33N/faLALa3JlF1jp9pIhfTD5Vxk59ULe4hZNRLYoGd+Bj
hw8kyps1q4WMvkm/fueIrIGjqD2gwvopb4iwy/+n3rbFfHfE0UL8tEXqR3eWnhW1
D4pFAoGAccR4eMJD43hJWaUQLtsj0RoW9lFKVXj7aqkIIeupXwt7Ic2z/FhCAJi+
V0MWpd3K6+kPl+ifdt8U4kcYfubPMfJhd7IkMcgQS+yZK1+5xWdRISvI8GpNwIHE
LUkVkCCadXNNZ7b1nmUKjse95u4IaE6hwAqjSTNb05gPmCfoEjg=
-----END RSA PRIVATE KEY-----`;
const esKeyP256 = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2
OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G 1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G
-----END PRIVATE KEY-----`; -----END PRIVATE KEY-----`;
const esKeyP384 = `-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDpgCvB2frnLKd7TuWe
JM1ejXXmr9y/5gskxKuuylLvpQTiDdtLtuhJnvw1/zWKWO6hZANiAAQ5Crhsi5FD
t55i53dCtdzG9OzCnbDFf/6136ZfEiakDTDeWCdUvNnB3WQEcVBr97BfSWLI9mO+
T5yzm0RfhgvWIq/tBou+sIDeGp6NQfJwhDhf+JsdeF174gtfNMZGj/s=
-----END PRIVATE KEY-----`;
const esKeyP521 = `-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIA0dBErrZ5ovKq4Xf/
iTlRkYxuOfgBZ6+tWIfG13YwthB1XrH06YmteZGNjHHLZEeycwUt0jM4kUb+tOsJ
3ckhj1ihgYkDgYYABACYgsa8JWKH46CQagwNw14v/L+DIs1WAjJdMXZySjKlRkD9
LtLMxkbX2H4H4Zl2KzCMJkwTSETzSKNlXvAUJqKbRwHezCp4y5XZN9MOBYdmyylZ
NOVxwwTouimNkJ0K6A8+/Im5S3PWB8Ra1D6t+bT1WHHhEePZcltSLLFlbIIyot5m
2w==
-----END PRIVATE KEY-----`;
TestRegister.addTests([ TestRegister.addTests([
{ {
@ -88,7 +129,24 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES256", "{}"], args: [esKeyP256, "ES256", "{}"],
},
{
op: "JWT Decode",
args: []
}
],
},
{
name: "JWT Sign: ES384 - P256 key",
input: inputObject,
expectedOutput: `Error: Have you entered the key correctly? The key should be either the secret for HMAC algorithms or the PEM-encoded private key for RSA and ECDSA.
Error: "alg" parameter "ES384" requires curve "secp384r1".`,
recipeConfig: [
{
op: "JWT Sign",
args: [esKeyP256, "ES384", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -103,7 +161,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES384", "{}"], args: [esKeyP384, "ES384", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -118,7 +176,24 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES512", "{}"], args: [esKeyP521, "ES512", "{}"],
},
{
op: "JWT Decode",
args: []
}
],
},
{
name: "JWT Sign: RS256, weak key",
input: inputObject,
expectedOutput: `Error: Have you entered the key correctly? The key should be either the secret for HMAC algorithms or the PEM-encoded private key for RSA and ECDSA.
Error: secretOrPrivateKey has a minimum key size of 2048 bits for RS256`,
recipeConfig: [
{
op: "JWT Sign",
args: [rsKey1024, "RS256", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -133,7 +208,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [rsKey, "RS256", "{}"], args: [rsKey2048, "RS256", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -148,7 +223,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [rsKey, "RS384", "{}"], args: [rsKey2048, "RS384", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -163,7 +238,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "RS512", "{}"], args: [rsKey2048, "RS512", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",