cyberchef 10.24.0 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/devcontainer.json +1 -1
- package/.nvmrc +1 -1
- package/CHANGELOG.md +172 -0
- package/Dockerfile +2 -2
- package/Gruntfile.js +11 -6
- package/README.md +6 -4
- package/SECURITY.md +8 -18
- package/babel.config.js +4 -1
- package/package.json +48 -43
- package/src/core/ChefWorker.js +1 -1
- package/src/core/Recipe.mjs +1 -1
- package/src/core/config/Categories.json +6 -0
- package/src/core/config/OperationConfig.json +140 -16
- package/src/core/config/modules/Default.mjs +8 -0
- package/src/core/config/modules/PGP.mjs +2 -0
- package/src/core/config/scripts/generateOpsIndex.mjs +63 -0
- package/src/core/config/scripts/newOperation.mjs +31 -4
- package/src/core/lib/Magic.mjs +1 -1
- package/src/core/operations/AESDecrypt.mjs +61 -16
- package/src/core/operations/AESEncrypt.mjs +26 -11
- package/src/core/operations/BLAKE3.mjs +13 -7
- package/src/core/operations/BSONDeserialise.mjs +2 -2
- package/src/core/operations/BSONSerialise.mjs +3 -2
- package/src/core/operations/Bcrypt.mjs +1 -1
- package/src/core/operations/BcryptCompare.mjs +1 -1
- package/src/core/operations/DecodeText.mjs +4 -0
- package/src/core/operations/EncodeText.mjs +4 -0
- package/src/core/operations/EscapeSmartCharacters.mjs +129 -0
- package/src/core/operations/FromPunycode.mjs +1 -1
- package/src/core/operations/GenerateLoremIpsum.mjs +34 -3
- package/src/core/operations/GeneratePGPKeyPair.mjs +8 -7
- package/src/core/operations/PGPSign.mjs +83 -0
- package/src/core/operations/ParseEthernetFrame.mjs +1 -1
- package/src/core/operations/ParseIPv4Header.mjs +1 -1
- package/src/core/operations/ParseObjectIDTimestamp.mjs +2 -2
- package/src/core/operations/ParseUserAgent.mjs +1 -1
- package/src/core/operations/ROR13.mjs +83 -0
- package/src/core/operations/RemoveANSIEscapeCodes.mjs +41 -0
- package/src/core/operations/SeriesChart.mjs +16 -0
- package/src/core/operations/ShowBase64Offsets.mjs +28 -28
- package/src/core/operations/ToPunycode.mjs +1 -1
- package/src/core/operations/Wrap.mjs +47 -0
- package/src/core/operations/index.mjs +10 -0
- package/src/node/NodeRecipe.mjs +8 -7
- package/src/node/api.mjs +4 -4
- package/src/node/index.mjs +25 -0
- package/src/web/App.mjs +19 -1
- package/src/web/HTMLIngredient.mjs +1 -0
- package/src/web/html/index.html +3 -3
- package/src/web/index.js +2 -2
- package/src/web/static/sitemap.mjs +4 -4
- package/src/web/waiters/RecipeWaiter.mjs +9 -1
- package/tests/browser/02_ops.js +7 -7
- package/tests/browser/03_recipe_load.js +48 -0
- package/tests/browser/browserUtils.js +6 -3
- package/tests/lib/wasmFetchPolyfill.mjs +31 -0
- package/tests/node/consumers/cjs-consumer.js +2 -2
- package/tests/node/consumers/esm-consumer.mjs +2 -2
- package/tests/node/index.mjs +1 -0
- package/tests/node/tests/Categories.mjs +2 -2
- package/tests/node/tests/PGP.mjs +69 -0
- package/tests/node/tests/nodeApi.mjs +55 -58
- package/tests/node/tests/operations.mjs +41 -2
- package/tests/operations/index.mjs +72 -66
- package/tests/operations/tests/BLAKE3.mjs +18 -0
- package/tests/operations/tests/Base64.mjs +11 -0
- package/tests/operations/tests/CharEnc.mjs +26 -0
- package/tests/operations/tests/Charts.mjs +11 -0
- package/tests/operations/tests/Crypt.mjs +288 -62
- package/tests/operations/tests/EscapeSmartCharacters.mjs +132 -0
- package/tests/operations/tests/FlaskSession.mjs +11 -8
- package/tests/operations/tests/GenerateLoremIpsum.mjs +80 -0
- package/tests/operations/tests/IPv6Transition.mjs +4 -4
- package/tests/operations/tests/PGP.mjs +178 -154
- package/tests/operations/tests/ParseEthernetFrame.mjs +11 -0
- package/tests/operations/tests/ParseIPv4Header.mjs +23 -0
- package/tests/operations/tests/ParseX509CRL.mjs +16 -16
- package/tests/operations/tests/ROR13.mjs +45 -0
- package/tests/operations/tests/Register.mjs +3 -1
- package/tests/operations/tests/RemoveANSIEscapeCodes.mjs +62 -0
- package/tests/operations/tests/Wrap.mjs +44 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import OperationConfig from "../../core/config/OperationConfig.json"
|
|
1
|
+
import { SitemapStream, streamToPromise } from "sitemap";
|
|
2
|
+
import OperationConfig from "../../core/config/OperationConfig.json" with { type: "json" };
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Generates an XML sitemap for all CyberChef operations and a number of recipes.
|
|
@@ -11,7 +11,7 @@ import OperationConfig from "../../core/config/OperationConfig.json" assert { ty
|
|
|
11
11
|
|
|
12
12
|
const baseUrl = "https://gchq.github.io/CyberChef/";
|
|
13
13
|
|
|
14
|
-
const smStream = new
|
|
14
|
+
const smStream = new SitemapStream({});
|
|
15
15
|
|
|
16
16
|
smStream.write({
|
|
17
17
|
url: baseUrl,
|
|
@@ -28,6 +28,6 @@ for (const op in OperationConfig) {
|
|
|
28
28
|
}
|
|
29
29
|
smStream.end();
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
streamToPromise(smStream).then(
|
|
32
32
|
(buffer) => console.log(buffer.toString()), // eslint-disable-line no-console
|
|
33
33
|
);
|
|
@@ -487,11 +487,19 @@ class RecipeWaiter {
|
|
|
487
487
|
* @param {HTMLElement} op
|
|
488
488
|
*/
|
|
489
489
|
triggerArgEvents(op) {
|
|
490
|
-
// Trigger
|
|
490
|
+
// Trigger argSelector events and populateOption events only where the target is empty.
|
|
491
|
+
// When loading a saved recipe, arguments are populated before this method is called, so
|
|
492
|
+
// re-triggering populateOption events would overwrite saved custom values with defaults.
|
|
493
|
+
const args = op.querySelectorAll(".arg");
|
|
491
494
|
const triggerableOptions = op.querySelectorAll(".populate-option, .arg-selector");
|
|
492
495
|
const evt = new Event("change", {bubbles: true});
|
|
496
|
+
|
|
493
497
|
if (triggerableOptions.length) {
|
|
494
498
|
for (const el of triggerableOptions) {
|
|
499
|
+
if (el.classList.contains("populate-option")) {
|
|
500
|
+
const target = args[el.getAttribute("data-target")];
|
|
501
|
+
if (target && target.value !== "") continue;
|
|
502
|
+
}
|
|
495
503
|
el.dispatchEvent(evt);
|
|
496
504
|
}
|
|
497
505
|
}
|
package/tests/browser/02_ops.js
CHANGED
|
@@ -30,8 +30,8 @@ module.exports = {
|
|
|
30
30
|
testOp(browser, "A1Z26 Cipher Decode", "20 5 19 20 15 21 20 16 21 20", "testoutput");
|
|
31
31
|
testOp(browser, "A1Z26 Cipher Encode", "test input", "20 5 19 20 9 14 16 21 20");
|
|
32
32
|
testOp(browser, "ADD", "test input", "Ê»ÉÊv¿ÄÆËÊ", [{ "option": "Hex", "string": "56" }]);
|
|
33
|
-
testOp(browser, "AES Decrypt", "b443f7f7c16ac5396a34273f6f639caa", "test output", [{ "option": "Hex", "string": "00112233445566778899aabbccddeeff" }, { "option": "Hex", "string": "00000000000000000000000000000000" }, "CBC", "Hex", "Raw", { "option": "Hex", "string": "" }]);
|
|
34
|
-
testOp(browser, "AES Encrypt", "test input", "e42eb8fbfb7a98fff061cd2c1a794d92", [{"option": "Hex", "string": "00112233445566778899aabbccddeeff"}, {"option": "Hex", "string": "00000000000000000000000000000000"}, "CBC", "Raw", "Hex"]);
|
|
33
|
+
testOp(browser, "AES Decrypt", "b443f7f7c16ac5396a34273f6f639caa", "test output", [{ "option": "Hex", "string": "00112233445566778899aabbccddeeff" }, { "option": "Hex", "string": "00000000000000000000000000000000" }, 16, "CBC", "Hex", "Raw", { "option": "Hex", "string": "" }, { "option": "Hex", "string": "" }, "Off"]);
|
|
34
|
+
testOp(browser, "AES Encrypt", "test input", "e42eb8fbfb7a98fff061cd2c1a794d92", [{"option": "Hex", "string": "00112233445566778899aabbccddeeff"}, {"option": "Hex", "string": "00000000000000000000000000000000"}, "CBC", "Raw", "Hex", "Off"]);
|
|
35
35
|
testOp(browser, "AND", "test input", "4$04 $044", [{ "option": "Hex", "string": "34" }]);
|
|
36
36
|
testOp(browser, "Add line numbers", "test input", "1 test input");
|
|
37
37
|
testOp(browser, ["From Hex", "Add Text To Image", "SHA2"], Images.PNG_HEX, "50cdf8ea483c55564a091650c2bccb4586f919b721e5fe9d6a61660505b4346d6ebdb2ef0cf075a7728cd84cb26ea3e477b5bd86a94a49a27d79423994afb60a", [[], ["Chef", "Center", "Middle", 0, 0, 16, "Roboto"], []]);
|
|
@@ -64,7 +64,7 @@ module.exports = {
|
|
|
64
64
|
testOp(browser, "BSON serialise", '{"a":"test"}', "\u0011\u0000\u0000\u0000\u0002a\u0000\u0005\u0000\u0000\u0000test\u0000\u0000");
|
|
65
65
|
// testOp(browser, "Bacon Cipher Decode", "test input", "test_output");
|
|
66
66
|
// testOp(browser, "Bacon Cipher Encode", "test input", "test_output");
|
|
67
|
-
testOp(browser, "Bcrypt", "test input", /^\$
|
|
67
|
+
testOp(browser, "Bcrypt", "test input", /^\$2b\$06\$.{53}$/, [6]);
|
|
68
68
|
testOp(browser, "Bcrypt compare", "test input", "Match: test input", ["$2a$05$FCfBSVX7OeRkK.9kQVFCiOYu9XtwtIbePqUiroD1lkASW9q5QClzG"]);
|
|
69
69
|
testOp(browser, "Bcrypt parse", "$2a$05$kXWtAIGB/R8VEzInoM5ocOTBtyc0m2YTIwFiBU/0XoW032f9QrkWW", /Rounds: 5/);
|
|
70
70
|
testOp(browser, "Bifid Cipher Decode", "qblb tfovy", "test input", ["pass"]);
|
|
@@ -80,8 +80,8 @@ module.exports = {
|
|
|
80
80
|
testOpHtml(browser, "Bombe", "XTSYN WAEUG EZALY NRQIM AMLZX MFUOD AWXLY LZCUZ QOQBQ JLCPK NDDRW F", "table tr:last-child td:first-child", "ECG", ["3-rotor", "LEYJVCNIXWPBQMDRTAKZGFUHOS", "BDFHJLCPRTXVZNYEIWGAKMUSQO<W", "AJDKSIRUXBLHWTMCQGZNPYFVOE<F", "ESOVPZJAYQUIRHXLNFTGKDCMWB<K", "AY BR CU DH EQ FS GL IP JX KN MO TZ VW", "HELLO CYBER CHEFU SER", 0, true]);
|
|
81
81
|
testOp(browser, ["Bzip2 Compress", "To Hex"], "test input", "42 5a 68 39 31 41 59 26 53 59 cf 96 82 1d 00 00 03 91 80 40 00 02 21 4e 00 20 00 21 90 c2 10 c0 88 33 92 8e df 17 72 45 38 50 90 cf 96 82 1d");
|
|
82
82
|
testOp(browser, ["From Hex", "Bzip2 Decompress"], "425a68393141592653597b0884b7000003038000008200ce00200021a647a4218013709517c5dc914e14241ec2212dc0", "test_output", [[], [true]]);
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
testOp(browser, ["From Hex", "CBOR Decode"], "f9 3e 00", "1.5");
|
|
84
|
+
testOp(browser, ["CBOR Encode", "To Hex"], "1.5", "f9 3e 00");
|
|
85
85
|
testOp(browser, "CRC Checksum", "test input", "77c7", ["CRC-16"]);
|
|
86
86
|
testOp(browser, "CRC Checksum", "test input", "29822bc8", ["CRC-32"]);
|
|
87
87
|
testOp(browser, "CRC Checksum", "test input", "9d", ["CRC-8"]);
|
|
@@ -278,11 +278,11 @@ module.exports = {
|
|
|
278
278
|
testOpHtml(browser, "Parse UDP", "04 89 00 35 00 2c 01 01", "tr:last-child td:last-child", "0x0101");
|
|
279
279
|
// testOp(browser, "Parse UNIX file permissions", "test input", "test_output");
|
|
280
280
|
// testOp(browser, "Parse URI", "test input", "test_output");
|
|
281
|
-
|
|
281
|
+
testOp(browser, "Parse User Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 ", /Architecture: amd64/);
|
|
282
282
|
// testOp(browser, "Parse X.509 certificate", "test input", "test_output");
|
|
283
283
|
testOpFile(browser, "Play Media", "files/mp3example.mp3", "audio", "");
|
|
284
284
|
// testOp(browser, "Power Set", "test input", "test_output");
|
|
285
|
-
|
|
285
|
+
testOp(browser, ["From Hex", "Protobuf Decode"], "0d1c0000001203596f751a024d65202b2a0a0a066162633132331200", /"1": "abc123"/, [[], ["", false, false]]);
|
|
286
286
|
// testOp(browser, "Pseudo-Random Number Generator", "test input", "test_output");
|
|
287
287
|
// testOp(browser, "RC2 Decrypt", "test input", "test_output");
|
|
288
288
|
// testOp(browser, "RC2 Encrypt", "test input", "test_output");
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for recipe loading behaviour.
|
|
3
|
+
*
|
|
4
|
+
* @author C85297 [95289555+C85297@users.noreply.github.com]
|
|
5
|
+
* @copyright Crown Copyright
|
|
6
|
+
* @license Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const utils = require("./browserUtils.js");
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
before: browser => {
|
|
13
|
+
browser
|
|
14
|
+
.resizeWindow(1280, 800)
|
|
15
|
+
.url(browser.launchUrl)
|
|
16
|
+
.useCss()
|
|
17
|
+
.waitForElementNotPresent("#preloader", 10000);
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
"Recipe load preserves populated arguments": browser => {
|
|
21
|
+
const inputFormat = "HH:mm:ss a MMM DD, YYYY ";
|
|
22
|
+
const input = "10:20:30 pm Sep 26, 2019 ";
|
|
23
|
+
|
|
24
|
+
utils.loadRecipe(
|
|
25
|
+
browser,
|
|
26
|
+
"Translate DateTime Format",
|
|
27
|
+
input,
|
|
28
|
+
[
|
|
29
|
+
"Standard date and time",
|
|
30
|
+
inputFormat,
|
|
31
|
+
"UTC",
|
|
32
|
+
"DD/MM/YYYY HH:mm:ss",
|
|
33
|
+
"UTC"
|
|
34
|
+
]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
browser.execute(() => {
|
|
38
|
+
return Array.from(document.querySelectorAll("#rec-list li.operation .arg"))
|
|
39
|
+
.map(arg => arg.value);
|
|
40
|
+
}, [], function({value}) {
|
|
41
|
+
browser.expect(value[1]).to.equal(inputFormat);
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
after: browser => {
|
|
46
|
+
browser.end();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -51,14 +51,17 @@ function setInput(browser, input, type=true) {
|
|
|
51
51
|
*/
|
|
52
52
|
function bake(browser) {
|
|
53
53
|
browser
|
|
54
|
+
// Let any pending debounced inputChange/stateChange (~20ms each) fire so the
|
|
55
|
+
// worker has the latest input buffer before we ask it to bake.
|
|
56
|
+
.pause(50)
|
|
54
57
|
// Ensure we're not currently busy
|
|
55
|
-
.waitForElementNotVisible("#output-loader",
|
|
58
|
+
.waitForElementNotVisible("#output-loader", 10000)
|
|
56
59
|
.expect.element("#bake span").text.to.equal("BAKE!");
|
|
57
60
|
|
|
58
61
|
browser
|
|
59
62
|
.click("#bake")
|
|
60
|
-
.waitForElementNotVisible("#stale-indicator",
|
|
61
|
-
.waitForElementNotVisible("#output-loader",
|
|
63
|
+
.waitForElementNotVisible("#stale-indicator", 10000)
|
|
64
|
+
.waitForElementNotVisible("#output-loader", 10000);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/** @function
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polyfill for Node.js 22+ where globalThis.fetch is built-in but rejects
|
|
3
|
+
* bare filesystem paths. WASM libraries like argon2-browser call fetch() with
|
|
4
|
+
* an absolute path (e.g. "/path/to/argon2.wasm") expecting a browser-style
|
|
5
|
+
* fallback, but Node.js 22's fetch throws synchronously for non-URL strings.
|
|
6
|
+
*
|
|
7
|
+
* This wrapper intercepts such calls and serves the file via Node's fs module,
|
|
8
|
+
* returning a synthetic Response so the WASM module loads correctly.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFile } from "fs/promises";
|
|
12
|
+
|
|
13
|
+
if (globalThis.fetch) {
|
|
14
|
+
const originalFetch = globalThis.fetch;
|
|
15
|
+
globalThis.fetch = async function patchedFetch(url, options) {
|
|
16
|
+
const urlStr = typeof url === "string" ?
|
|
17
|
+
url :
|
|
18
|
+
url instanceof URL ?
|
|
19
|
+
url.href :
|
|
20
|
+
String(url);
|
|
21
|
+
// Intercept bare filesystem paths (absolute POSIX or Windows)
|
|
22
|
+
if (urlStr.startsWith("/") || /^[A-Za-z]:[/\\]/.test(urlStr)) {
|
|
23
|
+
const buffer = await readFile(urlStr);
|
|
24
|
+
return new Response(buffer, {
|
|
25
|
+
status: 200,
|
|
26
|
+
headers: { "Content-Type": "application/wasm" },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return originalFetch(url, options);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -9,7 +9,7 @@ import assert from "assert";
|
|
|
9
9
|
import chef from "cyberchef";
|
|
10
10
|
import { bake, toHex, reverse, unique, multiply } from "cyberchef";
|
|
11
11
|
|
|
12
|
-
const a = bake("Testing, 1 2 3", [
|
|
12
|
+
const a = await bake("Testing, 1 2 3", [
|
|
13
13
|
toHex,
|
|
14
14
|
reverse,
|
|
15
15
|
{
|
|
@@ -28,7 +28,7 @@ const a = bake("Testing, 1 2 3", [
|
|
|
28
28
|
|
|
29
29
|
assert.equal(a.value, "630957449041920");
|
|
30
30
|
|
|
31
|
-
const b = chef.bake("Testing, 1 2 3", [
|
|
31
|
+
const b = await chef.bake("Testing, 1 2 3", [
|
|
32
32
|
chef.toHex,
|
|
33
33
|
chef.reverse,
|
|
34
34
|
{
|
package/tests/node/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import TestRegister from "../../lib/TestRegister.mjs";
|
|
2
|
-
import Categories from "../../../src/core/config/Categories.json"
|
|
3
|
-
import OperationConfig from "../../../src/core/config/OperationConfig.json"
|
|
2
|
+
import Categories from "../../../src/core/config/Categories.json" with { type: "json" };
|
|
3
|
+
import OperationConfig from "../../../src/core/config/OperationConfig.json" with { type: "json" };
|
|
4
4
|
import it from "../assertionHandler.mjs";
|
|
5
5
|
import assert from "assert";
|
|
6
6
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PGP node tests.
|
|
3
|
+
*
|
|
4
|
+
* @author C85297 [95289555+C85297@users.noreply.github.com]
|
|
5
|
+
* @copyright Crown Copyright 2026
|
|
6
|
+
* @license Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import assert from "assert";
|
|
10
|
+
import kbpgp from "kbpgp";
|
|
11
|
+
import * as es6promisify from "es6-promisify";
|
|
12
|
+
|
|
13
|
+
import TestRegister from "../../lib/TestRegister.mjs";
|
|
14
|
+
import it from "../assertionHandler.mjs";
|
|
15
|
+
import GeneratePGPKeyPair from "../../../src/core/operations/GeneratePGPKeyPair.mjs";
|
|
16
|
+
|
|
17
|
+
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
|
|
18
|
+
|
|
19
|
+
const PUBLIC_KEY_BLOCK = /-----BEGIN PGP PUBLIC KEY BLOCK-----[\s\S]*-----END PGP PUBLIC KEY BLOCK-----/;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate a PGP key pair and import the generated public key.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} keyType
|
|
25
|
+
* @returns {Promise<Object>}
|
|
26
|
+
*/
|
|
27
|
+
async function generateAndImportPublicKey(keyType) {
|
|
28
|
+
const operation = new GeneratePGPKeyPair();
|
|
29
|
+
const generatedKeyPair = await operation.run("", [
|
|
30
|
+
keyType,
|
|
31
|
+
"",
|
|
32
|
+
"User",
|
|
33
|
+
"akb@notreal.gchq.gov.uk"
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const publicKey = generatedKeyPair.match(PUBLIC_KEY_BLOCK);
|
|
37
|
+
|
|
38
|
+
assert(publicKey, "Generated key pair should contain an ASCII-armoured public key");
|
|
39
|
+
|
|
40
|
+
return promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
|
41
|
+
armored: publicKey[0],
|
|
42
|
+
opts: {
|
|
43
|
+
"no_check_keys": true
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
TestRegister.addApiTests([
|
|
49
|
+
it("Generate PGP Key Pair: ECC keys should include ECDSA signing and ECDH encryption subkeys", async () => {
|
|
50
|
+
const publicKey = await generateAndImportPublicKey("ECC-256");
|
|
51
|
+
const subkeyAlgorithms = publicKey.subkeys.map(subkey => subkey.key.type);
|
|
52
|
+
|
|
53
|
+
assert.strictEqual(
|
|
54
|
+
publicKey.primary.key.type,
|
|
55
|
+
kbpgp.const.openpgp.public_key_algorithms.ECDSA,
|
|
56
|
+
"Generated ECC PGP primary key should use ECDSA"
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
assert(
|
|
60
|
+
subkeyAlgorithms.includes(kbpgp.const.openpgp.public_key_algorithms.ECDSA),
|
|
61
|
+
"Generated ECC PGP key should include an ECDSA signing subkey"
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
assert(
|
|
65
|
+
subkeyAlgorithms.includes(kbpgp.const.openpgp.public_key_algorithms.ECDH),
|
|
66
|
+
"Generated ECC PGP key should include an ECDH encryption subkey"
|
|
67
|
+
);
|
|
68
|
+
})
|
|
69
|
+
]);
|
|
@@ -170,77 +170,77 @@ TestRegister.addApiTests([
|
|
|
170
170
|
assert(chef.bake);
|
|
171
171
|
}),
|
|
172
172
|
|
|
173
|
-
it("chef.bake: should return NodeDish", () => {
|
|
174
|
-
const result = chef.bake("input", "to base 64");
|
|
173
|
+
it("chef.bake: should return NodeDish", async () => {
|
|
174
|
+
const result = await chef.bake("input", "to base 64");
|
|
175
175
|
assert(result instanceof NodeDish);
|
|
176
176
|
}),
|
|
177
177
|
|
|
178
|
-
it("chef.bake: should take an input and an op name and perform it", () => {
|
|
179
|
-
const result = chef.bake("some input", "to base 32");
|
|
178
|
+
it("chef.bake: should take an input and an op name and perform it", async () => {
|
|
179
|
+
const result = await chef.bake("some input", "to base 32");
|
|
180
180
|
assert.strictEqual(result.toString(), "ONXW2ZJANFXHA5LU");
|
|
181
181
|
}),
|
|
182
182
|
|
|
183
|
-
it("chef.bake: should complain if recipe isnt a valid object", () => {
|
|
184
|
-
assert.
|
|
183
|
+
it("chef.bake: should complain if recipe isnt a valid object", async () => {
|
|
184
|
+
await assert.rejects(() => chef.bake("some input", 3264), {
|
|
185
185
|
name: "TypeError",
|
|
186
186
|
message: "Recipe can only contain function names or functions"
|
|
187
187
|
});
|
|
188
188
|
}),
|
|
189
189
|
|
|
190
|
-
it("chef.bake: Should complain if string op is invalid", () => {
|
|
191
|
-
assert.
|
|
190
|
+
it("chef.bake: Should complain if string op is invalid", async () => {
|
|
191
|
+
await assert.rejects(() => chef.bake("some input", "not a valid operation"), {
|
|
192
192
|
name: "TypeError",
|
|
193
193
|
message: "Couldn't find an operation with name 'not a valid operation'."
|
|
194
194
|
});
|
|
195
195
|
}),
|
|
196
196
|
|
|
197
|
-
it("chef.bake: Should take an input and an operation and perform it", () => {
|
|
198
|
-
const result = chef.bake("https://google.com/search?q=help", chef.parseURI);
|
|
197
|
+
it("chef.bake: Should take an input and an operation and perform it", async () => {
|
|
198
|
+
const result = await chef.bake("https://google.com/search?q=help", chef.parseURI);
|
|
199
199
|
assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = help\n");
|
|
200
200
|
}),
|
|
201
201
|
|
|
202
|
-
it("chef.bake: Should complain if an invalid operation is inputted", () => {
|
|
203
|
-
assert.
|
|
202
|
+
it("chef.bake: Should complain if an invalid operation is inputted", async () => {
|
|
203
|
+
await assert.rejects(() => chef.bake("https://google.com/search?q=help", () => {}), {
|
|
204
204
|
name: "TypeError",
|
|
205
205
|
message: "Inputted function not a Chef operation."
|
|
206
206
|
});
|
|
207
207
|
}),
|
|
208
208
|
|
|
209
|
-
it("chef.bake: accepts an array of operation names and performs them all in order", () => {
|
|
210
|
-
const result = chef.bake("https://google.com/search?q=that's a complicated question", ["URL encode", "URL decode", "Parse URI"]);
|
|
209
|
+
it("chef.bake: accepts an array of operation names and performs them all in order", async () => {
|
|
210
|
+
const result = await chef.bake("https://google.com/search?q=that's a complicated question", ["URL encode", "URL decode", "Parse URI"]);
|
|
211
211
|
assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
|
|
212
212
|
}),
|
|
213
213
|
|
|
214
|
-
it("chef.bake: forgiving with operation names", () =>{
|
|
215
|
-
const result = chef.bake("https://google.com/search?q=that's a complicated question", ["urlencode", "url decode", "parseURI"]);
|
|
214
|
+
it("chef.bake: forgiving with operation names", async () =>{
|
|
215
|
+
const result = await chef.bake("https://google.com/search?q=that's a complicated question", ["urlencode", "url decode", "parseURI"]);
|
|
216
216
|
assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
|
|
217
217
|
}),
|
|
218
218
|
|
|
219
|
-
it("chef.bake: forgiving with operation names", () =>{
|
|
220
|
-
const result = chef.bake("hello", ["to base 64"]);
|
|
219
|
+
it("chef.bake: forgiving with operation names", async () =>{
|
|
220
|
+
const result = await chef.bake("hello", ["to base 64"]);
|
|
221
221
|
assert.strictEqual(result.toString(), "aGVsbG8=");
|
|
222
222
|
}),
|
|
223
223
|
|
|
224
|
-
it("chef.bake: if recipe is empty array, return input as dish", () => {
|
|
225
|
-
const result = chef.bake("some input", []);
|
|
224
|
+
it("chef.bake: if recipe is empty array, return input as dish", async () => {
|
|
225
|
+
const result = await chef.bake("some input", []);
|
|
226
226
|
assert.strictEqual(result.toString(), "some input");
|
|
227
227
|
assert(result instanceof NodeDish, "Result is not instance of NodeDish");
|
|
228
228
|
}),
|
|
229
229
|
|
|
230
|
-
it("chef.bake: accepts an array of operations as recipe", () => {
|
|
231
|
-
const result = chef.bake("https://google.com/search?q=that's a complicated question", [chef.URLEncode, chef.URLDecode, chef.parseURI]);
|
|
230
|
+
it("chef.bake: accepts an array of operations as recipe", async () => {
|
|
231
|
+
const result = await chef.bake("https://google.com/search?q=that's a complicated question", [chef.URLEncode, chef.URLDecode, chef.parseURI]);
|
|
232
232
|
assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n");
|
|
233
233
|
}),
|
|
234
234
|
|
|
235
|
-
it("should complain if an invalid operation is inputted as part of array", () => {
|
|
236
|
-
assert.
|
|
235
|
+
it("should complain if an invalid operation is inputted as part of array", async () => {
|
|
236
|
+
await assert.rejects(() => chef.bake("something", [() => {}]), {
|
|
237
237
|
name: "TypeError",
|
|
238
238
|
message: "Inputted function not a Chef operation."
|
|
239
239
|
});
|
|
240
240
|
}),
|
|
241
241
|
|
|
242
|
-
it("chef.bake: should take single JSON object describing op and args OBJ", () => {
|
|
243
|
-
const result = chef.bake("some input", {
|
|
242
|
+
it("chef.bake: should take single JSON object describing op and args OBJ", async () => {
|
|
243
|
+
const result = await chef.bake("some input", {
|
|
244
244
|
op: chef.toHex,
|
|
245
245
|
args: {
|
|
246
246
|
Delimiter: "Colon"
|
|
@@ -249,23 +249,23 @@ TestRegister.addApiTests([
|
|
|
249
249
|
assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74");
|
|
250
250
|
}),
|
|
251
251
|
|
|
252
|
-
it("chef.bake: should take single JSON object desribing op with optional args", () => {
|
|
253
|
-
const result = chef.bake("some input", {
|
|
252
|
+
it("chef.bake: should take single JSON object desribing op with optional args", async () => {
|
|
253
|
+
const result = await chef.bake("some input", {
|
|
254
254
|
op: chef.toHex,
|
|
255
255
|
});
|
|
256
256
|
assert.strictEqual(result.toString(), "73 6f 6d 65 20 69 6e 70 75 74");
|
|
257
257
|
}),
|
|
258
258
|
|
|
259
|
-
it("chef.bake: should take single JSON object describing op and args ARRAY", () => {
|
|
260
|
-
const result = chef.bake("some input", {
|
|
259
|
+
it("chef.bake: should take single JSON object describing op and args ARRAY", async () => {
|
|
260
|
+
const result = await chef.bake("some input", {
|
|
261
261
|
op: chef.toHex,
|
|
262
262
|
args: ["Colon"]
|
|
263
263
|
});
|
|
264
264
|
assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74");
|
|
265
265
|
}),
|
|
266
266
|
|
|
267
|
-
it("chef.bake: should error if op in JSON is not chef op", () => {
|
|
268
|
-
assert.
|
|
267
|
+
it("chef.bake: should error if op in JSON is not chef op", async () => {
|
|
268
|
+
await assert.rejects(() => chef.bake("some input", {
|
|
269
269
|
op: () => {},
|
|
270
270
|
args: ["Colon"],
|
|
271
271
|
}), {
|
|
@@ -274,8 +274,8 @@ TestRegister.addApiTests([
|
|
|
274
274
|
});
|
|
275
275
|
}),
|
|
276
276
|
|
|
277
|
-
it("chef.bake: should take multiple ops in JSON object form, some ops by string", () => {
|
|
278
|
-
const result = chef.bake("some input", [
|
|
277
|
+
it("chef.bake: should take multiple ops in JSON object form, some ops by string", async () => {
|
|
278
|
+
const result = await chef.bake("some input", [
|
|
279
279
|
{
|
|
280
280
|
op: chef.toHex,
|
|
281
281
|
args: ["Colon"]
|
|
@@ -290,8 +290,8 @@ TestRegister.addApiTests([
|
|
|
290
290
|
assert.strictEqual(result.toString(), "67;63;72;66;146;72;66;144;72;66;65;72;62;60;72;66;71;72;66;145;72;67;60;72;67;65;72;67;64");
|
|
291
291
|
}),
|
|
292
292
|
|
|
293
|
-
it("chef.bake: should take multiple ops in JSON object form, some without args", () => {
|
|
294
|
-
const result = chef.bake("some input", [
|
|
293
|
+
it("chef.bake: should take multiple ops in JSON object form, some without args", async () => {
|
|
294
|
+
const result = await chef.bake("some input", [
|
|
295
295
|
{
|
|
296
296
|
op: chef.toHex,
|
|
297
297
|
},
|
|
@@ -305,8 +305,8 @@ TestRegister.addApiTests([
|
|
|
305
305
|
assert.strictEqual(result.toString(), "67;63;40;66;146;40;66;144;40;66;65;40;62;60;40;66;71;40;66;145;40;67;60;40;67;65;40;67;64");
|
|
306
306
|
}),
|
|
307
307
|
|
|
308
|
-
it("chef.bake: should handle op with multiple args", () => {
|
|
309
|
-
const result = chef.bake("some input", {
|
|
308
|
+
it("chef.bake: should handle op with multiple args", async () => {
|
|
309
|
+
const result = await chef.bake("some input", {
|
|
310
310
|
op: "to morse code",
|
|
311
311
|
args: {
|
|
312
312
|
formatOptions: "Dash/Dot",
|
|
@@ -317,13 +317,13 @@ TestRegister.addApiTests([
|
|
|
317
317
|
assert.strictEqual(result.toString(), "DotDotDot\\DashDashDash\\DashDash\\Dot,DotDot\\DashDot\\DotDashDashDot\\DotDotDash\\Dash");
|
|
318
318
|
}),
|
|
319
319
|
|
|
320
|
-
it("chef.bake: should take compact JSON format from Chef Website as recipe", () => {
|
|
321
|
-
const result = chef.bake("some input", [{"op": "To Morse Code", "args": ["Dash/Dot", "Backslash", "Comma"]}, {"op": "Hex to PEM", "args": ["SOMETHING"]}, {"op": "To Snake case", "args": [false]}]);
|
|
320
|
+
it("chef.bake: should take compact JSON format from Chef Website as recipe", async () => {
|
|
321
|
+
const result = await chef.bake("some input", [{"op": "To Morse Code", "args": ["Dash/Dot", "Backslash", "Comma"]}, {"op": "Hex to PEM", "args": ["SOMETHING"]}, {"op": "To Snake case", "args": [false]}]);
|
|
322
322
|
assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something");
|
|
323
323
|
}),
|
|
324
324
|
|
|
325
|
-
it("chef.bake: should accept Clean JSON format from Chef website as recipe", () => {
|
|
326
|
-
const result = chef.bake("some input", [
|
|
325
|
+
it("chef.bake: should accept Clean JSON format from Chef website as recipe", async () => {
|
|
326
|
+
const result = await chef.bake("some input", [
|
|
327
327
|
{ "op": "To Morse Code",
|
|
328
328
|
"args": ["Dash/Dot", "Backslash", "Comma"] },
|
|
329
329
|
{ "op": "Hex to PEM",
|
|
@@ -334,8 +334,8 @@ TestRegister.addApiTests([
|
|
|
334
334
|
assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something");
|
|
335
335
|
}),
|
|
336
336
|
|
|
337
|
-
it("chef.bake: should accept Clean JSON format from Chef website - args optional", () => {
|
|
338
|
-
const result = chef.bake("some input", [
|
|
337
|
+
it("chef.bake: should accept Clean JSON format from Chef website - args optional", async () => {
|
|
338
|
+
const result = await chef.bake("some input", [
|
|
339
339
|
{ "op": "To Morse Code" },
|
|
340
340
|
{ "op": "Hex to PEM",
|
|
341
341
|
"args": ["SOMETHING"] },
|
|
@@ -345,31 +345,28 @@ TestRegister.addApiTests([
|
|
|
345
345
|
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
|
|
346
346
|
}),
|
|
347
347
|
|
|
348
|
-
it("chef.bake: should accept operation names from Chef Website which contain forward slash", () => {
|
|
349
|
-
const result = chef.bake("I'll have the test salmon", [
|
|
348
|
+
it("chef.bake: should accept operation names from Chef Website which contain forward slash", async () => {
|
|
349
|
+
const result = await chef.bake("I'll have the test salmon", [
|
|
350
350
|
{ "op": "Find / Replace",
|
|
351
351
|
"args": [{ "option": "Regex", "string": "test" }, "good", true, false, true, false]}
|
|
352
352
|
]);
|
|
353
353
|
assert.strictEqual(result.toString(), "I'll have the good salmon");
|
|
354
354
|
}),
|
|
355
355
|
|
|
356
|
-
it("chef.bake: should accept operation names from Chef Website which contain a hyphen", () => {
|
|
357
|
-
const result = chef.bake("I'll have the test salmon", [
|
|
356
|
+
it("chef.bake: should accept operation names from Chef Website which contain a hyphen", async () => {
|
|
357
|
+
const result = await chef.bake("I'll have the test salmon", [
|
|
358
358
|
{ "op": "Adler-32 Checksum",
|
|
359
359
|
"args": [] }
|
|
360
360
|
]);
|
|
361
361
|
assert.strictEqual(result.toString(), "6e4208f8");
|
|
362
362
|
}),
|
|
363
363
|
|
|
364
|
-
it("chef.bake: should accept operation names from Chef Website which contain a period", () => {
|
|
365
|
-
const result = chef.bake("30 13 02 01 05 16 0e 41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f", [
|
|
364
|
+
it("chef.bake: should accept operation names from Chef Website which contain a period", async () => {
|
|
365
|
+
const result = await chef.bake("30 13 02 01 05 16 0e 41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f", [
|
|
366
366
|
{ "op": "Parse ASN.1 hex string",
|
|
367
367
|
"args": [0, 32] }
|
|
368
368
|
]);
|
|
369
|
-
assert.strictEqual(result.toString(), `SEQUENCE
|
|
370
|
-
INTEGER 05
|
|
371
|
-
IA5String 'Anybody there?'
|
|
372
|
-
`);
|
|
369
|
+
assert.strictEqual(result.toString(), `SEQUENCE\n INTEGER 05\n IA5String 'Anybody there?'\n`);
|
|
373
370
|
}),
|
|
374
371
|
|
|
375
372
|
it("Excluded operations: throw a sensible error when you try and call one", () => {
|
|
@@ -381,16 +378,16 @@ TestRegister.addApiTests([
|
|
|
381
378
|
}
|
|
382
379
|
}),
|
|
383
380
|
|
|
384
|
-
it("chef.bake: cannot accept flowControl operations in recipe", () => {
|
|
385
|
-
assert.
|
|
381
|
+
it("chef.bake: cannot accept flowControl operations in recipe", async () => {
|
|
382
|
+
await assert.rejects(() => chef.bake("some input", "magic"), {
|
|
386
383
|
name: "TypeError",
|
|
387
384
|
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
|
|
388
385
|
});
|
|
389
|
-
assert.
|
|
386
|
+
await assert.rejects(() => chef.bake("some input", magic), {
|
|
390
387
|
name: "TypeError",
|
|
391
388
|
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
|
|
392
389
|
});
|
|
393
|
-
assert.
|
|
390
|
+
await assert.rejects(() => chef.bake("some input", ["to base 64", "magic"]), {
|
|
394
391
|
name: "TypeError",
|
|
395
392
|
message: "flowControl operations like Magic are not currently allowed in recipes for chef.bake in the Node API"
|
|
396
393
|
});
|
|
@@ -79,7 +79,46 @@ TestRegister.addApiTests([
|
|
|
79
79
|
string: "some iv some iv1",
|
|
80
80
|
option: "utf8",
|
|
81
81
|
},
|
|
82
|
+
ivLength: 16,
|
|
82
83
|
mode: "OFB",
|
|
84
|
+
inputType: "Hex",
|
|
85
|
+
outputType: "Raw",
|
|
86
|
+
gcmTag: {
|
|
87
|
+
option: "Hex",
|
|
88
|
+
string: ""
|
|
89
|
+
},
|
|
90
|
+
aad: {
|
|
91
|
+
option: "Hex",
|
|
92
|
+
string: ""
|
|
93
|
+
},
|
|
94
|
+
ivFromInput: "Off"
|
|
95
|
+
});
|
|
96
|
+
assert.equal(result.toString(), "a slightly longer sampleinput?");
|
|
97
|
+
}),
|
|
98
|
+
|
|
99
|
+
it("AES decrypt: IV from input", () => {
|
|
100
|
+
const result = AESDecrypt("4a123af235a507bbc9d5871721d61b98504d569a9a5a7847e2d78315fec7736f6d6520697620736f6d6520697631", {
|
|
101
|
+
key: {
|
|
102
|
+
string: "some longer key1",
|
|
103
|
+
option: "utf8",
|
|
104
|
+
},
|
|
105
|
+
iv: {
|
|
106
|
+
string: "",
|
|
107
|
+
option: "Hex",
|
|
108
|
+
},
|
|
109
|
+
ivLength: 16,
|
|
110
|
+
mode: "OFB",
|
|
111
|
+
inputType: "Hex",
|
|
112
|
+
outputType: "Raw",
|
|
113
|
+
gcmTag: {
|
|
114
|
+
option: "Hex",
|
|
115
|
+
string: ""
|
|
116
|
+
},
|
|
117
|
+
aad: {
|
|
118
|
+
option: "Hex",
|
|
119
|
+
string: ""
|
|
120
|
+
},
|
|
121
|
+
ivFromInput: "From end"
|
|
83
122
|
});
|
|
84
123
|
assert.equal(result.toString(), "a slightly longer sampleinput?");
|
|
85
124
|
}),
|
|
@@ -136,8 +175,8 @@ Tiger-128`;
|
|
|
136
175
|
it("Bcrypt", async () => {
|
|
137
176
|
const result = await chef.bcrypt("Put a Sock In It");
|
|
138
177
|
const strResult = result.toString();
|
|
139
|
-
assert.
|
|
140
|
-
assert.equal(strResult.
|
|
178
|
+
assert.match(strResult, /^\$2b\$10\$[./A-Za-z0-9]{53}$/);
|
|
179
|
+
assert.equal(strResult.split("$").length, 4);
|
|
141
180
|
}),
|
|
142
181
|
|
|
143
182
|
it("bcryptCompare", async() => {
|