Merge branch 'master' into linuxgemini-patch-modhex

This commit is contained in:
a3957273 2025-02-16 01:55:11 +00:00 committed by GitHub
commit eb912547ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 12005 additions and 3586 deletions

View file

@ -19,6 +19,7 @@ jobs:
- name: Install - name: Install
run: | run: |
export DETECT_CHROMEDRIVER_VERSION=true
npm install npm install
npm run setheapsize npm run setheapsize

View file

@ -18,6 +18,7 @@ jobs:
- name: Install - name: Install
run: | run: |
export DETECT_CHROMEDRIVER_VERSION=true
npm install npm install
npm run setheapsize npm run setheapsize

View file

@ -13,6 +13,10 @@ All major and minor version changes will be documented in this file. Details of
## Details ## Details
### [10.19.0] - 2024-06-21
- Add support for ECDSA and DSA in 'Parse CSR' [@robinsandhu] | [#1828]
- Fix typos in SIGABA.mjs [@eltociear] | [#1834]
### [10.18.0] - 2024-04-24 ### [10.18.0] - 2024-04-24
- Added 'XXTEA Encrypt' and 'XXTEA Decrypt' operations [@n1474335] | [0a353ee] - Added 'XXTEA Encrypt' and 'XXTEA Decrypt' operations [@n1474335] | [0a353ee]
@ -436,6 +440,7 @@ All major and minor version changes will be documented in this file. Details of
## [4.0.0] - 2016-11-28 ## [4.0.0] - 2016-11-28
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306) - Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
[10.19.0]: https://github.com/gchq/CyberChef/releases/tag/v10.19.0
[10.18.0]: https://github.com/gchq/CyberChef/releases/tag/v10.18.0 [10.18.0]: https://github.com/gchq/CyberChef/releases/tag/v10.18.0
[10.17.0]: https://github.com/gchq/CyberChef/releases/tag/v10.17.0 [10.17.0]: https://github.com/gchq/CyberChef/releases/tag/v10.17.0
[10.16.0]: https://github.com/gchq/CyberChef/releases/tag/v10.16.0 [10.16.0]: https://github.com/gchq/CyberChef/releases/tag/v10.16.0
@ -623,6 +628,8 @@ All major and minor version changes will be documented in this file. Details of
[@TheZ3ro]: https://github.com/TheZ3ro [@TheZ3ro]: https://github.com/TheZ3ro
[@EvieHarv]: https://github.com/EvieHarv [@EvieHarv]: https://github.com/EvieHarv
[@cplussharp]: https://github.com/cplussharp [@cplussharp]: https://github.com/cplussharp
[@robinsandhu]: https://github.com/robinsandhu
[@eltociear]: https://github.com/eltociear
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7 [8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7

8438
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "cyberchef", "name": "cyberchef",
"version": "10.18.8", "version": "10.19.4",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>", "author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef", "homepage": "https://gchq.github.io/CyberChef",
@ -55,9 +55,10 @@
"babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-transform-builtin-extend": "1.1.2", "babel-plugin-transform-builtin-extend": "1.1.2",
"base64-loader": "^1.0.0", "base64-loader": "^1.0.0",
"chromedriver": "^125.0.3", "chromedriver": "^130.0.0",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",
"colors": "^1.4.0", "colors": "^1.4.0",
"compression-webpack-plugin": "^11.1.0",
"copy-webpack-plugin": "^12.0.2", "copy-webpack-plugin": "^12.0.2",
"core-js": "^3.37.1", "core-js": "^3.37.1",
"css-loader": "7.1.2", "css-loader": "7.1.2",
@ -159,7 +160,7 @@
"notepack.io": "^3.0.1", "notepack.io": "^3.0.1",
"ntlm": "^0.1.3", "ntlm": "^0.1.3",
"nwmatcher": "^1.4.4", "nwmatcher": "^1.4.4",
"otp": "0.1.3", "otpauth": "9.3.6",
"path": "^0.12.7", "path": "^0.12.7",
"popper.js": "^1.16.1", "popper.js": "^1.16.1",
"process": "^0.11.10", "process": "^0.11.10",

View file

@ -166,6 +166,7 @@
"name": "Public Key", "name": "Public Key",
"ops": [ "ops": [
"Parse X.509 certificate", "Parse X.509 certificate",
"Parse X.509 CRL",
"Parse ASN.1 hex string", "Parse ASN.1 hex string",
"PEM to Hex", "PEM to Hex",
"Hex to PEM", "Hex to PEM",
@ -235,9 +236,14 @@
"Parse User Agent", "Parse User Agent",
"Parse IP range", "Parse IP range",
"Parse IPv6 address", "Parse IPv6 address",
"IPv6 Transition Addresses",
"Parse IPv4 header", "Parse IPv4 header",
"Strip IPv4 header",
"Parse TCP", "Parse TCP",
"Strip TCP header",
"Parse TLS record",
"Parse UDP", "Parse UDP",
"Strip UDP header",
"Parse SSH Host Key", "Parse SSH Host Key",
"Parse URI", "Parse URI",
"URL Encode", "URL Encode",
@ -270,7 +276,8 @@
"Unicode Text Format", "Unicode Text Format",
"Remove Diacritics", "Remove Diacritics",
"Unescape Unicode Characters", "Unescape Unicode Characters",
"Convert to NATO alphabet" "Convert to NATO alphabet",
"Convert Leet Speak"
] ]
}, },
{ {
@ -322,7 +329,9 @@
"Unescape string", "Unescape string",
"Pseudo-Random Number Generator", "Pseudo-Random Number Generator",
"Sleep", "Sleep",
"File Tree" "File Tree",
"Take nth bytes",
"Drop nth bytes"
] ]
}, },
{ {

View file

@ -44,7 +44,7 @@ export function toJA4(bytes) {
the TLS version is the value of the Protocol Version. Handshake version (located at the top of the packet) the TLS version is the value of the Protocol Version. Handshake version (located at the top of the packet)
should be ignored. should be ignored.
*/ */
let version = tlsr.version.value; let version = tlsr.handshake.value.helloVersion.value;
for (const ext of tlsr.handshake.value.extensions.value) { for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value === "supported_versions") { if (ext.type.value === "supported_versions") {
version = parseHighestSupportedVersion(ext.value.data); version = parseHighestSupportedVersion(ext.value.data);
@ -189,7 +189,7 @@ export function toJA4S(bytes) {
the TLS version is the value of the Protocol Version. Handshake version (located at the top of the packet) the TLS version is the value of the Protocol Version. Handshake version (located at the top of the packet)
should be ignored. should be ignored.
*/ */
let version = tlsr.version.value; let version = tlsr.handshake.value.helloVersion.value;
for (const ext of tlsr.handshake.value.extensions.value) { for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value === "supported_versions") { if (ext.type.value === "supported_versions") {
version = parseHighestSupportedVersion(ext.value.data); version = parseHighestSupportedVersion(ext.value.data);

View file

@ -26,6 +26,9 @@ export function objToTable(obj, nested=false) {
</tr>`; </tr>`;
for (const key in obj) { for (const key in obj) {
if (typeof obj[key] === "function")
continue;
html += `<tr><td style='word-wrap: break-word'>${key}</td>`; html += `<tr><td style='word-wrap: break-word'>${key}</td>`;
if (typeof obj[key] === "object") if (typeof obj[key] === "object")
html += `<td style='padding: 0'>${objToTable(obj[key], true)}</td>`; html += `<td style='padding: 0'>${objToTable(obj[key], true)}</td>`;

View file

@ -22,7 +22,13 @@ class AddLineNumbers extends Operation {
this.description = "Adds line numbers to the output."; this.description = "Adds line numbers to the output.";
this.inputType = "string"; this.inputType = "string";
this.outputType = "string"; this.outputType = "string";
this.args = []; this.args = [
{
"name": "Offset",
"type": "number",
"value": 0
}
];
} }
/** /**
@ -33,10 +39,11 @@ class AddLineNumbers extends Operation {
run(input, args) { run(input, args) {
const lines = input.split("\n"), const lines = input.split("\n"),
width = lines.length.toString().length; width = lines.length.toString().length;
const offset = args[0] ? parseInt(args[0], 10) : 0;
let output = ""; let output = "";
for (let n = 0; n < lines.length; n++) { for (let n = 0; n < lines.length; n++) {
output += (n+1).toString().padStart(width, " ") + " " + lines[n] + "\n"; output += (n+1+offset).toString().padStart(width, " ") + " " + lines[n] + "\n";
} }
return output.slice(0, output.length-1); return output.slice(0, output.length-1);
} }

View file

@ -76,8 +76,8 @@ class BlowfishDecrypt extends Operation {
Blowfish's key length needs to be between 4 and 56 bytes (32-448 bits).`); Blowfish's key length needs to be between 4 and 56 bytes (32-448 bits).`);
} }
if (iv.length !== 8) { if (mode !== "ECB" && iv.length !== 8) {
throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes`); throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes.`);
} }
input = Utils.convertToByteString(input, inputType); input = Utils.convertToByteString(input, inputType);

View file

@ -76,8 +76,8 @@ class BlowfishEncrypt extends Operation {
Blowfish's key length needs to be between 4 and 56 bytes (32-448 bits).`); Blowfish's key length needs to be between 4 and 56 bytes (32-448 bits).`);
} }
if (iv.length !== 8) { if (mode !== "ECB" && iv.length !== 8) {
throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes`); throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes.`);
} }
input = Utils.convertToByteString(input, inputType); input = Utils.convertToByteString(input, inputType);

View file

@ -0,0 +1,113 @@
/**
* @author bartblaze []
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
/**
* Convert Leet Speak operation
*/
class ConvertLeetSpeak extends Operation {
/**
* ConvertLeetSpeak constructor
*/
constructor() {
super();
this.name = "Convert Leet Speak";
this.module = "Default";
this.description = "Converts to and from Leet Speak";
this.infoURL = "https://wikipedia.org/wiki/Leet";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Direction",
type: "option",
value: ["To Leet Speak", "From Leet Speak"],
defaultIndex: 0
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const direction = args[0];
if (direction === "To Leet Speak") {
return input.replace(/[abcdefghijklmnopqrstuvwxyz]/gi, char => {
return toLeetMap[char.toLowerCase()] || char;
});
} else if (direction === "From Leet Speak") {
return input.replace(/[48cd3f6h1jklmn0pqr57uvwxyz]/g, char => {
return fromLeetMap[char] || char;
});
}
}
}
const toLeetMap = {
"a": "4",
"b": "b",
"c": "c",
"d": "d",
"e": "3",
"f": "f",
"g": "g",
"h": "h",
"i": "1",
"j": "j",
"k": "k",
"l": "l",
"m": "m",
"n": "n",
"o": "0",
"p": "p",
"q": "q",
"r": "r",
"s": "5",
"t": "7",
"u": "u",
"v": "v",
"w": "w",
"x": "x",
"y": "y",
"z": "z"
};
const fromLeetMap = {
"4": "a",
"b": "b",
"c": "c",
"d": "d",
"3": "e",
"f": "f",
"g": "g",
"h": "h",
"1": "i",
"j": "j",
"k": "k",
"l": "l",
"m": "m",
"n": "n",
"0": "o",
"p": "p",
"q": "q",
"r": "r",
"5": "s",
"7": "t",
"u": "u",
"v": "v",
"w": "w",
"x": "x",
"y": "y",
"z": "z"
};
export default ConvertLeetSpeak;

View file

@ -22,7 +22,7 @@ class DESDecrypt extends Operation {
this.name = "DES Decrypt"; this.name = "DES Decrypt";
this.module = "Ciphers"; this.module = "Ciphers";
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default."; this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default.";
this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard"; this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard";
this.inputType = "string"; this.inputType = "string";
this.outputType = "string"; this.outputType = "string";
@ -72,8 +72,7 @@ class DESDecrypt extends Operation {
if (key.length !== 8) { if (key.length !== 8) {
throw new OperationError(`Invalid key length: ${key.length} bytes throw new OperationError(`Invalid key length: ${key.length} bytes
DES uses a key length of 8 bytes (64 bits). DES uses a key length of 8 bytes (64 bits).`);
Triple DES uses a key length of 24 bytes (192 bits).`);
} }
if (iv.length !== 8 && mode !== "ECB") { if (iv.length !== 8 && mode !== "ECB") {
throw new OperationError(`Invalid IV length: ${iv.length} bytes throw new OperationError(`Invalid IV length: ${iv.length} bytes

View file

@ -22,7 +22,7 @@ class DESEncrypt extends Operation {
this.name = "DES Encrypt"; this.name = "DES Encrypt";
this.module = "Ciphers"; this.module = "Ciphers";
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used."; this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br><br>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard"; this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard";
this.inputType = "string"; this.inputType = "string";
this.outputType = "string"; this.outputType = "string";
@ -70,8 +70,7 @@ class DESEncrypt extends Operation {
if (key.length !== 8) { if (key.length !== 8) {
throw new OperationError(`Invalid key length: ${key.length} bytes throw new OperationError(`Invalid key length: ${key.length} bytes
DES uses a key length of 8 bytes (64 bits). DES uses a key length of 8 bytes (64 bits).`);
Triple DES uses a key length of 24 bytes (192 bits).`);
} }
if (iv.length !== 8 && mode !== "ECB") { if (iv.length !== 8 && mode !== "ECB") {
throw new OperationError(`Invalid IV length: ${iv.length} bytes throw new OperationError(`Invalid IV length: ${iv.length} bytes

View file

@ -0,0 +1,79 @@
/**
* @author Oshawk [oshawk@protonmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Drop nth bytes operation
*/
class DropNthBytes extends Operation {
/**
* DropNthBytes constructor
*/
constructor() {
super();
this.name = "Drop nth bytes";
this.module = "Default";
this.description = "Drops every nth byte starting with a given byte.";
this.infoURL = "";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
name: "Drop every",
type: "number",
value: 4
},
{
name: "Starting at",
type: "number",
value: 0
},
{
name: "Apply to each line",
type: "boolean",
value: false
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const n = args[0];
const start = args[1];
const eachLine = args[2];
if (parseInt(n, 10) !== n || n <= 0) {
throw new OperationError("'Drop every' must be a positive integer.");
}
if (parseInt(start, 10) !== start || start < 0) {
throw new OperationError("'Starting at' must be a positive or zero integer.");
}
let offset = 0;
const output = [];
for (let i = 0; i < input.length; i++) {
if (eachLine && input[i] === 0x0a) {
output.push(0x0a);
offset = i + 1;
} else if (i - offset < start || (i - (start + offset)) % n !== 0) {
output.push(input[i]);
}
}
return output;
}
}
export default DropNthBytes;

View file

@ -5,16 +5,14 @@
*/ */
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import otp from "otp"; import * as OTPAuth from "otpauth";
import ToBase32 from "./ToBase32.mjs";
/** /**
* Generate HOTP operation * Generate HOTP operation
*/ */
class GenerateHOTP extends Operation { class GenerateHOTP extends Operation {
/** /**
* GenerateHOTP constructor *
*/ */
constructor() { constructor() {
super(); super();
@ -31,11 +29,6 @@ class GenerateHOTP extends Operation {
"type": "string", "type": "string",
"value": "" "value": ""
}, },
{
"name": "Key size",
"type": "number",
"value": 32
},
{ {
"name": "Code length", "name": "Code length",
"type": "number", "type": "number",
@ -50,21 +43,26 @@ class GenerateHOTP extends Operation {
} }
/** /**
* @param {ArrayBuffer} input *
* @param {Object[]} args
* @returns {string}
*/ */
run(input, args) { run(input, args) {
const otpObj = otp({ const secretStr = new TextDecoder("utf-8").decode(input).trim();
name: args[0], const secret = secretStr ? secretStr.toUpperCase().replace(/\s+/g, "") : "";
keySize: args[1],
codeLength: args[2],
secret: (new ToBase32).run(input, []).split("=")[0],
});
const counter = args[3];
return `URI: ${otpObj.hotpURL}\n\nPassword: ${otpObj.hotp(counter)}`;
}
const hotp = new OTPAuth.HOTP({
issuer: "",
label: args[0],
algorithm: "SHA1",
digits: args[1],
counter: args[2],
secret: OTPAuth.Secret.fromBase32(secret)
});
const uri = hotp.toString();
const code = hotp.generate();
return `URI: ${uri}\n\nPassword: ${code}`;
}
} }
export default GenerateHOTP; export default GenerateHOTP;

View file

@ -5,20 +5,17 @@
*/ */
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import otp from "otp"; import * as OTPAuth from "otpauth";
import ToBase32 from "./ToBase32.mjs";
/** /**
* Generate TOTP operation * Generate TOTP operation
*/ */
class GenerateTOTP extends Operation { class GenerateTOTP extends Operation {
/** /**
* GenerateTOTP constructor *
*/ */
constructor() { constructor() {
super(); super();
this.name = "Generate TOTP"; this.name = "Generate TOTP";
this.module = "Default"; this.module = "Default";
this.description = "The Time-based One-Time Password algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, is the cornerstone of Initiative For Open Authentication (OAUTH), and is used in a number of two-factor authentication systems. A TOTP is an HOTP where the counter is the current time.<br><br>Enter the secret as the input or leave it blank for a random secret to be generated. T0 and T1 are in seconds."; this.description = "The Time-based One-Time Password algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, is the cornerstone of Initiative For Open Authentication (OAUTH), and is used in a number of two-factor authentication systems. A TOTP is an HOTP where the counter is the current time.<br><br>Enter the secret as the input or leave it blank for a random secret to be generated. T0 and T1 are in seconds.";
@ -31,11 +28,6 @@ class GenerateTOTP extends Operation {
"type": "string", "type": "string",
"value": "" "value": ""
}, },
{
"name": "Key size",
"type": "number",
"value": 32
},
{ {
"name": "Code length", "name": "Code length",
"type": "number", "type": "number",
@ -55,22 +47,27 @@ class GenerateTOTP extends Operation {
} }
/** /**
* @param {ArrayBuffer} input *
* @param {Object[]} args
* @returns {string}
*/ */
run(input, args) { run(input, args) {
const otpObj = otp({ const secretStr = new TextDecoder("utf-8").decode(input).trim();
name: args[0], const secret = secretStr ? secretStr.toUpperCase().replace(/\s+/g, "") : "";
keySize: args[1],
codeLength: args[2],
secret: (new ToBase32).run(input, []).split("=")[0],
epoch: args[3],
timeSlice: args[4]
});
return `URI: ${otpObj.totpURL}\n\nPassword: ${otpObj.totp()}`;
}
const totp = new OTPAuth.TOTP({
issuer: "",
label: args[0],
algorithm: "SHA1",
digits: args[1],
period: args[3],
epoch: args[2] * 1000, // Convert seconds to milliseconds
secret: OTPAuth.Secret.fromBase32(secret)
});
const uri = totp.toString();
const code = totp.generate();
return `URI: ${uri}\n\nPassword: ${code}`;
}
} }
export default GenerateTOTP; export default GenerateTOTP;

View file

@ -0,0 +1,209 @@
/**
* @author jb30795 [jb30795@proton.me]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
/**
* IPv6 Transition Addresses operation
*/
class IPv6TransitionAddresses extends Operation {
/**
* IPv6TransitionAddresses constructor
*/
constructor() {
super();
this.name = "IPv6 Transition Addresses";
this.module = "Default";
this.description = "Converts IPv4 addresses to their IPv6 Transition addresses. IPv6 Transition addresses can also be converted back into their original IPv4 address. MAC addresses can also be converted into the EUI-64 format, this can them be appended to your IPv6 /64 range to obtain a full /128 address.<br><br>Transition technologies enable translation between IPv4 and IPv6 addresses or tunneling to allow traffic to pass through the incompatible network, allowing the two standards to coexist.<br><br>Only /24 ranges and currently handled. Remove headers to easily copy out results.";
this.infoURL = "https://wikipedia.org/wiki/IPv6_transition_mechanism";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Ignore ranges",
"type": "boolean",
"value": true
},
{
"name": "Remove headers",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const XOR = {"0": "2", "1": "3", "2": "0", "3": "1", "4": "6", "5": "7", "6": "4", "7": "5", "8": "a", "9": "b", "a": "8", "b": "9", "c": "e", "d": "f", "e": "c", "f": "d"};
/**
* Function to convert to hex
*/
function hexify(octet) {
return Number(octet).toString(16).padStart(2, "0");
}
/**
* Function to convert Hex to Int
*/
function intify(hex) {
return parseInt(hex, 16);
}
/**
* Function converts IPv4 to IPv6 Transtion address
*/
function ipTransition(input, range) {
let output = "";
const HEXIP = input.split(".");
/**
* 6to4
*/
if (!args[1]) {
output += "6to4: ";
}
output += "2002:" + hexify(HEXIP[0]) + hexify(HEXIP[1]) + ":" + hexify(HEXIP[2]);
if (range) {
output += "00::/40\n";
} else {
output += hexify(HEXIP[3]) + "::/48\n";
}
/**
* Mapped
*/
if (!args[1]) {
output += "IPv4 Mapped: ";
}
output += "::ffff:" + hexify(HEXIP[0]) + hexify(HEXIP[1]) + ":" + hexify(HEXIP[2]);
if (range) {
output += "00/120\n";
} else {
output += hexify(HEXIP[3]) + "\n";
}
/**
* Translated
*/
if (!args[1]) {
output += "IPv4 Translated: ";
}
output += "::ffff:0:" + hexify(HEXIP[0]) + hexify(HEXIP[1]) + ":" + hexify(HEXIP[2]);
if (range) {
output += "00/120\n";
} else {
output += hexify(HEXIP[3]) + "\n";
}
/**
* Nat64
*/
if (!args[1]) {
output += "Nat 64: ";
}
output += "64:ff9b::" + hexify(HEXIP[0]) + hexify(HEXIP[1]) + ":" + hexify(HEXIP[2]);
if (range) {
output += "00/120\n";
} else {
output += hexify(HEXIP[3]) + "\n";
}
return output;
}
/**
* Convert MAC to EUI-64
*/
function macTransition(input) {
let output = "";
const MACPARTS = input.split(":");
if (!args[1]) {
output += "EUI-64 Interface ID: ";
}
const MAC = MACPARTS[0] + MACPARTS[1] + ":" + MACPARTS[2] + "ff:fe" + MACPARTS[3] + ":" + MACPARTS[4] + MACPARTS[5];
output += MAC.slice(0, 1) + XOR[MAC.slice(1, 2)] + MAC.slice(2);
return output;
}
/**
* Convert IPv6 address to its original IPv4 or MAC address
*/
function unTransition(input) {
let output = "";
let hextets = "";
/**
* 6to4
*/
if (input.startsWith("2002:")) {
if (!args[1]) {
output += "IPv4: ";
}
output += String(intify(input.slice(5, 7))) + "." + String(intify(input.slice(7, 9)))+ "." + String(intify(input.slice(10, 12)))+ "." + String(intify(input.slice(12, 14))) + "\n";
} else if (input.startsWith("::ffff:") || input.startsWith("0000:0000:0000:0000:0000:ffff:") || input.startsWith("::ffff:0000:") || input.startsWith("0000:0000:0000:0000:ffff:0000:") || input.startsWith("64:ff9b::") || input.startsWith("0064:ff9b:0000:0000:0000:0000:")) {
/**
* Mapped/Translated/Nat64
*/
hextets = /:([0-9a-z]{1,4}):[0-9a-z]{1,4}$/.exec(input)[1].padStart(4, "0") + /:([0-9a-z]{1,4})$/.exec(input)[1].padStart(4, "0");
if (!args[1]) {
output += "IPv4: ";
}
output += intify(hextets.slice(-8, -7) + hextets.slice(-7, -6)) + "." +intify(hextets.slice(-6, -5) + hextets.slice(-5, -4)) + "." +intify(hextets.slice(-4, -3) + hextets.slice(-3, -2)) + "." +intify(hextets.slice(-2, -1) + hextets.slice(-1,)) + "\n";
} else if (input.slice(-12, -7).toUpperCase() === "FF:FE") {
/**
* EUI-64
*/
if (!args[1]) {
output += "Mac Address: ";
}
const MAC = (input.slice(-19, -17) + ":" + input.slice(-17, -15) + ":" + input.slice(-14, -12) + ":" + input.slice(-7, -5) + ":" + input.slice(-4, -2) + ":" + input.slice(-2,)).toUpperCase();
output += MAC.slice(0, 1) + XOR[MAC.slice(1, 2)] + MAC.slice(2) + "\n";
}
return output;
}
/**
* Main
*/
let output = "";
let inputs = input.split("\n");
// Remove blank rows
inputs = inputs.filter(Boolean);
for (let i = 0; i < inputs.length; i++) {
// if ignore ranges is checked and input is a range, skip
if ((args[0] && !inputs[i].includes("/")) || (!args[0])) {
if (/^[0-9]{1,3}(?:\.[0-9]{1,3}){3}$/.test(inputs[i])) {
output += ipTransition(inputs[i], false);
} else if (/\/24$/.test(inputs[i])) {
output += ipTransition(inputs[i], true);
} else if (/^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/.test(inputs[i])) {
output += macTransition(inputs[i]);
} else if (/^((?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(inputs[i])) {
output += unTransition(inputs[i]);
} else {
output = "Enter compressed or expanded IPv6 address, IPv4 address or MAC Address.";
}
}
}
return output;
}
}
export default IPv6TransitionAddresses;

View file

@ -36,6 +36,11 @@ class JWTSign extends Operation {
name: "Signing algorithm", name: "Signing algorithm",
type: "option", type: "option",
value: JWT_ALGORITHMS value: JWT_ALGORITHMS
},
{
name: "Header",
type: "text",
value: "{}"
} }
]; ];
} }
@ -46,11 +51,12 @@ class JWTSign extends Operation {
* @returns {string} * @returns {string}
*/ */
run(input, args) { run(input, args) {
const [key, algorithm] = args; const [key, algorithm, header] = args;
try { try {
return jwt.sign(input, key, { return jwt.sign(input, key, {
algorithm: algorithm === "None" ? "none" : algorithm algorithm: algorithm === "None" ? "none" : algorithm,
header: JSON.parse(header || "{}")
}); });
} catch (err) { } catch (err) {
throw new OperationError(`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. throw new OperationError(`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.

View file

@ -22,7 +22,7 @@ class JWTVerify extends Operation {
this.name = "JWT Verify"; this.name = "JWT Verify";
this.module = "Crypto"; this.module = "Crypto";
this.description = "Verifies that a JSON Web Token is valid and has been signed with the provided secret / private key.<br><br>The key should be either the secret for HMAC algorithms or the PEM-encoded private key for RSA and ECDSA."; this.description = "Verifies that a JSON Web Token is valid and has been signed with the provided secret / private key.<br><br>The key should be either the secret for HMAC algorithms or the PEM-encoded public key for RSA and ECDSA.";
this.infoURL = "https://wikipedia.org/wiki/JSON_Web_Token"; this.infoURL = "https://wikipedia.org/wiki/JSON_Web_Token";
this.inputType = "string"; this.inputType = "string";
this.outputType = "JSON"; this.outputType = "JSON";

View file

@ -1,5 +1,6 @@
/** /**
* @author n1073645 [n1073645@gmail.com] * @author n1073645 [n1073645@gmail.com]
* @author k3ach [k3ach@proton.me]
* @copyright Crown Copyright 2020 * @copyright Crown Copyright 2020
* @license Apache-2.0 * @license Apache-2.0
*/ */
@ -20,39 +21,46 @@ class LuhnChecksum extends Operation {
this.name = "Luhn Checksum"; this.name = "Luhn Checksum";
this.module = "Default"; this.module = "Default";
this.description = "The Luhn algorithm, also known as the modulus 10 or mod 10 algorithm, is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers, IMEI numbers and Canadian Social Insurance Numbers."; this.description = "The Luhn mod N algorithm using the english alphabet. The Luhn mod N algorithm is an extension to the Luhn algorithm (also known as mod 10 algorithm) that allows it to work with sequences of values in any even-numbered base. This can be useful when a check digit is required to validate an identification string composed of letters, a combination of letters and digits or any arbitrary set of N characters where N is divisible by 2.";
this.infoURL = "https://wikipedia.org/wiki/Luhn_algorithm"; this.infoURL = "https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm";
this.inputType = "string"; this.inputType = "string";
this.outputType = "string"; this.outputType = "string";
this.args = []; this.args = [
{
"name": "Radix",
"type": "number",
"value": 10
}
];
} }
/** /**
* Generates the Luhn Checksum from the input. * Generates the Luhn checksum from the input.
* *
* @param {string} inputStr * @param {string} inputStr
* @returns {number} * @returns {number}
*/ */
checksum(inputStr) { checksum(inputStr, radix = 10) {
let even = false; let even = false;
return inputStr.split("").reverse().reduce((acc, elem) => { return inputStr.split("").reverse().reduce((acc, elem) => {
// Convert element to integer. // Convert element to an integer based on the provided radix.
let temp = parseInt(elem, 10); let temp = parseInt(elem, radix);
// If element is not an integer. // If element is not a valid number in the given radix.
if (isNaN(temp)) if (isNaN(temp)) {
throw new OperationError("Character: " + elem + " is not a digit."); throw new Error("Character: " + elem + " is not valid in radix " + radix + ".");
}
// If element is in an even position // If element is in an even position
if (even) { if (even) {
// Double the element and add the quotient and remainder together. // Double the element and sum the quotient and remainder.
temp = 2 * elem; temp = 2 * temp;
temp = Math.floor(temp/10) + (temp % 10); temp = Math.floor(temp / radix) + (temp % radix);
} }
even = !even; even = !even;
return acc + temp; return acc + temp;
}, 0) % 10; }, 0) % radix; // Use radix as the modulus base
} }
/** /**
@ -63,9 +71,20 @@ class LuhnChecksum extends Operation {
run(input, args) { run(input, args) {
if (!input) return ""; if (!input) return "";
const checkSum = this.checksum(input); const radix = args[0];
let checkDigit = this.checksum(input + "0");
checkDigit = checkDigit === 0 ? 0 : (10-checkDigit); if (radix < 2 || radix > 36) {
throw new OperationError("Error: Radix argument must be between 2 and 36");
}
if (radix % 2 !== 0) {
throw new OperationError("Error: Radix argument must be divisible by 2");
}
const checkSum = this.checksum(input, radix).toString(radix);
let checkDigit = this.checksum(input + "0", radix);
checkDigit = checkDigit === 0 ? 0 : (radix - checkDigit);
checkDigit = checkDigit.toString(radix);
return `Checksum: ${checkSum} return `Checksum: ${checkSum}
Checkdigit: ${checkDigit} Checkdigit: ${checkDigit}

View file

@ -12,9 +12,10 @@ import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import process from "process";
import { createWorker } from "tesseract.js"; import { createWorker } from "tesseract.js";
const OEM_MODES = ["Tesseract only", "LSTM only", "Tesseract/LSTM Combined"];
/** /**
* Optical Character Recognition operation * Optical Character Recognition operation
*/ */
@ -37,6 +38,12 @@ class OpticalCharacterRecognition extends Operation {
name: "Show confidence", name: "Show confidence",
type: "boolean", type: "boolean",
value: true value: true
},
{
name: "OCR Engine Mode",
type: "option",
value: OEM_MODES,
defaultIndex: 1
} }
]; ];
} }
@ -47,7 +54,7 @@ class OpticalCharacterRecognition extends Operation {
* @returns {string} * @returns {string}
*/ */
async run(input, args) { async run(input, args) {
const [showConfidence] = args; const [showConfidence, oemChoice] = args;
if (!isWorkerEnvironment()) throw new OperationError("This operation only works in a browser"); if (!isWorkerEnvironment()) throw new OperationError("This operation only works in a browser");
@ -56,12 +63,13 @@ class OpticalCharacterRecognition extends Operation {
throw new OperationError("Unsupported file type (supported: jpg,png,pbm,bmp) or no file provided"); throw new OperationError("Unsupported file type (supported: jpg,png,pbm,bmp) or no file provided");
} }
const assetDir = isWorkerEnvironment() ? `${self.docURL}/assets/` : `${process.cwd()}/src/core/vendor/`; const assetDir = `${self.docURL}/assets/`;
const oem = OEM_MODES.indexOf(oemChoice);
try { try {
self.sendStatusMessage("Spinning up Tesseract worker..."); self.sendStatusMessage("Spinning up Tesseract worker...");
const image = `data:${type};base64,${toBase64(input)}`; const image = `data:${type};base64,${toBase64(input)}`;
const worker = createWorker({ const worker = await createWorker("eng", oem, {
workerPath: `${assetDir}tesseract/worker.min.js`, workerPath: `${assetDir}tesseract/worker.min.js`,
langPath: `${assetDir}tesseract/lang-data`, langPath: `${assetDir}tesseract/lang-data`,
corePath: `${assetDir}tesseract/tesseract-core.wasm.js`, corePath: `${assetDir}tesseract/tesseract-core.wasm.js`,
@ -71,11 +79,6 @@ class OpticalCharacterRecognition extends Operation {
} }
} }
}); });
await worker.load();
self.sendStatusMessage(`Loading English language pack...`);
await worker.loadLanguage("eng");
self.sendStatusMessage("Intialising Tesseract API...");
await worker.initialize("eng");
self.sendStatusMessage("Finding text..."); self.sendStatusMessage("Finding text...");
const result = await worker.recognize(image); const result = await worker.recognize(image);

View file

@ -4,8 +4,9 @@
* @license Apache-2.0 * @license Apache-2.0
*/ */
import r from "jsrsasign";
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import forge from "node-forge"; import { formatDnObj } from "../lib/PublicKey.mjs";
import Utils from "../Utils.mjs"; import Utils from "../Utils.mjs";
/** /**
@ -30,16 +31,6 @@ class ParseCSR extends Operation {
"name": "Input format", "name": "Input format",
"type": "option", "type": "option",
"value": ["PEM"] "value": ["PEM"]
},
{
"name": "Key type",
"type": "option",
"value": ["RSA"]
},
{
"name": "Strict ASN.1 value lengths",
"type": "boolean",
"value": true
} }
]; ];
this.checks = [ this.checks = [
@ -61,73 +52,71 @@ class ParseCSR extends Operation {
return "No input"; return "No input";
} }
const csr = forge.pki.certificationRequestFromPem(input, args[1]); // Parse the CSR into JSON parameters
const csrParam = new r.KJUR.asn1.csr.CSRUtil.getParam(input);
// RSA algorithm is the only one supported for CSR in node-forge as of 1.3.1 return `Subject\n${formatDnObj(csrParam.subject, 2)}
return `Version: ${1 + csr.version} (0x${Utils.hex(csr.version)}) Public Key${formatSubjectPublicKey(csrParam.sbjpubkey)}
Subject${formatSubject(csr.subject)} Signature${formatSignature(csrParam.sigalg, csrParam.sighex)}
Subject Alternative Names${formatSubjectAlternativeNames(csr)} Requested Extensions${formatRequestedExtensions(csrParam)}`;
Public Key
Algorithm: RSA
Length: ${csr.publicKey.n.bitLength()} bits
Modulus: ${formatMultiLine(chop(csr.publicKey.n.toString(16).replace(/(..)/g, "$&:")))}
Exponent: ${csr.publicKey.e} (0x${Utils.hex(csr.publicKey.e)})
Signature
Algorithm: ${forge.pki.oids[csr.signatureOid]}
Signature: ${formatMultiLine(Utils.strToByteArray(csr.signature).map(b => Utils.hex(b)).join(":"))}
Extensions${formatExtensions(csr)}`;
} }
} }
/** /**
* Format Subject of the request as a multi-line string * Format signature of a CSR
* @param {*} subject CSR Subject * @param {*} sigAlg string
* @returns Multi-line string describing Subject * @param {*} sigHex string
* @returns Multi-line string describing CSR Signature
*/ */
function formatSubject(subject) { function formatSignature(sigAlg, sigHex) {
let out = "\n"; let out = `\n`;
for (const attribute of subject.attributes) { out += ` Algorithm: ${sigAlg}\n`;
out += ` ${attribute.shortName} = ${attribute.value}\n`;
if (new RegExp("withdsa", "i").test(sigAlg)) {
const d = new r.KJUR.crypto.DSA();
const sigParam = d.parseASN1Signature(sigHex);
out += ` Signature:
R: ${formatHexOntoMultiLine(absBigIntToHex(sigParam[0]))}
S: ${formatHexOntoMultiLine(absBigIntToHex(sigParam[1]))}\n`;
} else if (new RegExp("withrsa", "i").test(sigAlg)) {
out += ` Signature: ${formatHexOntoMultiLine(sigHex)}\n`;
} else {
out += ` Signature: ${formatHexOntoMultiLine(ensureHexIsPositiveInTwosComplement(sigHex))}\n`;
} }
return chop(out); return chop(out);
} }
/** /**
* Format Subject Alternative Names from the name `subjectAltName` extension * Format Subject Public Key from PEM encoded public key string
* @param {*} extension CSR object * @param {*} publicKeyPEM string
* @returns Multi-line string describing Subject Alternative Names * @returns Multi-line string describing Subject Public Key Info
*/ */
function formatSubjectAlternativeNames(csr) { function formatSubjectPublicKey(publicKeyPEM) {
let out = "\n"; let out = "\n";
for (const attribute of csr.attributes) { const publicKey = r.KEYUTIL.getKey(publicKeyPEM);
for (const extension of attribute.extensions) { if (publicKey instanceof r.RSAKey) {
if (extension.name === "subjectAltName") { out += ` Algorithm: RSA
const names = []; Length: ${publicKey.n.bitLength()} bits
for (const altName of extension.altNames) { Modulus: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.n))}
switch (altName.type) { Exponent: ${publicKey.e} (0x${Utils.hex(publicKey.e)})\n`;
case 1: } else if (publicKey instanceof r.KJUR.crypto.ECDSA) {
names.push(`EMAIL: ${altName.value}`); out += ` Algorithm: ECDSA
break; Length: ${publicKey.ecparams.keylen} bits
case 2: Pub: ${formatHexOntoMultiLine(publicKey.pubKeyHex)}
names.push(`DNS: ${altName.value}`); ASN1 OID: ${r.KJUR.crypto.ECDSA.getName(publicKey.getShortNISTPCurveName())}
break; NIST CURVE: ${publicKey.getShortNISTPCurveName()}\n`;
case 6: } else if (publicKey instanceof r.KJUR.crypto.DSA) {
names.push(`URI: ${altName.value}`); out += ` Algorithm: DSA
break; Length: ${publicKey.p.toString(16).length * 4} bits
case 7: Pub: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.y))}
names.push(`IP: ${altName.ip}`); P: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.p))}
break; Q: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.q))}
default: G: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.g))}\n`;
names.push(`(unable to format type ${altName.type} name)\n`); } else {
} out += `unsupported public key algorithm\n`;
}
out += indent(2, names);
}
}
} }
return chop(out); return chop(out);
@ -135,45 +124,105 @@ function formatSubjectAlternativeNames(csr) {
/** /**
* Format known extensions of a CSR * Format known extensions of a CSR
* @param {*} csr CSR object * @param {*} csrParam object
* @returns Multi-line string describing attributes * @returns Multi-line string describing CSR Requested Extensions
*/ */
function formatExtensions(csr) { function formatRequestedExtensions(csrParam) {
let out = "\n"; const formattedExtensions = new Array(4).fill("");
for (const attribute of csr.attributes) { if (Object.hasOwn(csrParam, "extreq")) {
for (const extension of attribute.extensions) { for (const extension of csrParam.extreq) {
// formatted separately
if (extension.name === "subjectAltName") {
continue;
}
out += ` ${extension.name}${(extension.critical ? " CRITICAL" : "")}:\n`;
let parts = []; let parts = [];
switch (extension.name) { switch (extension.extname) {
case "basicConstraints" : case "basicConstraints" :
parts = describeBasicConstraints(extension); parts = describeBasicConstraints(extension);
formattedExtensions[0] = ` Basic Constraints:${formatExtensionCriticalTag(extension)}\n${indent(4, parts)}`;
break; break;
case "keyUsage" : case "keyUsage" :
parts = describeKeyUsage(extension); parts = describeKeyUsage(extension);
formattedExtensions[1] = ` Key Usage:${formatExtensionCriticalTag(extension)}\n${indent(4, parts)}`;
break; break;
case "extKeyUsage" : case "extKeyUsage" :
parts = describeExtendedKeyUsage(extension); parts = describeExtendedKeyUsage(extension);
formattedExtensions[2] = ` Extended Key Usage:${formatExtensionCriticalTag(extension)}\n${indent(4, parts)}`;
break;
case "subjectAltName" :
parts = describeSubjectAlternativeName(extension);
formattedExtensions[3] = ` Subject Alternative Name:${formatExtensionCriticalTag(extension)}\n${indent(4, parts)}`;
break; break;
default : default :
parts = ["(unable to format extension)"]; parts = ["(unsuported extension)"];
} formattedExtensions.push(` ${extension.extname}:${formatExtensionCriticalTag(extension)}\n${indent(4, parts)}`);
out += indent(4, parts);
} }
} }
}
let out = "\n";
formattedExtensions.forEach((formattedExtension) => {
if (formattedExtension !== undefined && formattedExtension !== null && formattedExtension.length !== 0) {
out += formattedExtension;
}
});
return chop(out); return chop(out);
} }
/**
* Format extension critical tag
* @param {*} extension Object
* @returns String describing whether the extension is critical or not
*/
function formatExtensionCriticalTag(extension) {
return Object.hasOwn(extension, "critical") && extension.critical ? " critical" : "";
}
/** /**
* Format hex string onto multiple lines * Format string input as a comma separated hex string on multiple lines
* @param {*} hex String
* @returns Multi-line string describing the Hex input
*/
function formatHexOntoMultiLine(hex) {
if (hex.length % 2 !== 0) {
hex = "0" + hex;
}
return formatMultiLine(chop(hex.replace(/(..)/g, "$&:")));
}
/**
* Convert BigInt to abs value in Hex
* @param {*} int BigInt
* @returns String representing absolute value in Hex
*/
function absBigIntToHex(int) {
int = int < 0n ? -int : int;
return ensureHexIsPositiveInTwosComplement(int.toString(16));
}
/**
* Ensure Hex String remains positive in 2's complement
* @param {*} hex String
* @returns Hex String ensuring value remains positive in 2's complement
*/
function ensureHexIsPositiveInTwosComplement(hex) {
if (hex.length % 2 !== 0) {
return "0" + hex;
}
// prepend 00 if most significant bit is 1 (sign bit)
if (hex.length >=2 && (parseInt(hex.substring(0, 2), 16) & 128)) {
hex = "00" + hex;
}
return hex;
}
/**
* Format string onto multiple lines
* @param {*} longStr * @param {*} longStr
* @returns Hex string as a multi-line hex string * @returns String as a multi-line string
*/ */
function formatMultiLine(longStr) { function formatMultiLine(longStr) {
const lines = []; const lines = [];
@ -194,8 +243,8 @@ function formatMultiLine(longStr) {
function describeBasicConstraints(extension) { function describeBasicConstraints(extension) {
const constraints = []; const constraints = [];
constraints.push(`CA = ${extension.cA}`); constraints.push(`CA = ${Object.hasOwn(extension, "cA") && extension.cA ? "true" : "false"}`);
if (extension.pathLenConstraint !== undefined) constraints.push(`PathLenConstraint = ${extension.pathLenConstraint}`); if (Object.hasOwn(extension, "pathLen")) constraints.push(`PathLenConstraint = ${extension.pathLen}`);
return constraints; return constraints;
} }
@ -209,15 +258,27 @@ function describeBasicConstraints(extension) {
function describeKeyUsage(extension) { function describeKeyUsage(extension) {
const usage = []; const usage = [];
if (extension.digitalSignature) usage.push("Digital signature"); const kuIdentifierToName = {
if (extension.nonRepudiation) usage.push("Non-repudiation"); digitalSignature: "Digital Signature",
if (extension.keyEncipherment) usage.push("Key encipherment"); nonRepudiation: "Non-repudiation",
if (extension.dataEncipherment) usage.push("Data encipherment"); keyEncipherment: "Key encipherment",
if (extension.keyAgreement) usage.push("Key agreement"); dataEncipherment: "Data encipherment",
if (extension.keyCertSign) usage.push("Key certificate signing"); keyAgreement: "Key agreement",
if (extension.cRLSign) usage.push("CRL signing"); keyCertSign: "Key certificate signing",
if (extension.encipherOnly) usage.push("Encipher only"); cRLSign: "CRL signing",
if (extension.decipherOnly) usage.push("Decipher only"); encipherOnly: "Encipher Only",
decipherOnly: "Decipher Only",
};
if (Object.hasOwn(extension, "names")) {
extension.names.forEach((ku) => {
if (Object.hasOwn(kuIdentifierToName, ku)) {
usage.push(kuIdentifierToName[ku]);
} else {
usage.push(`unknown key usage (${ku})`);
}
});
}
if (usage.length === 0) usage.push("(none)"); if (usage.length === 0) usage.push("(none)");
@ -233,23 +294,79 @@ function describeKeyUsage(extension) {
function describeExtendedKeyUsage(extension) { function describeExtendedKeyUsage(extension) {
const usage = []; const usage = [];
if (extension.serverAuth) usage.push("TLS Web Server Authentication"); const ekuIdentifierToName = {
if (extension.clientAuth) usage.push("TLS Web Client Authentication"); "serverAuth": "TLS Web Server Authentication",
if (extension.codeSigning) usage.push("Code signing"); "clientAuth": "TLS Web Client Authentication",
if (extension.emailProtection) usage.push("E-mail Protection (S/MIME)"); "codeSigning": "Code signing",
if (extension.timeStamping) usage.push("Trusted Timestamping"); "emailProtection": "E-mail Protection (S/MIME)",
if (extension.msCodeInd) usage.push("Microsoft Individual Code Signing"); "timeStamping": "Trusted Timestamping",
if (extension.msCodeCom) usage.push("Microsoft Commercial Code Signing"); "1.3.6.1.4.1.311.2.1.21": "Microsoft Individual Code Signing", // msCodeInd
if (extension.msCTLSign) usage.push("Microsoft Trust List Signing"); "1.3.6.1.4.1.311.2.1.22": "Microsoft Commercial Code Signing", // msCodeCom
if (extension.msSGC) usage.push("Microsoft Server Gated Crypto"); "1.3.6.1.4.1.311.10.3.1": "Microsoft Trust List Signing", // msCTLSign
if (extension.msEFS) usage.push("Microsoft Encrypted File System"); "1.3.6.1.4.1.311.10.3.3": "Microsoft Server Gated Crypto", // msSGC
if (extension.nsSGC) usage.push("Netscape Server Gated Crypto"); "1.3.6.1.4.1.311.10.3.4": "Microsoft Encrypted File System", // msEFS
"1.3.6.1.4.1.311.20.2.2": "Microsoft Smartcard Login", // msSmartcardLogin
"2.16.840.1.113730.4.1": "Netscape Server Gated Crypto", // nsSGC
};
if (Object.hasOwn(extension, "array")) {
extension.array.forEach((eku) => {
if (Object.hasOwn(ekuIdentifierToName, eku)) {
usage.push(ekuIdentifierToName[eku]);
} else {
usage.push(eku);
}
});
}
if (usage.length === 0) usage.push("(none)"); if (usage.length === 0) usage.push("(none)");
return usage; return usage;
} }
/**
* Format Subject Alternative Names from the name `subjectAltName` extension
* @see RFC 5280 4.2.1.6. Subject Alternative Name https://www.ietf.org/rfc/rfc5280.txt
* @param {*} extension object
* @returns Array of strings describing Subject Alternative Name extension
*/
function describeSubjectAlternativeName(extension) {
const names = [];
if (Object.hasOwn(extension, "extname") && extension.extname === "subjectAltName") {
if (Object.hasOwn(extension, "array")) {
for (const altName of extension.array) {
Object.keys(altName).forEach((key) => {
switch (key) {
case "rfc822":
names.push(`EMAIL: ${altName[key]}`);
break;
case "dns":
names.push(`DNS: ${altName[key]}`);
break;
case "uri":
names.push(`URI: ${altName[key]}`);
break;
case "ip":
names.push(`IP: ${altName[key]}`);
break;
case "dn":
names.push(`DIR: ${altName[key].str}`);
break;
case "other" :
names.push(`Other: ${altName[key].oid}::${altName[key].value.utf8str.str}`);
break;
default:
names.push(`(unable to format SAN '${key}':${altName[key]})\n`);
}
});
}
}
}
return names;
}
/** /**
* Join an array of strings and add leading spaces to each line. * Join an array of strings and add leading spaces to each line.
* @param {*} n How many leading spaces * @param {*} n How many leading spaces

View file

@ -0,0 +1,884 @@
/**
* @author c65722 []
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {toHexFast} from "../lib/Hex.mjs";
import {objToTable} from "../lib/Protocol.mjs";
import Stream from "../lib/Stream.mjs";
/**
* Parse TLS record operation.
*/
class ParseTLSRecord extends Operation {
/**
* ParseTLSRecord constructor.
*/
constructor() {
super();
this.name = "Parse TLS record";
this.module = "Default";
this.description = "Parses one or more TLS records";
this.infoURL = "https://wikipedia.org/wiki/Transport_Layer_Security";
this.inputType = "ArrayBuffer";
this.outputType = "json";
this.presentType = "html";
this.args = [];
this._handshakeParser = new HandshakeParser();
this._contentTypes = new Map();
for (const key in ContentType) {
this._contentTypes[ContentType[key]] = key.toString().toLocaleLowerCase();
}
}
/**
* @param {ArrayBuffer} input - Stream, containing one or more raw TLS Records.
* @param {Object[]} args
* @returns {Object[]} Array of Object representations of TLS Records contained within input.
*/
run(input, args) {
const s = new Stream(new Uint8Array(input));
const output = [];
while (s.hasMore()) {
const record = this._readRecord(s);
if (record) {
output.push(record);
}
}
return output;
}
/**
* Reads a TLS Record from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw TLS Record.
* @returns {Object} Object representation of TLS Record.
*/
_readRecord(input) {
const RECORD_HEADER_LEN = 5;
if (input.position + RECORD_HEADER_LEN > input.length) {
input.moveTo(input.length);
return null;
}
const type = input.readInt(1);
const typeString = this._contentTypes[type] ?? type.toString();
const version = "0x" + toHexFast(input.getBytes(2));
const length = input.readInt(2);
const content = input.getBytes(length);
const truncated = content.length < length;
const recordHeader = new RecordHeader(typeString, version, length, truncated);
if (!content.length) {
return {...recordHeader};
}
if (type === ContentType.HANDSHAKE) {
return this._handshakeParser.parse(new Stream(content), recordHeader);
}
const record = {...recordHeader};
record.value = "0x" + toHexFast(content);
return record;
}
/**
* Displays the parsed TLS Records in a tabular style.
*
* @param {Object[]} data - Array of Object representations of the TLS Records.
* @returns {html} HTML representation of TLS Records contained within data.
*/
present(data) {
return data.map(r => objToTable(r)).join("\n\n");
}
}
export default ParseTLSRecord;
/**
* Repesents the known values of type field of a TLS Record header.
*/
const ContentType = Object.freeze({
CHANGE_CIPHER_SPEC: 20,
ALERT: 21,
HANDSHAKE: 22,
APPLICATION_DATA: 23,
});
/**
* Represents a TLS Record header
*/
class RecordHeader {
/**
* RecordHeader cosntructor.
*
* @param {string} type - String representation of TLS Record type field.
* @param {string} version - Hex representation of TLS Record version field.
* @param {int} length - Length of TLS Record.
* @param {bool} truncated - Is TLS Record truncated.
*/
constructor(type, version, length, truncated) {
this.type = type;
this.version = version;
this.length = length;
if (truncated) {
this.truncated = true;
}
}
}
/**
* Parses TLS Handshake messages.
*/
class HandshakeParser {
/**
* HandshakeParser constructor.
*/
constructor() {
this._clientHelloParser = new ClientHelloParser();
this._serverHelloParser = new ServerHelloParser();
this._newSessionTicketParser = new NewSessionTicketParser();
this._certificateParser = new CertificateParser();
this._certificateRequestParser = new CertificateRequestParser();
this._certificateVerifyParser = new CertificateVerifyParser();
this._handshakeTypes = new Map();
for (const key in HandshakeType) {
this._handshakeTypes[HandshakeType[key]] = key.toString().toLowerCase();
}
}
/**
* Parses a single TLS handshake message.
*
* @param {Stream} input - Stream, containing a raw Handshake message.
* @param {RecordHeader} recordHeader - TLS Record header.
* @returns {Object} Object representation of Handshake.
*/
parse(input, recordHeader) {
const output = {...recordHeader};
if (!input.hasMore()) {
return output;
}
const handshakeType = input.readInt(1);
output.handshakeType = this._handshakeTypes[handshakeType] ?? handshakeType.toString();
if (input.position + 3 > input.length) {
input.moveTo(input.length);
return output;
}
const handshakeLength = input.readInt(3);
if (handshakeLength + 4 !== recordHeader.length) {
input.moveTo(0);
output.handshakeType = this._handshakeTypes[HandshakeType.FINISHED];
output.handshakeValue = "0x" + toHexFast(input.bytes);
return output;
}
const content = input.getBytes(handshakeLength);
if (!content.length) {
return output;
}
switch (handshakeType) {
case HandshakeType.CLIENT_HELLO:
return {...output, ...this._clientHelloParser.parse(new Stream(content))};
case HandshakeType.SERVER_HELLO:
return {...output, ...this._serverHelloParser.parse(new Stream(content))};
case HandshakeType.NEW_SESSION_TICKET:
return {...output, ...this._newSessionTicketParser.parse(new Stream(content))};
case HandshakeType.CERTIFICATE:
return {...output, ...this._certificateParser.parse(new Stream(content))};
case HandshakeType.CERTIFICATE_REQUEST:
return {...output, ...this._certificateRequestParser.parse(new Stream(content))};
case HandshakeType.CERTIFICATE_VERIFY:
return {...output, ...this._certificateVerifyParser.parse(new Stream(content))};
default:
output.handshakeValue = "0x" + toHexFast(content);
}
return output;
}
}
/**
* Represents the known values of the msg_type field of a TLS Handshake message.
*/
const HandshakeType = Object.freeze({
HELLO_REQUEST: 0,
CLIENT_HELLO: 1,
SERVER_HELLO: 2,
NEW_SESSION_TICKET: 4,
CERTIFICATE: 11,
SERVER_KEY_EXCHANGE: 12,
CERTIFICATE_REQUEST: 13,
SERVER_HELLO_DONE: 14,
CERTIFICATE_VERIFY: 15,
CLIENT_KEY_EXCHANGE: 16,
FINISHED: 20,
});
/**
* Parses TLS Handshake ClientHello messages.
*/
class ClientHelloParser {
/**
* ClientHelloParser constructor.
*/
constructor() {
this._extensionsParser = new ExtensionsParser();
}
/**
* Parses a single TLS Handshake ClientHello message.
*
* @param {Stream} input - Stream, containing a raw ClientHello message.
* @returns {Object} Object representation of ClientHello.
*/
parse(input) {
const output = {};
output.clientVersion = this._readClientVersion(input);
output.random = this._readRandom(input);
const sessionID = this._readSessionID(input);
if (sessionID) {
output.sessionID = sessionID;
}
output.cipherSuites = this._readCipherSuites(input);
output.compressionMethods = this._readCompressionMethods(input);
output.extensions = this._readExtensions(input);
return output;
}
/**
* Reads the client_version field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ClientHello message, with position before client_version field.
* @returns {string} Hex representation of client_version.
*/
_readClientVersion(input) {
return readBytesAsHex(input, 2);
}
/**
* Reads the random field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ClientHello message, with position before random field.
* @returns {string} Hex representation of random.
*/
_readRandom(input) {
return readBytesAsHex(input, 32);
}
/**
* Reads the session_id field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ClientHello message, with position before session_id length field.
* @returns {string} Hex representation of session_id, or empty string if session_id not present.
*/
_readSessionID(input) {
return readSizePrefixedBytesAsHex(input, 1);
}
/**
* Reads the cipher_suites field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ClientHello message, with position before cipher_suites length field.
* @returns {Object} Object represention of cipher_suites field.
*/
_readCipherSuites(input) {
const output = {};
output.length = input.readInt(2);
if (!output.length) {
return {};
}
const cipherSuites = new Stream(input.getBytes(output.length));
if (cipherSuites.length < output.length) {
output.truncated = true;
}
output.values = [];
while (cipherSuites.hasMore()) {
const cipherSuite = readBytesAsHex(cipherSuites, 2);
if (cipherSuite) {
output.values.push(cipherSuite);
}
}
return output;
}
/**
* Reads the compression_methods field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ClientHello message, with position before compression_methods length field.
* @returns {Object} Object representation of compression_methods field.
*/
_readCompressionMethods(input) {
const output = {};
output.length = input.readInt(1);
if (!output.length) {
return {};
}
const compressionMethods = new Stream(input.getBytes(output.length));
if (compressionMethods.length < output.length) {
output.truncated = true;
}
output.values = [];
while (compressionMethods.hasMore()) {
const compressionMethod = readBytesAsHex(compressionMethods, 1);
if (compressionMethod) {
output.values.push(compressionMethod);
}
}
return output;
}
/**
* Reads the extensions field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ClientHello message, with position before extensions length field.
* @returns {Object} Object representations of extensions field.
*/
_readExtensions(input) {
const output = {};
output.length = input.readInt(2);
if (!output.length) {
return {};
}
const extensions = new Stream(input.getBytes(output.length));
if (extensions.length < output.length) {
output.truncated = true;
}
output.values = this._extensionsParser.parse(extensions);
return output;
}
}
/**
* Parses TLS Handshake ServeHello messages.
*/
class ServerHelloParser {
/**
* ServerHelloParser constructor.
*/
constructor() {
this._extensionsParser = new ExtensionsParser();
}
/**
* Parses a single TLS Handshake ServerHello message.
*
* @param {Stream} input - Stream, containing a raw ServerHello message.
* @return {Object} Object representation of ServerHello.
*/
parse(input) {
const output = {};
output.serverVersion = this._readServerVersion(input);
output.random = this._readRandom(input);
const sessionID = this._readSessionID(input);
if (sessionID) {
output.sessionID = sessionID;
}
output.cipherSuite = this._readCipherSuite(input);
output.compressionMethod = this._readCompressionMethod(input);
output.extensions = this._readExtensions(input);
return output;
}
/**
* Reads the server_version field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ServerHello message, with position before server_version field.
* @returns {string} Hex representation of server_version.
*/
_readServerVersion(input) {
return readBytesAsHex(input, 2);
}
/**
* Reads the random field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ServerHello message, with position before random field.
* @returns {string} Hex representation of random.
*/
_readRandom(input) {
return readBytesAsHex(input, 32);
}
/**
* Reads the session_id field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ServertHello message, with position before session_id length field.
* @returns {string} Hex representation of session_id, or empty string if session_id not present.
*/
_readSessionID(input) {
return readSizePrefixedBytesAsHex(input, 1);
}
/**
* Reads the cipher_suite field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ServerHello message, with position before cipher_suite field.
* @returns {string} Hex represention of cipher_suite.
*/
_readCipherSuite(input) {
return readBytesAsHex(input, 2);
}
/**
* Reads the compression_method field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ServerHello message, with position before compression_method field.
* @returns {string} Hex represention of compression_method.
*/
_readCompressionMethod(input) {
return readBytesAsHex(input, 1);
}
/**
* Reads the extensions field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw ServerHello message, with position before extensions length field.
* @returns {Object} Object representation of extensions field.
*/
_readExtensions(input) {
const output = {};
output.length = input.readInt(2);
if (!output.length) {
return {};
}
const extensions = new Stream(input.getBytes(output.length));
if (extensions.length < output.length) {
output.truncated = true;
}
output.values = this._extensionsParser.parse(extensions);
return output;
}
}
/**
* Parses TLS Handshake Hello Extensions.
*/
class ExtensionsParser {
/**
* Parses a stream of TLS Handshake Hello Extensions.
*
* @param {Stream} input - Stream, containing multiple raw Extensions, with position before first extension length field.
* @returns {Object[]} Array of Object representations of Extensions contained within input.
*/
parse(input) {
const output = [];
while (input.hasMore()) {
const extension = this._readExtension(input);
if (extension) {
output.push(extension);
}
}
return output;
}
/**
* Reads a single Extension from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a list of Extensions, with position before the length field of the next Extension.
* @returns {Object} Object representation of Extension.
*/
_readExtension(input) {
const output = {};
if (input.position + 4 > input.length) {
input.moveTo(input.length);
return null;
}
output.type = "0x" + toHexFast(input.getBytes(2));
output.length = input.readInt(2);
if (!output.length) {
return output;
}
const value = input.getBytes(output.length);
if (!value || value.length !== output.length) {
output.truncated = true;
}
if (value && value.length) {
output.value = "0x" + toHexFast(value);
}
return output;
}
}
/**
* Parses TLS Handshake NewSessionTicket messages.
*/
class NewSessionTicketParser {
/**
* Parses a single TLS Handshake NewSessionTicket message.
*
* @param {Stream} input - Stream, containing a raw NewSessionTicket message.
* @returns {Object} Object representation of NewSessionTicket.
*/
parse(input) {
return {
ticketLifetimeHint: this._readTicketLifetimeHint(input),
ticket: this._readTicket(input),
};
}
/**
* Reads the ticket_lifetime_hint field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw NewSessionTicket message, with position before ticket_lifetime_hint field.
* @returns {string} Lifetime hint, in seconds.
*/
_readTicketLifetimeHint(input) {
if (input.position + 4 > input.length) {
input.moveTo(input.length);
return "";
}
return input.readInt(4) + "s";
}
/**
* Reads the ticket field fromt the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw NewSessionTicket message, with position before ticket length field.
* @returns {string} Hex representation of ticket.
*/
_readTicket(input) {
return readSizePrefixedBytesAsHex(input, 2);
}
}
/**
* Parses TLS Handshake Certificate messages.
*/
class CertificateParser {
/**
* Parses a single TLS Handshake Certificate message.
*
* @param {Stream} input - Stream, containing a raw Certificate message.
* @returns {Object} Object representation of Certificate.
*/
parse(input) {
const output = {};
output.certificateList = this._readCertificateList(input);
return output;
}
/**
* Reads the certificate_list field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw Certificate message, with position before certificate_list length field.
* @returns {string[]} Array of strings, each containing a hex representation of a value within the certificate_list field.
*/
_readCertificateList(input) {
const output = {};
if (input.position + 3 > input.length) {
input.moveTo(input.length);
return output;
}
output.length = input.readInt(3);
if (!output.length) {
return output;
}
const certificates = new Stream(input.getBytes(output.length));
if (certificates.length < output.length) {
output.truncated = true;
}
output.values = [];
while (certificates.hasMore()) {
const certificate = this._readCertificate(certificates);
if (certificate) {
output.values.push(certificate);
}
}
return output;
}
/**
* Reads a single certificate from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a list of certificicates, with position before the length field of the next certificate.
* @returns {string} Hex representation of certificate.
*/
_readCertificate(input) {
return readSizePrefixedBytesAsHex(input, 3);
}
}
/**
* Parses TLS Handshake CertificateRequest messages.
*/
class CertificateRequestParser {
/**
* Parses a single TLS Handshake CertificateRequest message.
*
* @param {Stream} input - Stream, containing a raw CertificateRequest message.
* @return {Object} Object representation of CertificateRequest.
*/
parse(input) {
const output = {};
output.certificateTypes = this._readCertificateTypes(input);
output.supportedSignatureAlgorithms = this._readSupportedSignatureAlgorithms(input);
const certificateAuthorities = this._readCertificateAuthorities(input);
if (certificateAuthorities.length) {
output.certificateAuthorities = certificateAuthorities;
}
return output;
}
/**
* Reads the certificate_types field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw CertificateRequest message, with position before certificate_types length field.
* @return {string[]} Array of strings, each containing a hex representation of a value within the certificate_types field.
*/
_readCertificateTypes(input) {
const output = {};
output.length = input.readInt(1);
if (!output.length) {
return {};
}
const certificateTypes = new Stream(input.getBytes(output.length));
if (certificateTypes.length < output.length) {
output.truncated = true;
}
output.values = [];
while (certificateTypes.hasMore()) {
const certificateType = readBytesAsHex(certificateTypes, 1);
if (certificateType) {
output.values.push(certificateType);
}
}
return output;
}
/**
* Reads the supported_signature_algorithms field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw CertificateRequest message, with position before supported_signature_algorithms length field.
* @returns {string[]} Array of strings, each containing a hex representation of a value within the supported_signature_algorithms field.
*/
_readSupportedSignatureAlgorithms(input) {
const output = {};
output.length = input.readInt(2);
if (!output.length) {
return {};
}
const signatureAlgorithms = new Stream(input.getBytes(output.length));
if (signatureAlgorithms.length < output.length) {
output.truncated = true;
}
output.values = [];
while (signatureAlgorithms.hasMore()) {
const signatureAlgorithm = readBytesAsHex(signatureAlgorithms, 2);
if (signatureAlgorithm) {
output.values.push(signatureAlgorithm);
}
}
return output;
}
/**
* Reads the certificate_authorities field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw CertificateRequest message, with position before certificate_authorities length field.
* @returns {string[]} Array of strings, each containing a hex representation of a value within the certificate_authorities field.
*/
_readCertificateAuthorities(input) {
const output = {};
output.length = input.readInt(2);
if (!output.length) {
return {};
}
const certificateAuthorities = new Stream(input.getBytes(output.length));
if (certificateAuthorities.length < output.length) {
output.truncated = true;
}
output.values = [];
while (certificateAuthorities.hasMore()) {
const certificateAuthority = this._readCertificateAuthority(certificateAuthorities);
if (certificateAuthority) {
output.values.push(certificateAuthority);
}
}
return output;
}
/**
* Reads a single certificate authority from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a list of raw certificate authorities, with position before the length field of the next certificate authority.
* @returns {string} Hex representation of certificate authority.
*/
_readCertificateAuthority(input) {
return readSizePrefixedBytesAsHex(input, 2);
}
}
/**
* Parses TLS Handshake CertificateVerify messages.
*/
class CertificateVerifyParser {
/**
* Parses a single CertificateVerify Message.
*
* @param {Stream} input - Stream, containing a raw CertificateVerify message.
* @returns {Object} Object representation of CertificateVerify.
*/
parse(input) {
return {
algorithmHash: this._readAlgorithmHash(input),
algorithmSignature: this._readAlgorithmSignature(input),
signature: this._readSignature(input),
};
}
/**
* Reads the algorithm.hash field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw CertificateVerify message, with position before algorithm.hash field.
* @return {string} Hex representation of hash algorithm.
*/
_readAlgorithmHash(input) {
return readBytesAsHex(input, 1);
}
/**
* Reads the algorithm.signature field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw CertificateVerify message, with position before algorithm.signature field.
* @return {string} Hex representation of signature algorithm.
*/
_readAlgorithmSignature(input) {
return readBytesAsHex(input, 1);
}
/**
* Reads the signature field from the following bytes in the provided Stream.
*
* @param {Stream} input - Stream, containing a raw CertificateVerify message, with position before signature field.
* @return {string} Hex representation of signature.
*/
_readSignature(input) {
return readSizePrefixedBytesAsHex(input, 2);
}
}
/**
* Read the following size prefixed bytes from the provided Stream, and reuturn as a hex string.
*
* @param {Stream} input - Stream to read from.
* @param {int} sizePrefixLength - Length of the size prefix field.
* @returns {string} Hex representation of bytes read from Stream, empty string is returned if
* field cannot be read in full.
*/
function readSizePrefixedBytesAsHex(input, sizePrefixLength) {
const length = input.readInt(sizePrefixLength);
if (!length) {
return "";
}
return readBytesAsHex(input, length);
}
/**
* Read n bytes from the provided Stream, and return as a hex string.
*
* @param {Stream} input - Stream to read from.
* @param {int} n - Number of bytes to read.
* @returns {string} Hex representation of bytes read from Stream, or empty string if field cannot
* be read in full.
*/
function readBytesAsHex(input, n) {
const bytes = input.getBytes(n);
if (!bytes || bytes.length !== n) {
return "";
}
return "0x" + toHexFast(bytes);
}

View file

@ -0,0 +1,391 @@
/**
* @author robinsandhu
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation.mjs";
import { fromBase64 } from "../lib/Base64.mjs";
import { toHex } from "../lib/Hex.mjs";
import { formatDnObj } from "../lib/PublicKey.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
/**
* Parse X.509 CRL operation
*/
class ParseX509CRL extends Operation {
/**
* ParseX509CRL constructor
*/
constructor() {
super();
this.name = "Parse X.509 CRL";
this.module = "PublicKey";
this.description = "Parse Certificate Revocation List (CRL)";
this.infoURL = "https://wikipedia.org/wiki/Certificate_revocation_list";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Input format",
"type": "option",
"value": ["PEM", "DER Hex", "Base64", "Raw"]
}
];
this.checks = [
{
"pattern": "^-+BEGIN X509 CRL-+\\r?\\n[\\da-z+/\\n\\r]+-+END X509 CRL-+\\r?\\n?$",
"flags": "i",
"args": ["PEM"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string} Human-readable description of a Certificate Revocation List (CRL).
*/
run(input, args) {
if (!input.length) {
return "No input";
}
const inputFormat = args[0];
let undefinedInputFormat = false;
try {
switch (inputFormat) {
case "DER Hex":
input = input.replace(/\s/g, "").toLowerCase();
break;
case "PEM":
break;
case "Base64":
input = toHex(fromBase64(input, null, "byteArray"), "");
break;
case "Raw":
input = toHex(Utils.strToArrayBuffer(input), "");
break;
default:
undefinedInputFormat = true;
}
} catch (e) {
throw "Certificate load error (non-certificate input?)";
}
if (undefinedInputFormat) throw "Undefined input format";
const crl = new r.X509CRL(input);
let out = `Certificate Revocation List (CRL):
Version: ${crl.getVersion() === null ? "1 (0x0)" : "2 (0x1)"}
Signature Algorithm: ${crl.getSignatureAlgorithmField()}
Issuer:\n${formatDnObj(crl.getIssuer(), 8)}
Last Update: ${generalizedDateTimeToUTC(crl.getThisUpdate())}
Next Update: ${generalizedDateTimeToUTC(crl.getNextUpdate())}\n`;
if (crl.getParam().ext !== undefined) {
out += `\tCRL extensions:\n${formatCRLExtensions(crl.getParam().ext, 8)}\n`;
}
out += `Revoked Certificates:\n${formatRevokedCertificates(crl.getRevCertArray(), 4)}
Signature Value:\n${formatCRLSignature(crl.getSignatureValueHex(), 8)}`;
return out;
}
}
/**
* Generalized date time string to UTC.
* @param {string} datetime
* @returns UTC datetime string.
*/
function generalizedDateTimeToUTC(datetime) {
// Ensure the string is in the correct format
if (!/^\d{12,14}Z$/.test(datetime)) {
throw new OperationError(`failed to format datetime string ${datetime}`);
}
// Extract components
let centuary = "20";
if (datetime.length === 15) {
centuary = datetime.substring(0, 2);
datetime = datetime.slice(2);
}
const year = centuary + datetime.substring(0, 2);
const month = datetime.substring(2, 4);
const day = datetime.substring(4, 6);
const hour = datetime.substring(6, 8);
const minute = datetime.substring(8, 10);
const second = datetime.substring(10, 12);
// Construct ISO 8601 format string
const isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}Z`;
// Parse using standard Date object
const isoDateTime = new Date(isoString);
return isoDateTime.toUTCString();
}
/**
* Format CRL extensions.
* @param {r.ExtParam[] | undefined} extensions
* @param {Number} indent
* @returns Formatted string detailing CRL extensions.
*/
function formatCRLExtensions(extensions, indent) {
if (Array.isArray(extensions) === false || extensions.length === 0) {
return indentString(`No CRL extensions.`, indent);
}
let out = ``;
extensions.sort((a, b) => {
if (!Object.hasOwn(a, "extname") || !Object.hasOwn(b, "extname")) {
return 0;
}
if (a.extname < b.extname) {
return -1;
} else if (a.extname === b.extname) {
return 0;
} else {
return 1;
}
});
extensions.forEach((ext) => {
if (!Object.hasOwn(ext, "extname")) {
throw new OperationError(`CRL entry extension object missing 'extname' key: ${ext}`);
}
switch (ext.extname) {
case "authorityKeyIdentifier":
out += `X509v3 Authority Key Identifier:\n`;
if (Object.hasOwn(ext, "kid")) {
out += `\tkeyid:${colonDelimitedHexFormatString(ext.kid.hex.toUpperCase())}\n`;
}
if (Object.hasOwn(ext, "issuer")) {
out += `\tDirName:${ext.issuer.str}\n`;
}
if (Object.hasOwn(ext, "sn")) {
out += `\tserial:${colonDelimitedHexFormatString(ext.sn.hex.toUpperCase())}\n`;
}
break;
case "cRLDistributionPoints":
out += `X509v3 CRL Distribution Points:\n`;
ext.array.forEach((distPoint) => {
const fullName = `Full Name:\n${formatGeneralNames(distPoint.dpname.full, 4)}`;
out += indentString(fullName, 4) + "\n";
});
break;
case "cRLNumber":
if (!Object.hasOwn(ext, "num")) {
throw new OperationError(`'cRLNumber' CRL entry extension missing 'num' key: ${ext}`);
}
out += `X509v3 CRL Number:\n\t${ext.num.hex.toUpperCase()}\n`;
break;
case "issuerAltName":
out += `X509v3 Issuer Alternative Name:\n${formatGeneralNames(ext.array, 4)}\n`;
break;
default:
out += `${ext.extname}:\n`;
out += `\tUnsupported CRL extension. Try openssl CLI.\n`;
break;
}
});
return indentString(chop(out), indent);
}
/**
* Format general names array.
* @param {Object[]} names
* @returns Multi-line formatted string describing all supported general name types.
*/
function formatGeneralNames(names, indent) {
let out = ``;
names.forEach((name) => {
const key = Object.keys(name)[0];
switch (key) {
case "ip":
out += `IP:${name.ip}\n`;
break;
case "dns":
out += `DNS:${name.dns}\n`;
break;
case "uri":
out += `URI:${name.uri}\n`;
break;
case "rfc822":
out += `EMAIL:${name.rfc822}\n`;
break;
case "dn":
out += `DIR:${name.dn.str}\n`;
break;
case "other":
out += `OtherName:${name.other.oid}::${Object.values(name.other.value)[0].str}\n`;
break;
default:
out += `${key}: unsupported general name type`;
break;
}
});
return indentString(chop(out), indent);
}
/**
* Colon-delimited hex formatted output.
* @param {string} hexString Hex String
* @returns String representing input hex string with colon delimiter.
*/
function colonDelimitedHexFormatString(hexString) {
if (hexString.length % 2 !== 0) {
hexString = "0" + hexString;
}
return chop(hexString.replace(/(..)/g, "$&:"));
}
/**
* Format revoked certificates array
* @param {r.RevokedCertificate[] | null} revokedCertificates
* @param {Number} indent
* @returns Multi-line formatted string output of revoked certificates array
*/
function formatRevokedCertificates(revokedCertificates, indent) {
if (Array.isArray(revokedCertificates) === false || revokedCertificates.length === 0) {
return indentString("No Revoked Certificates.", indent);
}
let out=``;
revokedCertificates.forEach((revCert) => {
if (!Object.hasOwn(revCert, "sn") || !Object.hasOwn(revCert, "date")) {
throw new OperationError("invalid revoked certificate object, missing either serial number or date");
}
out += `Serial Number: ${revCert.sn.hex.toUpperCase()}
Revocation Date: ${generalizedDateTimeToUTC(revCert.date)}\n`;
if (Object.hasOwn(revCert, "ext") && Array.isArray(revCert.ext) && revCert.ext.length !== 0) {
out += `\tCRL entry extensions:\n${indentString(formatCRLEntryExtensions(revCert.ext), 2*indent)}\n`;
}
});
return indentString(chop(out), indent);
}
/**
* Format CRL entry extensions.
* @param {Object[]} exts
* @returns Formatted multi-line string describing CRL entry extensions.
*/
function formatCRLEntryExtensions(exts) {
let out = ``;
const crlReasonCodeToReasonMessage = {
0: "Unspecified",
1: "Key Compromise",
2: "CA Compromise",
3: "Affiliation Changed",
4: "Superseded",
5: "Cessation Of Operation",
6: "Certificate Hold",
8: "Remove From CRL",
9: "Privilege Withdrawn",
10: "AA Compromise",
};
const holdInstructionOIDToName = {
"1.2.840.10040.2.1": "Hold Instruction None",
"1.2.840.10040.2.2": "Hold Instruction Call Issuer",
"1.2.840.10040.2.3": "Hold Instruction Reject",
};
exts.forEach((ext) => {
if (!Object.hasOwn(ext, "extname")) {
throw new OperationError(`CRL entry extension object missing 'extname' key: ${ext}`);
}
switch (ext.extname) {
case "cRLReason":
if (!Object.hasOwn(ext, "code")) {
throw new OperationError(`'cRLReason' CRL entry extension missing 'code' key: ${ext}`);
}
out += `X509v3 CRL Reason Code:
${Object.hasOwn(crlReasonCodeToReasonMessage, ext.code) ? crlReasonCodeToReasonMessage[ext.code] : `invalid reason code: ${ext.code}`}\n`;
break;
case "2.5.29.23": // Hold instruction
out += `Hold Instruction Code:\n\t${Object.hasOwn(holdInstructionOIDToName, ext.extn.oid) ? holdInstructionOIDToName[ext.extn.oid] : `${ext.extn.oid}: unknown hold instruction OID`}\n`;
break;
case "2.5.29.24": // Invalidity Date
out += `Invalidity Date:\n\t${generalizedDateTimeToUTC(ext.extn.gentime.str)}\n`;
break;
default:
out += `${ext.extname}:\n`;
out += `\tUnsupported CRL entry extension. Try openssl CLI.\n`;
break;
}
});
return chop(out);
}
/**
* Format CRL signature.
* @param {String} sigHex
* @param {Number} indent
* @returns String representing hex signature value formatted on multiple lines.
*/
function formatCRLSignature(sigHex, indent) {
if (sigHex.length % 2 !== 0) {
sigHex = "0" + sigHex;
}
return indentString(formatMultiLine(chop(sigHex.replace(/(..)/g, "$&:"))), indent);
}
/**
* Format string onto multiple lines.
* @param {string} longStr
* @returns String as a multi-line string.
*/
function formatMultiLine(longStr) {
const lines = [];
for (let remain = longStr ; remain !== "" ; remain = remain.substring(54)) {
lines.push(remain.substring(0, 54));
}
return lines.join("\n");
}
/**
* Indent a multi-line string by n spaces.
* @param {string} input String
* @param {number} spaces How many leading spaces
* @returns Indented string.
*/
function indentString(input, spaces) {
const indent = " ".repeat(spaces);
return input.replace(/^/gm, indent);
}
/**
* Remove last character from a string.
* @param {string} s String
* @returns Chopped string.
*/
function chop(s) {
if (s.length < 1) {
return s;
}
return s.substring(0, s.length - 1);
}
export default ParseX509CRL;

View file

@ -59,15 +59,16 @@ class ROT13 extends Operation {
rot13Upperacse = args[1], rot13Upperacse = args[1],
rotNumbers = args[2]; rotNumbers = args[2];
let amount = args[3], let amount = args[3],
chr; amountNumbers = args[3];
if (amount) { if (amount) {
if (amount < 0) { if (amount < 0) {
amount = 26 - (Math.abs(amount) % 26); amount = 26 - (Math.abs(amount) % 26);
amountNumbers = 10 - (Math.abs(amountNumbers) % 10);
} }
for (let i = 0; i < input.length; i++) { for (let i = 0; i < input.length; i++) {
chr = input[i]; let chr = input[i];
if (rot13Upperacse && chr >= 65 && chr <= 90) { // Upper case if (rot13Upperacse && chr >= 65 && chr <= 90) { // Upper case
chr = (chr - 65 + amount) % 26; chr = (chr - 65 + amount) % 26;
output[i] = chr + 65; output[i] = chr + 65;
@ -75,7 +76,7 @@ class ROT13 extends Operation {
chr = (chr - 97 + amount) % 26; chr = (chr - 97 + amount) % 26;
output[i] = chr + 97; output[i] = chr + 97;
} else if (rotNumbers && chr >= 48 && chr <= 57) { // Numbers } else if (rotNumbers && chr >= 48 && chr <= 57) { // Numbers
chr = (chr - 48 + amount) % 10; chr = (chr - 48 + amountNumbers) % 10;
output[i] = chr + 48; output[i] = chr + 48;
} }
} }

View file

@ -60,7 +60,7 @@ class RSASign extends Operation {
const privateKey = forge.pki.decryptRsaPrivateKey(key, password); const privateKey = forge.pki.decryptRsaPrivateKey(key, password);
// Generate message hash // Generate message hash
const md = MD_ALGORITHMS[mdAlgo].create(); const md = MD_ALGORITHMS[mdAlgo].create();
md.update(input, "utf8"); md.update(input, "raw");
// Sign message hash // Sign message hash
const sig = privateKey.sign(md); const sig = privateKey.sign(md);
return sig; return sig;

View file

@ -8,6 +8,7 @@ import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import forge from "node-forge"; import forge from "node-forge";
import { MD_ALGORITHMS } from "../lib/RSA.mjs"; import { MD_ALGORITHMS } from "../lib/RSA.mjs";
import Utils from "../Utils.mjs";
/** /**
* RSA Verify operation * RSA Verify operation
@ -37,6 +38,11 @@ class RSAVerify extends Operation {
type: "text", type: "text",
value: "" value: ""
}, },
{
name: "Message format",
type: "option",
value: ["Raw", "Hex", "Base64"]
},
{ {
name: "Message Digest Algorithm", name: "Message Digest Algorithm",
type: "option", type: "option",
@ -51,7 +57,7 @@ class RSAVerify extends Operation {
* @returns {string} * @returns {string}
*/ */
run(input, args) { run(input, args) {
const [pemKey, message, mdAlgo] = args; const [pemKey, message, format, mdAlgo] = args;
if (pemKey.replace("-----BEGIN RSA PUBLIC KEY-----", "").length === 0) { if (pemKey.replace("-----BEGIN RSA PUBLIC KEY-----", "").length === 0) {
throw new OperationError("Please enter a public key."); throw new OperationError("Please enter a public key.");
} }
@ -60,7 +66,8 @@ class RSAVerify extends Operation {
const pubKey = forge.pki.publicKeyFromPem(pemKey); const pubKey = forge.pki.publicKeyFromPem(pemKey);
// Generate message digest // Generate message digest
const md = MD_ALGORITHMS[mdAlgo].create(); const md = MD_ALGORITHMS[mdAlgo].create();
md.update(message, "utf8"); const messageStr = Utils.convertToByteString(message, format);
md.update(messageStr, "raw");
// Compare signed message digest and generated message digest // Compare signed message digest and generated message digest
const result = pubKey.verify(md.digest().bytes(), input); const result = pubKey.verify(md.digest().bytes(), input);
return result ? "Verified OK" : "Verification Failure"; return result ? "Verified OK" : "Verification Failure";

View file

@ -0,0 +1,57 @@
/**
* @author c65722 []
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Stream from "../lib/Stream.mjs";
/**
* Strip IPv4 header operation
*/
class StripIPv4Header extends Operation {
/**
* StripIPv4Header constructor
*/
constructor() {
super();
this.name = "Strip IPv4 header";
this.module = "Default";
this.description = "Strips the IPv4 header from an IPv4 packet, outputting the payload.";
this.infoURL = "https://wikipedia.org/wiki/IPv4";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
const MIN_HEADER_LEN = 20;
const s = new Stream(new Uint8Array(input));
if (s.length < MIN_HEADER_LEN) {
throw new OperationError("Input length is less than minimum IPv4 header length");
}
const ihl = s.readInt(1) & 0x0f;
const dataOffsetBytes = ihl * 4;
if (s.length < dataOffsetBytes) {
throw new OperationError("Input length is less than IHL");
}
s.moveTo(dataOffsetBytes);
return s.getBytes().buffer;
}
}
export default StripIPv4Header;

View file

@ -0,0 +1,60 @@
/**
* @author c65722 []
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Stream from "../lib/Stream.mjs";
/**
* Strip TCP header operation
*/
class StripTCPHeader extends Operation {
/**
* StripTCPHeader constructor
*/
constructor() {
super();
this.name = "Strip TCP header";
this.module = "Default";
this.description = "Strips the TCP header from a TCP segment, outputting the payload.";
this.infoURL = "https://wikipedia.org/wiki/Transmission_Control_Protocol";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
const MIN_HEADER_LEN = 20;
const DATA_OFFSET_OFFSET = 12;
const DATA_OFFSET_LEN_BITS = 4;
const s = new Stream(new Uint8Array(input));
if (s.length < MIN_HEADER_LEN) {
throw new OperationError("Need at least 20 bytes for a TCP Header");
}
s.moveTo(DATA_OFFSET_OFFSET);
const dataOffsetWords = s.readBits(DATA_OFFSET_LEN_BITS);
const dataOffsetBytes = dataOffsetWords * 4;
if (s.length < dataOffsetBytes) {
throw new OperationError("Input length is less than data offset");
}
s.moveTo(dataOffsetBytes);
return s.getBytes().buffer;
}
}
export default StripTCPHeader;

View file

@ -0,0 +1,51 @@
/**
* @author c65722 []
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Stream from "../lib/Stream.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Strip UDP header operation
*/
class StripUDPHeader extends Operation {
/**
* StripUDPHeader constructor
*/
constructor() {
super();
this.name = "Strip UDP header";
this.module = "Default";
this.description = "Strips the UDP header from a UDP datagram, outputting the payload.";
this.infoURL = "https://wikipedia.org/wiki/User_Datagram_Protocol";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
const HEADER_LEN = 8;
const s = new Stream(new Uint8Array(input));
if (s.length < HEADER_LEN) {
throw new OperationError("Need 8 bytes for a UDP Header");
}
s.moveTo(HEADER_LEN);
return s.getBytes().buffer;
}
}
export default StripUDPHeader;

View file

@ -0,0 +1,79 @@
/**
* @author Oshawk [oshawk@protonmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Take nth bytes operation
*/
class TakeNthBytes extends Operation {
/**
* TakeNthBytes constructor
*/
constructor() {
super();
this.name = "Take nth bytes";
this.module = "Default";
this.description = "Takes every nth byte starting with a given byte.";
this.infoURL = "";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
name: "Take every",
type: "number",
value: 4
},
{
name: "Starting at",
type: "number",
value: 0
},
{
name: "Apply to each line",
type: "boolean",
value: false
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const n = args[0];
const start = args[1];
const eachLine = args[2];
if (parseInt(n, 10) !== n || n <= 0) {
throw new OperationError("'Take every' must be a positive integer.");
}
if (parseInt(start, 10) !== start || start < 0) {
throw new OperationError("'Starting at' must be a positive or zero integer.");
}
let offset = 0;
const output = [];
for (let i = 0; i < input.length; i++) {
if (eachLine && input[i] === 0x0a) {
output.push(0x0a);
offset = i + 1;
} else if (i - offset >= start && (i - (start + offset)) % n === 0) {
output.push(input[i]);
}
}
return output;
}
}
export default TakeNthBytes;

View file

@ -22,7 +22,7 @@ class TripleDESDecrypt extends Operation {
this.name = "Triple DES Decrypt"; this.name = "Triple DES Decrypt";
this.module = "Ciphers"; this.module = "Ciphers";
this.description = "Triple DES applies DES three times to each block to increase key size.<br><br><b>Key:</b> Triple DES uses a key length of 24 bytes (192 bits).<br>DES uses a key length of 8 bytes (64 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default."; this.description = "Triple DES applies DES three times to each block to increase key size.<br><br><b>Key:</b> Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default.";
this.infoURL = "https://wikipedia.org/wiki/Triple_DES"; this.infoURL = "https://wikipedia.org/wiki/Triple_DES";
this.inputType = "string"; this.inputType = "string";
this.outputType = "string"; this.outputType = "string";
@ -73,8 +73,7 @@ class TripleDESDecrypt extends Operation {
if (key.length !== 24 && key.length !== 16) { if (key.length !== 24 && key.length !== 16) {
throw new OperationError(`Invalid key length: ${key.length} bytes throw new OperationError(`Invalid key length: ${key.length} bytes
Triple DES uses a key length of 24 bytes (192 bits). Triple DES uses a key length of 24 bytes (192 bits).`);
DES uses a key length of 8 bytes (64 bits).`);
} }
if (iv.length !== 8 && mode !== "ECB") { if (iv.length !== 8 && mode !== "ECB") {
throw new OperationError(`Invalid IV length: ${iv.length} bytes throw new OperationError(`Invalid IV length: ${iv.length} bytes

View file

@ -22,7 +22,7 @@ class TripleDESEncrypt extends Operation {
this.name = "Triple DES Encrypt"; this.name = "Triple DES Encrypt";
this.module = "Ciphers"; this.module = "Ciphers";
this.description = "Triple DES applies DES three times to each block to increase key size.<br><br><b>Key:</b> Triple DES uses a key length of 24 bytes (192 bits).<br>DES uses a key length of 8 bytes (64 bits).<br><br>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used."; this.description = "Triple DES applies DES three times to each block to increase key size.<br><br><b>Key:</b> Triple DES uses a key length of 24 bytes (192 bits).<br><br>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
this.infoURL = "https://wikipedia.org/wiki/Triple_DES"; this.infoURL = "https://wikipedia.org/wiki/Triple_DES";
this.inputType = "string"; this.inputType = "string";
this.outputType = "string"; this.outputType = "string";
@ -72,8 +72,7 @@ class TripleDESEncrypt extends Operation {
if (key.length !== 24 && key.length !== 16) { if (key.length !== 24 && key.length !== 16) {
throw new OperationError(`Invalid key length: ${key.length} bytes throw new OperationError(`Invalid key length: ${key.length} bytes
Triple DES uses a key length of 24 bytes (192 bits). Triple DES uses a key length of 24 bytes (192 bits).`);
DES uses a key length of 8 bytes (64 bits).`);
} }
if (iv.length !== 8 && mode !== "ECB") { if (iv.length !== 8 && mode !== "ECB") {
throw new OperationError(`Invalid IV length: ${iv.length} bytes throw new OperationError(`Invalid IV length: ${iv.length} bytes

View file

@ -23,7 +23,7 @@ const dir = path.join(`${process.cwd()}/src/node`);
if (!fs.existsSync(dir)) { if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd()); console.log("\nCWD: " + process.cwd());
console.log("Error: generateNodeIndex.mjs should be run from the project root"); console.log("Error: generateNodeIndex.mjs should be run from the project root");
console.log("Example> node --experimental-modules src/core/config/scripts/generateNodeIndex.mjs"); console.log("Example> node --experimental-modules src/node/config/scripts/generateNodeIndex.mjs");
process.exit(1); process.exit(1);
} }

3
src/web/App.mjs Executable file → Normal file
View file

@ -60,6 +60,7 @@ class App {
this.initialiseSplitter(); this.initialiseSplitter();
this.loadLocalStorage(); this.loadLocalStorage();
this.manager.options.applyPreferredColorScheme();
this.populateOperationsList(); this.populateOperationsList();
this.manager.setup(); this.manager.setup();
this.manager.output.saveBombe(); this.manager.output.saveBombe();
@ -536,6 +537,8 @@ class App {
// Read in theme from URI params // Read in theme from URI params
if (this.uriParams.theme) { if (this.uriParams.theme) {
this.manager.options.changeTheme(Utils.escapeHtml(this.uriParams.theme)); this.manager.options.changeTheme(Utils.escapeHtml(this.uriParams.theme));
} else {
this.manager.options.applyPreferredColorScheme();
} }
window.dispatchEvent(this.manager.statechange); window.dispatchEvent(this.manager.statechange);

8
src/web/waiters/OptionsWaiter.mjs Executable file → Normal file
View file

@ -163,6 +163,14 @@ class OptionsWaiter {
themeSelect.selectedIndex = themeSelect.querySelector(`option[value="${theme}"`).index; themeSelect.selectedIndex = themeSelect.querySelector(`option[value="${theme}"`).index;
} }
/**
* Applies the user's preferred color scheme using the `prefers-color-scheme` media query.
*/
applyPreferredColorScheme() {
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)").matches;
const theme = prefersDarkScheme ? "dark" : "classic";
this.changeTheme(theme);
}
/** /**
* Changes the console logging level. * Changes the console logging level.

View file

@ -675,42 +675,42 @@ module.exports = {
} }
}, },
"Loading from URL": browser => { // "Loading from URL": browser => {
utils.clear(browser); // utils.clear(browser);
/* Side panel displays correct info */ // /* Side panel displays correct info */
utils.uploadFile(browser, "files/TowelDay.jpeg"); // utils.uploadFile(browser, "files/TowelDay.jpeg");
browser // browser
.waitForElementVisible("#input-text .cm-file-details") // .waitForElementVisible("#input-text .cm-file-details")
.waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown") // .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
.waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail") // .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
.waitForElementVisible("#input-text .cm-file-details .file-details-name") // .waitForElementVisible("#input-text .cm-file-details .file-details-name")
.waitForElementVisible("#input-text .cm-file-details .file-details-size") // .waitForElementVisible("#input-text .cm-file-details .file-details-size")
.waitForElementVisible("#input-text .cm-file-details .file-details-type") // .waitForElementVisible("#input-text .cm-file-details .file-details-type")
.waitForElementVisible("#input-text .cm-file-details .file-details-loaded"); // .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
/* Complex deep link populates the input correctly (encoding, eol, input) */ // /* Complex deep link populates the input correctly (encoding, eol, input) */
browser // browser
.urlHash("recipe=To_Base64('A-Za-z0-9%2B/%3D')&input=VGhlIHNoaXBzIGh1bmcgaW4gdGhlIHNreSBpbiBtdWNoIHRoZSBzYW1lIHdheSB0aGF0IGJyaWNrcyBkb24ndC4M&ienc=21866&oenc=1201&ieol=FF&oeol=PS") // .urlHash("recipe=To_Base64('A-Za-z0-9%2B/%3D')&input=VGhlIHNoaXBzIGh1bmcgaW4gdGhlIHNreSBpbiBtdWNoIHRoZSBzYW1lIHdheSB0aGF0IGJyaWNrcyBkb24ndC4M&ienc=21866&oenc=1201&ieol=FF&oeol=PS")
.waitForElementVisible("#rec-list li.operation"); // .waitForElementVisible("#rec-list li.operation");
browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^.{65}$/); // browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^.{65}$/);
browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("66"); // browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("66");
browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2"); // browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
browser.expect.element("#input-text .chr-enc-value").text.that.equals("KOI8-U Ukrainian Cyrillic"); // browser.expect.element("#input-text .chr-enc-value").text.that.equals("KOI8-U Ukrainian Cyrillic");
browser.expect.element("#output-text .chr-enc-value").text.that.equals("UTF-16BE"); // browser.expect.element("#output-text .chr-enc-value").text.that.equals("UTF-16BE");
browser.expect.element("#input-text .eol-value").text.that.equals("FF"); // browser.expect.element("#input-text .eol-value").text.that.equals("FF");
browser.expect.element("#output-text .eol-value").text.that.equals("PS"); // browser.expect.element("#output-text .eol-value").text.that.equals("PS");
utils.bake(browser); // utils.bake(browser);
browser.expect.element(`#output-text .cm-content`).to.have.property("textContent").match(/^.{44}$/); // browser.expect.element(`#output-text .cm-content`).to.have.property("textContent").match(/^.{44}$/);
browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("44"); // browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("44");
browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1"); // browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
}, // },
"Replace input with output": browser => { "Replace input with output": browser => {
/* Input is correctly populated */ /* Input is correctly populated */

View file

@ -236,7 +236,7 @@ module.exports = {
// testOp(browser, "OR", "test input", "test_output"); // testOp(browser, "OR", "test input", "test_output");
// testOp(browser, "Object Identifier to Hex", "test input", "test_output"); // testOp(browser, "Object Identifier to Hex", "test input", "test_output");
testOpHtml(browser, "Offset checker", "test input\n\nbest input", ".hl5", "est input"); testOpHtml(browser, "Offset checker", "test input\n\nbest input", ".hl5", "est input");
// testOp(browser, "Optical Character Recognition", "test input", "test_output"); testOpFile(browser, "Optical Character Recognition", "files/testocr.png", false, /This is a lot of 12 point text to test the/, [], 10000);
// testOp(browser, "PEM to Hex", "test input", "test_output"); // testOp(browser, "PEM to Hex", "test input", "test_output");
// testOp(browser, "PGP Decrypt", "test input", "test_output"); // testOp(browser, "PGP Decrypt", "test input", "test_output");
// testOp(browser, "PGP Decrypt and Verify", "test input", "test_output"); // testOp(browser, "PGP Decrypt and Verify", "test input", "test_output");
@ -408,7 +408,7 @@ module.exports = {
* @param {Browser} browser - Nightwatch client * @param {Browser} browser - Nightwatch client
* @param {string|Array<string>} opName - name of operation to be tested, array for multiple ops * @param {string|Array<string>} opName - name of operation to be tested, array for multiple ops
* @param {string} input - input text for test * @param {string} input - input text for test
* @param {Array<string>|Array<Array<string>>} args - arguments, nested if multiple ops * @param {Array<string>|Array<Array<string>>} [args=[]] - arguments, nested if multiple ops
*/ */
function bakeOp(browser, opName, input, args=[]) { function bakeOp(browser, opName, input, args=[]) {
browser.perform(function() { browser.perform(function() {
@ -425,8 +425,8 @@ function bakeOp(browser, opName, input, args=[]) {
* @param {Browser} browser - Nightwatch client * @param {Browser} browser - Nightwatch client
* @param {string|Array<string>} opName - name of operation to be tested, array for multiple ops * @param {string|Array<string>} opName - name of operation to be tested, array for multiple ops
* @param {string} input - input text * @param {string} input - input text
* @param {string} output - expected output * @param {string|RegExp} output - expected output
* @param {Array<string>|Array<Array<string>>} args - arguments, nested if multiple ops * @param {Array<string>|Array<Array<string>>} [args=[]] - arguments, nested if multiple ops
*/ */
function testOp(browser, opName, input, output, args=[]) { function testOp(browser, opName, input, output, args=[]) {
bakeOp(browser, opName, input, args); bakeOp(browser, opName, input, args);
@ -440,8 +440,8 @@ function testOp(browser, opName, input, output, args=[]) {
* @param {string|Array<string>} opName - name of operation to be tested array for multiple ops * @param {string|Array<string>} opName - name of operation to be tested array for multiple ops
* @param {string} input - input text * @param {string} input - input text
* @param {string} cssSelector - CSS selector for HTML output * @param {string} cssSelector - CSS selector for HTML output
* @param {string} output - expected output * @param {string|RegExp} output - expected output
* @param {Array<string>|Array<Array<string>>} args - arguments, nested if multiple ops * @param {Array<string>|Array<Array<string>>} [args=[]] - arguments, nested if multiple ops
*/ */
function testOpHtml(browser, opName, input, cssSelector, output, args=[]) { function testOpHtml(browser, opName, input, cssSelector, output, args=[]) {
bakeOp(browser, opName, input, args); bakeOp(browser, opName, input, args);
@ -459,9 +459,9 @@ function testOpHtml(browser, opName, input, cssSelector, output, args=[]) {
* @param {Browser} browser - Nightwatch client * @param {Browser} browser - Nightwatch client
* @param {string|Array<string>} opName - name of operation to be tested array for multiple ops * @param {string|Array<string>} opName - name of operation to be tested array for multiple ops
* @param {string} filename - filename of image file from samples directory * @param {string} filename - filename of image file from samples directory
* @param {Array<string>|Array<Array<string>>} args - arguments, nested if multiple ops * @param {Array<string>|Array<Array<string>>} [args=[]] - arguments, nested if multiple ops
*/ */
function testOpImage(browser, opName, filename, args) { function testOpImage(browser, opName, filename, args=[]) {
browser.perform(function() { browser.perform(function() {
console.log(`Current test: ${opName}`); console.log(`Current test: ${opName}`);
}); });
@ -481,11 +481,12 @@ function testOpImage(browser, opName, filename, args) {
* @param {Browser} browser - Nightwatch client * @param {Browser} browser - Nightwatch client
* @param {string|Array<string>} opName - name of operation to be tested array for multiple ops * @param {string|Array<string>} opName - name of operation to be tested array for multiple ops
* @param {string} filename - filename of file from samples directory * @param {string} filename - filename of file from samples directory
* @param {string} cssSelector - CSS selector for HTML output * @param {string|boolean} cssSelector - CSS selector for HTML output or false for normal text output
* @param {string} output - expected output * @param {string|RegExp} output - expected output
* @param {Array<string>|Array<Array<string>>} args - arguments, nested if multiple ops * @param {Array<string>|Array<Array<string>>} [args=[]] - arguments, nested if multiple ops
* @param {number} [waitWindow=1000] - The number of milliseconds to wait for the output to be correct
*/ */
function testOpFile(browser, opName, filename, cssSelector, output, args) { function testOpFile(browser, opName, filename, cssSelector, output, args=[], waitWindow=1000) {
browser.perform(function() { browser.perform(function() {
console.log(`Current test: ${opName}`); console.log(`Current test: ${opName}`);
}); });
@ -494,9 +495,14 @@ function testOpFile(browser, opName, filename, cssSelector, output, args) {
browser.pause(100).waitForElementVisible("#stale-indicator", 5000); browser.pause(100).waitForElementVisible("#stale-indicator", 5000);
utils.bake(browser); utils.bake(browser);
if (typeof output === "string") { if (!cssSelector) {
// Text output
utils.expectOutput(browser, output, true, waitWindow);
} else if (typeof output === "string") {
// HTML output - string match
browser.expect.element("#output-html " + cssSelector).text.that.equals(output); browser.expect.element("#output-html " + cssSelector).text.that.equals(output);
} else if (output instanceof RegExp) { } else if (output instanceof RegExp) {
// HTML output - RegEx match
browser.expect.element("#output-html " + cssSelector).text.that.matches(output); browser.expect.element("#output-html " + cssSelector).text.that.matches(output);
} }
} }

View file

@ -180,15 +180,16 @@ function loadRecipe(browser, opName, input, args) {
* @param {Browser} browser - Nightwatch client * @param {Browser} browser - Nightwatch client
* @param {string|RegExp} expected - The expected output value * @param {string|RegExp} expected - The expected output value
* @param {boolean} [waitNotNull=false] - Wait for the output to not be empty before testing the value * @param {boolean} [waitNotNull=false] - Wait for the output to not be empty before testing the value
* @param {number} [waitWindow=1000] - The number of milliseconds to wait for the output to be correct
*/ */
function expectOutput(browser, expected, waitNotNull=false) { function expectOutput(browser, expected, waitNotNull=false, waitWindow=1000) {
if (waitNotNull && expected !== "") { if (waitNotNull && expected !== "") {
browser.waitUntil(async function() { browser.waitUntil(async function() {
const output = await this.execute(function() { const output = await this.execute(function() {
return window.app.manager.output.outputEditorView.state.doc.toString(); return window.app.manager.output.outputEditorView.state.doc.toString();
}); });
return output.length; return output.length;
}, 1000); }, waitWindow);
} }
browser.execute(expected => { browser.execute(expected => {

View file

@ -119,7 +119,7 @@ TestRegister.addApiTests([
assert.strictEqual(result[0].module, "Ciphers"); assert.strictEqual(result[0].module, "Ciphers");
assert.strictEqual(result[0].inputType, "string"); assert.strictEqual(result[0].inputType, "string");
assert.strictEqual(result[0].outputType, "string"); assert.strictEqual(result[0].outputType, "string");
assert.strictEqual(result[0].description, "Triple DES applies DES three times to each block to increase key size.<br><br><b>Key:</b> Triple DES uses a key length of 24 bytes (192 bits).<br>DES uses a key length of 8 bytes (64 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default."); assert.strictEqual(result[0].description, "Triple DES applies DES three times to each block to increase key size.<br><br><b>Key:</b> Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default.");
assert.strictEqual(result[0].args.length, 5); assert.strictEqual(result[0].args.length, 5);
}), }),

View file

@ -575,12 +575,11 @@ Top Drawer`, {
}), }),
it("Generate HOTP", () => { it("Generate HOTP", () => {
const result = chef.generateHOTP("Cut The Mustard", { const result = chef.generateHOTP("JBSWY3DPEHPK3PXP", {
name: "colonel",
}); });
const expected = `URI: otpauth://hotp/colonel?secret=IN2XIICUNBSSATLVON2GC4TE const expected = `URI: otpauth://hotp/?secret=JBSWY3DPEHPK3PXP&algorithm=SHA1&digits=6&counter=0
Password: 034148`; Password: 282760`;
assert.strictEqual(result.toString(), expected); assert.strictEqual(result.toString(), expected);
}), }),

View file

@ -54,11 +54,13 @@ import "./tests/Comment.mjs";
import "./tests/Compress.mjs"; import "./tests/Compress.mjs";
import "./tests/ConditionalJump.mjs"; import "./tests/ConditionalJump.mjs";
import "./tests/ConvertCoordinateFormat.mjs"; import "./tests/ConvertCoordinateFormat.mjs";
import "./tests/ConvertLeetSpeak.mjs";
import "./tests/ConvertToNATOAlphabet.mjs"; import "./tests/ConvertToNATOAlphabet.mjs";
import "./tests/Crypt.mjs"; import "./tests/Crypt.mjs";
import "./tests/CSV.mjs"; import "./tests/CSV.mjs";
import "./tests/DateTime.mjs"; import "./tests/DateTime.mjs";
import "./tests/DefangIP.mjs"; import "./tests/DefangIP.mjs";
import "./tests/DropNthBytes.mjs";
import "./tests/ECDSA.mjs"; import "./tests/ECDSA.mjs";
import "./tests/ELFInfo.mjs"; import "./tests/ELFInfo.mjs";
import "./tests/Enigma.mjs"; import "./tests/Enigma.mjs";
@ -116,6 +118,7 @@ import "./tests/ParseObjectIDTimestamp.mjs";
import "./tests/ParseQRCode.mjs"; import "./tests/ParseQRCode.mjs";
import "./tests/ParseSSHHostKey.mjs"; import "./tests/ParseSSHHostKey.mjs";
import "./tests/ParseTCP.mjs"; import "./tests/ParseTCP.mjs";
import "./tests/ParseTLSRecord.mjs";
import "./tests/ParseTLV.mjs"; import "./tests/ParseTLV.mjs";
import "./tests/ParseUDP.mjs"; import "./tests/ParseUDP.mjs";
import "./tests/PEMtoHex.mjs"; import "./tests/PEMtoHex.mjs";
@ -143,9 +146,13 @@ import "./tests/SIGABA.mjs";
import "./tests/SM4.mjs"; import "./tests/SM4.mjs";
// import "./tests/SplitColourChannels.mjs"; // Cannot test operations that use the File type yet // import "./tests/SplitColourChannels.mjs"; // Cannot test operations that use the File type yet
import "./tests/StrUtils.mjs"; import "./tests/StrUtils.mjs";
import "./tests/StripIPv4Header.mjs";
import "./tests/StripTCPHeader.mjs";
import "./tests/StripUDPHeader.mjs";
import "./tests/Subsection.mjs"; import "./tests/Subsection.mjs";
import "./tests/SwapCase.mjs"; import "./tests/SwapCase.mjs";
import "./tests/SymmetricDifference.mjs"; import "./tests/SymmetricDifference.mjs";
import "./tests/TakeNthBytes.mjs";
import "./tests/TextEncodingBruteForce.mjs"; import "./tests/TextEncodingBruteForce.mjs";
import "./tests/ToFromInsensitiveRegex.mjs"; import "./tests/ToFromInsensitiveRegex.mjs";
import "./tests/TranslateDateTimeFormat.mjs"; import "./tests/TranslateDateTimeFormat.mjs";

View file

@ -0,0 +1,33 @@
/**
* @author bartblaze []
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Convert to Leet Speak: basic text",
input: "leet",
expectedOutput: "l337",
recipeConfig: [
{
op: "Convert Leet Speak",
args: ["To Leet Speak"]
}
]
},
{
name: "Convert from Leet Speak: basic leet",
input: "l337",
expectedOutput: "leet",
recipeConfig: [
{
op: "Convert Leet Speak",
args: ["From Leet Speak"]
}
]
}
]);

View file

@ -580,8 +580,7 @@ Tag: a8f04c4d93bbef82bef61a103371aef9`,
input: "", input: "",
expectedOutput: `Invalid key length: 0 bytes expectedOutput: `Invalid key length: 0 bytes
DES uses a key length of 8 bytes (64 bits). DES uses a key length of 8 bytes (64 bits).`,
Triple DES uses a key length of 24 bytes (192 bits).`,
recipeConfig: [ recipeConfig: [
{ {
"op": "DES Encrypt", "op": "DES Encrypt",
@ -674,8 +673,7 @@ Triple DES uses a key length of 24 bytes (192 bits).`,
input: "", input: "",
expectedOutput: `Invalid key length: 0 bytes expectedOutput: `Invalid key length: 0 bytes
Triple DES uses a key length of 24 bytes (192 bits). Triple DES uses a key length of 24 bytes (192 bits).`,
DES uses a key length of 8 bytes (64 bits).`,
recipeConfig: [ recipeConfig: [
{ {
"op": "Triple DES Encrypt", "op": "Triple DES Encrypt",
@ -1300,8 +1298,7 @@ The following algorithms will be used based on the size of the key:
input: "", input: "",
expectedOutput: `Invalid key length: 0 bytes expectedOutput: `Invalid key length: 0 bytes
DES uses a key length of 8 bytes (64 bits). DES uses a key length of 8 bytes (64 bits).`,
Triple DES uses a key length of 24 bytes (192 bits).`,
recipeConfig: [ recipeConfig: [
{ {
"op": "DES Decrypt", "op": "DES Decrypt",
@ -1394,8 +1391,7 @@ Triple DES uses a key length of 24 bytes (192 bits).`,
input: "", input: "",
expectedOutput: `Invalid key length: 0 bytes expectedOutput: `Invalid key length: 0 bytes
Triple DES uses a key length of 24 bytes (192 bits). Triple DES uses a key length of 24 bytes (192 bits).`,
DES uses a key length of 8 bytes (64 bits).`,
recipeConfig: [ recipeConfig: [
{ {
"op": "Triple DES Decrypt", "op": "Triple DES Decrypt",
@ -1579,19 +1575,31 @@ DES uses a key length of 8 bytes (64 bits).`,
from Crypto.Cipher import Blowfish from Crypto.Cipher import Blowfish
import binascii import binascii
input_data = b"The quick brown fox jumps over the lazy dog." # Blowfish cipher parameters - key, mode, iv, segment_size, nonce
key = binascii.unhexlify("0011223344556677") key = binascii.unhexlify("0011223344556677")
iv = binascii.unhexlify("0000000000000000")
mode = Blowfish.MODE_CBC mode = Blowfish.MODE_CBC
kwargs = {}
iv = binascii.unhexlify("ffeeddccbbaa9988")
if mode in [Blowfish.MODE_CBC, Blowfish.MODE_CFB, Blowfish.MODE_OFB]:
kwargs = {"iv": iv}
if mode == Blowfish.MODE_CFB:
kwargs["segment_size"] = 64
if mode == Blowfish.MODE_CTR:
nonce = binascii.unhexlify("0000000000000000")
nonce = nonce[:7]
kwargs["nonce"] = nonce
cipher = Blowfish.new(key, mode, **kwargs)
# Input data and padding
input_data = b"The quick brown fox jumps over the lazy dog."
if mode == Blowfish.MODE_ECB or mode == Blowfish.MODE_CBC: if mode == Blowfish.MODE_ECB or mode == Blowfish.MODE_CBC:
padding_len = 8-(len(input_data) & 7) padding_len = 8-(len(input_data) & 7)
for i in range(padding_len): for i in range(padding_len):
input_data += bytes([padding_len]) input_data += bytes([padding_len])
cipher = Blowfish.new(key, mode) # set iv, nonce, segment_size etc. here # Encrypted text
cipher_text = cipher.encrypt(input_data) cipher_text = cipher.encrypt(input_data)
cipher_text = binascii.hexlify(cipher_text).decode("UTF-8") cipher_text = binascii.hexlify(cipher_text).decode("UTF-8")
print("Encrypted: {}".format(cipher_text)) print("Encrypted: {}".format(cipher_text))

View file

@ -0,0 +1,123 @@
/**
* @author Oshawk [oshawk@protonmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
/**
* Drop nth bytes tests
*/
TestRegister.addTests([
{
name: "Drop nth bytes: Nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 0, false],
},
],
},
{
name: "Drop nth bytes: Nothing (apply to each line)",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 0, true],
},
],
},
{
name: "Drop nth bytes: Basic single line",
input: "0123456789",
expectedOutput: "1235679",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 0, false],
},
],
},
{
name: "Drop nth bytes: Basic single line (apply to each line)",
input: "0123456789",
expectedOutput: "1235679",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 0, true],
},
],
},
{
name: "Drop nth bytes: Complex single line",
input: "0123456789",
expectedOutput: "01234678",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 5, false],
},
],
},
{
name: "Drop nth bytes: Complex single line (apply to each line)",
input: "0123456789",
expectedOutput: "01234678",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 5, true],
},
],
},
{
name: "Drop nth bytes: Basic multi line",
input: "01234\n56789",
expectedOutput: "123\n5689",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 0, false],
},
],
},
{
name: "Drop nth bytes: Basic multi line (apply to each line)",
input: "01234\n56789",
expectedOutput: "123\n678",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 0, true],
},
],
},
{
name: "Drop nth bytes: Complex multi line",
input: "01234\n56789",
expectedOutput: "012345679",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 5, false],
},
],
},
{
name: "Drop nth bytes: Complex multi line (apply to each line)",
input: "012345\n6789ab",
expectedOutput: "01234\n6789a",
recipeConfig: [
{
op: "Drop nth bytes",
args: [4, 5, true],
},
],
}
]);

View file

@ -0,0 +1,63 @@
/**
* IPv6Transition tests.
*
* @author jb30795
*
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "IPv6 Transition: IPv4 to IPv6",
input: "198.51.100.7",
expectedOutput: "6to4: 2002:c633:6407::/48\nIPv4 Mapped: ::ffff:c633:6407\nIPv4 Translated: ::ffff:0:c633:6407\nNat 64: 64:ff9b::c633:6407",
recipeConfig: [
{
op: "IPv6 Transition Addresses",
args: [true, false],
},
],
}, {
name: "IPv6 Transition: IPv4 /24 Range to IPv6",
input: "198.51.100.0/24",
expectedOutput: "6to4: 2002:c633:6400::/40\nIPv4 Mapped: ::ffff:c633:6400/120\nIPv4 Translated: ::ffff:0:c633:6400/120\nNat 64: 64:ff9b::c633:6400/120",
recipeConfig: [
{
op: "IPv6 Transition Addresses",
args: [false, false],
},
],
}, {
name: "IPv6 Transition: IPv4 to IPv6 Remove headers",
input: "198.51.100.7",
expectedOutput: "2002:c633:6407::/48\n::ffff:c633:6407\n::ffff:0:c633:6407\n64:ff9b::c633:6407",
recipeConfig: [
{
op: "IPv6 Transition Addresses",
args: [true, true],
},
],
}, {
name: "IPv6 Transition: IPv6 to IPv4",
input: "64:ff9b::c633:6407",
expectedOutput: "IPv4: 198.51.100.7",
recipeConfig: [
{
op: "IPv6 Transition Addresses",
args: [true, false],
},
],
}, {
name: "IPv6 Transition: MAC to EUI-64",
input: "a1:b2:c3:d4:e5:f6",
expectedOutput: "EUI-64 Interface ID: a3b2:c3ff:fed4:e5f6",
recipeConfig: [
{
op: "IPv6 Transition Addresses",
args: [true, false],
},
],
},
]);

View file

@ -44,7 +44,18 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [hsKey, "HS256"], args: [hsKey, "HS256", "{}"],
}
],
},
{
name: "JWT Sign: HS256 with custom header",
input: inputObject,
expectedOutput: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImN1c3RvbS5rZXkifQ.eyJTdHJpbmciOiJTb21lU3RyaW5nIiwiTnVtYmVyIjo0MiwiaWF0IjoxfQ.kXln8btJburfRlND8IDZAQ8NZGFFZhvHyooHa6N9za8",
recipeConfig: [
{
op: "JWT Sign",
args: [hsKey, "HS256", `{"kid":"custom.key"}`],
} }
], ],
}, },
@ -55,7 +66,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [hsKey, "HS384"], args: [hsKey, "HS384", "{}"],
} }
], ],
}, },
@ -66,7 +77,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [hsKey, "HS512"], args: [hsKey, "HS512", "{}"],
} }
], ],
}, },
@ -77,7 +88,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES256"], args: [esKey, "ES256", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -92,7 +103,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES384"], args: [esKey, "ES384", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -107,7 +118,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES512"], args: [esKey, "ES512", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -122,7 +133,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [rsKey, "RS256"], args: [rsKey, "RS256", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -137,7 +148,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [rsKey, "RS384"], args: [rsKey, "RS384", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -152,7 +163,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "RS512"], args: [esKey, "RS512", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",

View file

@ -2,11 +2,392 @@
* From Decimal tests * From Decimal tests
* *
* @author n1073645 [n1073645@gmail.com] * @author n1073645 [n1073645@gmail.com]
* @author k3ach [k3ach@proton.me]
* @copyright Crown Copyright 2020 * @copyright Crown Copyright 2020
* @licence Apache-2.0 * @licence Apache-2.0
*/ */
import TestRegister from "../../lib/TestRegister.mjs"; import TestRegister from "../../lib/TestRegister.mjs";
const testCases = [
{
radix: 2,
input: "01",
checksum: "1",
checkdigit: "1",
}, {
radix: 2,
input: "001111",
checksum: "0",
checkdigit: "0",
}, {
radix: 2,
input: "00011101",
checksum: "0",
checkdigit: "0",
}, {
radix: 2,
input: "0100101101",
checksum: "1",
checkdigit: "1",
}, {
radix: 4,
input: "0123",
checksum: "1",
checkdigit: "1",
}, {
radix: 4,
input: "130100",
checksum: "2",
checkdigit: "2",
}, {
radix: 4,
input: "32020313",
checksum: "3",
checkdigit: "0",
}, {
radix: 4,
input: "302233210112",
checksum: "3",
checkdigit: "0",
}, {
radix: 6,
input: "012345",
checksum: "4",
checkdigit: "4",
}, {
radix: 6,
input: "134255",
checksum: "2",
checkdigit: "4",
}, {
radix: 6,
input: "15021453",
checksum: "5",
checkdigit: "4",
}, {
radix: 6,
input: "211450230513",
checksum: "3",
checkdigit: "1",
}, {
radix: 8,
input: "01234567",
checksum: "2",
checkdigit: "2",
}, {
radix: 8,
input: "340624",
checksum: "0",
checkdigit: "4",
}, {
radix: 8,
input: "07260247",
checksum: "3",
checkdigit: "3",
}, {
radix: 8,
input: "026742114675",
checksum: "7",
checkdigit: "1",
}, {
radix: 10,
input: "0123456789",
checksum: "7",
checkdigit: "7",
}, {
radix: 10,
input: "468543",
checksum: "7",
checkdigit: "4",
}, {
radix: 10,
input: "59377601",
checksum: "5",
checkdigit: "6",
}, {
radix: 10,
input: "013909981254",
checksum: "1",
checkdigit: "3",
}, {
radix: 12,
input: "0123456789ab",
checksum: "3",
checkdigit: "3",
}, {
radix: 12,
input: "284685",
checksum: "0",
checkdigit: "6",
}, {
radix: 12,
input: "951a2661",
checksum: "0",
checkdigit: "8",
}, {
radix: 12,
input: "898202676387",
checksum: "b",
checkdigit: "9",
}, {
radix: 14,
input: "0123456789abcd",
checksum: "a",
checkdigit: "a",
}, {
radix: 14,
input: "33db25",
checksum: "0",
checkdigit: "d",
}, {
radix: 14,
input: "0b4ac128",
checksum: "b",
checkdigit: "3",
}, {
radix: 14,
input: "3d1c6d16160d",
checksum: "3",
checkdigit: "c",
}, {
radix: 16,
input: "0123456789abcdef",
checksum: "4",
checkdigit: "4",
}, {
radix: 16,
input: "e1fe64",
checksum: "b",
checkdigit: "6",
}, {
radix: 16,
input: "241a5dcd",
checksum: "1",
checkdigit: "9",
}, {
radix: 16,
input: "1fea740e0e1f",
checksum: "7",
checkdigit: "4",
}, {
radix: 18,
input: "0123456789abcdefgh",
checksum: "d",
checkdigit: "d",
}, {
radix: 18,
input: "995dgf",
checksum: "9",
checkdigit: "1",
}, {
radix: 18,
input: "9f80h32h",
checksum: "1",
checkdigit: "0",
}, {
radix: 18,
input: "5f9428e493g4",
checksum: "8",
checkdigit: "c",
}, {
radix: 20,
input: "0123456789abcdefghij",
checksum: "5",
checkdigit: "5",
}, {
radix: 20,
input: "918jci",
checksum: "h",
checkdigit: "d",
}, {
radix: 20,
input: "jab7j50d",
checksum: "g",
checkdigit: "j",
}, {
radix: 20,
input: "c56fe85eb6gg",
checksum: "g",
checkdigit: "5",
}, {
radix: 22,
input: "0123456789abcdefghijkl",
checksum: "g",
checkdigit: "g",
}, {
radix: 22,
input: "de57le",
checksum: "5",
checkdigit: "l",
}, {
radix: 22,
input: "e3fg6dfc",
checksum: "f",
checkdigit: "d",
}, {
radix: 22,
input: "1f8l80ai4kbg",
checksum: "l",
checkdigit: "f",
}, {
radix: 24,
input: "0123456789abcdefghijklmn",
checksum: "6",
checkdigit: "6",
}, {
radix: 24,
input: "agne7d",
checksum: "4",
checkdigit: "f",
}, {
radix: 24,
input: "1l4d9cf4",
checksum: "d",
checkdigit: "c",
}, {
radix: 24,
input: "blc1j09i3296",
checksum: "8",
checkdigit: "7",
}, {
radix: 26,
input: "0123456789abcdefghijklmnop",
checksum: "j",
checkdigit: "j",
}, {
radix: 26,
input: "82n9op",
checksum: "i",
checkdigit: "2",
}, {
radix: 26,
input: "e9cddn70",
checksum: "9",
checkdigit: "i",
}, {
radix: 26,
input: "ck0ep419knom",
checksum: "p",
checkdigit: "g",
}, {
radix: 28,
input: "0123456789abcdefghijklmnopqr",
checksum: "7",
checkdigit: "7",
}, {
radix: 28,
input: "a6hnoo",
checksum: "h",
checkdigit: "9",
}, {
radix: 28,
input: "lblc7kh0",
checksum: "a",
checkdigit: "f",
}, {
radix: 28,
input: "64k5piod3lmf",
checksum: "0",
checkdigit: "p",
}, {
radix: 30,
input: "0123456789abcdefghijklmnopqrst",
checksum: "m",
checkdigit: "m",
}, {
radix: 30,
input: "t69j7d",
checksum: "9",
checkdigit: "s",
}, {
radix: 30,
input: "p54o9ig3",
checksum: "a",
checkdigit: "o",
}, {
radix: 30,
input: "gc1njrt55030",
checksum: "6",
checkdigit: "1",
}, {
radix: 32,
input: "0123456789abcdefghijklmnopqrstuv",
checksum: "8",
checkdigit: "8",
}, {
radix: 32,
input: "rdou19",
checksum: "u",
checkdigit: "3",
}, {
radix: 32,
input: "ighj0pc7",
checksum: "3",
checkdigit: "8",
}, {
radix: 32,
input: "op4nn5fvjsrs",
checksum: "g",
checkdigit: "j",
}, {
radix: 34,
input: "0123456789abcdefghijklmnopqrstuvwx",
checksum: "p",
checkdigit: "p",
}, {
radix: 34,
input: "nvftj5",
checksum: "b",
checkdigit: "f",
}, {
radix: 34,
input: "u9v9g162",
checksum: "j",
checkdigit: "b",
}, {
radix: 34,
input: "o5gqg5d7gjh9",
checksum: "5",
checkdigit: "q",
}, {
radix: 36,
input: "0123456789abcdefghijklmnopqrstuvwxyz",
checksum: "9",
checkdigit: "9",
}, {
radix: 36,
input: "29zehu",
checksum: "i",
checkdigit: "j",
}, {
radix: 36,
input: "1snmikbu",
checksum: "s",
checkdigit: "v",
}, {
radix: 36,
input: "jpkar545q7gb",
checksum: "3",
checkdigit: "d",
},
];
testCases.forEach(element => {
TestRegister.addTests([
{
name: "Luhn Checksum Mod " + element.radix + " on " + element.input,
input: element.input,
expectedOutput: "Checksum: " + element.checksum + "\nCheckdigit: " + element.checkdigit + "\nLuhn Validated String: " + element.input + element.checkdigit,
recipeConfig: [
{
op: "Luhn Checksum",
args: [element.radix]
},
],
},
]);
});
TestRegister.addTests([ TestRegister.addTests([
{ {
name: "Luhn Checksum on standard data", name: "Luhn Checksum on standard data",
@ -15,7 +396,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "Luhn Checksum", op: "Luhn Checksum",
args: [] args: [10]
}, },
], ],
}, },
@ -26,7 +407,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "Luhn Checksum", op: "Luhn Checksum",
args: [] args: [10]
}, },
], ],
}, },
@ -37,18 +418,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "Luhn Checksum", op: "Luhn Checksum",
args: [] args: [10]
},
],
},
{
name: "Luhn Checksum on invalid data",
input: "35641709b012469",
expectedOutput: "Character: b is not a digit.",
recipeConfig: [
{
op: "Luhn Checksum",
args: []
}, },
], ],
}, },
@ -59,8 +429,8 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "Luhn Checksum", op: "Luhn Checksum",
args: [] args: [10]
}, },
], ],
} },
]); ]);

View file

@ -11,12 +11,12 @@ import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([ TestRegister.addTests([
{ {
name: "Generate HOTP", name: "Generate HOTP",
input: "12345678901234567890", input: "JBSWY3DPEHPK3PXP",
expectedOutput: "URI: otpauth://hotp/OTPAuthentication?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ\n\nPassword: 755224", expectedOutput: `URI: otpauth://hotp/?secret=JBSWY3DPEHPK3PXP&algorithm=SHA1&digits=6&counter=0\n\nPassword: 282760`,
recipeConfig: [ recipeConfig: [
{ {
op: "Generate HOTP", op: "Generate HOTP",
args: ["", 32, 6, 0], args: ["", 6, 0], // [Name, Code length, Counter]
}, },
], ],
}, },

View file

@ -29,31 +29,28 @@ NFgFNIvSXhbqMYoHAAApMHJOxiWpBFdYKp3tESnlgh2lUh7lQtmOjD4a1dzfU8PU
oViyp+UJGasN2WRd+4VtaPw64w== oViyp+UJGasN2WRd+4VtaPw64w==
-----END CERTIFICATE REQUEST-----`; -----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_RSA_1024 = `Version: 1 (0x00) const OUT_EXAMPLE_COM_RSA_1024 = `Subject
Subject
C = CH C = CH
ST = Zurich ST = Zurich
L = Zurich L = Zurich
O = Example RE O = Example RE
OU = IT Department OU = IT Department
CN = example.com CN = example.com
Subject Alternative Names
DNS: example.com
DNS: www.example.com
Public Key Public Key
Algorithm: RSA Algorithm: RSA
Length: 1024 bits Length: 1024 bits
Modulus: ae:b4:eb:2c:8e:85:93:38:d7:f0:56:5f:72:5b:76:a3: Modulus: 00:ae:b4:eb:2c:8e:85:93:38:d7:f0:56:5f:72:5b:76:
1d:43:cf:b2:91:c2:de:5f:e9:f7:d9:89:ce:ed:c0:b0: a3:1d:43:cf:b2:91:c2:de:5f:e9:f7:d9:89:ce:ed:c0:
0a:27:86:a8:fc:7d:c0:3e:3c:28:15:55:17:1a:38:8d: b0:0a:27:86:a8:fc:7d:c0:3e:3c:28:15:55:17:1a:38:
8f:f5:c5:d9:19:48:77:85:31:07:56:fa:0a:05:a3:ba: 8d:8f:f5:c5:d9:19:48:77:85:31:07:56:fa:0a:05:a3:
30:5b:f5:6e:75:ad:37:6f:7d:62:f2:00:7b:2b:2d:ca: ba:30:5b:f5:6e:75:ad:37:6f:7d:62:f2:00:7b:2b:2d:
6d:a5:5c:fe:57:d6:3c:5f:d9:04:14:24:46:18:3c:86: ca:6d:a5:5c:fe:57:d6:3c:5f:d9:04:14:24:46:18:3c:
e7:e5:fe:36:ee:82:3b:34:e9:50:f0:e3:e1:b2:08:5f: 86:e7:e5:fe:36:ee:82:3b:34:e9:50:f0:e3:e1:b2:08:
fb:8f:93:77:c3:60:31:2a:2c:29:55:cb:cf:d5:4b:8f 5f:fb:8f:93:77:c3:60:31:2a:2c:29:55:cb:cf:d5:4b:
8f
Exponent: 65537 (0x10001) Exponent: 65537 (0x10001)
Signature Signature
Algorithm: sha256WithRSAEncryption Algorithm: SHA256withRSA
Signature: 74:99:49:4f:82:de:a9:b7:f9:23:0f:4a:73:39:43:64: Signature: 74:99:49:4f:82:de:a9:b7:f9:23:0f:4a:73:39:43:64:
e5:ef:67:04:54:18:40:6b:86:20:71:98:6c:f5:f7:9a: e5:ef:67:04:54:18:40:6b:86:20:71:98:6c:f5:f7:9a:
2e:16:77:db:d4:09:d3:e2:c6:d3:d2:4e:e9:c7:5a:cd: 2e:16:77:db:d4:09:d3:e2:c6:d3:d2:4e:e9:c7:5a:cd:
@ -62,14 +59,17 @@ Signature
25:a9:04:57:58:2a:9d:ed:11:29:e5:82:1d:a5:52:1e: 25:a9:04:57:58:2a:9d:ed:11:29:e5:82:1d:a5:52:1e:
e5:42:d9:8e:8c:3e:1a:d5:dc:df:53:c3:d4:a1:58:b2: e5:42:d9:8e:8c:3e:1a:d5:dc:df:53:c3:d4:a1:58:b2:
a7:e5:09:19:ab:0d:d9:64:5d:fb:85:6d:68:fc:3a:e3 a7:e5:09:19:ab:0d:d9:64:5d:fb:85:6d:68:fc:3a:e3
Extensions Requested Extensions
basicConstraints CRITICAL: Basic Constraints: critical
CA = false CA = false
keyUsage CRITICAL: Key Usage: critical
Digital signature Digital Signature
Key encipherment Key encipherment
extKeyUsage: Extended Key Usage:
TLS Web Server Authentication`; TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
// openssl req -newkey rsa:2048 -keyout test-rsa-2048.key -out test-rsa-2048.csr \ // openssl req -newkey rsa:2048 -keyout test-rsa-2048.key -out test-rsa-2048.csr \
// -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com" \ // -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com" \
@ -97,39 +97,36 @@ m9cpVxuxGLtONBnohzohnFECytSXWEXPIj8L9SpYK97G02nJYYCAcb5BF11Alfux
sNxtsr6zgPaLRrvOBT11WxJVKerbhfezAJ3naem1eM3VLxCGWwMwxg== sNxtsr6zgPaLRrvOBT11WxJVKerbhfezAJ3naem1eM3VLxCGWwMwxg==
-----END CERTIFICATE REQUEST-----`; -----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_RSA_2048 = `Version: 1 (0x00) const OUT_EXAMPLE_COM_RSA_2048 = `Subject
Subject
C = CH C = CH
ST = Zurich ST = Zurich
L = Zurich L = Zurich
O = Example RE O = Example RE
OU = IT Department OU = IT Department
CN = example.com CN = example.com
Subject Alternative Names
DNS: example.com
DNS: www.example.com
Public Key Public Key
Algorithm: RSA Algorithm: RSA
Length: 2048 bits Length: 2048 bits
Modulus: a3:e8:80:b9:96:3e:e2:bf:20:67:5c:b7:6b:ff:dc:c1: Modulus: 00:a3:e8:80:b9:96:3e:e2:bf:20:67:5c:b7:6b:ff:dc:
4a:55:a5:5e:2a:9d:87:97:96:ad:ff:30:c5:2c:20:1e: c1:4a:55:a5:5e:2a:9d:87:97:96:ad:ff:30:c5:2c:20:
e7:56:f0:87:b0:6a:35:52:44:72:2e:00:a7:09:57:03: 1e:e7:56:f0:87:b0:6a:35:52:44:72:2e:00:a7:09:57:
55:95:99:03:c1:14:12:65:63:04:19:56:3c:f9:50:03: 03:55:95:99:03:c1:14:12:65:63:04:19:56:3c:f9:50:
76:0a:63:47:c6:e7:79:9d:5d:37:62:66:76:fc:89:a5: 03:76:0a:63:47:c6:e7:79:9d:5d:37:62:66:76:fc:89:
47:3a:4a:71:93:0f:a9:4f:a5:88:90:82:d3:82:fe:5c: a5:47:3a:4a:71:93:0f:a9:4f:a5:88:90:82:d3:82:fe:
86:ce:77:1f:95:cf:9d:9d:17:ef:82:73:e1:6e:48:5a: 5c:86:ce:77:1f:95:cf:9d:9d:17:ef:82:73:e1:6e:48:
bc:d3:7c:96:fa:a7:9f:2b:c2:6c:24:d3:bd:2a:e3:f1: 5a:bc:d3:7c:96:fa:a7:9f:2b:c2:6c:24:d3:bd:2a:e3:
44:b6:0a:48:00:03:6b:d3:08:26:2b:2b:bb:53:f3:70: f1:44:b6:0a:48:00:03:6b:d3:08:26:2b:2b:bb:53:f3:
10:0e:72:29:8e:98:d9:c5:5a:ea:3e:2c:ab:1d:e2:55: 70:10:0e:72:29:8e:98:d9:c5:5a:ea:3e:2c:ab:1d:e2:
37:d0:e1:31:0d:d2:87:c2:dc:ad:eb:63:23:d5:cd:e8: 55:37:d0:e1:31:0d:d2:87:c2:dc:ad:eb:63:23:d5:cd:
94:ed:49:8e:f9:23:b5:65:a3:c0:72:3e:d0:48:13:8e: e8:94:ed:49:8e:f9:23:b5:65:a3:c0:72:3e:d0:48:13:
f9:1e:5e:57:14:61:9b:ef:2e:5c:ac:74:a1:11:31:1a: 8e:f9:1e:5e:57:14:61:9b:ef:2e:5c:ac:74:a1:11:31:
33:bc:c4:c6:aa:aa:07:58:28:16:97:e4:6a:f5:9e:8f: 1a:33:bc:c4:c6:aa:aa:07:58:28:16:97:e4:6a:f5:9e:
4e:03:6c:44:ee:02:2a:e8:35:67:09:a1:f3:2e:9a:71: 8f:4e:03:6c:44:ee:02:2a:e8:35:67:09:a1:f3:2e:9a:
9e:ec:61:bf:dd:6a:bf:07:39:ea:89:9d:cd:29:0a:ff 71:9e:ec:61:bf:dd:6a:bf:07:39:ea:89:9d:cd:29:0a:
ff
Exponent: 65537 (0x10001) Exponent: 65537 (0x10001)
Signature Signature
Algorithm: sha256WithRSAEncryption Algorithm: SHA256withRSA
Signature: 1b:47:23:7d:10:58:d6:90:73:bb:e8:df:ef:23:10:ac: Signature: 1b:47:23:7d:10:58:d6:90:73:bb:e8:df:ef:23:10:ac:
ae:66:42:b8:7b:d9:a8:ab:56:e5:c7:9a:87:21:9b:25: ae:66:42:b8:7b:d9:a8:ab:56:e5:c7:9a:87:21:9b:25:
31:ca:dd:06:ee:8b:e7:36:12:84:af:e5:fd:b2:74:a1: 31:ca:dd:06:ee:8b:e7:36:12:84:af:e5:fd:b2:74:a1:
@ -146,14 +143,17 @@ Signature
be:41:17:5d:40:95:fb:b1:b0:dc:6d:b2:be:b3:80:f6: be:41:17:5d:40:95:fb:b1:b0:dc:6d:b2:be:b3:80:f6:
8b:46:bb:ce:05:3d:75:5b:12:55:29:ea:db:85:f7:b3: 8b:46:bb:ce:05:3d:75:5b:12:55:29:ea:db:85:f7:b3:
00:9d:e7:69:e9:b5:78:cd:d5:2f:10:86:5b:03:30:c6 00:9d:e7:69:e9:b5:78:cd:d5:2f:10:86:5b:03:30:c6
Extensions Requested Extensions
basicConstraints CRITICAL: Basic Constraints: critical
CA = false CA = false
keyUsage CRITICAL: Key Usage: critical
Digital signature Digital Signature
Key encipherment Key encipherment
extKeyUsage: Extended Key Usage:
TLS Web Server Authentication`; TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
// openssl genpkey -genparam -algorithm ec -pkeyopt ec_paramgen_curve:P-256 -out test-ec-param.pem // openssl genpkey -genparam -algorithm ec -pkeyopt ec_paramgen_curve:P-256 -out test-ec-param.pem
// openssl req -newkey ec:test-ec-param.pem -keyout test-ec.key -out test-ec.csr \ // openssl req -newkey ec:test-ec-param.pem -keyout test-ec.key -out test-ec.csr \
@ -162,7 +162,7 @@ Extensions
// -addext "basicConstraints = critical,CA:FALSE" \ // -addext "basicConstraints = critical,CA:FALSE" \
// -addext "keyUsage = critical,digitalSignature,keyEncipherment" \ // -addext "keyUsage = critical,digitalSignature,keyEncipherment" \
// -addext "extendedKeyUsage = serverAuth" // -addext "extendedKeyUsage = serverAuth"
const IN_EXAMPLE_COM_EC = `-----BEGIN CERTIFICATE REQUEST----- const IN_EXAMPLE_COM_EC_P256 = `-----BEGIN CERTIFICATE REQUEST-----
MIIBmzCCAUECAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G MIIBmzCCAUECAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G
A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE
ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqG ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqG
@ -174,7 +174,690 @@ zj0EAwIDSAAwRQIgQkum/qaLzE3QZ3WD00uLpalUn113FObd7rM5Mr3HQwQCIQCr
7OjzYI9v7qIJp/E9N16XfJN87G2ZVIZ4FuPXVjokCQ== 7OjzYI9v7qIJp/E9N16XfJN87G2ZVIZ4FuPXVjokCQ==
-----END CERTIFICATE REQUEST-----`; -----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_EC = `Parse CSR - Cannot read public key. OID is not RSA.`; const OUT_EXAMPLE_COM_EC_P256 = `Subject
C = CH
ST = Zurich
L = Zurich
O = Example RE
OU = IT Department
CN = example.com
Public Key
Algorithm: ECDSA
Length: 256 bits
Pub: 04:09:a9:61:73:61:f8:bf:44:d1:0d:ec:2e:1a:ce:f8:
c1:75:5e:02:82:7e:a2:67:b6:b3:b2:22:4a:c6:c2:88:
90:7e:d1:db:25:64:c0:e9:db:b1:42:15:3f:dd:df:41:
f9:23:7f:89:b7:8a:63:ec:5e:88:d0:6b:b3:67:93:61:
9e
ASN1 OID: secp256r1
NIST CURVE: P-256
Signature
Algorithm: SHA256withECDSA
Signature: 30:45:02:20:42:4b:a6:fe:a6:8b:cc:4d:d0:67:75:83:
d3:4b:8b:a5:a9:54:9f:5d:77:14:e6:dd:ee:b3:39:32:
bd:c7:43:04:02:21:00:ab:ec:e8:f3:60:8f:6f:ee:a2:
09:a7:f1:3d:37:5e:97:7c:93:7c:ec:6d:99:54:86:78:
16:e3:d7:56:3a:24:09
Requested Extensions
Basic Constraints: critical
CA = false
Key Usage: critical
Digital Signature
Key encipherment
Extended Key Usage:
TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
// openssl ecparam -name secp384r1 -genkey -noout -out test-ec-key.pem
// openssl req -new -key test-ec-key.pem -out test-ec.csr
// -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com"
// -addext "subjectAltName = DNS:example.com,DNS:www.example.com"
// -addext "basicConstraints = critical,CA:FALSE"
// -addext "keyUsage = critical,digitalSignature,keyEncipherment"
// -addext "extendedKeyUsage = serverAuth"
const IN_EXAMPLE_COM_EC_P384 = `-----BEGIN CERTIFICATE REQUEST-----
MIIB2TCCAV4CAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G
A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE
ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTB2MBAGByqGSM49AgEGBSuB
BAAiA2IABE3rpRO164NtXx2kYMP1zlN7YgHEincO4YgwoyAYyJm3LwcbR+XyKg6A
/i+DUaGWa2FQ+f8w8VmEUFAgLozVxwnntPOCSODrXAQwJFPLCqs7m3o8OuzU3t07
POGhPtj7f6BtMGsGCSqGSIb3DQEJDjFeMFwwJwYDVR0RBCAwHoILZXhhbXBsZS5j
b22CD3d3dy5leGFtcGxlLmNvbTAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF
oDATBgNVHSUEDDAKBggrBgEFBQcDATAKBggqhkjOPQQDAgNpADBmAjEAlq7RaEXU
aNHEC+qfuIitonWHOatm+qiiaNSh80QjLw5P1rszg9yQQigHd8cD7I4DAjEAzmo1
DLpcESwZCBrh3sPflDA38TZjoedRNeWcVxdn1QmwDWMeprD/zgPAey8GOmyj
-----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_EC_P384 = `Subject
C = CH
ST = Zurich
L = Zurich
O = Example RE
OU = IT Department
CN = example.com
Public Key
Algorithm: ECDSA
Length: 384 bits
Pub: 04:4d:eb:a5:13:b5:eb:83:6d:5f:1d:a4:60:c3:f5:ce:
53:7b:62:01:c4:8a:77:0e:e1:88:30:a3:20:18:c8:99:
b7:2f:07:1b:47:e5:f2:2a:0e:80:fe:2f:83:51:a1:96:
6b:61:50:f9:ff:30:f1:59:84:50:50:20:2e:8c:d5:c7:
09:e7:b4:f3:82:48:e0:eb:5c:04:30:24:53:cb:0a:ab:
3b:9b:7a:3c:3a:ec:d4:de:dd:3b:3c:e1:a1:3e:d8:fb:
7f
ASN1 OID: secp384r1
NIST CURVE: P-384
Signature
Algorithm: SHA256withECDSA
Signature: 30:66:02:31:00:96:ae:d1:68:45:d4:68:d1:c4:0b:ea:
9f:b8:88:ad:a2:75:87:39:ab:66:fa:a8:a2:68:d4:a1:
f3:44:23:2f:0e:4f:d6:bb:33:83:dc:90:42:28:07:77:
c7:03:ec:8e:03:02:31:00:ce:6a:35:0c:ba:5c:11:2c:
19:08:1a:e1:de:c3:df:94:30:37:f1:36:63:a1:e7:51:
35:e5:9c:57:17:67:d5:09:b0:0d:63:1e:a6:b0:ff:ce:
03:c0:7b:2f:06:3a:6c:a3
Requested Extensions
Basic Constraints: critical
CA = false
Key Usage: critical
Digital Signature
Key encipherment
Extended Key Usage:
TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
// openssl ecparam -name secp521r1 -genkey -noout -out test-ec-key.pem
// openssl req -new -key test-ec-key.pem -out test-ec.csr
// -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com"
// -addext "subjectAltName = DNS:example.com,DNS:www.example.com"
// -addext "basicConstraints = critical,CA:FALSE"
// -addext "keyUsage = critical,digitalSignature,keyEncipherment"
// -addext "extendedKeyUsage = serverAuth"
const IN_EXAMPLE_COM_EC_P521 = `-----BEGIN CERTIFICATE REQUEST-----
MIICIjCCAYQCAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G
A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE
ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTCBmzAQBgcqhkjOPQIBBgUr
gQQAIwOBhgAEAKf5BRB57svfglRz5dM0bnJAnieMFjNjOFca5/pJ2bOpORkp9Uol
x//mHY5WOMYYC/xvM5lJRcmUnL791zQ6rf6pAD/CrEpDF2svae6e5nA/fN2XsB98
xjmkTpYZVC5nFT83Ceo9J0kHbvliYlAMsEOO60qGghyWV7myiDgORfE+POU3oG0w
awYJKoZIhvcNAQkOMV4wXDAnBgNVHREEIDAeggtleGFtcGxlLmNvbYIPd3d3LmV4
YW1wbGUuY29tMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQM
MAoGCCsGAQUFBwMBMAoGCCqGSM49BAMCA4GLADCBhwJBDeIpSuvIT+kiE0ZnJwPS
DVik93CLqjFm5Ieq02d81GwusSgAA82WlZZVZRsTEjkZXtk96zMBnh5/uxk+wN+j
+PoCQgEDmXREwi0BPkHj6QlktE+7SLELVkrd75D9mfw/SV6ZJiLiLIT9yeoA0Zon
uhcl2rK/DLQutuJF6JIBe5s7lieKfQ==
-----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_EC_P521 = `Subject
C = CH
ST = Zurich
L = Zurich
O = Example RE
OU = IT Department
CN = example.com
Public Key
Algorithm: ECDSA
Length: 521 bits
Pub: 04:00:a7:f9:05:10:79:ee:cb:df:82:54:73:e5:d3:34:
6e:72:40:9e:27:8c:16:33:63:38:57:1a:e7:fa:49:d9:
b3:a9:39:19:29:f5:4a:25:c7:ff:e6:1d:8e:56:38:c6:
18:0b:fc:6f:33:99:49:45:c9:94:9c:be:fd:d7:34:3a:
ad:fe:a9:00:3f:c2:ac:4a:43:17:6b:2f:69:ee:9e:e6:
70:3f:7c:dd:97:b0:1f:7c:c6:39:a4:4e:96:19:54:2e:
67:15:3f:37:09:ea:3d:27:49:07:6e:f9:62:62:50:0c:
b0:43:8e:eb:4a:86:82:1c:96:57:b9:b2:88:38:0e:45:
f1:3e:3c:e5:37
ASN1 OID: secp521r1
NIST CURVE: P-521
Signature
Algorithm: SHA256withECDSA
Signature: 30:81:87:02:41:0d:e2:29:4a:eb:c8:4f:e9:22:13:46:
67:27:03:d2:0d:58:a4:f7:70:8b:aa:31:66:e4:87:aa:
d3:67:7c:d4:6c:2e:b1:28:00:03:cd:96:95:96:55:65:
1b:13:12:39:19:5e:d9:3d:eb:33:01:9e:1e:7f:bb:19:
3e:c0:df:a3:f8:fa:02:42:01:03:99:74:44:c2:2d:01:
3e:41:e3:e9:09:64:b4:4f:bb:48:b1:0b:56:4a:dd:ef:
90:fd:99:fc:3f:49:5e:99:26:22:e2:2c:84:fd:c9:ea:
00:d1:9a:27:ba:17:25:da:b2:bf:0c:b4:2e:b6:e2:45:
e8:92:01:7b:9b:3b:96:27:8a:7d
Requested Extensions
Basic Constraints: critical
CA = false
Key Usage: critical
Digital Signature
Key encipherment
Extended Key Usage:
TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
// openssl dsaparam -out dsaparam.pem 1024
// openssl gendsa -out dsakey.pem dsaparam.pem
// openssl req -new -key dsakey.pem -out test-dsa.csr \
// -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com" \
// -addext "subjectAltName = DNS:example.com,DNS:www.example.com" \
// -addext "basicConstraints = critical,CA:FALSE" \
// -addext "keyUsage = critical,digitalSignature,keyEncipherment" \
// -addext "extendedKeyUsage = serverAuth"
const IN_EXAMPLE_COM_DSA_1024 = `-----BEGIN CERTIFICATE REQUEST-----
MIIC/jCCAqoCAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G
A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE
ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAcAwggE0BgcqhkjOOAQB
MIIBJwKBgQD8vvCmdM8wttdbq3kWigTEnnug4+2SLMl2RNXrlCQjmuZc7tGMyP1u
gsSc9Pxd/tMrPKRawFP5SvUOkZ4cIrujdJVTb/hlfnGH4cWACe8EupwRzoqwZB1x
awiHFzL9G6Go0HOy7bSbRdxBIYu46fnxNsDFf7lMlcBOKdq4Y12kvwIdAN4/vtK9
KxhQfcrrzHsPXW+/xW0CMfr+NQir8PkCgYEAiNdM7IRZhXPaGRtGDpepSoRAf4uQ
LWY9q+vFUx4fVRSSgwKBKLjW+BvzE2eJq0pXv7O09QHOghtcwzY3UrdN952sjUkJ
LItt+5FxB7/JqCBPRrrVsyGEjR3+WbeI3wl6OvQFxm/OTNTTkemFdAfpT/YDSw+n
1xLODTfegT/oyOoDgYUAAoGBAMz15lRPVAj8cje3ShbuACHPVE85d0Tk0Dw9qUcQ
NCNS6A3STSbUiLGKeiRMGg2v/HM9ivV8tq1rywmgBAwtidcQ6P5yqYSZs6z3x9xZ
OzeQ5jXftBQ1GXeU8zi1fC99inFGNixbPFVIz4/KiV0+So44n9ki2ylhbz0YQtpU
wMF+oG0wawYJKoZIhvcNAQkOMV4wXDAnBgNVHREEIDAeggtleGFtcGxlLmNvbYIP
d3d3LmV4YW1wbGUuY29tMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBMG
A1UdJQQMMAoGCCsGAQUFBwMBMAsGCWCGSAFlAwQDAgNBADA+Ah0AkTogUUyKE5v9
ezKrOKpP07i2E9Zz0n/yjIvw4wIdAMB5yVMOEgI877vOFQ7zzf7oDR9eJMYlf4QV
2sQ=
-----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_DSA_1024 = `Subject
C = CH
ST = Zurich
L = Zurich
O = Example RE
OU = IT Department
CN = example.com
Public Key
Algorithm: DSA
Length: 1024 bits
Pub: 00:cc:f5:e6:54:4f:54:08:fc:72:37:b7:4a:16:ee:00:
21:cf:54:4f:39:77:44:e4:d0:3c:3d:a9:47:10:34:23:
52:e8:0d:d2:4d:26:d4:88:b1:8a:7a:24:4c:1a:0d:af:
fc:73:3d:8a:f5:7c:b6:ad:6b:cb:09:a0:04:0c:2d:89:
d7:10:e8:fe:72:a9:84:99:b3:ac:f7:c7:dc:59:3b:37:
90:e6:35:df:b4:14:35:19:77:94:f3:38:b5:7c:2f:7d:
8a:71:46:36:2c:5b:3c:55:48:cf:8f:ca:89:5d:3e:4a:
8e:38:9f:d9:22:db:29:61:6f:3d:18:42:da:54:c0:c1:
7e
P: 00:fc:be:f0:a6:74:cf:30:b6:d7:5b:ab:79:16:8a:04:
c4:9e:7b:a0:e3:ed:92:2c:c9:76:44:d5:eb:94:24:23:
9a:e6:5c:ee:d1:8c:c8:fd:6e:82:c4:9c:f4:fc:5d:fe:
d3:2b:3c:a4:5a:c0:53:f9:4a:f5:0e:91:9e:1c:22:bb:
a3:74:95:53:6f:f8:65:7e:71:87:e1:c5:80:09:ef:04:
ba:9c:11:ce:8a:b0:64:1d:71:6b:08:87:17:32:fd:1b:
a1:a8:d0:73:b2:ed:b4:9b:45:dc:41:21:8b:b8:e9:f9:
f1:36:c0:c5:7f:b9:4c:95:c0:4e:29:da:b8:63:5d:a4:
bf
Q: 00:de:3f:be:d2:bd:2b:18:50:7d:ca:eb:cc:7b:0f:5d:
6f:bf:c5:6d:02:31:fa:fe:35:08:ab:f0:f9
G: 00:88:d7:4c:ec:84:59:85:73:da:19:1b:46:0e:97:a9:
4a:84:40:7f:8b:90:2d:66:3d:ab:eb:c5:53:1e:1f:55:
14:92:83:02:81:28:b8:d6:f8:1b:f3:13:67:89:ab:4a:
57:bf:b3:b4:f5:01:ce:82:1b:5c:c3:36:37:52:b7:4d:
f7:9d:ac:8d:49:09:2c:8b:6d:fb:91:71:07:bf:c9:a8:
20:4f:46:ba:d5:b3:21:84:8d:1d:fe:59:b7:88:df:09:
7a:3a:f4:05:c6:6f:ce:4c:d4:d3:91:e9:85:74:07:e9:
4f:f6:03:4b:0f:a7:d7:12:ce:0d:37:de:81:3f:e8:c8:
ea
Signature
Algorithm: SHA256withDSA
Signature:
R: 00:91:3a:20:51:4c:8a:13:9b:fd:7b:32:ab:38:aa:4f:
d3:b8:b6:13:d6:73:d2:7f:f2:8c:8b:f0:e3
S: 00:c0:79:c9:53:0e:12:02:3c:ef:bb:ce:15:0e:f3:cd:
fe:e8:0d:1f:5e:24:c6:25:7f:84:15:da:c4
Requested Extensions
Basic Constraints: critical
CA = false
Key Usage: critical
Digital Signature
Key encipherment
Extended Key Usage:
TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
// openssl dsaparam -out dsaparam.pem 2048
// openssl gendsa -out dsakey.pem dsaparam.pem
// openssl req -new -key dsakey.pem -out test-dsa.csr \
// -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com" \
// -addext "subjectAltName = DNS:example.com,DNS:www.example.com" \
// -addext "basicConstraints = critical,CA:FALSE" \
// -addext "keyUsage = critical,digitalSignature,keyEncipherment" \
// -addext "extendedKeyUsage = serverAuth"
const IN_EXAMPLE_COM_DSA_2048 = `-----BEGIN CERTIFICATE REQUEST-----
MIIEfzCCBCwCAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G
A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE
ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCA0IwggI1BgcqhkjOOAQB
MIICKAKCAQEAsvoKmCHcR2y8qQ/kpBHOvlaGifq//F/0zhWSpfjvwqI3g2EjqXL7
rCYyu9wxoogODo6Dnenxfw1xp3ZIJNCtfrSJyt0AudjOedtVWMSnTndoQVQtYSI0
mmrBAqFL26i1bmEMxsd6pz2nU3p8yGY/wpYiWwyy+/TZv8a2t58owpw9Qkm4cX4E
Po3ih/XbN6eooOx9ZaErcS9mg3UvwQDm0VYD3ZjSeqwP7YWGyhq7gPJsEiMrft12
1SjyNz8rkhXzqZFRujjmfTT5dpCC/Z4d7/ZE30tbqHaNDM+YwBrb/aL7PnoWs847
VpjCVxmVmgIPoMHlTbg29RsIUoFlFScaUQIdAMGwwpzilrReaEqcoX7PY5u4vtV0
5zuiVIqkdBMCggEAQZhk5qdAYoMvZhPi5TOgysTzQE1FeAEtgypxZI65TpwO/JOr
AX9vYZ/qCYX/ncj455qiPZenl59lo/iQPzhJUubuCevPWJ3dsKRbAyL/5NCwifnf
YBMJGj0UFGL4ekVV0emLL9H5eqYz64w0eV2Sp40O8yCu0qr7QTi3zpqzJZ43E+26
Z9bgR6c1lmgKW2QN72PHwMlTlq0O6mN+eikEWoGr09JWpXMThZemAO2mHLAiq6ju
0+zduzWZyjZPZA1B4XUlTgCtzHveYpUzZ1NhZyM8jcGFOmmZWAFNwt03bq9/Ma0q
3jB0Dyz7IDGm8D6Y770wJRP3jf7iCVYt8jB49gOCAQUAAoIBACnVv+1ROrUiHAwn
xXGlsZdTEYZfWbE8Cter15JNNqh/Z1cdIp9m1t/rVF69nSWQvrvLeFo5p5mGxK8r
IKHTZTaAn6uO6PcNJc6iB7fS15L4uiB7p73MdjE+3PcYMbhttDlexdm6QxsmCP1F
3LYW3Uh879AURWZwPH3z4NZL2u1AFSyS1vQhtiCmztq94QwhjoDf9anFR8q05dAC
juPlKYEIhMsoq+r/l/kOM1UghhXX6BmeF8R9hhW1p4Rv+gyAgbYjowJFtZnwE5p0
OYLJzSQWjFMYEzHAoH8J4+D5okt4IXEd0BDxLBkm1WonIxYL/NL95p3qXpgUXqRX
M9spEzWgbTBrBgkqhkiG9w0BCQ4xXjBcMCcGA1UdEQQgMB6CC2V4YW1wbGUuY29t
gg93d3cuZXhhbXBsZS5jb20wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAw
EwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYJYIZIAWUDBAMCA0AAMD0CHQCyrstoqfvs
MCfsZUeycKrKQmAJAHxuoGPCKl7yAhwhNH9RNxBm5roO2U901BeF2p0pT410ghH8
oA+F
-----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_DSA_2048 = `Subject
C = CH
ST = Zurich
L = Zurich
O = Example RE
OU = IT Department
CN = example.com
Public Key
Algorithm: DSA
Length: 2048 bits
Pub: 29:d5:bf:ed:51:3a:b5:22:1c:0c:27:c5:71:a5:b1:97:
53:11:86:5f:59:b1:3c:0a:d7:ab:d7:92:4d:36:a8:7f:
67:57:1d:22:9f:66:d6:df:eb:54:5e:bd:9d:25:90:be:
bb:cb:78:5a:39:a7:99:86:c4:af:2b:20:a1:d3:65:36:
80:9f:ab:8e:e8:f7:0d:25:ce:a2:07:b7:d2:d7:92:f8:
ba:20:7b:a7:bd:cc:76:31:3e:dc:f7:18:31:b8:6d:b4:
39:5e:c5:d9:ba:43:1b:26:08:fd:45:dc:b6:16:dd:48:
7c:ef:d0:14:45:66:70:3c:7d:f3:e0:d6:4b:da:ed:40:
15:2c:92:d6:f4:21:b6:20:a6:ce:da:bd:e1:0c:21:8e:
80:df:f5:a9:c5:47:ca:b4:e5:d0:02:8e:e3:e5:29:81:
08:84:cb:28:ab:ea:ff:97:f9:0e:33:55:20:86:15:d7:
e8:19:9e:17:c4:7d:86:15:b5:a7:84:6f:fa:0c:80:81:
b6:23:a3:02:45:b5:99:f0:13:9a:74:39:82:c9:cd:24:
16:8c:53:18:13:31:c0:a0:7f:09:e3:e0:f9:a2:4b:78:
21:71:1d:d0:10:f1:2c:19:26:d5:6a:27:23:16:0b:fc:
d2:fd:e6:9d:ea:5e:98:14:5e:a4:57:33:db:29:13:35
P: 00:b2:fa:0a:98:21:dc:47:6c:bc:a9:0f:e4:a4:11:ce:
be:56:86:89:fa:bf:fc:5f:f4:ce:15:92:a5:f8:ef:c2:
a2:37:83:61:23:a9:72:fb:ac:26:32:bb:dc:31:a2:88:
0e:0e:8e:83:9d:e9:f1:7f:0d:71:a7:76:48:24:d0:ad:
7e:b4:89:ca:dd:00:b9:d8:ce:79:db:55:58:c4:a7:4e:
77:68:41:54:2d:61:22:34:9a:6a:c1:02:a1:4b:db:a8:
b5:6e:61:0c:c6:c7:7a:a7:3d:a7:53:7a:7c:c8:66:3f:
c2:96:22:5b:0c:b2:fb:f4:d9:bf:c6:b6:b7:9f:28:c2:
9c:3d:42:49:b8:71:7e:04:3e:8d:e2:87:f5:db:37:a7:
a8:a0:ec:7d:65:a1:2b:71:2f:66:83:75:2f:c1:00:e6:
d1:56:03:dd:98:d2:7a:ac:0f:ed:85:86:ca:1a:bb:80:
f2:6c:12:23:2b:7e:dd:76:d5:28:f2:37:3f:2b:92:15:
f3:a9:91:51:ba:38:e6:7d:34:f9:76:90:82:fd:9e:1d:
ef:f6:44:df:4b:5b:a8:76:8d:0c:cf:98:c0:1a:db:fd:
a2:fb:3e:7a:16:b3:ce:3b:56:98:c2:57:19:95:9a:02:
0f:a0:c1:e5:4d:b8:36:f5:1b:08:52:81:65:15:27:1a:
51
Q: 00:c1:b0:c2:9c:e2:96:b4:5e:68:4a:9c:a1:7e:cf:63:
9b:b8:be:d5:74:e7:3b:a2:54:8a:a4:74:13
G: 41:98:64:e6:a7:40:62:83:2f:66:13:e2:e5:33:a0:ca:
c4:f3:40:4d:45:78:01:2d:83:2a:71:64:8e:b9:4e:9c:
0e:fc:93:ab:01:7f:6f:61:9f:ea:09:85:ff:9d:c8:f8:
e7:9a:a2:3d:97:a7:97:9f:65:a3:f8:90:3f:38:49:52:
e6:ee:09:eb:cf:58:9d:dd:b0:a4:5b:03:22:ff:e4:d0:
b0:89:f9:df:60:13:09:1a:3d:14:14:62:f8:7a:45:55:
d1:e9:8b:2f:d1:f9:7a:a6:33:eb:8c:34:79:5d:92:a7:
8d:0e:f3:20:ae:d2:aa:fb:41:38:b7:ce:9a:b3:25:9e:
37:13:ed:ba:67:d6:e0:47:a7:35:96:68:0a:5b:64:0d:
ef:63:c7:c0:c9:53:96:ad:0e:ea:63:7e:7a:29:04:5a:
81:ab:d3:d2:56:a5:73:13:85:97:a6:00:ed:a6:1c:b0:
22:ab:a8:ee:d3:ec:dd:bb:35:99:ca:36:4f:64:0d:41:
e1:75:25:4e:00:ad:cc:7b:de:62:95:33:67:53:61:67:
23:3c:8d:c1:85:3a:69:99:58:01:4d:c2:dd:37:6e:af:
7f:31:ad:2a:de:30:74:0f:2c:fb:20:31:a6:f0:3e:98:
ef:bd:30:25:13:f7:8d:fe:e2:09:56:2d:f2:30:78:f6
Signature
Algorithm: SHA256withDSA
Signature:
R: 00:b2:ae:cb:68:a9:fb:ec:30:27:ec:65:47:b2:70:aa:
ca:42:60:09:00:7c:6e:a0:63:c2:2a:5e:f2
S: 21:34:7f:51:37:10:66:e6:ba:0e:d9:4f:74:d4:17:85:
da:9d:29:4f:8d:74:82:11:fc:a0:0f:85
Requested Extensions
Basic Constraints: critical
CA = false
Key Usage: critical
Digital Signature
Key encipherment
Extended Key Usage:
TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
// openssl req -newkey rsa:4096 -keyout test-rsa-4096.key -out test-rsa-4096.csr
// -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com"
// -addext "subjectAltName = DNS:example.com,DNS:www.example.com,IP:127.0.0.1, \
// email:user@example.com,URI:http://example.com/api,otherName:1.2.3.4;UTF8:some value"
// -addext "basicConstraints = critical,CA:FALSE"
// -addext "keyUsage = critical,digitalSignature,keyEncipherment"
// -addext "extendedKeyUsage = serverAuth"
const IN_EXAMPLE_COM_SAN = `-----BEGIN CERTIFICATE REQUEST-----
MIIFbTCCA1UCAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G
A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE
ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBAJf8uQDFcQfj6qCuPa4hNyDWr3Lwzfc3qQZdOgNJ/kym
GxxRHUXJyBtgkmAqDoSGmg1hUWgt9eZwd/Cf4Wd3qr+Q0ppg6dwZeWgYSunseoKl
f0E5FvUfECNyDwCSbltN9TCsom2ePNOOJJHWo4Y3E3jGXz0n1Vwa6ePR0j62Rcey
4lHLscQ3GoNvMLcXbY1HIhnbaI25MmFPB8p4PvpPsAYgbWHbw0jIR9dSxEK0HAU3
2VkRkm8XaF4BOEfugqT3Bc7zAvwdFZRTTTZIICYW5T3zvtxBidJ8OSej16LV6ZeE
/4VcTzXYTzIUXbNaev3XN1r5ZodkbZvxxk/EZmfes2OtedPulW4TW27HSl6XBos/
8VQohelUXiyCLPrtbnjeHKSz47+ZAm23jMAFYWkTVdWvAa+G74UstuRRXfLAKCNv
7VeA3l8IgEkfj48u+EenV6cJ3ZJJ5/qvZo7OUjhAtYJmNtlRYE4r3uWRmaNXYwrD
7vJuMiZafaVC+74/UHLGGm7sHVJdo4KBO/LUbHJ/SKZIYMc14kJLOf6TPZXSGm9N
TxbOV9Vzcjzivq1HxaYirLAM+nyVApVwwpVq/uiEFz579yrwySvBuwnewfdfZ6EZ
iNAKiBwQ8diFMnFfd/28hJ8TrIlq+5bkVo1ODuhyRIw9YB19IrmytaVvkR8624Ld
AgMBAAGggbUwgbIGCSqGSIb3DQEJDjGBpDCBoTBsBgNVHREEZTBjggtleGFtcGxl
LmNvbYIPd3d3LmV4YW1wbGUuY29thwR/AAABgRB1c2VyQGV4YW1wbGUuY29thhZo
dHRwOi8vZXhhbXBsZS5jb20vYXBpoBMGAyoDBKAMDApzb21lIHZhbHVlMAwGA1Ud
EwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0G
CSqGSIb3DQEBCwUAA4ICAQAtOuh6MEralwgChJHBaGJavBxpCQ0p5K77RlAPIk5Q
Mv5086DxiZEFBKCRiZRtkOvo0aCHUn3awDrlEOgECiAYQqMIBUWeNwImtmpDopuI
ZMmVmzc2ojf9nUlPrPV+B6P2jTxTIQYpDQocbOgxDkcdZVSvLyMEFnHIMNQV7GS2
gBmUnPp+4z2d8X9XaRspkuEt2nbA1NoXekWaG46jG56VoBycepOiNkwL4AsqunLa
T0urcHq34g+HRQWwOA+q/72qP4oaj2ZO0fFJQl2ZsGRT/IuM1g2YsnVSpBOGY/J6
Qi2hDr6EEqphg501ny+FZE1BouQ/lSykafYyauwNq1puu/VyuF8grFmL0SoxWWfP
h6viblGM/Vu69Bhl4gkWKtufWpOVpCA4vHzes8IVMFg7vhpwm33Xjo0lCPcIUin6
0CqHZQCsWtj2yIAF66WHB0I1DHL5FNCWRPnQCo54qRZIYqtSP20QRr6GWC2d+ZgX
wDxRpmzr8T8owBYWw3j+RK9CtZoWO4O586UR4J1Bn5PQfoR78Z/4mzv2sxVi9Fdf
sJzlG6/nhmMaCqneIn97gkguvSgpOuKSeo/fjbpnthufgilrpDQoGrhZaXic0GVZ
6JmbOh3tLMVf4ooyyaLfOCfV2FN12rDa3pdWhQ4MVN4gg9U3Cq0x7yRQKiSBlBnw
oA==
-----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_SAN = `Subject
C = CH
ST = Zurich
L = Zurich
O = Example RE
OU = IT Department
CN = example.com
Public Key
Algorithm: RSA
Length: 4096 bits
Modulus: 00:97:fc:b9:00:c5:71:07:e3:ea:a0:ae:3d:ae:21:37:
20:d6:af:72:f0:cd:f7:37:a9:06:5d:3a:03:49:fe:4c:
a6:1b:1c:51:1d:45:c9:c8:1b:60:92:60:2a:0e:84:86:
9a:0d:61:51:68:2d:f5:e6:70:77:f0:9f:e1:67:77:aa:
bf:90:d2:9a:60:e9:dc:19:79:68:18:4a:e9:ec:7a:82:
a5:7f:41:39:16:f5:1f:10:23:72:0f:00:92:6e:5b:4d:
f5:30:ac:a2:6d:9e:3c:d3:8e:24:91:d6:a3:86:37:13:
78:c6:5f:3d:27:d5:5c:1a:e9:e3:d1:d2:3e:b6:45:c7:
b2:e2:51:cb:b1:c4:37:1a:83:6f:30:b7:17:6d:8d:47:
22:19:db:68:8d:b9:32:61:4f:07:ca:78:3e:fa:4f:b0:
06:20:6d:61:db:c3:48:c8:47:d7:52:c4:42:b4:1c:05:
37:d9:59:11:92:6f:17:68:5e:01:38:47:ee:82:a4:f7:
05:ce:f3:02:fc:1d:15:94:53:4d:36:48:20:26:16:e5:
3d:f3:be:dc:41:89:d2:7c:39:27:a3:d7:a2:d5:e9:97:
84:ff:85:5c:4f:35:d8:4f:32:14:5d:b3:5a:7a:fd:d7:
37:5a:f9:66:87:64:6d:9b:f1:c6:4f:c4:66:67:de:b3:
63:ad:79:d3:ee:95:6e:13:5b:6e:c7:4a:5e:97:06:8b:
3f:f1:54:28:85:e9:54:5e:2c:82:2c:fa:ed:6e:78:de:
1c:a4:b3:e3:bf:99:02:6d:b7:8c:c0:05:61:69:13:55:
d5:af:01:af:86:ef:85:2c:b6:e4:51:5d:f2:c0:28:23:
6f:ed:57:80:de:5f:08:80:49:1f:8f:8f:2e:f8:47:a7:
57:a7:09:dd:92:49:e7:fa:af:66:8e:ce:52:38:40:b5:
82:66:36:d9:51:60:4e:2b:de:e5:91:99:a3:57:63:0a:
c3:ee:f2:6e:32:26:5a:7d:a5:42:fb:be:3f:50:72:c6:
1a:6e:ec:1d:52:5d:a3:82:81:3b:f2:d4:6c:72:7f:48:
a6:48:60:c7:35:e2:42:4b:39:fe:93:3d:95:d2:1a:6f:
4d:4f:16:ce:57:d5:73:72:3c:e2:be:ad:47:c5:a6:22:
ac:b0:0c:fa:7c:95:02:95:70:c2:95:6a:fe:e8:84:17:
3e:7b:f7:2a:f0:c9:2b:c1:bb:09:de:c1:f7:5f:67:a1:
19:88:d0:0a:88:1c:10:f1:d8:85:32:71:5f:77:fd:bc:
84:9f:13:ac:89:6a:fb:96:e4:56:8d:4e:0e:e8:72:44:
8c:3d:60:1d:7d:22:b9:b2:b5:a5:6f:91:1f:3a:db:82:
dd
Exponent: 65537 (0x10001)
Signature
Algorithm: SHA256withRSA
Signature: 2d:3a:e8:7a:30:4a:da:97:08:02:84:91:c1:68:62:5a:
bc:1c:69:09:0d:29:e4:ae:fb:46:50:0f:22:4e:50:32:
fe:74:f3:a0:f1:89:91:05:04:a0:91:89:94:6d:90:eb:
e8:d1:a0:87:52:7d:da:c0:3a:e5:10:e8:04:0a:20:18:
42:a3:08:05:45:9e:37:02:26:b6:6a:43:a2:9b:88:64:
c9:95:9b:37:36:a2:37:fd:9d:49:4f:ac:f5:7e:07:a3:
f6:8d:3c:53:21:06:29:0d:0a:1c:6c:e8:31:0e:47:1d:
65:54:af:2f:23:04:16:71:c8:30:d4:15:ec:64:b6:80:
19:94:9c:fa:7e:e3:3d:9d:f1:7f:57:69:1b:29:92:e1:
2d:da:76:c0:d4:da:17:7a:45:9a:1b:8e:a3:1b:9e:95:
a0:1c:9c:7a:93:a2:36:4c:0b:e0:0b:2a:ba:72:da:4f:
4b:ab:70:7a:b7:e2:0f:87:45:05:b0:38:0f:aa:ff:bd:
aa:3f:8a:1a:8f:66:4e:d1:f1:49:42:5d:99:b0:64:53:
fc:8b:8c:d6:0d:98:b2:75:52:a4:13:86:63:f2:7a:42:
2d:a1:0e:be:84:12:aa:61:83:9d:35:9f:2f:85:64:4d:
41:a2:e4:3f:95:2c:a4:69:f6:32:6a:ec:0d:ab:5a:6e:
bb:f5:72:b8:5f:20:ac:59:8b:d1:2a:31:59:67:cf:87:
ab:e2:6e:51:8c:fd:5b:ba:f4:18:65:e2:09:16:2a:db:
9f:5a:93:95:a4:20:38:bc:7c:de:b3:c2:15:30:58:3b:
be:1a:70:9b:7d:d7:8e:8d:25:08:f7:08:52:29:fa:d0:
2a:87:65:00:ac:5a:d8:f6:c8:80:05:eb:a5:87:07:42:
35:0c:72:f9:14:d0:96:44:f9:d0:0a:8e:78:a9:16:48:
62:ab:52:3f:6d:10:46:be:86:58:2d:9d:f9:98:17:c0:
3c:51:a6:6c:eb:f1:3f:28:c0:16:16:c3:78:fe:44:af:
42:b5:9a:16:3b:83:b9:f3:a5:11:e0:9d:41:9f:93:d0:
7e:84:7b:f1:9f:f8:9b:3b:f6:b3:15:62:f4:57:5f:b0:
9c:e5:1b:af:e7:86:63:1a:0a:a9:de:22:7f:7b:82:48:
2e:bd:28:29:3a:e2:92:7a:8f:df:8d:ba:67:b6:1b:9f:
82:29:6b:a4:34:28:1a:b8:59:69:78:9c:d0:65:59:e8:
99:9b:3a:1d:ed:2c:c5:5f:e2:8a:32:c9:a2:df:38:27:
d5:d8:53:75:da:b0:da:de:97:56:85:0e:0c:54:de:20:
83:d5:37:0a:ad:31:ef:24:50:2a:24:81:94:19:f0:a0
Requested Extensions
Basic Constraints: critical
CA = false
Key Usage: critical
Digital Signature
Key encipherment
Extended Key Usage:
TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com
IP: 127.0.0.1
EMAIL: user@example.com
URI: http://example.com/api
Other: 1.2.3.4::some value`;
// openssl req -newkey rsa:2048 -keyout test-rsa-2048.key -out test-rsa-2048.csr \
// -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com" \
// -addext "subjectAltName = DNS:example.com,DNS:www.example.com" \
// -addext "basicConstraints = critical,CA:FALSE" \
// -addext "keyUsage = critical,digitalSignature,keyEncipherment," \
// -addext "extendedKeyUsage = serverAuth"
const IN_EXAMPLE_COM_KEY_USAGE = `-----BEGIN CERTIFICATE REQUEST-----
MIIDJDCCAgwCAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G
A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE
ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAKHQWxqtdJQ1l7ApTgwgsyrN/kRDrog/DsUlZQg3YodY
4RRAgPr+AeQ1BhuWDVxaXein0XmXOESHgK9Z7X/hLgRy2ifK+n20Ij3+k6VSh6Lt
lpjUPwK7PWBtZ969DukBIvq64XrJTNWIJPvXXQxkL4dk5NcDY4TjXWt0GgDVR+GH
OU1JwfzviGVRdOmY8+Ckfxc+3QytTdP6KBQaiUk5sBEniovDpKfImtql72JsCRbA
9Wue7X4EbXi2zvoAlJ5NXF3Ps1q2XsVJeIx/mMDcgRW7s5AVM9NQW0O1JLoA7dY+
vSrKZj+ssuKCIWM7u9Big2I0miEl5AXrDlwZPBhM9FMCAwEAAaBtMGsGCSqGSIb3
DQEJDjFeMFwwJwYDVR0RBCAwHoILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNv
bTAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIB/jATBgNVHSUEDDAKBggrBgEF
BQcDATANBgkqhkiG9w0BAQsFAAOCAQEAPOr6jfq/mXilqXA11CTza69Ydd4fvp6q
UG47PefzQqSmYtpUytwZRLGQ1IFRlYeXwbazVLkRmLNwpbB8C5fh9FPp55JCpM/O
tgCW2uqLkCtkQMUCaSdRX/Y+9ypYhdBkSNv1Q+3QXi2jmi5QMqwerAwNmeXmH6AZ
swMgAhuoLS9OrIqHjFoHGoXsgXMkbLr6m6hgyFt8ZbbwK4WpVcgCZfhtBiLilCJN
Xr9GUXL3FqUb7sIaYKAaghr2haqKhFsIH57XVK3DZYhOkLd9uC8TLdl2e+t9Hcy9
ymLwiIGMUfuBQMP8nVu3jGXAQ5N4VV+IZfF8UaBFW8tG+Ms2TeW68Q==
-----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_KEY_USAGE = `Subject
C = CH
ST = Zurich
L = Zurich
O = Example RE
OU = IT Department
CN = example.com
Public Key
Algorithm: RSA
Length: 2048 bits
Modulus: 00:a1:d0:5b:1a:ad:74:94:35:97:b0:29:4e:0c:20:b3:
2a:cd:fe:44:43:ae:88:3f:0e:c5:25:65:08:37:62:87:
58:e1:14:40:80:fa:fe:01:e4:35:06:1b:96:0d:5c:5a:
5d:e8:a7:d1:79:97:38:44:87:80:af:59:ed:7f:e1:2e:
04:72:da:27:ca:fa:7d:b4:22:3d:fe:93:a5:52:87:a2:
ed:96:98:d4:3f:02:bb:3d:60:6d:67:de:bd:0e:e9:01:
22:fa:ba:e1:7a:c9:4c:d5:88:24:fb:d7:5d:0c:64:2f:
87:64:e4:d7:03:63:84:e3:5d:6b:74:1a:00:d5:47:e1:
87:39:4d:49:c1:fc:ef:88:65:51:74:e9:98:f3:e0:a4:
7f:17:3e:dd:0c:ad:4d:d3:fa:28:14:1a:89:49:39:b0:
11:27:8a:8b:c3:a4:a7:c8:9a:da:a5:ef:62:6c:09:16:
c0:f5:6b:9e:ed:7e:04:6d:78:b6:ce:fa:00:94:9e:4d:
5c:5d:cf:b3:5a:b6:5e:c5:49:78:8c:7f:98:c0:dc:81:
15:bb:b3:90:15:33:d3:50:5b:43:b5:24:ba:00:ed:d6:
3e:bd:2a:ca:66:3f:ac:b2:e2:82:21:63:3b:bb:d0:62:
83:62:34:9a:21:25:e4:05:eb:0e:5c:19:3c:18:4c:f4:
53
Exponent: 65537 (0x10001)
Signature
Algorithm: SHA256withRSA
Signature: 3c:ea:fa:8d:fa:bf:99:78:a5:a9:70:35:d4:24:f3:6b:
af:58:75:de:1f:be:9e:aa:50:6e:3b:3d:e7:f3:42:a4:
a6:62:da:54:ca:dc:19:44:b1:90:d4:81:51:95:87:97:
c1:b6:b3:54:b9:11:98:b3:70:a5:b0:7c:0b:97:e1:f4:
53:e9:e7:92:42:a4:cf:ce:b6:00:96:da:ea:8b:90:2b:
64:40:c5:02:69:27:51:5f:f6:3e:f7:2a:58:85:d0:64:
48:db:f5:43:ed:d0:5e:2d:a3:9a:2e:50:32:ac:1e:ac:
0c:0d:99:e5:e6:1f:a0:19:b3:03:20:02:1b:a8:2d:2f:
4e:ac:8a:87:8c:5a:07:1a:85:ec:81:73:24:6c:ba:fa:
9b:a8:60:c8:5b:7c:65:b6:f0:2b:85:a9:55:c8:02:65:
f8:6d:06:22:e2:94:22:4d:5e:bf:46:51:72:f7:16:a5:
1b:ee:c2:1a:60:a0:1a:82:1a:f6:85:aa:8a:84:5b:08:
1f:9e:d7:54:ad:c3:65:88:4e:90:b7:7d:b8:2f:13:2d:
d9:76:7b:eb:7d:1d:cc:bd:ca:62:f0:88:81:8c:51:fb:
81:40:c3:fc:9d:5b:b7:8c:65:c0:43:93:78:55:5f:88:
65:f1:7c:51:a0:45:5b:cb:46:f8:cb:36:4d:e5:ba:f1
Requested Extensions
Basic Constraints: critical
CA = false
Key Usage: critical
Digital Signature
Non-repudiation
Key encipherment
Data encipherment
Key agreement
Key certificate signing
CRL signing
Extended Key Usage:
TLS Web Server Authentication
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
// openssl req -newkey rsa:2048 -keyout test-rsa-2048.key -out test-rsa-2048.csr \
// -subj "/C=CH/ST=Zurich/L=Zurich/O=Example RE/OU=IT Department/CN=example.com" \
// -addext "subjectAltName = DNS:example.com,DNS:www.example.com" \
// -addext "basicConstraints = critical,CA:FALSE" \
// -addext "keyUsage = critical,digitalSignature,keyEncipherment" \
// -addext "extendedKeyUsage = serverAuth"
const IN_EXAMPLE_COM_EXTENDED_KEY_USAGE = `-----BEGIN CERTIFICATE REQUEST-----
MIIDpzCCAo8CAQAwcjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0G
A1UEBwwGWnVyaWNoMRMwEQYDVQQKDApFeGFtcGxlIFJFMRYwFAYDVQQLDA1JVCBE
ZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMjQ/Bz+CzA/WaS+Nyp3ijWzYlKY7GmA/a2FuzNSPQlr
WuGyZJcfb0CpLIpRF8qcDllAe+hFQnVGnk3svQIhfEOD7qwzBRMHVhe59jkv2kER
s+u88KBCNfIAS6m5d45y4xH338aXq4lZexiEASWHS7SsWAR3kL3c9p14U9EHOaym
ZWPO/SCfCJyhxszDLM2eG5S2rviuu9nY+rk0Oo7z8x8PZF9Wl1NamLl1tWPqsznS
3bfjdJYeUlm7XvTzC6EMAT6K/5ker0chl7Hg0mcEO9w4c2cSTAHvZ2b2sRYbxNQZ
49byQsRAXW8TNnOaK9Phmvwy/irEXU9PEl3u7KvSnNcCAwEAAaCB7zCB7AYJKoZI
hvcNAQkOMYHeMIHbMCcGA1UdEQQgMB6CC2V4YW1wbGUuY29tgg93d3cuZXhhbXBs
ZS5jb20wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwgZEGA1UdJQSBiTCB
hgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUHAwQGCCsGAQUF
BwMIBgorBgEEAYI3AgEVBgorBgEEAYI3AgEWBgorBgEEAYI3CgMBBgorBgEEAYI3
CgMDBgorBgEEAYI3CgMEBgorBgEEAYI3FAICBgorBgEEAYI3CgMDMA0GCSqGSIb3
DQEBCwUAA4IBAQCcYWj1eIxj/FUEhhm2lZr06Pq4GEtIVsMWw5IrUn2FIFb/yY8x
GHuB5v7XNA/8zhRWvIAXGaa8Bnajk4mR0rkxy1MXpd2YevdrF/XFa2Totv4E4/I6
pvrFefYTSGpmCu5zQTuoanM7JjE81vvbTLFdaHMdLOekpuK5v5kbuNdtDpEiAkd0
vmV4BQ0BV3b3zhIRQqBB60pSBHYvMhHNn/80RhVUQxaPTS7/AMHRZGRc1lD9/bjA
pMBis9CL4AbXtTcztU5qy4VpB1/Ej3AbAjuJIbpbPH6XtxIEtqdM4Seqi44w9oX4
rxQagXmvJPp+E4253EkeHwhfHh4SnJEtsibQ
-----END CERTIFICATE REQUEST-----`;
const OUT_EXAMPLE_COM_EXTENDED_KEY_USAGE = `Subject
C = CH
ST = Zurich
L = Zurich
O = Example RE
OU = IT Department
CN = example.com
Public Key
Algorithm: RSA
Length: 2048 bits
Modulus: 00:c8:d0:fc:1c:fe:0b:30:3f:59:a4:be:37:2a:77:8a:
35:b3:62:52:98:ec:69:80:fd:ad:85:bb:33:52:3d:09:
6b:5a:e1:b2:64:97:1f:6f:40:a9:2c:8a:51:17:ca:9c:
0e:59:40:7b:e8:45:42:75:46:9e:4d:ec:bd:02:21:7c:
43:83:ee:ac:33:05:13:07:56:17:b9:f6:39:2f:da:41:
11:b3:eb:bc:f0:a0:42:35:f2:00:4b:a9:b9:77:8e:72:
e3:11:f7:df:c6:97:ab:89:59:7b:18:84:01:25:87:4b:
b4:ac:58:04:77:90:bd:dc:f6:9d:78:53:d1:07:39:ac:
a6:65:63:ce:fd:20:9f:08:9c:a1:c6:cc:c3:2c:cd:9e:
1b:94:b6:ae:f8:ae:bb:d9:d8:fa:b9:34:3a:8e:f3:f3:
1f:0f:64:5f:56:97:53:5a:98:b9:75:b5:63:ea:b3:39:
d2:dd:b7:e3:74:96:1e:52:59:bb:5e:f4:f3:0b:a1:0c:
01:3e:8a:ff:99:1e:af:47:21:97:b1:e0:d2:67:04:3b:
dc:38:73:67:12:4c:01:ef:67:66:f6:b1:16:1b:c4:d4:
19:e3:d6:f2:42:c4:40:5d:6f:13:36:73:9a:2b:d3:e1:
9a:fc:32:fe:2a:c4:5d:4f:4f:12:5d:ee:ec:ab:d2:9c:
d7
Exponent: 65537 (0x10001)
Signature
Algorithm: SHA256withRSA
Signature: 9c:61:68:f5:78:8c:63:fc:55:04:86:19:b6:95:9a:f4:
e8:fa:b8:18:4b:48:56:c3:16:c3:92:2b:52:7d:85:20:
56:ff:c9:8f:31:18:7b:81:e6:fe:d7:34:0f:fc:ce:14:
56:bc:80:17:19:a6:bc:06:76:a3:93:89:91:d2:b9:31:
cb:53:17:a5:dd:98:7a:f7:6b:17:f5:c5:6b:64:e8:b6:
fe:04:e3:f2:3a:a6:fa:c5:79:f6:13:48:6a:66:0a:ee:
73:41:3b:a8:6a:73:3b:26:31:3c:d6:fb:db:4c:b1:5d:
68:73:1d:2c:e7:a4:a6:e2:b9:bf:99:1b:b8:d7:6d:0e:
91:22:02:47:74:be:65:78:05:0d:01:57:76:f7:ce:12:
11:42:a0:41:eb:4a:52:04:76:2f:32:11:cd:9f:ff:34:
46:15:54:43:16:8f:4d:2e:ff:00:c1:d1:64:64:5c:d6:
50:fd:fd:b8:c0:a4:c0:62:b3:d0:8b:e0:06:d7:b5:37:
33:b5:4e:6a:cb:85:69:07:5f:c4:8f:70:1b:02:3b:89:
21:ba:5b:3c:7e:97:b7:12:04:b6:a7:4c:e1:27:aa:8b:
8e:30:f6:85:f8:af:14:1a:81:79:af:24:fa:7e:13:8d:
b9:dc:49:1e:1f:08:5f:1e:1e:12:9c:91:2d:b2:26:d0
Requested Extensions
Basic Constraints: critical
CA = false
Key Usage: critical
Digital Signature
Key encipherment
Extended Key Usage:
TLS Web Server Authentication
TLS Web Client Authentication
Code signing
E-mail Protection (S/MIME)
Trusted Timestamping
Microsoft Individual Code Signing
Microsoft Commercial Code Signing
Microsoft Trust List Signing
Microsoft Server Gated Crypto
Microsoft Encrypted File System
Microsoft Smartcard Login
Microsoft Server Gated Crypto
Subject Alternative Name:
DNS: example.com
DNS: www.example.com`;
TestRegister.addTests([ TestRegister.addTests([
{ {
@ -184,7 +867,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
"op": "Parse CSR", "op": "Parse CSR",
"args": ["PEM", true] "args": ["PEM"]
} }
] ]
}, },
@ -195,21 +878,107 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
"op": "Parse CSR", "op": "Parse CSR",
"args": ["PEM", true] "args": ["PEM"]
} }
] ]
}, },
// RSA algorithm is the only one supported for CSR in node-forge as of 1.3.1
{ {
name: "Parse CSR: Example Certificate Signing Request (CSR) with EC 256", name: "Parse CSR: Example Certificate Signing Request (CSR) with EC 256",
input: IN_EXAMPLE_COM_EC, input: IN_EXAMPLE_COM_EC_P256,
expectedError: true, expectedOutput: OUT_EXAMPLE_COM_EC_P256,
expectedOutput: OUT_EXAMPLE_COM_EC,
recipeConfig: [ recipeConfig: [
{ {
"op": "Parse CSR", "op": "Parse CSR",
"args": ["PEM", true] "args": ["PEM"]
} }
] ]
},
{
name: "Parse CSR: Example Certificate Signing Request (CSR) with EC 384",
input: IN_EXAMPLE_COM_EC_P384,
expectedOutput: OUT_EXAMPLE_COM_EC_P384,
recipeConfig: [
{
"op": "Parse CSR",
"args": ["PEM"]
} }
]
},
{
name: "Parse CSR: Example Certificate Signing Request (CSR) with EC 521",
input: IN_EXAMPLE_COM_EC_P521,
expectedOutput: OUT_EXAMPLE_COM_EC_P521,
recipeConfig: [
{
"op": "Parse CSR",
"args": ["PEM"]
}
]
},
{
name: "Parse CSR: Example Certificate Signing Request (CSR) with DSA 1024",
input: IN_EXAMPLE_COM_DSA_1024,
expectedOutput: OUT_EXAMPLE_COM_DSA_1024,
recipeConfig: [
{
"op": "Parse CSR",
"args": ["PEM"]
}
]
},
{
name: "Parse CSR: Example Certificate Signing Request (CSR) with DSA 2048",
input: IN_EXAMPLE_COM_DSA_2048,
expectedOutput: OUT_EXAMPLE_COM_DSA_2048,
recipeConfig: [
{
"op": "Parse CSR",
"args": ["PEM"]
}
]
},
{
name: "Parse CSR: Example Certificate Signing Request (CSR) with DSA 2048",
input: IN_EXAMPLE_COM_DSA_2048,
expectedOutput: OUT_EXAMPLE_COM_DSA_2048,
recipeConfig: [
{
"op": "Parse CSR",
"args": ["PEM"]
}
]
},
{
name: "Parse CSR: Example Certificate Signing Request (CSR) with various SAN types",
input: IN_EXAMPLE_COM_SAN,
expectedOutput: OUT_EXAMPLE_COM_SAN,
recipeConfig: [
{
"op": "Parse CSR",
"args": ["PEM"]
}
]
},
{
name: "Parse CSR: Example Certificate Signing Request (CSR) with various Key Usages",
input: IN_EXAMPLE_COM_KEY_USAGE,
expectedOutput: OUT_EXAMPLE_COM_KEY_USAGE,
recipeConfig: [
{
"op": "Parse CSR",
"args": ["PEM"]
}
]
},
{
name: "Parse CSR: Example Certificate Signing Request (CSR) with various Extended Key Usages",
input: IN_EXAMPLE_COM_EXTENDED_KEY_USAGE,
expectedOutput: OUT_EXAMPLE_COM_EXTENDED_KEY_USAGE,
recipeConfig: [
{
"op": "Parse CSR",
"args": ["PEM"]
}
]
},
]); ]);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,331 @@
/**
* Parse X.509 CRL tests.
*
* @author robinsandhu
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const IN_CRL_PEM_RSA = `-----BEGIN X509 CRL-----
MIID7jCCAdYCAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVUsxDzANBgNV
BAgMBkxvbmRvbjELMAkGA1UECgwCQkIxFTATBgNVBAMMDFRlc3QgUm9vdCBDQRcN
MjQwODI1MTE0OTEwWhcNMjQwOTI0MTE0OTEwWjA1MDMCAhAAFw0yNDA4MjUwMzIz
MDhaMB4wCgYDVR0VBAMKAQYwEAYDVR0XBAkGByqGSM44AgOgggEnMIIBIzAJBgNV
HRIEAjAAMH0GA1UdIwR2MHSAFLjJrf2oUFTVhW40i0xgL7BJtodGoUakRDBCMQsw
CQYDVQQGEwJVSzEPMA0GA1UECAwGTG9uZG9uMQswCQYDVQQKDAJCQjEVMBMGA1UE
AwwMVGVzdCBSb290IENBghQ3XUv2vXwRfMxGGv/XLywm+B5LPTAtBgNVHS4EJjAk
MCKgIKAehhxodHRwOi8vZXhhbXBsZS5jb20vZGVsdGEtY3JsMFsGA1UdHwRUMFIw
IaAfoB2GG2h0dHA6Ly9leGFtcGxlLmNvbS9mdWxsLWNybDAhoB+gHYYbbGRhcDov
L2V4YW1wbGUuY29tL2Z1bGwtY3JsMAqgCKAGhwR/AAABMAsGA1UdFAQEAgIePDAN
BgkqhkiG9w0BAQsFAAOCAgEAAxsr+9nELUVWhFekwy6GsqH8xOf6EqGjRaEdX49W
mB40m2VajOkK8UHGoVyZzoDI2r/c8OPXUtbpK0fpvEl3SZU5j/C8JbZaZFFrEGeH
fSEqdVHFjohpawNcG41Qs+YT21TBqH1hD5yVI7gjVvfKICRfxDpl5oGClxBCVOSV
gVtLbe9q44uCBJ1kUkoc9Vz47Hv7JyckgqVXkORWHt2SFNALxlMEzOEQTpuC5Kcb
4i7hTCUF+kpkIvr02LJImq0Aaqzs6cC/DcdJiRPPyfaN8fQryFv76gg9i8zZcb6c
W42rvumiyw+7nnZfmq53webr5fCHaXhZk47ASOJD6GC5cX9rje1qGRgULXRhqcvK
n319s2iXj3FStDDorKGgsCV2zYmotX17ExB98CcCgBE52zMtRZilwhOGeh8mx3qT
l0W2B8uKKAq5BMmiziSBzQt700JPiruURZXbQ1fH1n7pKP6wGEh2e9TfQMlN20hE
I+CMt+1bG0Bpt5AfiwE8UykQ/WvpVxdJrgj0JM0yA37KfC8XD+cmavJ5/grorbj3
t0zBdK7bl+Y45VU/5/mX5ZR3O3ea1RclPM3hKMREfPneOlpan6r3dVwFqEN/TeTu
46vuDeKaEr3yJkOFfy0lSYPhPhzhU5vDR5ibxqvwxZNznI2AdTnZLEf8LRqnTVo1
qx0=
-----END X509 CRL-----`;
const OUT_CRL_PEM_RSA = `Certificate Revocation List (CRL):
Version: 2 (0x1)
Signature Algorithm: SHA256withRSA
Issuer:
C = UK
ST = London
O = BB
CN = Test Root CA
Last Update: Sun, 25 Aug 2024 11:49:10 GMT
Next Update: Tue, 24 Sep 2024 11:49:10 GMT
CRL extensions:
2.5.29.46:
Unsupported CRL extension. Try openssl CLI.
X509v3 Authority Key Identifier:
keyid:B8:C9:AD:FD:A8:50:54:D5:85:6E:34:8B:4C:60:2F:B0:49:B6:87:46
DirName:/C=UK/ST=London/O=BB/CN=Test Root CA
serial:37:5D:4B:F6:BD:7C:11:7C:CC:46:1A:FF:D7:2F:2C:26:F8:1E:4B:3D
X509v3 CRL Distribution Points:
Full Name:
URI:http://example.com/full-crl
Full Name:
URI:ldap://example.com/full-crl
Full Name:
IP:127.0.0.1
X509v3 CRL Number:
1E3C
issuerAltName:
Unsupported CRL extension. Try openssl CLI.
Revoked Certificates:
Serial Number: 1000
Revocation Date: Sun, 25 Aug 2024 03:23:08 GMT
CRL entry extensions:
X509v3 CRL Reason Code:
Certificate Hold
Hold Instruction Code:
Hold Instruction Reject
Signature Value:
03:1b:2b:fb:d9:c4:2d:45:56:84:57:a4:c3:2e:86:b2:a1:fc:
c4:e7:fa:12:a1:a3:45:a1:1d:5f:8f:56:98:1e:34:9b:65:5a:
8c:e9:0a:f1:41:c6:a1:5c:99:ce:80:c8:da:bf:dc:f0:e3:d7:
52:d6:e9:2b:47:e9:bc:49:77:49:95:39:8f:f0:bc:25:b6:5a:
64:51:6b:10:67:87:7d:21:2a:75:51:c5:8e:88:69:6b:03:5c:
1b:8d:50:b3:e6:13:db:54:c1:a8:7d:61:0f:9c:95:23:b8:23:
56:f7:ca:20:24:5f:c4:3a:65:e6:81:82:97:10:42:54:e4:95:
81:5b:4b:6d:ef:6a:e3:8b:82:04:9d:64:52:4a:1c:f5:5c:f8:
ec:7b:fb:27:27:24:82:a5:57:90:e4:56:1e:dd:92:14:d0:0b:
c6:53:04:cc:e1:10:4e:9b:82:e4:a7:1b:e2:2e:e1:4c:25:05:
fa:4a:64:22:fa:f4:d8:b2:48:9a:ad:00:6a:ac:ec:e9:c0:bf:
0d:c7:49:89:13:cf:c9:f6:8d:f1:f4:2b:c8:5b:fb:ea:08:3d:
8b:cc:d9:71:be:9c:5b:8d:ab:be:e9:a2:cb:0f:bb:9e:76:5f:
9a:ae:77:c1:e6:eb:e5:f0:87:69:78:59:93:8e:c0:48:e2:43:
e8:60:b9:71:7f:6b:8d:ed:6a:19:18:14:2d:74:61:a9:cb:ca:
9f:7d:7d:b3:68:97:8f:71:52:b4:30:e8:ac:a1:a0:b0:25:76:
cd:89:a8:b5:7d:7b:13:10:7d:f0:27:02:80:11:39:db:33:2d:
45:98:a5:c2:13:86:7a:1f:26:c7:7a:93:97:45:b6:07:cb:8a:
28:0a:b9:04:c9:a2:ce:24:81:cd:0b:7b:d3:42:4f:8a:bb:94:
45:95:db:43:57:c7:d6:7e:e9:28:fe:b0:18:48:76:7b:d4:df:
40:c9:4d:db:48:44:23:e0:8c:b7:ed:5b:1b:40:69:b7:90:1f:
8b:01:3c:53:29:10:fd:6b:e9:57:17:49:ae:08:f4:24:cd:32:
03:7e:ca:7c:2f:17:0f:e7:26:6a:f2:79:fe:0a:e8:ad:b8:f7:
b7:4c:c1:74:ae:db:97:e6:38:e5:55:3f:e7:f9:97:e5:94:77:
3b:77:9a:d5:17:25:3c:cd:e1:28:c4:44:7c:f9:de:3a:5a:5a:
9f:aa:f7:75:5c:05:a8:43:7f:4d:e4:ee:e3:ab:ee:0d:e2:9a:
12:bd:f2:26:43:85:7f:2d:25:49:83:e1:3e:1c:e1:53:9b:c3:
47:98:9b:c6:ab:f0:c5:93:73:9c:8d:80:75:39:d9:2c:47:fc:
2d:1a:a7:4d:5a:35:ab:1d`;
const IN_CRL_PEM_RSA_CRL_REASON_AND_INVALIDITY_DATE = `-----BEGIN X509 CRL-----
MIID9jCCAd4CAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVUsxDzANBgNV
BAgMBkxvbmRvbjELMAkGA1UECgwCQkIxFTATBgNVBAMMDFRlc3QgUm9vdCBDQRcN
MjQwODI1MTIwODU2WhcNMjQwOTI0MTIwODU2WjA9MDsCAhAAFw0yNDA4MjUxMjA4
NDhaMCYwCgYDVR0VBAMKAQEwGAYDVR0YBBEYDzIwMjQwODI1MDAwMDAwWqCCAScw
ggEjMAkGA1UdEgQCMAAwfQYDVR0jBHYwdIAUuMmt/ahQVNWFbjSLTGAvsEm2h0ah
RqREMEIxCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xCzAJBgNVBAoMAkJC
MRUwEwYDVQQDDAxUZXN0IFJvb3QgQ0GCFDddS/a9fBF8zEYa/9cvLCb4Hks9MC0G
A1UdLgQmMCQwIqAgoB6GHGh0dHA6Ly9leGFtcGxlLmNvbS9kZWx0YS1jcmwwWwYD
VR0fBFQwUjAhoB+gHYYbaHR0cDovL2V4YW1wbGUuY29tL2Z1bGwtY3JsMCGgH6Ad
hhtsZGFwOi8vZXhhbXBsZS5jb20vZnVsbC1jcmwwCqAIoAaHBH8AAAEwCwYDVR0U
BAQCAh49MA0GCSqGSIb3DQEBCwUAA4ICAQByLp7JWQmB1NhlLACH6zFOe31yCTVy
xJQtgujtSri1LNu6IwzBGsKBQIl3ucwMxPvoZzlujNLmshUT3nSogV0/5n1q0Gyj
5Yiz2iw8mmKJLmGZ9Oz3QoGxgFww0/0x/VwRHuS2hw+A7JB8tO/2nW3oTclvS55l
R+VtkDjUN58+Yl2SQksvb3qD6bHHJTCaP7Dskls0fdBIoYIDvZejrTYSSzTX/Kw4
735P0GBMhj7zVF8azGz2PFpSISg4huJMyp7EDKZf2c2dnkuwmEUlPQEBLX25j/Il
81OxfVVFja+wUagaGtjEPGy5gsU8zFwkWhjaD5PGBbZvnT+EDsOtJPU7Ot/sBHfz
XqUtMrfmz/S/GsQ+QCpnBvarBy9QYuk9M0ePBGy33CUQpjPULxuJJVAHxNoetHCv
7udng2Pi4D8vDNfzbMwHt7HurMo0CsSju+cL4rnIfsz02RrD9WC84KxBLWkqC7Hi
IKGIpF740Yc4BliVE1HDaOKyI6FEft5asj3OgXwmBw7pVlxSNWACaA2vOFkdN/V5
XZZjVJdRJxkgEfCvsJVenFp8ND6gmJmWum7tqM5ytmiXjPtejsPpVq4IclG+Yhnr
tFQ9TDEuCrNsRIGGGDodyXq1+kGXY0w8RqGEb7J4Og/M6r4LMAKPkO7e0nEibTqX
d2igvR2e5p+yKw==
-----END X509 CRL-----`;
const OUT_CRL_PEM_RSA_CRL_REASON_AND_INVALIDITY_DATE = `Certificate Revocation List (CRL):
Version: 2 (0x1)
Signature Algorithm: SHA256withRSA
Issuer:
C = UK
ST = London
O = BB
CN = Test Root CA
Last Update: Sun, 25 Aug 2024 12:08:56 GMT
Next Update: Tue, 24 Sep 2024 12:08:56 GMT
CRL extensions:
2.5.29.46:
Unsupported CRL extension. Try openssl CLI.
X509v3 Authority Key Identifier:
keyid:B8:C9:AD:FD:A8:50:54:D5:85:6E:34:8B:4C:60:2F:B0:49:B6:87:46
DirName:/C=UK/ST=London/O=BB/CN=Test Root CA
serial:37:5D:4B:F6:BD:7C:11:7C:CC:46:1A:FF:D7:2F:2C:26:F8:1E:4B:3D
X509v3 CRL Distribution Points:
Full Name:
URI:http://example.com/full-crl
Full Name:
URI:ldap://example.com/full-crl
Full Name:
IP:127.0.0.1
X509v3 CRL Number:
1E3D
issuerAltName:
Unsupported CRL extension. Try openssl CLI.
Revoked Certificates:
Serial Number: 1000
Revocation Date: Sun, 25 Aug 2024 12:08:48 GMT
CRL entry extensions:
X509v3 CRL Reason Code:
Key Compromise
Invalidity Date:
Sun, 25 Aug 2024 00:00:00 GMT
Signature Value:
72:2e:9e:c9:59:09:81:d4:d8:65:2c:00:87:eb:31:4e:7b:7d:
72:09:35:72:c4:94:2d:82:e8:ed:4a:b8:b5:2c:db:ba:23:0c:
c1:1a:c2:81:40:89:77:b9:cc:0c:c4:fb:e8:67:39:6e:8c:d2:
e6:b2:15:13:de:74:a8:81:5d:3f:e6:7d:6a:d0:6c:a3:e5:88:
b3:da:2c:3c:9a:62:89:2e:61:99:f4:ec:f7:42:81:b1:80:5c:
30:d3:fd:31:fd:5c:11:1e:e4:b6:87:0f:80:ec:90:7c:b4:ef:
f6:9d:6d:e8:4d:c9:6f:4b:9e:65:47:e5:6d:90:38:d4:37:9f:
3e:62:5d:92:42:4b:2f:6f:7a:83:e9:b1:c7:25:30:9a:3f:b0:
ec:92:5b:34:7d:d0:48:a1:82:03:bd:97:a3:ad:36:12:4b:34:
d7:fc:ac:38:ef:7e:4f:d0:60:4c:86:3e:f3:54:5f:1a:cc:6c:
f6:3c:5a:52:21:28:38:86:e2:4c:ca:9e:c4:0c:a6:5f:d9:cd:
9d:9e:4b:b0:98:45:25:3d:01:01:2d:7d:b9:8f:f2:25:f3:53:
b1:7d:55:45:8d:af:b0:51:a8:1a:1a:d8:c4:3c:6c:b9:82:c5:
3c:cc:5c:24:5a:18:da:0f:93:c6:05:b6:6f:9d:3f:84:0e:c3:
ad:24:f5:3b:3a:df:ec:04:77:f3:5e:a5:2d:32:b7:e6:cf:f4:
bf:1a:c4:3e:40:2a:67:06:f6:ab:07:2f:50:62:e9:3d:33:47:
8f:04:6c:b7:dc:25:10:a6:33:d4:2f:1b:89:25:50:07:c4:da:
1e:b4:70:af:ee:e7:67:83:63:e2:e0:3f:2f:0c:d7:f3:6c:cc:
07:b7:b1:ee:ac:ca:34:0a:c4:a3:bb:e7:0b:e2:b9:c8:7e:cc:
f4:d9:1a:c3:f5:60:bc:e0:ac:41:2d:69:2a:0b:b1:e2:20:a1:
88:a4:5e:f8:d1:87:38:06:58:95:13:51:c3:68:e2:b2:23:a1:
44:7e:de:5a:b2:3d:ce:81:7c:26:07:0e:e9:56:5c:52:35:60:
02:68:0d:af:38:59:1d:37:f5:79:5d:96:63:54:97:51:27:19:
20:11:f0:af:b0:95:5e:9c:5a:7c:34:3e:a0:98:99:96:ba:6e:
ed:a8:ce:72:b6:68:97:8c:fb:5e:8e:c3:e9:56:ae:08:72:51:
be:62:19:eb:b4:54:3d:4c:31:2e:0a:b3:6c:44:81:86:18:3a:
1d:c9:7a:b5:fa:41:97:63:4c:3c:46:a1:84:6f:b2:78:3a:0f:
cc:ea:be:0b:30:02:8f:90:ee:de:d2:71:22:6d:3a:97:77:68:
a0:bd:1d:9e:e6:9f:b2:2b`;
const IN_CRL_PEM_RSA_CRL_EXTENSIONS = `-----BEGIN X509 CRL-----
MIIE0DCCArgCAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVUsxDzANBgNV
BAgMBkxvbmRvbjELMAkGA1UECgwCQkIxFTATBgNVBAMMDFRlc3QgUm9vdCBDQRcN
MjQwODI1MTIzNzEwWhcNMjQwOTI0MTIzNzEwWjA9MDsCAhAAFw0yNDA4MjUxMjA4
NDhaMCYwCgYDVR0VBAMKAQEwGAYDVR0YBBEYDzIwMjQwODI1MDAwMDAwWqCCAgEw
ggH9MIHiBgNVHRIEgdowgdegFAYEKgMEBaAMFgpDdXN0b21OYW1lgQ5jYUBleGFt
cGxlLmNvbYYSaHR0cDovL2V4YW1wbGUuY29tgg5jYS5leGFtcGxlLmNvbYcEwKgB
AaSBhDCBgTELMAkGA1UEBhMCVVMxFTATBgNVBAgMDEV4YW1wbGVTdGF0ZTEUMBIG
A1UEBwwLRXhhbXBsZUNpdHkxEzARBgNVBAoMCkV4YW1wbGVPcmcxFDASBgNVBAsM
C0V4YW1wbGVVbml0MRowGAYDVQQDDBFFeGFtcGxlQ29tbW9uTmFtZTB9BgNVHSME
djB0gBS4ya39qFBU1YVuNItMYC+wSbaHRqFGpEQwQjELMAkGA1UEBhMCVUsxDzAN
BgNVBAgMBkxvbmRvbjELMAkGA1UECgwCQkIxFTATBgNVBAMMDFRlc3QgUm9vdCBD
QYIUN11L9r18EXzMRhr/1y8sJvgeSz0wLQYDVR0uBCYwJDAioCCgHoYcaHR0cDov
L2V4YW1wbGUuY29tL2RlbHRhLWNybDBbBgNVHR8EVDBSMCGgH6AdhhtodHRwOi8v
ZXhhbXBsZS5jb20vZnVsbC1jcmwwIaAfoB2GG2xkYXA6Ly9leGFtcGxlLmNvbS9m
dWxsLWNybDAKoAigBocEfwAAATALBgNVHRQEBAICHkIwDQYJKoZIhvcNAQELBQAD
ggIBAF/9L4aGmId2igw7+MfDxokevIJkJX/MkmHpXBl1b4hL85FGD7OPCmn47VzC
Wejlc/AQB7mWyUugvrVEq/FiCO8a8Fieyjw5uCYz0eiNnuvHVRGM2mOEkiA0I/rn
F5AFB1YfCFGXPyRkXNRbOBE91mhOzh1H9PX2qVnj5l3KsPE/7YuteacR0TkfkRJa
BXLic+5F/CCV/J/iYR7LncuLUlhBfsosG/ucHL70EytlfX6CBWY3kBbmj7nd497T
QG392+m9xp7MIsJAS+3qEzwJAfni6zUV0fWh/ucOl8BIjHEh97VqI3+8yzhdXfkF
2gkfpkqJQY0+5OO1VSRYTlQNld3QjN/VVJjatfHyaXfPCx4VEKW1kWYo+0zxO4SL
SB/+S/o99bCeNy1MXqEvy5HoDwFHePXGsAEPHWPdj7EWm7g9T/Fl1iSR6hpohvDD
K4LaGdVhzvCraLIh8H7XW3KztvZvDQejYQAgADW0UO0rFHJ1XXhKYSqXNGnfDt+3
cRpt2XxSxt5HJtHlatiI25PuBMNWV2Zod4RHB/8UEvs1KC7dcwkAiCEY+E3o/zkC
rdZ/8XtNf5a4WSN/D7pPsfsO6SE+7lxkJ+UQcZLXAz8b5ArPTlWt2HdJIBEVs25K
FAkizyldhnAcNHFk7XN94eTLNeD6hUbFL9pNHiSmKu5A9YW0
-----END X509 CRL-----`;
const OUT_CRL_PEM_RSA_CRL_EXTENSIONS = `Certificate Revocation List (CRL):
Version: 2 (0x1)
Signature Algorithm: SHA256withRSA
Issuer:
C = UK
ST = London
O = BB
CN = Test Root CA
Last Update: Sun, 25 Aug 2024 12:37:10 GMT
Next Update: Tue, 24 Sep 2024 12:37:10 GMT
CRL extensions:
2.5.29.46:
Unsupported CRL extension. Try openssl CLI.
X509v3 Authority Key Identifier:
keyid:B8:C9:AD:FD:A8:50:54:D5:85:6E:34:8B:4C:60:2F:B0:49:B6:87:46
DirName:/C=UK/ST=London/O=BB/CN=Test Root CA
serial:37:5D:4B:F6:BD:7C:11:7C:CC:46:1A:FF:D7:2F:2C:26:F8:1E:4B:3D
X509v3 CRL Distribution Points:
Full Name:
URI:http://example.com/full-crl
Full Name:
URI:ldap://example.com/full-crl
Full Name:
IP:127.0.0.1
X509v3 CRL Number:
1E42
X509v3 Issuer Alternative Name:
OtherName:1.2.3.4.5::CustomName
EMAIL:ca@example.com
URI:http://example.com
DNS:ca.example.com
IP:192.168.1.1
DIR:/C=US/ST=ExampleState/L=ExampleCity/O=ExampleOrg/OU=ExampleUnit/CN=ExampleCommonName
Revoked Certificates:
Serial Number: 1000
Revocation Date: Sun, 25 Aug 2024 12:08:48 GMT
CRL entry extensions:
X509v3 CRL Reason Code:
Key Compromise
Invalidity Date:
Sun, 25 Aug 2024 00:00:00 GMT
Signature Value:
5f:fd:2f:86:86:98:87:76:8a:0c:3b:f8:c7:c3:c6:89:1e:bc:
82:64:25:7f:cc:92:61:e9:5c:19:75:6f:88:4b:f3:91:46:0f:
b3:8f:0a:69:f8:ed:5c:c2:59:e8:e5:73:f0:10:07:b9:96:c9:
4b:a0:be:b5:44:ab:f1:62:08:ef:1a:f0:58:9e:ca:3c:39:b8:
26:33:d1:e8:8d:9e:eb:c7:55:11:8c:da:63:84:92:20:34:23:
fa:e7:17:90:05:07:56:1f:08:51:97:3f:24:64:5c:d4:5b:38:
11:3d:d6:68:4e:ce:1d:47:f4:f5:f6:a9:59:e3:e6:5d:ca:b0:
f1:3f:ed:8b:ad:79:a7:11:d1:39:1f:91:12:5a:05:72:e2:73:
ee:45:fc:20:95:fc:9f:e2:61:1e:cb:9d:cb:8b:52:58:41:7e:
ca:2c:1b:fb:9c:1c:be:f4:13:2b:65:7d:7e:82:05:66:37:90:
16:e6:8f:b9:dd:e3:de:d3:40:6d:fd:db:e9:bd:c6:9e:cc:22:
c2:40:4b:ed:ea:13:3c:09:01:f9:e2:eb:35:15:d1:f5:a1:fe:
e7:0e:97:c0:48:8c:71:21:f7:b5:6a:23:7f:bc:cb:38:5d:5d:
f9:05:da:09:1f:a6:4a:89:41:8d:3e:e4:e3:b5:55:24:58:4e:
54:0d:95:dd:d0:8c:df:d5:54:98:da:b5:f1:f2:69:77:cf:0b:
1e:15:10:a5:b5:91:66:28:fb:4c:f1:3b:84:8b:48:1f:fe:4b:
fa:3d:f5:b0:9e:37:2d:4c:5e:a1:2f:cb:91:e8:0f:01:47:78:
f5:c6:b0:01:0f:1d:63:dd:8f:b1:16:9b:b8:3d:4f:f1:65:d6:
24:91:ea:1a:68:86:f0:c3:2b:82:da:19:d5:61:ce:f0:ab:68:
b2:21:f0:7e:d7:5b:72:b3:b6:f6:6f:0d:07:a3:61:00:20:00:
35:b4:50:ed:2b:14:72:75:5d:78:4a:61:2a:97:34:69:df:0e:
df:b7:71:1a:6d:d9:7c:52:c6:de:47:26:d1:e5:6a:d8:88:db:
93:ee:04:c3:56:57:66:68:77:84:47:07:ff:14:12:fb:35:28:
2e:dd:73:09:00:88:21:18:f8:4d:e8:ff:39:02:ad:d6:7f:f1:
7b:4d:7f:96:b8:59:23:7f:0f:ba:4f:b1:fb:0e:e9:21:3e:ee:
5c:64:27:e5:10:71:92:d7:03:3f:1b:e4:0a:cf:4e:55:ad:d8:
77:49:20:11:15:b3:6e:4a:14:09:22:cf:29:5d:86:70:1c:34:
71:64:ed:73:7d:e1:e4:cb:35:e0:fa:85:46:c5:2f:da:4d:1e:
24:a6:2a:ee:40:f5:85:b4`;
TestRegister.addTests([
{
name: "Parse X.509 CRL: Example PEM encoded CRL with RSA signature",
input: IN_CRL_PEM_RSA,
expectedOutput: OUT_CRL_PEM_RSA,
recipeConfig: [
{
"op": "Parse X.509 CRL",
"args": ["PEM"]
}
]
},
{
name: "Parse X.509 CRL: Example PEM encoded CRL with RSA signature, CRL Reason and Invalidity Date",
input: IN_CRL_PEM_RSA_CRL_REASON_AND_INVALIDITY_DATE,
expectedOutput: OUT_CRL_PEM_RSA_CRL_REASON_AND_INVALIDITY_DATE,
recipeConfig: [
{
"op": "Parse X.509 CRL",
"args": ["PEM"]
}
]
},
{
name: "Parse X.509 CRL: Example PEM encoded CRL with RSA signature and CRL Extensions",
input: IN_CRL_PEM_RSA_CRL_EXTENSIONS,
expectedOutput: OUT_CRL_PEM_RSA_CRL_EXTENSIONS,
recipeConfig: [
{
"op": "Parse X.509 CRL",
"args": ["PEM"]
}
]
},
]);

View file

@ -135,10 +135,21 @@ TestRegister.addTests([
}, },
], ],
}, },
{
name: "ROT13: no shift amount",
input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
recipeConfig: [
{
op: "ROT13",
args: [true, true, true, 0]
},
],
},
{ {
name: "ROT13: normal", name: "ROT13: normal",
input: "The Quick Brown Fox Jumped Over The Lazy Dog.", input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "Gur Dhvpx Oebja Sbk Whzcrq Bire Gur Ynml Qbt.", expectedOutput: "Gur Dhvpx Oebja Sbk Whzcrq Bire Gur Ynml Qbt. 3456789012",
recipeConfig: [ recipeConfig: [
{ {
op: "ROT13", op: "ROT13",
@ -146,10 +157,21 @@ TestRegister.addTests([
}, },
], ],
}, },
{
name: "ROT13: negative shift amount",
input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "Gur Dhvpx Oebja Sbk Whzcrq Bire Gur Ynml Qbt. 7890123456",
recipeConfig: [
{
op: "ROT13",
args: [true, true, true, -13]
},
],
},
{ {
name: "ROT13: full loop", name: "ROT13: full loop",
input: "The Quick Brown Fox Jumped Over The Lazy Dog.", input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog.", expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 6789012345",
recipeConfig: [ recipeConfig: [
{ {
op: "ROT13", op: "ROT13",
@ -157,10 +179,21 @@ TestRegister.addTests([
}, },
], ],
}, },
{
name: "ROT13: full loop (negative shift amount)",
input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 4567890123",
recipeConfig: [
{
op: "ROT13",
args: [true, true, true, -26]
},
],
},
{ {
name: "ROT13: lowercase only", name: "ROT13: lowercase only",
input: "The Quick Brown Fox Jumped Over The Lazy Dog.", input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "Tur Qhvpx Bebja Fbk Jhzcrq Oire Tur Lnml Dbt.", expectedOutput: "Tur Qhvpx Bebja Fbk Jhzcrq Oire Tur Lnml Dbt. 0123456789",
recipeConfig: [ recipeConfig: [
{ {
op: "ROT13", op: "ROT13",
@ -170,8 +203,8 @@ TestRegister.addTests([
}, },
{ {
name: "ROT13: uppercase only", name: "ROT13: uppercase only",
input: "The Quick Brown Fox Jumped Over The Lazy Dog.", input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "Ghe Duick Orown Sox Wumped Bver Ghe Yazy Qog.", expectedOutput: "Ghe Duick Orown Sox Wumped Bver Ghe Yazy Qog. 0123456789",
recipeConfig: [ recipeConfig: [
{ {
op: "ROT13", op: "ROT13",
@ -179,6 +212,50 @@ TestRegister.addTests([
}, },
], ],
}, },
{
name: "ROT13: numbers only",
input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 5678901234",
recipeConfig: [
{
op: "ROT13",
args: [false, false, true, 5]
},
],
},
{
name: "ROT13: numbers only (negative shift amount)",
input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 5678901234",
recipeConfig: [
{
op: "ROT13",
args: [false, false, true, 5]
},
],
},
{
name: "ROT13: numbers only loop",
input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
recipeConfig: [
{
op: "ROT13",
args: [false, false, true, 10]
},
],
},
{
name: "ROT13: numbers only loop (negative shift amount)",
input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
recipeConfig: [
{
op: "ROT13",
args: [false, false, true, -10]
},
],
},
{ {
name: "ROT47: nothing", name: "ROT47: nothing",
input: "", input: "",

View file

@ -0,0 +1,126 @@
/**
* Strip IPv4 header tests.
*
* @author c65722 []
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Strip IPv4 header: No options, No payload",
input: "450000140005400080060000c0a80001c0a80002",
expectedOutput: "",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip IPv4 header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip IPv4 header: No options, Payload",
input: "450000140005400080060000c0a80001c0a80002ffffffffffffffff",
expectedOutput: "ffffffffffffffff",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip IPv4 header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip IPv4 header: Options, No payload",
input: "460000140005400080060000c0a80001c0a8000207000000",
expectedOutput: "",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip IPv4 header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip IPv4 header: Options, Payload",
input: "460000140005400080060000c0a80001c0a8000207000000ffffffffffffffff",
expectedOutput: "ffffffffffffffff",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip IPv4 header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip IPv4 header: Input length lesss than minimum header length",
input: "450000140005400080060000c0a80001c0a800",
expectedOutput: "Input length is less than minimum IPv4 header length",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip IPv4 header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip IPv4 header: Input length less than IHL",
input: "460000140005400080060000c0a80001c0a80000",
expectedOutput: "Input length is less than IHL",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip IPv4 header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
}
]);

View file

@ -0,0 +1,126 @@
/**
* Strip TCP header tests.
*
* @author c65722 []
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Strip TCP header: No options, No payload",
input: "7f900050000fa4b2000cb2a45010bff100000000",
expectedOutput: "",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip TCP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip TCP header: No options, Payload",
input: "7f900050000fa4b2000cb2a45010bff100000000ffffffffffffffff",
expectedOutput: "ffffffffffffffff",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip TCP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip TCP header: Options, No payload",
input: "7f900050000fa4b2000cb2a47010bff100000000020405b404020000",
expectedOutput: "",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip TCP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip TCP header: Options, Payload",
input: "7f900050000fa4b2000cb2a47010bff100000000020405b404020000ffffffffffffffff",
expectedOutput: "ffffffffffffffff",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip TCP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip TCP header: Input length less than minimum header length",
input: "7f900050000fa4b2000cb2a45010bff1000000",
expectedOutput: "Need at least 20 bytes for a TCP Header",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip TCP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip TCP header: Input length less than data offset",
input: "7f900050000fa4b2000cb2a47010bff100000000",
expectedOutput: "Input length is less than data offset",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip TCP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
}
]);

View file

@ -0,0 +1,69 @@
/**
* Strip UDP header tests.
*
* @author c65722 []
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Strip UDP header: No payload",
input: "8111003500000000",
expectedOutput: "",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip UDP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip UDP header: Payload",
input: "8111003500080000ffffffffffffffff",
expectedOutput: "ffffffffffffffff",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip UDP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
},
{
name: "Strip UDP header: Input length less than header length",
input: "81110035000000",
expectedOutput: "Need 8 bytes for a UDP Header",
recipeConfig: [
{
op: "From Hex",
args: ["None"]
},
{
op: "Strip UDP header",
args: [],
},
{
op: "To Hex",
args: ["None", 0]
}
]
}
]);

View file

@ -0,0 +1,123 @@
/**
* @author Oshawk [oshawk@protonmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
/**
* Take nth bytes tests
*/
TestRegister.addTests([
{
name: "Take nth bytes: Nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 0, false],
},
],
},
{
name: "Take nth bytes: Nothing (apply to each line)",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 0, true],
},
],
},
{
name: "Take nth bytes: Basic single line",
input: "0123456789",
expectedOutput: "048",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 0, false],
},
],
},
{
name: "Take nth bytes: Basic single line (apply to each line)",
input: "0123456789",
expectedOutput: "048",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 0, true],
},
],
},
{
name: "Take nth bytes: Complex single line",
input: "0123456789",
expectedOutput: "59",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 5, false],
},
],
},
{
name: "Take nth bytes: Complex single line (apply to each line)",
input: "0123456789",
expectedOutput: "59",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 5, true],
},
],
},
{
name: "Take nth bytes: Basic multi line",
input: "01234\n56789",
expectedOutput: "047",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 0, false],
},
],
},
{
name: "Take nth bytes: Basic multi line (apply to each line)",
input: "01234\n56789",
expectedOutput: "04\n59",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 0, true],
},
],
},
{
name: "Take nth bytes: Complex multi line",
input: "01234\n56789",
expectedOutput: "\n8",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 5, false],
},
],
},
{
name: "Take nth bytes: Complex multi line (apply to each line)",
input: "012345\n6789ab",
expectedOutput: "5\nb",
recipeConfig: [
{
op: "Take nth bytes",
args: [4, 5, true],
},
],
}
]);

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -1,8 +1,10 @@
const webpack = require("webpack"); const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin");
const { ModifySourcePlugin, ReplaceOperation } = require("modify-source-webpack-plugin"); const { ModifySourcePlugin, ReplaceOperation } = require("modify-source-webpack-plugin");
const path = require("path"); const path = require("path");
const zlib = require("zlib");
/** /**
* Webpack configuration details for use with Grunt. * Webpack configuration details for use with Grunt.
@ -64,6 +66,21 @@ module.exports = {
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: "assets/[name].css" filename: "assets/[name].css"
}), }),
new CompressionPlugin({
filename: "[path][base].gz",
algorithm: "gzip",
test: /\.(js|css|html)$/,
}),
new CompressionPlugin({
filename: "[path][base].br",
algorithm: "brotliCompress",
test: /\.(js|css|html)$/,
compressionOptions: {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: 11,
},
},
}),
new CopyWebpackPlugin({ new CopyWebpackPlugin({
patterns: [ patterns: [
{ {