Merge branch 'gchq:master' into leon/node-updates
This commit is contained in:
commit
f9b7389511
82
package-lock.json
generated
82
package-lock.json
generated
@ -52,14 +52,14 @@
|
||||
"highlight.js": "^11.11.1",
|
||||
"ieee754": "^1.2.1",
|
||||
"jimp": "^1.6.0",
|
||||
"jq-web": "^0.6.2",
|
||||
"jq-wasm": "^1.1.0-jq-1.8.1",
|
||||
"jquery": "3.7.1",
|
||||
"js-sha3": "^0.9.3",
|
||||
"jsesc": "^3.1.0",
|
||||
"json5": "^2.2.3",
|
||||
"jsonata": "^2.1.0",
|
||||
"jsonpath-plus": "^10.4.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jsonwebtoken": "9.0.0",
|
||||
"jsqr": "^1.4.0",
|
||||
"jsrsasign": "^11.1.1",
|
||||
"kbpgp": "^2.1.17",
|
||||
@ -12442,11 +12442,11 @@
|
||||
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/jq-web": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/jq-web/-/jq-web-0.6.2.tgz",
|
||||
"integrity": "sha512-+7XvjBYwTx4vP5PYkf6Q6orubO/v+UgMU6By1GritrmShr9QpT3UKa4ANzXWQfhdqtBnQYXsm7ZNbdIHT6tYpQ==",
|
||||
"license": "ISC"
|
||||
"node_modules/jq-wasm": {
|
||||
"version": "1.1.0-jq-1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/jq-wasm/-/jq-wasm-1.1.0-jq-1.8.1.tgz",
|
||||
"integrity": "sha512-lWfu34lpDFIygOYcL5TzxhZIApDR9iR5XywcVoyUAZ6jlQrj8HKHOKeCcHgUm2dE9RVdbP3eqNAKGLuj+k4seQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jquery": {
|
||||
"version": "3.7.1",
|
||||
@ -12627,34 +12627,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
|
||||
"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
|
||||
"integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"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",
|
||||
"lodash": "^4.17.21",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^5.6.0"
|
||||
"semver": "^7.3.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4",
|
||||
"npm": ">=1.4.28"
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jsqr": {
|
||||
@ -12965,18 +12962,6 @@
|
||||
"dev": true,
|
||||
"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": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz",
|
||||
@ -12984,28 +12969,11 @@
|
||||
"dev": true,
|
||||
"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": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"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==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
@ -13015,12 +12983,6 @@
|
||||
"dev": true,
|
||||
"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": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||
|
||||
@ -134,14 +134,14 @@
|
||||
"highlight.js": "^11.11.1",
|
||||
"ieee754": "^1.2.1",
|
||||
"jimp": "^1.6.0",
|
||||
"jq-web": "^0.6.2",
|
||||
"jq-wasm": "^1.1.0-jq-1.8.1",
|
||||
"jquery": "3.7.1",
|
||||
"js-sha3": "^0.9.3",
|
||||
"jsesc": "^3.1.0",
|
||||
"json5": "^2.2.3",
|
||||
"jsonata": "^2.1.0",
|
||||
"jsonpath-plus": "^10.4.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jsonwebtoken": "9.0.0",
|
||||
"jsqr": "^1.4.0",
|
||||
"jsrsasign": "^11.1.1",
|
||||
"kbpgp": "^2.1.17",
|
||||
|
||||
@ -164,7 +164,10 @@
|
||||
"Typex",
|
||||
"Lorenz",
|
||||
"Colossus",
|
||||
"SIGABA"
|
||||
"SIGABA",
|
||||
"Flask Session Decode",
|
||||
"Flask Session Sign",
|
||||
"Flask Session Verify"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -222,6 +225,9 @@
|
||||
"Subtract",
|
||||
"Multiply",
|
||||
"Divide",
|
||||
"Modular Exponentiation",
|
||||
"Modular Inverse",
|
||||
"Extended GCD",
|
||||
"Mean",
|
||||
"Median",
|
||||
"Standard Deviation",
|
||||
@ -551,6 +557,7 @@
|
||||
"P-list Viewer",
|
||||
"Disassemble x86",
|
||||
"Pseudo-Random Number Generator",
|
||||
"Pseudo-Random Integer Generator",
|
||||
"Generate De Bruijn Sequence",
|
||||
"Generate UUID",
|
||||
"Analyse UUID",
|
||||
|
||||
73
src/core/lib/BigIntUtils.mjs
Normal file
73
src/core/lib/BigIntUtils.mjs
Normal 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;
|
||||
}
|
||||
|
||||
80
src/core/operations/FlaskSessionDecode.mjs
Normal file
80
src/core/operations/FlaskSessionDecode.mjs
Normal 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;
|
||||
89
src/core/operations/FlaskSessionSign.mjs
Normal file
89
src/core/operations/FlaskSessionSign.mjs
Normal 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;
|
||||
136
src/core/operations/FlaskSessionVerify.mjs
Normal file
136
src/core/operations/FlaskSessionVerify.mjs
Normal 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;
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import jq from "jq-web";
|
||||
import * as jq from "jq-wasm";
|
||||
|
||||
/**
|
||||
* jq operation
|
||||
@ -40,16 +40,15 @@ class Jq extends Operation {
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [query] = args;
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = jq.json(input, query);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Invalid jq expression: ${err.message}`);
|
||||
}
|
||||
|
||||
return JSON.stringify(result);
|
||||
return (async () => {
|
||||
const [query] = args;
|
||||
try {
|
||||
const result = await jq.json(input, query);
|
||||
return JSON.stringify(result);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Invalid jq expression: ${err.message}`);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
164
src/core/operations/PseudoRandomIntegerGenerator.mjs
Normal file
164
src/core/operations/PseudoRandomIntegerGenerator.mjs
Normal 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;
|
||||
@ -249,6 +249,13 @@ optgroup {
|
||||
}
|
||||
|
||||
|
||||
/* Bootstrap form inside CodeMirror editor */
|
||||
|
||||
.cm-panel > .bmd-form-group {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
|
||||
/* CodeMirror */
|
||||
|
||||
.ͼ2 .cm-specialChar,
|
||||
|
||||
@ -50,6 +50,14 @@ module.exports = {
|
||||
testOp(browser, "Analyse hash", "0123456789abcdef", /CRC-64/);
|
||||
testOp(browser, "Atbash Cipher", "test input", "gvhg rmkfg");
|
||||
// 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, "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}');
|
||||
@ -206,6 +214,7 @@ module.exports = {
|
||||
testOpHtml(browser, "Index of Coincidence", "test input", "", /Index of Coincidence: 0.08333333333333333/);
|
||||
testOpImage(browser, "Invert Image", "files/Hitchhikers_Guide.jpeg");
|
||||
// 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");
|
||||
// testOp(browser, "JSON Minify", "test input", "test_output");
|
||||
// testOp(browser, "JSON to CSV", "test input", "test_output");
|
||||
|
||||
@ -23,6 +23,7 @@ import "./tests/Dish.mjs";
|
||||
import "./tests/NodeDish.mjs";
|
||||
import "./tests/Utils.mjs";
|
||||
import "./tests/Categories.mjs";
|
||||
import "./tests/lib/BigIntUtils.mjs";
|
||||
|
||||
const testStatus = {
|
||||
allTestsPassing: true,
|
||||
|
||||
150
tests/node/tests/lib/BigIntUtils.mjs
Normal file
150
tests/node/tests/lib/BigIntUtils.mjs
Normal 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"));
|
||||
}),
|
||||
]);
|
||||
246
tests/operations/tests/FlaskSession.mjs
Normal file
246
tests/operations/tests/FlaskSession.mjs
Normal 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,
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
]);
|
||||
@ -15,7 +15,7 @@ const inputObject = JSON.stringify({
|
||||
}, null, 4);
|
||||
|
||||
const hsKey = "secret_cat";
|
||||
const rsKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
const rsKey1024 = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw
|
||||
33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW
|
||||
+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB
|
||||
@ -30,11 +30,52 @@ fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523
|
||||
Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP
|
||||
FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==
|
||||
-----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
|
||||
OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
|
||||
1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G
|
||||
-----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([
|
||||
{
|
||||
@ -88,7 +129,24 @@ TestRegister.addTests([
|
||||
recipeConfig: [
|
||||
{
|
||||
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",
|
||||
@ -103,7 +161,7 @@ TestRegister.addTests([
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JWT Sign",
|
||||
args: [esKey, "ES384", "{}"],
|
||||
args: [esKeyP384, "ES384", "{}"],
|
||||
},
|
||||
{
|
||||
op: "JWT Decode",
|
||||
@ -118,7 +176,24 @@ TestRegister.addTests([
|
||||
recipeConfig: [
|
||||
{
|
||||
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",
|
||||
@ -133,7 +208,7 @@ TestRegister.addTests([
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JWT Sign",
|
||||
args: [rsKey, "RS256", "{}"],
|
||||
args: [rsKey2048, "RS256", "{}"],
|
||||
},
|
||||
{
|
||||
op: "JWT Decode",
|
||||
@ -148,7 +223,7 @@ TestRegister.addTests([
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JWT Sign",
|
||||
args: [rsKey, "RS384", "{}"],
|
||||
args: [rsKey2048, "RS384", "{}"],
|
||||
},
|
||||
{
|
||||
op: "JWT Decode",
|
||||
@ -163,7 +238,7 @@ TestRegister.addTests([
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JWT Sign",
|
||||
args: [esKey, "RS512", "{}"],
|
||||
args: [rsKey2048, "RS512", "{}"],
|
||||
},
|
||||
{
|
||||
op: "JWT Decode",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user