object
tags.PHP Deserialize
.[5,"abc",true]
a:3:{i:0;i:5;i:1;s:3:"abc";i:2;b:1;}";
- this.infoURL = "https://www.phpinternalsbook.com/php5/classes_objects/serialization.html";
- this.inputType = "JSON";
- this.outputType = "string";
- this.args = [];
- }
-
- /**
- * @param {JSON} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- /**
- * Determines if a number is an integer
- * @param {number} value
- * @returns {boolean}
- */
- function isInteger(value) {
- return typeof value === "number" && parseInt(value.toString(), 10) === value;
- }
-
- /**
- * Serialize basic types
- * @param {string | number | boolean} content
- * @returns {string}
- */
- function serializeBasicTypes(content) {
- const basicTypes = {
- "string": "s",
- "integer": "i",
- "float": "d",
- "boolean": "b"
- };
- /**
- * Booleans
- * cast to 0 or 1
- */
- if (typeof content === "boolean") {
- return `${basicTypes.boolean}:${content ? 1 : 0}`;
- }
- /* Numbers */
- if (typeof content === "number") {
- if (isInteger(content)) {
- return `${basicTypes.integer}:${content.toString()}`;
- } else {
- return `${basicTypes.float}:${content.toString()}`;
- }
- }
- /* Strings */
- if (typeof content === "string")
- return `${basicTypes.string}:${content.length}:"${content}"`;
-
- /** This should be unreachable */
- throw new OperationError(`Encountered a non-implemented type: ${typeof content}`);
- }
-
- /**
- * Recursively serialize
- * @param {*} object
- * @returns {string}
- */
- function serialize(object) {
- /* Null */
- if (object == null) {
- return `N;`;
- }
-
- if (typeof object !== "object") {
- /* Basic types */
- return `${serializeBasicTypes(object)};`;
- } else if (object instanceof Array) {
- /* Arrays */
- const serializedElements = [];
-
- for (let i = 0; i < object.length; i++) {
- serializedElements.push(`${serialize(i)}${serialize(object[i])}`);
- }
-
- return `a:${object.length}:{${serializedElements.join("")}}`;
- } else if (object instanceof Object) {
- /**
- * Objects
- * Note: the output cannot be guaranteed to be in the same order as the input
- */
- const serializedElements = [];
- const keys = Object.keys(object);
-
- for (const key of keys) {
- serializedElements.push(`${serialize(key)}${serialize(object[key])}`);
- }
-
- return `a:${keys.length}:{${serializedElements.join("")}}`;
- }
-
- /** This should be unreachable */
- throw new OperationError(`Encountered a non-implemented type: ${typeof object}`);
- }
-
- return serialize(input);
- }
-}
-
-export default PHPSerialize;
diff --git a/src/core/operations/ParseASN1HexString.mjs b/src/core/operations/ParseASN1HexString.mjs
index 35fd5ba4..14890186 100644
--- a/src/core/operations/ParseASN1HexString.mjs
+++ b/src/core/operations/ParseASN1HexString.mjs
@@ -20,7 +20,7 @@ class ParseASN1HexString extends Operation {
this.name = "Parse ASN.1 hex string";
this.module = "PublicKey";
- this.description = "Abstract Syntax Notation One (ASN.1) is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunications and computer networking.
This operation parses arbitrary ASN.1 data (encoded as an hex string: use the 'To Hex' operation if necessary) and presents the resulting tree.";
+ this.description = "Abstract Syntax Notation One (ASN.1) is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunications and computer networking.
This operation parses arbitrary ASN.1 data and presents the resulting tree.";
this.infoURL = "https://wikipedia.org/wiki/Abstract_Syntax_Notation_One";
this.inputType = "string";
this.outputType = "string";
diff --git a/src/core/operations/ParseCSR.mjs b/src/core/operations/ParseCSR.mjs
deleted file mode 100644
index d3b3c364..00000000
--- a/src/core/operations/ParseCSR.mjs
+++ /dev/null
@@ -1,390 +0,0 @@
-/**
- * @author jkataja
- * @copyright Crown Copyright 2023
- * @license Apache-2.0
- */
-
-import r from "jsrsasign";
-import Operation from "../Operation.mjs";
-import { formatDnObj } from "../lib/PublicKey.mjs";
-import Utils from "../Utils.mjs";
-
-/**
- * Parse CSR operation
- */
-class ParseCSR extends Operation {
-
- /**
- * ParseCSR constructor
- */
- constructor() {
- super();
-
- this.name = "Parse CSR";
- this.module = "PublicKey";
- this.description = "Parse Certificate Signing Request (CSR) for an X.509 certificate";
- this.infoURL = "https://wikipedia.org/wiki/Certificate_signing_request";
- this.inputType = "string";
- this.outputType = "string";
- this.args = [
- {
- "name": "Input format",
- "type": "option",
- "value": ["PEM"]
- }
- ];
- this.checks = [
- {
- "pattern": "^-+BEGIN CERTIFICATE REQUEST-+\\r?\\n[\\da-z+/\\n\\r]+-+END CERTIFICATE REQUEST-+\\r?\\n?$",
- "flags": "i",
- "args": ["PEM"]
- }
- ];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string} Human-readable description of a Certificate Signing Request (CSR).
- */
- run(input, args) {
- if (!input.length) {
- return "No input";
- }
-
- // Parse the CSR into JSON parameters
- const csrParam = new r.KJUR.asn1.csr.CSRUtil.getParam(input);
-
- return `Subject\n${formatDnObj(csrParam.subject, 2)}
-Public Key${formatSubjectPublicKey(csrParam.sbjpubkey)}
-Signature${formatSignature(csrParam.sigalg, csrParam.sighex)}
-Requested Extensions${formatRequestedExtensions(csrParam)}`;
- }
-}
-
-/**
- * Format signature of a CSR
- * @param {*} sigAlg string
- * @param {*} sigHex string
- * @returns Multi-line string describing CSR Signature
- */
-function formatSignature(sigAlg, sigHex) {
- let out = `\n`;
-
- out += ` Algorithm: ${sigAlg}\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);
-}
-
-/**
- * Format Subject Public Key from PEM encoded public key string
- * @param {*} publicKeyPEM string
- * @returns Multi-line string describing Subject Public Key Info
- */
-function formatSubjectPublicKey(publicKeyPEM) {
- let out = "\n";
-
- const publicKey = r.KEYUTIL.getKey(publicKeyPEM);
- if (publicKey instanceof r.RSAKey) {
- out += ` Algorithm: RSA
- Length: ${publicKey.n.bitLength()} bits
- Modulus: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.n))}
- Exponent: ${publicKey.e} (0x${Utils.hex(publicKey.e)})\n`;
- } else if (publicKey instanceof r.KJUR.crypto.ECDSA) {
- out += ` Algorithm: ECDSA
- Length: ${publicKey.ecparams.keylen} bits
- Pub: ${formatHexOntoMultiLine(publicKey.pubKeyHex)}
- ASN1 OID: ${r.KJUR.crypto.ECDSA.getName(publicKey.getShortNISTPCurveName())}
- NIST CURVE: ${publicKey.getShortNISTPCurveName()}\n`;
- } else if (publicKey instanceof r.KJUR.crypto.DSA) {
- out += ` Algorithm: DSA
- Length: ${publicKey.p.toString(16).length * 4} bits
- Pub: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.y))}
- P: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.p))}
- Q: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.q))}
- G: ${formatHexOntoMultiLine(absBigIntToHex(publicKey.g))}\n`;
- } else {
- out += `unsupported public key algorithm\n`;
- }
-
- return chop(out);
-}
-
-/**
- * Format known extensions of a CSR
- * @param {*} csrParam object
- * @returns Multi-line string describing CSR Requested Extensions
- */
-function formatRequestedExtensions(csrParam) {
- const formattedExtensions = new Array(4).fill("");
-
- if (Object.hasOwn(csrParam, "extreq")) {
- for (const extension of csrParam.extreq) {
- let parts = [];
- switch (extension.extname) {
- case "basicConstraints" :
- parts = describeBasicConstraints(extension);
- formattedExtensions[0] = ` Basic Constraints:${formatExtensionCriticalTag(extension)}\n${indent(4, parts)}`;
- break;
- case "keyUsage" :
- parts = describeKeyUsage(extension);
- formattedExtensions[1] = ` Key Usage:${formatExtensionCriticalTag(extension)}\n${indent(4, parts)}`;
- break;
- case "extKeyUsage" :
- 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;
- default :
- parts = ["(unsuported extension)"];
- formattedExtensions.push(` ${extension.extname}:${formatExtensionCriticalTag(extension)}\n${indent(4, parts)}`);
- }
- }
- }
-
- let out = "\n";
-
- formattedExtensions.forEach((formattedExtension) => {
- if (formattedExtension !== undefined && formattedExtension !== null && formattedExtension.length !== 0) {
- out += formattedExtension;
- }
- });
-
- 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 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
- * @returns String as a multi-line string
- */
-function formatMultiLine(longStr) {
- const lines = [];
-
- for (let remain = longStr ; remain !== "" ; remain = remain.substring(48)) {
- lines.push(remain.substring(0, 48));
- }
-
- return lines.join("\n ");
-}
-
-/**
- * Describe Basic Constraints
- * @see RFC 5280 4.2.1.9. Basic Constraints https://www.ietf.org/rfc/rfc5280.txt
- * @param {*} extension CSR extension with the name `basicConstraints`
- * @returns Array of strings describing Basic Constraints
- */
-function describeBasicConstraints(extension) {
- const constraints = [];
-
- constraints.push(`CA = ${Object.hasOwn(extension, "cA") && extension.cA ? "true" : "false"}`);
- if (Object.hasOwn(extension, "pathLen")) constraints.push(`PathLenConstraint = ${extension.pathLen}`);
-
- return constraints;
-}
-
-/**
- * Describe Key Usage extension permitted use cases
- * @see RFC 5280 4.2.1.3. Key Usage https://www.ietf.org/rfc/rfc5280.txt
- * @param {*} extension CSR extension with the name `keyUsage`
- * @returns Array of strings describing Key Usage extension permitted use cases
- */
-function describeKeyUsage(extension) {
- const usage = [];
-
- const kuIdentifierToName = {
- digitalSignature: "Digital Signature",
- nonRepudiation: "Non-repudiation",
- keyEncipherment: "Key encipherment",
- dataEncipherment: "Data encipherment",
- keyAgreement: "Key agreement",
- keyCertSign: "Key certificate signing",
- cRLSign: "CRL signing",
- 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)");
-
- return usage;
-}
-
-/**
- * Describe Extended Key Usage extension permitted use cases
- * @see RFC 5280 4.2.1.12. Extended Key Usage https://www.ietf.org/rfc/rfc5280.txt
- * @param {*} extension CSR extension with the name `extendedKeyUsage`
- * @returns Array of strings describing Extended Key Usage extension permitted use cases
- */
-function describeExtendedKeyUsage(extension) {
- const usage = [];
-
- const ekuIdentifierToName = {
- "serverAuth": "TLS Web Server Authentication",
- "clientAuth": "TLS Web Client Authentication",
- "codeSigning": "Code signing",
- "emailProtection": "E-mail Protection (S/MIME)",
- "timeStamping": "Trusted Timestamping",
- "1.3.6.1.4.1.311.2.1.21": "Microsoft Individual Code Signing", // msCodeInd
- "1.3.6.1.4.1.311.2.1.22": "Microsoft Commercial Code Signing", // msCodeCom
- "1.3.6.1.4.1.311.10.3.1": "Microsoft Trust List Signing", // msCTLSign
- "1.3.6.1.4.1.311.10.3.3": "Microsoft Server Gated Crypto", // msSGC
- "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)");
-
- 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.
- * @param {*} n How many leading spaces
- * @param {*} parts Array of strings
- * @returns Joined and indented string.
- */
-function indent(n, parts) {
- const fluff = " ".repeat(n);
- return fluff + parts.join("\n" + fluff) + "\n";
-}
-
-/**
- * Remove last character from a string.
- * @param {*} s String
- * @returns Chopped string.
- */
-function chop(s) {
- return s.substring(0, s.length - 1);
-}
-
-export default ParseCSR;
diff --git a/src/core/operations/ParseTLSRecord.mjs b/src/core/operations/ParseTLSRecord.mjs
deleted file mode 100644
index 57a339a8..00000000
--- a/src/core/operations/ParseTLSRecord.mjs
+++ /dev/null
@@ -1,884 +0,0 @@
-/**
- * @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);
-}
diff --git a/src/core/operations/ParseX509CRL.mjs b/src/core/operations/ParseX509CRL.mjs
deleted file mode 100644
index f498375d..00000000
--- a/src/core/operations/ParseX509CRL.mjs
+++ /dev/null
@@ -1,391 +0,0 @@
-/**
- * @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;
diff --git a/src/core/operations/ParseX509Certificate.mjs b/src/core/operations/ParseX509Certificate.mjs
index cdd1e9c7..11e63424 100644
--- a/src/core/operations/ParseX509Certificate.mjs
+++ b/src/core/operations/ParseX509Certificate.mjs
@@ -6,8 +6,7 @@
import r from "jsrsasign";
import { fromBase64 } from "../lib/Base64.mjs";
-import { runHash } from "../lib/Hash.mjs";
-import { fromHex, toHex } from "../lib/Hex.mjs";
+import { toHex } from "../lib/Hex.mjs";
import { formatByteStr, formatDnObj } from "../lib/PublicKey.mjs";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
@@ -82,8 +81,7 @@ class ParseX509Certificate extends Operation {
}
if (undefinedInputFormat) throw "Undefined input format";
- const hex = Utils.strToArrayBuffer(Utils.byteArrayToChars(fromHex(cert.hex))),
- sn = cert.getSerialNumberHex(),
+ const sn = cert.getSerialNumberHex(),
issuer = cert.getIssuer(),
subject = cert.getSubject(),
pk = cert.getPublicKey(),
@@ -193,10 +191,6 @@ Issuer
${issuerStr}
Subject
${subjectStr}
-Fingerprints
- MD5: ${runHash("md5", hex)}
- SHA1: ${runHash("sha1", hex)}
- SHA256: ${runHash("sha256", hex)}
Public Key
${pkStr.slice(0, -1)}
Certificate Signature
diff --git a/src/core/operations/PubKeyFromCert.mjs b/src/core/operations/PubKeyFromCert.mjs
deleted file mode 100644
index 0233b04a..00000000
--- a/src/core/operations/PubKeyFromCert.mjs
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * @author cplussharp
- * @copyright Crown Copyright 2023
- * @license Apache-2.0
- */
-
-import r from "jsrsasign";
-import Operation from "../Operation.mjs";
-import OperationError from "../errors/OperationError.mjs";
-
-/**
- * Public Key from Certificate operation
- */
-class PubKeyFromCert extends Operation {
-
- /**
- * PubKeyFromCert constructor
- */
- constructor() {
- super();
-
- this.name = "Public Key from Certificate";
- this.module = "PublicKey";
- this.description = "Extracts the Public Key from a Certificate.";
- this.infoURL = "https://en.wikipedia.org/wiki/X.509";
- this.inputType = "string";
- this.outputType = "string";
- this.args = [];
- this.checks = [];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- let output = "";
- let match;
- const regex = /-----BEGIN CERTIFICATE-----/g;
- while ((match = regex.exec(input)) !== null) {
- // find corresponding end tag
- const indexBase64 = match.index + match[0].length;
- const footer = "-----END CERTIFICATE-----";
- const indexFooter = input.indexOf(footer, indexBase64);
- if (indexFooter === -1) {
- throw new OperationError(`PEM footer '${footer}' not found`);
- }
-
- const certPem = input.substring(match.index, indexFooter + footer.length);
- const cert = new r.X509();
- cert.readCertPEM(certPem);
- let pubKey;
- try {
- pubKey = cert.getPublicKey();
- } catch {
- throw new OperationError("Unsupported public key type");
- }
- const pubKeyPem = r.KEYUTIL.getPEM(pubKey);
-
- // PEM ends with '\n', so a new key always starts on a new line
- output += pubKeyPem;
- }
- return output;
- }
-}
-
-export default PubKeyFromCert;
diff --git a/src/core/operations/PubKeyFromPrivKey.mjs b/src/core/operations/PubKeyFromPrivKey.mjs
deleted file mode 100644
index 5a08882b..00000000
--- a/src/core/operations/PubKeyFromPrivKey.mjs
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * @author cplussharp
- * @copyright Crown Copyright 2023
- * @license Apache-2.0
- */
-
-import r from "jsrsasign";
-import Operation from "../Operation.mjs";
-import OperationError from "../errors/OperationError.mjs";
-
-/**
- * Public Key from Private Key operation
- */
-class PubKeyFromPrivKey extends Operation {
-
- /**
- * PubKeyFromPrivKey constructor
- */
- constructor() {
- super();
-
- this.name = "Public Key from Private Key";
- this.module = "PublicKey";
- this.description = "Extracts the Public Key from a Private Key.";
- this.infoURL = "https://en.wikipedia.org/wiki/PKCS_8";
- this.inputType = "string";
- this.outputType = "string";
- this.args = [];
- this.checks = [];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- let output = "";
- let match;
- const regex = /-----BEGIN ((RSA |EC |DSA )?PRIVATE KEY)-----/g;
- while ((match = regex.exec(input)) !== null) {
- // find corresponding end tag
- const indexBase64 = match.index + match[0].length;
- const footer = `-----END ${match[1]}-----`;
- const indexFooter = input.indexOf(footer, indexBase64);
- if (indexFooter === -1) {
- throw new OperationError(`PEM footer '${footer}' not found`);
- }
-
- const privKeyPem = input.substring(match.index, indexFooter + footer.length);
- let privKey;
- try {
- privKey = r.KEYUTIL.getKey(privKeyPem);
- } catch (err) {
- throw new OperationError(`Unsupported key type: ${err}`);
- }
- let pubKey;
- if (privKey.type && privKey.type === "EC") {
- pubKey = new r.KJUR.crypto.ECDSA({ curve: privKey.curve });
- pubKey.setPublicKeyHex(privKey.generatePublicKeyHex());
- } else if (privKey.type && privKey.type === "DSA") {
- if (!privKey.y) {
- throw new OperationError(`DSA Private Key in PKCS#8 is not supported`);
- }
- pubKey = new r.KJUR.crypto.DSA();
- pubKey.setPublic(privKey.p, privKey.q, privKey.g, privKey.y);
- } else if (privKey.n && privKey.e) {
- pubKey = new r.RSAKey();
- pubKey.setPublic(privKey.n, privKey.e);
- } else {
- throw new OperationError(`Unsupported key type`);
- }
- const pubKeyPem = r.KEYUTIL.getPEM(pubKey);
-
- // PEM ends with '\n', so a new key always starts on a new line
- output += pubKeyPem;
- }
- return output;
- }
-}
-
-export default PubKeyFromPrivKey;
diff --git a/src/core/operations/RAKE.mjs b/src/core/operations/RAKE.mjs
deleted file mode 100644
index 1470f5f0..00000000
--- a/src/core/operations/RAKE.mjs
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * @author sw5678
- * @copyright Crown Copyright 2024
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-
-/**
- * RAKE operation
- */
-class RAKE extends Operation {
-
- /**
- * RAKE constructor
- */
- constructor() {
- super();
-
- this.name = "RAKE";
- this.module = "Default";
- this.description = [
- "Rapid Keyword Extraction (RAKE)",
- "
",
- "RAKE is a domain-independent keyword extraction algorithm in Natural Language Processing.",
- "
",
- "The list of stop words are from the NLTK python package",
- ].join("\n");
- this.inputType = "string";
- this.outputType = "string";
- this.args = [
- {
- name: "Word Delimiter (Regex)",
- type: "text",
- value: "\\s"
- },
- {
- name: "Sentence Delimiter (Regex)",
- type: "text",
- value: "\\.\\s|\\n"
- },
- {
- name: "Stop Words",
- type: "text",
- value: "i,me,my,myself,we,our,ours,ourselves,you,you're,you've,you'll,you'd,your,yours,yourself,yourselves,he,him,his,himself,she,she's,her,hers,herself,it,it's,its,itsef,they,them,their,theirs,themselves,what,which,who,whom,this,that,that'll,these,those,am,is,are,was,were,be,been,being,have,has,had,having,do,does',did,doing,a,an,the,and,but,if,or,because,as,until,while,of,at,by,for,with,about,against,between,into,through,during,before,after,above,below,to,from,up,down,in,out,on,off,over,under,again,further,then,once,here,there,when,where,why,how,all,any,both,each,few,more,most,other,some,such,no,nor,not,only,own,same,so,than,too,very,s,t,can,will,just,don,don't,should,should've,now,d,ll,m,o,re,ve,y,ain,aren,aren't,couldn,couldn't,didn,didn't,doesn,doesn't,hadn,hadn't,hasn,hasn't,haven,haven't,isn,isn't,ma,mightn,mightn't,mustn,mustn't,needn,needn't,shan,shan't,shouldn,shouldn't,wasn,wasn't,weren,weren't,won,won't,wouldn,wouldn't"
- }
- ];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
-
- // Get delimiter regexs
- const wordDelim = new RegExp(args[0], "g");
- const sentDelim = new RegExp(args[1], "g");
-
- // Deduplicate the stop words and add the empty string
- const stopWords = args[2].toLowerCase().replace(/ /g, "").split(",").unique();
- stopWords.push("");
-
- // Lower case input and remove start and ending whitespace
- input = input.toLowerCase().trim();
-
- // Get tokens, token count, and phrases
- const tokens = [];
- const wordFrequencies = [];
- let phrases = [];
-
- // Build up list of phrases and token counts
- const sentences = input.split(sentDelim);
- for (const sent of sentences) {
-
- // Split sentence into words
- const splitSent = sent.split(wordDelim);
- let startIndex = 0;
-
- for (let i = 0; i < splitSent.length; i++) {
- const token = splitSent[i];
- if (stopWords.includes(token)) {
- // If token is stop word then split to create phrase
- phrases.push(splitSent.slice(startIndex, i));
- startIndex = i + 1;
- } else {
- // If token is not a stop word add to the count of the list of words
- if (tokens.includes(token)) {
- wordFrequencies[tokens.indexOf(token)]+=1;
- } else {
- tokens.push(token);
- wordFrequencies.push(1);
- }
- }
- }
- phrases.push(splitSent.slice(startIndex));
- }
-
- // remove empty phrases
- phrases = phrases.filter(subArray => subArray.length > 0);
-
- // Remove duplicate phrases
- phrases = phrases.unique();
-
- // Generate word_degree_matrix and populate
- const wordDegreeMatrix = Array(tokens.length).fill().map(() => Array(tokens.length).fill(0));
- for (const phrase of phrases) {
- for (const word1 of phrase) {
- for (const word2 of phrase) {
- wordDegreeMatrix[tokens.indexOf(word1)][tokens.indexOf(word2)]++;
- }
- }
- }
-
- // Calculate degree score for each token
- const degreeScores = Array(tokens.length).fill(0);
- for (let i=0; i b[0] - a[0]);
- scores.unshift(new Array("Scores: ", "Keywords: "));
-
- // Output works with the 'To Table' functionality already built into CC
- return scores.map(function (score) {
- return score.join(", ");
- }).join("\n");
- }
-}
-
-export default RAKE;
diff --git a/src/core/operations/ROT13.mjs b/src/core/operations/ROT13.mjs
index beec94a4..1d059565 100644
--- a/src/core/operations/ROT13.mjs
+++ b/src/core/operations/ROT13.mjs
@@ -59,16 +59,15 @@ class ROT13 extends Operation {
rot13Upperacse = args[1],
rotNumbers = args[2];
let amount = args[3],
- amountNumbers = args[3];
+ chr;
if (amount) {
if (amount < 0) {
amount = 26 - (Math.abs(amount) % 26);
- amountNumbers = 10 - (Math.abs(amountNumbers) % 10);
}
for (let i = 0; i < input.length; i++) {
- let chr = input[i];
+ chr = input[i];
if (rot13Upperacse && chr >= 65 && chr <= 90) { // Upper case
chr = (chr - 65 + amount) % 26;
output[i] = chr + 65;
@@ -76,7 +75,7 @@ class ROT13 extends Operation {
chr = (chr - 97 + amount) % 26;
output[i] = chr + 97;
} else if (rotNumbers && chr >= 48 && chr <= 57) { // Numbers
- chr = (chr - 48 + amountNumbers) % 10;
+ chr = (chr - 48 + amount) % 10;
output[i] = chr + 48;
}
}
diff --git a/src/core/operations/RSASign.mjs b/src/core/operations/RSASign.mjs
index 5091549f..25160f53 100644
--- a/src/core/operations/RSASign.mjs
+++ b/src/core/operations/RSASign.mjs
@@ -60,7 +60,7 @@ class RSASign extends Operation {
const privateKey = forge.pki.decryptRsaPrivateKey(key, password);
// Generate message hash
const md = MD_ALGORITHMS[mdAlgo].create();
- md.update(input, "raw");
+ md.update(input, "utf8");
// Sign message hash
const sig = privateKey.sign(md);
return sig;
diff --git a/src/core/operations/RSAVerify.mjs b/src/core/operations/RSAVerify.mjs
index 8160438c..89b7d81f 100644
--- a/src/core/operations/RSAVerify.mjs
+++ b/src/core/operations/RSAVerify.mjs
@@ -8,7 +8,6 @@ import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import forge from "node-forge";
import { MD_ALGORITHMS } from "../lib/RSA.mjs";
-import Utils from "../Utils.mjs";
/**
* RSA Verify operation
@@ -38,11 +37,6 @@ class RSAVerify extends Operation {
type: "text",
value: ""
},
- {
- name: "Message format",
- type: "option",
- value: ["Raw", "Hex", "Base64"]
- },
{
name: "Message Digest Algorithm",
type: "option",
@@ -57,7 +51,7 @@ class RSAVerify extends Operation {
* @returns {string}
*/
run(input, args) {
- const [pemKey, message, format, mdAlgo] = args;
+ const [pemKey, message, mdAlgo] = args;
if (pemKey.replace("-----BEGIN RSA PUBLIC KEY-----", "").length === 0) {
throw new OperationError("Please enter a public key.");
}
@@ -66,8 +60,7 @@ class RSAVerify extends Operation {
const pubKey = forge.pki.publicKeyFromPem(pemKey);
// Generate message digest
const md = MD_ALGORITHMS[mdAlgo].create();
- const messageStr = Utils.convertToByteString(message, format);
- md.update(messageStr, "raw");
+ md.update(message, "utf8");
// Compare signed message digest and generated message digest
const result = pubKey.verify(md.digest().bytes(), input);
return result ? "Verified OK" : "Verification Failure";
diff --git a/src/core/operations/RandomizeColourPalette.mjs b/src/core/operations/RandomizeColourPalette.mjs
index fa8fa59e..e3baf54b 100644
--- a/src/core/operations/RandomizeColourPalette.mjs
+++ b/src/core/operations/RandomizeColourPalette.mjs
@@ -10,7 +10,7 @@ import Utils from "../Utils.mjs";
import { isImage } from "../lib/FileType.mjs";
import { runHash } from "../lib/Hash.mjs";
import { toBase64 } from "../lib/Base64.mjs";
-import Jimp from "jimp/es/index.js";
+import jimp from "jimp";
/**
* Randomize Colour Palette operation
@@ -48,7 +48,7 @@ class RandomizeColourPalette extends Operation {
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
const seed = args[0] || (Math.random().toString().substr(2)),
- parsedImage = await Jimp.read(input),
+ parsedImage = await jimp.read(input),
width = parsedImage.bitmap.width,
height = parsedImage.bitmap.height;
@@ -61,7 +61,7 @@ class RandomizeColourPalette extends Operation {
parsedImage.setPixelColor(parseInt(rgbHex, 16), x, y);
});
- const imageBuffer = await parsedImage.getBufferAsync(Jimp.AUTO);
+ const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO);
return new Uint8Array(imageBuffer).buffer;
}
diff --git a/src/core/operations/RegularExpression.mjs b/src/core/operations/RegularExpression.mjs
index 9ea17e83..1d8de9c4 100644
--- a/src/core/operations/RegularExpression.mjs
+++ b/src/core/operations/RegularExpression.mjs
@@ -67,10 +67,6 @@ class RegularExpression extends Operation {
name: "MAC address",
value: "[A-Fa-f\\d]{2}(?:[:-][A-Fa-f\\d]{2}){5}"
},
- {
- name: "UUID",
- value: "[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12}"
- },
{
name: "Date (yyyy-mm-dd)",
value: "((?:19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"
diff --git a/src/core/operations/ResizeImage.mjs b/src/core/operations/ResizeImage.mjs
index 2d2af045..b2ed3bbf 100644
--- a/src/core/operations/ResizeImage.mjs
+++ b/src/core/operations/ResizeImage.mjs
@@ -9,7 +9,7 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
-import Jimp from "jimp/es/index.js";
+import jimp from "jimp";
/**
* Resize Image operation
@@ -80,11 +80,11 @@ class ResizeImage extends Operation {
resizeAlg = args[4];
const resizeMap = {
- "Nearest Neighbour": Jimp.RESIZE_NEAREST_NEIGHBOR,
- "Bilinear": Jimp.RESIZE_BILINEAR,
- "Bicubic": Jimp.RESIZE_BICUBIC,
- "Hermite": Jimp.RESIZE_HERMITE,
- "Bezier": Jimp.RESIZE_BEZIER
+ "Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR,
+ "Bilinear": jimp.RESIZE_BILINEAR,
+ "Bicubic": jimp.RESIZE_BICUBIC,
+ "Hermite": jimp.RESIZE_HERMITE,
+ "Bezier": jimp.RESIZE_BEZIER
};
if (!isImage(input)) {
@@ -93,7 +93,7 @@ class ResizeImage extends Operation {
let image;
try {
- image = await Jimp.read(input);
+ image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
@@ -113,9 +113,9 @@ class ResizeImage extends Operation {
let imageBuffer;
if (image.getMIME() === "image/gif") {
- imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG);
+ imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
- imageBuffer = await image.getBufferAsync(Jimp.AUTO);
+ imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
diff --git a/src/core/operations/RisonDecode.mjs b/src/core/operations/RisonDecode.mjs
deleted file mode 100644
index d4e36f80..00000000
--- a/src/core/operations/RisonDecode.mjs
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * @author sg5506844 [sg5506844@gmail.com]
- * @copyright Crown Copyright 2021
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import OperationError from "../errors/OperationError.mjs";
-import rison from "rison";
-
-/**
- * Rison Decode operation
- */
-class RisonDecode extends Operation {
-
- /**
- * RisonDecode constructor
- */
- constructor() {
- super();
-
- this.name = "Rison Decode";
- this.module = "Encodings";
- this.description = "Rison, a data serialization format optimized for compactness in URIs. Rison is a slight variation of JSON that looks vastly superior after URI encoding. Rison still expresses exactly the same set of data structures as JSON, so data can be translated back and forth without loss or guesswork.";
- this.infoURL = "https://github.com/Nanonid/rison";
- this.inputType = "string";
- this.outputType = "Object";
- this.args = [
- {
- name: "Decode Option",
- type: "editableOption",
- value: ["Decode", "Decode Object", "Decode Array"]
- },
- ];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {Object}
- */
- run(input, args) {
- const [decodeOption] = args;
- switch (decodeOption) {
- case "Decode":
- return rison.decode(input);
- case "Decode Object":
- return rison.decode_object(input);
- case "Decode Array":
- return rison.decode_array(input);
- default:
- throw new OperationError("Invalid Decode option");
- }
- }
-}
-
-export default RisonDecode;
diff --git a/src/core/operations/RisonEncode.mjs b/src/core/operations/RisonEncode.mjs
deleted file mode 100644
index 12b13b66..00000000
--- a/src/core/operations/RisonEncode.mjs
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * @author sg5506844 [sg5506844@gmail.com]
- * @copyright Crown Copyright 2021
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import OperationError from "../errors/OperationError.mjs";
-import rison from "rison";
-
-/**
- * Rison Encode operation
- */
-class RisonEncode extends Operation {
-
- /**
- * RisonEncode constructor
- */
- constructor() {
- super();
-
- this.name = "Rison Encode";
- this.module = "Encodings";
- this.description = "Rison, a data serialization format optimized for compactness in URIs. Rison is a slight variation of JSON that looks vastly superior after URI encoding. Rison still expresses exactly the same set of data structures as JSON, so data can be translated back and forth without loss or guesswork.";
- this.infoURL = "https://github.com/Nanonid/rison";
- this.inputType = "Object";
- this.outputType = "string";
- this.args = [
- {
- name: "Encode Option",
- type: "option",
- value: ["Encode", "Encode Object", "Encode Array", "Encode URI"]
- },
- ];
- }
-
- /**
- * @param {Object} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- const [encodeOption] = args;
- switch (encodeOption) {
- case "Encode":
- return rison.encode(input);
- case "Encode Object":
- return rison.encode_object(input);
- case "Encode Array":
- return rison.encode_array(input);
- case "Encode URI":
- return rison.encode_uri(input);
- default:
- throw new OperationError("Invalid encode option");
- }
- }
-}
-
-export default RisonEncode;
diff --git a/src/core/operations/RotateImage.mjs b/src/core/operations/RotateImage.mjs
index 894ec785..a4659b12 100644
--- a/src/core/operations/RotateImage.mjs
+++ b/src/core/operations/RotateImage.mjs
@@ -9,7 +9,7 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
-import Jimp from "jimp/es/index.js";
+import jimp from "jimp";
/**
* Rotate Image operation
@@ -52,7 +52,7 @@ class RotateImage extends Operation {
let image;
try {
- image = await Jimp.read(input);
+ image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
@@ -63,9 +63,9 @@ class RotateImage extends Operation {
let imageBuffer;
if (image.getMIME() === "image/gif") {
- imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG);
+ imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
- imageBuffer = await image.getBufferAsync(Jimp.AUTO);
+ imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs
index e3a9b82e..274f09f6 100644
--- a/src/core/operations/SIGABA.mjs
+++ b/src/core/operations/SIGABA.mjs
@@ -40,7 +40,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "1st cipher rotor initial value",
+ name: "1st cipher rotor intial value",
type: "option",
value: LETTERS
},
@@ -56,7 +56,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "2nd cipher rotor initial value",
+ name: "2nd cipher rotor intial value",
type: "option",
value: LETTERS
},
@@ -72,7 +72,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "3rd cipher rotor initial value",
+ name: "3rd cipher rotor intial value",
type: "option",
value: LETTERS
},
@@ -88,7 +88,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "4th cipher rotor initial value",
+ name: "4th cipher rotor intial value",
type: "option",
value: LETTERS
},
@@ -104,7 +104,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "5th cipher rotor initial value",
+ name: "5th cipher rotor intial value",
type: "option",
value: LETTERS
},
@@ -120,7 +120,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "1st control rotor initial value",
+ name: "1st control rotor intial value",
type: "option",
value: LETTERS
},
@@ -136,7 +136,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "2nd control rotor initial value",
+ name: "2nd control rotor intial value",
type: "option",
value: LETTERS
},
@@ -152,7 +152,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "3rd control rotor initial value",
+ name: "3rd control rotor intial value",
type: "option",
value: LETTERS
},
@@ -168,7 +168,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "4th control rotor initial value",
+ name: "4th control rotor intial value",
type: "option",
value: LETTERS
},
@@ -184,7 +184,7 @@ class Sigaba extends Operation {
value: false
},
{
- name: "5th control rotor initial value",
+ name: "5th control rotor intial value",
type: "option",
value: LETTERS
},
@@ -195,7 +195,7 @@ class Sigaba extends Operation {
defaultIndex: 0
},
{
- name: "1st index rotor initial value",
+ name: "1st index rotor intial value",
type: "option",
value: NUMBERS
},
@@ -206,7 +206,7 @@ class Sigaba extends Operation {
defaultIndex: 0
},
{
- name: "2nd index rotor initial value",
+ name: "2nd index rotor intial value",
type: "option",
value: NUMBERS
},
@@ -217,7 +217,7 @@ class Sigaba extends Operation {
defaultIndex: 0
},
{
- name: "3rd index rotor initial value",
+ name: "3rd index rotor intial value",
type: "option",
value: NUMBERS
},
@@ -228,7 +228,7 @@ class Sigaba extends Operation {
defaultIndex: 0
},
{
- name: "4th index rotor initial value",
+ name: "4th index rotor intial value",
type: "option",
value: NUMBERS
},
@@ -239,7 +239,7 @@ class Sigaba extends Operation {
defaultIndex: 0
},
{
- name: "5th index rotor initial value",
+ name: "5th index rotor intial value",
type: "option",
value: NUMBERS
},
diff --git a/src/core/operations/SM2Decrypt.mjs b/src/core/operations/SM2Decrypt.mjs
deleted file mode 100644
index 39657110..00000000
--- a/src/core/operations/SM2Decrypt.mjs
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * @author flakjacket95 [dflack95@gmail.com]
- * @copyright Crown Copyright 2024
- * @license Apache-2.0
- */
-
-import OperationError from "../errors/OperationError.mjs";
-import Operation from "../Operation.mjs";
-
-import { SM2 } from "../lib/SM2.mjs";
-
-/**
- * SM2Decrypt operation
- */
-class SM2Decrypt extends Operation {
-
- /**
- * SM2Decrypt constructor
- */
- constructor() {
- super();
-
- this.name = "SM2 Decrypt";
- this.module = "Crypto";
- this.description = "Decrypts a message utilizing the SM2 standard";
- this.infoURL = ""; // Usually a Wikipedia link. Remember to remove localisation (i.e. https://wikipedia.org/etc rather than https://en.wikipedia.org/etc)
- this.inputType = "string";
- this.outputType = "ArrayBuffer";
- this.args = [
- {
- name: "Private Key",
- type: "string",
- value: "DEADBEEF"
- },
- {
- "name": "Input Format",
- "type": "option",
- "value": ["C1C3C2", "C1C2C3"],
- "defaultIndex": 0
- },
- {
- name: "Curve",
- type: "option",
- "value": ["sm2p256v1"],
- "defaultIndex": 0
- }
- ];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {ArrayBuffer}
- */
- run(input, args) {
- const [privateKey, inputFormat, curveName] = args;
-
- if (privateKey.length !== 64) {
- throw new OperationError("Input private key must be in hex; and should be 32 bytes");
- }
-
- const sm2 = new SM2(curveName, inputFormat);
- sm2.setPrivateKey(privateKey);
-
- const result = sm2.decrypt(input);
- return result;
- }
-
-}
-
-export default SM2Decrypt;
diff --git a/src/core/operations/SM2Encrypt.mjs b/src/core/operations/SM2Encrypt.mjs
deleted file mode 100644
index b1e5f901..00000000
--- a/src/core/operations/SM2Encrypt.mjs
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * @author flakjacket95 [dflack95@gmail.com]
- * @copyright Crown Copyright 2024
- * @license Apache-2.0
- */
-
-import OperationError from "../errors/OperationError.mjs";
-import Operation from "../Operation.mjs";
-
-import { SM2 } from "../lib/SM2.mjs";
-
-/**
- * SM2 Encrypt operation
- */
-class SM2Encrypt extends Operation {
-
- /**
- * SM2Encrypt constructor
- */
- constructor() {
- super();
-
- this.name = "SM2 Encrypt";
- this.module = "Crypto";
- this.description = "Encrypts a message utilizing the SM2 standard";
- this.infoURL = ""; // Usually a Wikipedia link. Remember to remove localisation (i.e. https://wikipedia.org/etc rather than https://en.wikipedia.org/etc)
- this.inputType = "ArrayBuffer";
- this.outputType = "string";
-
- this.args = [
- {
- name: "Public Key X",
- type: "string",
- value: "DEADBEEF"
- },
- {
- name: "Public Key Y",
- type: "string",
- value: "DEADBEEF"
- },
- {
- "name": "Output Format",
- "type": "option",
- "value": ["C1C3C2", "C1C2C3"],
- "defaultIndex": 0
- },
- {
- name: "Curve",
- type: "option",
- "value": ["sm2p256v1"],
- "defaultIndex": 0
- }
- ];
- }
-
- /**
- * @param {ArrayBuffer} input
- * @param {Object[]} args
- * @returns {byteArray}
- */
- run(input, args) {
- const [publicKeyX, publicKeyY, outputFormat, curveName] = args;
- this.outputFormat = outputFormat;
-
- if (publicKeyX.length !== 64 || publicKeyY.length !== 64) {
- throw new OperationError("Invalid Public Key - Ensure each component is 32 bytes in size and in hex");
- }
-
- const sm2 = new SM2(curveName, outputFormat);
- sm2.setPublicKey(publicKeyX, publicKeyY);
-
- const result = sm2.encrypt(new Uint8Array(input));
- return result;
- }
-}
-
-export default SM2Encrypt;
diff --git a/src/core/operations/SSDEEP.mjs b/src/core/operations/SSDEEP.mjs
index 6a76a68b..df1557d5 100644
--- a/src/core/operations/SSDEEP.mjs
+++ b/src/core/operations/SSDEEP.mjs
@@ -21,7 +21,7 @@ class SSDEEP extends Operation {
this.name = "SSDEEP";
this.module = "Crypto";
this.description = "SSDEEP is a program for computing context triggered piecewise hashes (CTPH). Also called fuzzy hashes, CTPH can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.
SSDEEP hashes are now widely used for simple identification purposes (e.g. the 'Basic Properties' section in VirusTotal). Although 'better' fuzzy hashes are available, SSDEEP is still one of the primary choices because of its speed and being a de facto standard.
This operation is fundamentally the same as the CTPH operation, however their outputs differ in format.";
- this.infoURL = "https://forensics.wiki/ssdeep";
+ this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Ssdeep";
this.inputType = "string";
this.outputType = "string";
this.args = [];
diff --git a/src/core/operations/Salsa20.mjs b/src/core/operations/Salsa20.mjs
deleted file mode 100644
index 7a76cf26..00000000
--- a/src/core/operations/Salsa20.mjs
+++ /dev/null
@@ -1,154 +0,0 @@
-/**
- * @author joostrijneveld [joost@joostrijneveld.nl]
- * @copyright Crown Copyright 2024
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import OperationError from "../errors/OperationError.mjs";
-import Utils from "../Utils.mjs";
-import { toHex } from "../lib/Hex.mjs";
-import { salsa20Block } from "../lib/Salsa20.mjs";
-
-/**
- * Salsa20 operation
- */
-class Salsa20 extends Operation {
-
- /**
- * Salsa20 constructor
- */
- constructor() {
- super();
-
- this.name = "Salsa20";
- this.module = "Ciphers";
- this.description = "Salsa20 is a stream cipher designed by Daniel J. Bernstein and submitted to the eSTREAM project; Salsa20/8 and Salsa20/12 are round-reduced variants. It is closely related to the ChaCha stream cipher.
Key: Salsa20 uses a key of 16 or 32 bytes (128 or 256 bits).
Nonce: Salsa20 uses a nonce of 8 bytes (64 bits).
Counter: Salsa uses a counter of 8 bytes (64 bits). The counter starts at zero at the start of the keystream, and is incremented at every 64 bytes.";
- this.infoURL = "https://wikipedia.org/wiki/Salsa20";
- this.inputType = "string";
- this.outputType = "string";
- this.args = [
- {
- "name": "Key",
- "type": "toggleString",
- "value": "",
- "toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
- },
- {
- "name": "Nonce",
- "type": "toggleString",
- "value": "",
- "toggleValues": ["Hex", "UTF8", "Latin1", "Base64", "Integer"]
- },
- {
- "name": "Counter",
- "type": "number",
- "value": 0,
- "min": 0
- },
- {
- "name": "Rounds",
- "type": "option",
- "value": ["20", "12", "8"]
- },
- {
- "name": "Input",
- "type": "option",
- "value": ["Hex", "Raw"]
- },
- {
- "name": "Output",
- "type": "option",
- "value": ["Raw", "Hex"]
- }
- ];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- const key = Utils.convertToByteArray(args[0].string, args[0].option),
- nonceType = args[1].option,
- rounds = parseInt(args[3], 10),
- inputType = args[4],
- outputType = args[5];
-
- if (key.length !== 16 && key.length !== 32) {
- throw new OperationError(`Invalid key length: ${key.length} bytes.
-
-Salsa20 uses a key of 16 or 32 bytes (128 or 256 bits).`);
- }
-
- let counter, nonce;
- if (nonceType === "Integer") {
- nonce = Utils.intToByteArray(parseInt(args[1].string, 10), 8, "little");
- } else {
- nonce = Utils.convertToByteArray(args[1].string, args[1].option);
- if (!(nonce.length === 8)) {
- throw new OperationError(`Invalid nonce length: ${nonce.length} bytes.
-
-Salsa20 uses a nonce of 8 bytes (64 bits).`);
- }
- }
- counter = Utils.intToByteArray(args[2], 8, "little");
-
- const output = [];
- input = Utils.convertToByteArray(input, inputType);
-
- let counterAsInt = Utils.byteArrayToInt(counter, "little");
- for (let i = 0; i < input.length; i += 64) {
- counter = Utils.intToByteArray(counterAsInt, 8, "little");
- const stream = salsa20Block(key, nonce, counter, rounds);
- for (let j = 0; j < 64 && i + j < input.length; j++) {
- output.push(input[i + j] ^ stream[j]);
- }
- counterAsInt++;
- }
-
- if (outputType === "Hex") {
- return toHex(output);
- } else {
- return Utils.arrayBufferToStr(Uint8Array.from(output).buffer);
- }
- }
-
- /**
- * Highlight Salsa20
- *
- * @param {Object[]} pos
- * @param {number} pos[].start
- * @param {number} pos[].end
- * @param {Object[]} args
- * @returns {Object[]} pos
- */
- highlight(pos, args) {
- const inputType = args[4],
- outputType = args[5];
- if (inputType === "Raw" && outputType === "Raw") {
- return pos;
- }
- }
-
- /**
- * Highlight Salsa20 in reverse
- *
- * @param {Object[]} pos
- * @param {number} pos[].start
- * @param {number} pos[].end
- * @param {Object[]} args
- * @returns {Object[]} pos
- */
- highlightReverse(pos, args) {
- const inputType = args[4],
- outputType = args[5];
- if (inputType === "Raw" && outputType === "Raw") {
- return pos;
- }
- }
-
-}
-
-export default Salsa20;
diff --git a/src/core/operations/SharpenImage.mjs b/src/core/operations/SharpenImage.mjs
index 5cf6b606..eb033ad2 100644
--- a/src/core/operations/SharpenImage.mjs
+++ b/src/core/operations/SharpenImage.mjs
@@ -10,7 +10,7 @@ import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { gaussianBlur } from "../lib/ImageManipulation.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
-import Jimp from "jimp/es/index.js";
+import jimp from "jimp";
/**
* Sharpen Image operation
@@ -68,7 +68,7 @@ class SharpenImage extends Operation {
let image;
try {
- image = await Jimp.read(input);
+ image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
@@ -137,9 +137,9 @@ class SharpenImage extends Operation {
let imageBuffer;
if (image.getMIME() === "image/gif") {
- imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG);
+ imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
- imageBuffer = await image.getBufferAsync(Jimp.AUTO);
+ imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
diff --git a/src/core/operations/SplitColourChannels.mjs b/src/core/operations/SplitColourChannels.mjs
index d5a26a2d..339a356d 100644
--- a/src/core/operations/SplitColourChannels.mjs
+++ b/src/core/operations/SplitColourChannels.mjs
@@ -8,7 +8,7 @@ import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import {isImage} from "../lib/FileType.mjs";
-import Jimp from "jimp/es/index.js";
+import jimp from "jimp";
/**
* Split Colour Channels operation
@@ -41,7 +41,7 @@ class SplitColourChannels extends Operation {
// Make sure that the input is an image
if (!isImage(input)) throw new OperationError("Invalid file type.");
- const parsedImage = await Jimp.read(Buffer.from(input));
+ const parsedImage = await jimp.read(Buffer.from(input));
const red = new Promise(async (resolve, reject) => {
try {
@@ -51,7 +51,7 @@ class SplitColourChannels extends Operation {
{apply: "blue", params: [-255]},
{apply: "green", params: [-255]}
])
- .getBufferAsync(Jimp.MIME_PNG);
+ .getBufferAsync(jimp.MIME_PNG);
resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"}));
} catch (err) {
reject(new OperationError(`Could not split red channel: ${err}`));
@@ -64,7 +64,7 @@ class SplitColourChannels extends Operation {
.color([
{apply: "red", params: [-255]},
{apply: "blue", params: [-255]},
- ]).getBufferAsync(Jimp.MIME_PNG);
+ ]).getBufferAsync(jimp.MIME_PNG);
resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"}));
} catch (err) {
reject(new OperationError(`Could not split green channel: ${err}`));
@@ -77,7 +77,7 @@ class SplitColourChannels extends Operation {
.color([
{apply: "red", params: [-255]},
{apply: "green", params: [-255]},
- ]).getBufferAsync(Jimp.MIME_PNG);
+ ]).getBufferAsync(jimp.MIME_PNG);
resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"}));
} catch (err) {
reject(new OperationError(`Could not split blue channel: ${err}`));
diff --git a/src/core/operations/Streebog.mjs b/src/core/operations/Streebog.mjs
index b65accf6..c5e5bb89 100644
--- a/src/core/operations/Streebog.mjs
+++ b/src/core/operations/Streebog.mjs
@@ -28,7 +28,7 @@ class Streebog extends Operation {
this.outputType = "string";
this.args = [
{
- "name": "Digest length",
+ "name": "Size",
"type": "option",
"value": ["256", "512"]
}
@@ -41,16 +41,13 @@ class Streebog extends Operation {
* @returns {string}
*/
run(input, args) {
- const [length] = args;
-
- const algorithm = {
- version: 2012,
- mode: "HASH",
- length: parseInt(length, 10)
- };
-
try {
- const gostDigest = new GostDigest(algorithm);
+ const length = parseInt(args[0], 10);
+ const gostDigest = new GostDigest({
+ name: "GOST R 34.11",
+ version: 2012,
+ length: length
+ });
return toHexFast(gostDigest.digest(input));
} catch (err) {
diff --git a/src/core/operations/StripIPv4Header.mjs b/src/core/operations/StripIPv4Header.mjs
deleted file mode 100644
index 4b6ef1af..00000000
--- a/src/core/operations/StripIPv4Header.mjs
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * @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;
diff --git a/src/core/operations/StripTCPHeader.mjs b/src/core/operations/StripTCPHeader.mjs
deleted file mode 100644
index 747744b2..00000000
--- a/src/core/operations/StripTCPHeader.mjs
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * @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;
diff --git a/src/core/operations/StripUDPHeader.mjs b/src/core/operations/StripUDPHeader.mjs
deleted file mode 100644
index 0847c58f..00000000
--- a/src/core/operations/StripUDPHeader.mjs
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * @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;
diff --git a/src/core/operations/SwapCase.mjs b/src/core/operations/SwapCase.mjs
deleted file mode 100644
index 679d7703..00000000
--- a/src/core/operations/SwapCase.mjs
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * @author mikecat
- * @copyright Crown Copyright 2023
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-
-/**
- * Swap case operation
- */
-class SwapCase extends Operation {
-
- /**
- * SwapCase constructor
- */
- constructor() {
- super();
-
- this.name = "Swap case";
- this.module = "Default";
- this.description = "Converts uppercase letters to lowercase ones, and lowercase ones to uppercase ones.";
- this.infoURL = "";
- this.inputType = "string";
- this.outputType = "string";
- this.args = [];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- let result = "";
- for (let i = 0; i < input.length; i++) {
- const c = input.charAt(i);
- const upper = c.toUpperCase();
- if (c === upper) {
- result += c.toLowerCase();
- } else {
- result += upper;
- }
- }
- return result;
- }
-
- /**
- * Highlight Swap case
- *
- * @param {Object[]} pos
- * @param {number} pos[].start
- * @param {number} pos[].end
- * @param {Object[]} args
- * @returns {Object[]} pos
- */
- highlight(pos, args) {
- return pos;
- }
-
- /**
- * Highlight Swap case in reverse
- *
- * @param {Object[]} pos
- * @param {number} pos[].start
- * @param {number} pos[].end
- * @param {Object[]} args
- * @returns {Object[]} pos
- */
- highlightReverse(pos, args) {
- return pos;
- }
-
-}
-
-export default SwapCase;
diff --git a/src/core/operations/TakeNthBytes.mjs b/src/core/operations/TakeNthBytes.mjs
deleted file mode 100644
index 05de8869..00000000
--- a/src/core/operations/TakeNthBytes.mjs
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * @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;
diff --git a/src/core/operations/ToBase32.mjs b/src/core/operations/ToBase32.mjs
index 44eb8b48..fd36f550 100644
--- a/src/core/operations/ToBase32.mjs
+++ b/src/core/operations/ToBase32.mjs
@@ -6,7 +6,6 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
-import {ALPHABET_OPTIONS} from "../lib/Base32.mjs";
/**
* To Base32 operation
@@ -28,8 +27,8 @@ class ToBase32 extends Operation {
this.args = [
{
name: "Alphabet",
- type: "editableOption",
- value: ALPHABET_OPTIONS
+ type: "binaryString",
+ value: "A-Z2-7="
}
];
}
@@ -84,4 +83,3 @@ class ToBase32 extends Operation {
}
export default ToBase32;
-
diff --git a/src/core/operations/ToBase58.mjs b/src/core/operations/ToBase58.mjs
index 2e71b20e..5353c40e 100644
--- a/src/core/operations/ToBase58.mjs
+++ b/src/core/operations/ToBase58.mjs
@@ -43,7 +43,7 @@ class ToBase58 extends Operation {
run(input, args) {
input = new Uint8Array(input);
let alphabet = args[0] || ALPHABET_OPTIONS[0].value,
- result = [];
+ result = [0];
alphabet = Utils.expandAlphRange(alphabet).join("");
@@ -60,9 +60,11 @@ class ToBase58 extends Operation {
}
input.forEach(function(b) {
- let carry = b;
+ let carry = (result[0] << 8) + b;
+ result[0] = carry % 58;
+ carry = (carry / 58) | 0;
- for (let i = 0; i < result.length; i++) {
+ for (let i = 1; i < result.length; i++) {
carry += result[i] << 8;
result[i] = carry % 58;
carry = (carry / 58) | 0;
diff --git a/src/core/operations/ToBase92.mjs b/src/core/operations/ToBase92.mjs
deleted file mode 100644
index bca8e872..00000000
--- a/src/core/operations/ToBase92.mjs
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * @author sg5506844 [sg5506844@gmail.com]
- * @copyright Crown Copyright 2021
- * @license Apache-2.0
- */
-
-import { base92Chr } from "../lib/Base92.mjs";
-import Operation from "../Operation.mjs";
-
-/**
- * To Base92 operation
- */
-class ToBase92 extends Operation {
- /**
- * ToBase92 constructor
- */
- constructor() {
- super();
-
- this.name = "To Base92";
- this.module = "Default";
- this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
- this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
- this.inputType = "string";
- this.outputType = "byteArray";
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {byteArray}
- */
- run(input, args) {
- const res = [];
- let bitString = "";
-
- while (input.length > 0) {
- while (bitString.length < 13 && input.length > 0) {
- bitString += input[0].charCodeAt(0).toString(2).padStart(8, "0");
- input = input.slice(1);
- }
- if (bitString.length < 13)
- break;
- const i = parseInt(bitString.slice(0, 13), 2);
- res.push(base92Chr(Math.floor(i / 91)));
- res.push(base92Chr(i % 91));
- bitString = bitString.slice(13);
- }
-
- if (bitString.length > 0) {
- if (bitString.length < 7) {
- bitString = bitString.padEnd(6, "0");
- res.push(base92Chr(parseInt(bitString, 2)));
- } else {
- bitString = bitString.padEnd(13, "0");
- const i = parseInt(bitString.slice(0, 13), 2);
- res.push(base92Chr(Math.floor(i / 91)));
- res.push(base92Chr(i % 91));
- }
- }
-
- return res;
-
- }
-}
-
-export default ToBase92;
diff --git a/src/core/operations/ToFloat.mjs b/src/core/operations/ToFloat.mjs
deleted file mode 100644
index e0c70bc6..00000000
--- a/src/core/operations/ToFloat.mjs
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * @author tcode2k16 [tcode2k16@gmail.com]
- * @copyright Crown Copyright 2019
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import OperationError from "../errors/OperationError.mjs";
-import Utils from "../Utils.mjs";
-import ieee754 from "ieee754";
-import {DELIM_OPTIONS} from "../lib/Delim.mjs";
-
-/**
- * To Float operation
- */
-class ToFloat extends Operation {
-
- /**
- * ToFloat constructor
- */
- constructor() {
- super();
-
- this.name = "To Float";
- this.module = "Default";
- this.description = "Convert to IEEE754 Floating Point Numbers";
- this.infoURL = "https://wikipedia.org/wiki/IEEE_754";
- this.inputType = "byteArray";
- this.outputType = "string";
- this.args = [
- {
- "name": "Endianness",
- "type": "option",
- "value": [
- "Big Endian",
- "Little Endian"
- ]
- },
- {
- "name": "Size",
- "type": "option",
- "value": [
- "Float (4 bytes)",
- "Double (8 bytes)"
- ]
- },
- {
- "name": "Delimiter",
- "type": "option",
- "value": DELIM_OPTIONS
- }
- ];
- }
-
- /**
- * @param {byteArray} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- const [endianness, size, delimiterName] = args;
- const delim = Utils.charRep(delimiterName || "Space");
- const byteSize = size === "Double (8 bytes)" ? 8 : 4;
- const isLE = endianness === "Little Endian";
- const mLen = byteSize === 4 ? 23 : 52;
-
- if (input.length % byteSize !== 0) {
- throw new OperationError(`Input is not a multiple of ${byteSize}`);
- }
-
- const output = [];
- for (let i = 0; i < input.length; i+=byteSize) {
- output.push(ieee754.read(input, i, isLE, mLen, byteSize));
- }
- return output.join(delim);
- }
-
-}
-
-export default ToFloat;
diff --git a/src/core/operations/ToModhex.mjs b/src/core/operations/ToModhex.mjs
deleted file mode 100644
index 6d91fb5d..00000000
--- a/src/core/operations/ToModhex.mjs
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * @author linuxgemini [ilteris@asenkron.com.tr]
- * @copyright Crown Copyright 2024
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import { TO_MODHEX_DELIM_OPTIONS, toModhex } from "../lib/Modhex.mjs";
-import Utils from "../Utils.mjs";
-
-/**
- * To Modhex operation
- */
-class ToModhex extends Operation {
-
- /**
- * ToModhex constructor
- */
- constructor() {
- super();
-
- this.name = "To Modhex";
- this.module = "Default";
- this.description = "Converts the input string to modhex bytes separated by the specified delimiter.";
- this.infoURL = "https://en.wikipedia.org/wiki/YubiKey#ModHex";
- this.inputType = "ArrayBuffer";
- this.outputType = "string";
- this.args = [
- {
- name: "Delimiter",
- type: "option",
- value: TO_MODHEX_DELIM_OPTIONS
- },
- {
- name: "Bytes per line",
- type: "number",
- value: 0
- }
- ];
- }
-
- /**
- * @param {ArrayBuffer} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- const delim = Utils.charRep(args[0]);
- const lineSize = args[1];
-
- return toModhex(new Uint8Array(input), delim, 2, "", lineSize);
- }
-}
-
-export default ToModhex;
diff --git a/src/core/operations/ToTable.mjs b/src/core/operations/ToTable.mjs
index 082c39bc..ff69023f 100644
--- a/src/core/operations/ToTable.mjs
+++ b/src/core/operations/ToTable.mjs
@@ -214,7 +214,7 @@ class ToTable extends Operation {
output += outputRow(row, longestCells);
let rowOutput = verticalBorder;
row.forEach(function(cell, index) {
- rowOutput += " " + headerDivider.repeat(longestCells[index]) + " " + verticalBorder;
+ rowOutput += " " + headerDivider + " " + verticalBorder;
});
output += rowOutput += "\n";
diff --git a/src/core/operations/TripleDESDecrypt.mjs b/src/core/operations/TripleDESDecrypt.mjs
index 927600de..8487509f 100644
--- a/src/core/operations/TripleDESDecrypt.mjs
+++ b/src/core/operations/TripleDESDecrypt.mjs
@@ -22,7 +22,7 @@ class TripleDESDecrypt extends Operation {
this.name = "Triple DES Decrypt";
this.module = "Ciphers";
- this.description = "Triple DES applies DES three times to each block to increase key size.
Key: Triple DES uses a key length of 24 bytes (192 bits).
IV: The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.
Padding: 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.
Key: Triple DES uses a key length of 24 bytes (192 bits).
DES uses a key length of 8 bytes (64 bits).
IV: The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.
Padding: In CBC and ECB mode, PKCS#7 padding will be used as a default.";
this.infoURL = "https://wikipedia.org/wiki/Triple_DES";
this.inputType = "string";
this.outputType = "string";
@@ -73,7 +73,8 @@ class TripleDESDecrypt extends Operation {
if (key.length !== 24 && key.length !== 16) {
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") {
throw new OperationError(`Invalid IV length: ${iv.length} bytes
diff --git a/src/core/operations/TripleDESEncrypt.mjs b/src/core/operations/TripleDESEncrypt.mjs
index b4a218d0..720d155d 100644
--- a/src/core/operations/TripleDESEncrypt.mjs
+++ b/src/core/operations/TripleDESEncrypt.mjs
@@ -22,7 +22,7 @@ class TripleDESEncrypt extends Operation {
this.name = "Triple DES Encrypt";
this.module = "Ciphers";
- this.description = "Triple DES applies DES three times to each block to increase key size.
Key: Triple DES uses a key length of 24 bytes (192 bits).
You can generate a password-based key using one of the KDF operations.
IV: The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.
Padding: 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.
Key: Triple DES uses a key length of 24 bytes (192 bits).
DES uses a key length of 8 bytes (64 bits).
You can generate a password-based key using one of the KDF operations.
IV: The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.
Padding: In CBC and ECB mode, PKCS#7 padding will be used.";
this.infoURL = "https://wikipedia.org/wiki/Triple_DES";
this.inputType = "string";
this.outputType = "string";
@@ -72,7 +72,8 @@ class TripleDESEncrypt extends Operation {
if (key.length !== 24 && key.length !== 16) {
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") {
throw new OperationError(`Invalid IV length: ${iv.length} bytes
diff --git a/src/core/operations/ViewBitPlane.mjs b/src/core/operations/ViewBitPlane.mjs
index 8c93f16c..acdd6cdd 100644
--- a/src/core/operations/ViewBitPlane.mjs
+++ b/src/core/operations/ViewBitPlane.mjs
@@ -9,7 +9,7 @@ import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
-import Jimp from "jimp/es/index.js";
+import jimp from "jimp";
/**
* View Bit Plane operation
@@ -52,7 +52,7 @@ class ViewBitPlane extends Operation {
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
const [colour, bit] = args,
- parsedImage = await Jimp.read(input),
+ parsedImage = await jimp.read(input),
width = parsedImage.bitmap.width,
height = parsedImage.bitmap.height,
colourIndex = COLOUR_OPTIONS.indexOf(colour),
@@ -78,7 +78,7 @@ class ViewBitPlane extends Operation {
});
- const imageBuffer = await parsedImage.getBufferAsync(Jimp.AUTO);
+ const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO);
return new Uint8Array(imageBuffer).buffer;
}
diff --git a/src/core/operations/XPathExpression.mjs b/src/core/operations/XPathExpression.mjs
index 768247cb..7bfe3ee1 100644
--- a/src/core/operations/XPathExpression.mjs
+++ b/src/core/operations/XPathExpression.mjs
@@ -6,7 +6,7 @@
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
-import xmldom from "@xmldom/xmldom";
+import xmldom from "xmldom";
import xpath from "xpath";
/**
@@ -52,6 +52,12 @@ class XPathExpression extends Operation {
try {
doc = new xmldom.DOMParser({
errorHandler: {
+ warning(w) {
+ throw w;
+ },
+ error(e) {
+ throw e;
+ },
fatalError(e) {
throw e;
}
diff --git a/src/core/operations/XSalsa20.mjs b/src/core/operations/XSalsa20.mjs
deleted file mode 100644
index d289585b..00000000
--- a/src/core/operations/XSalsa20.mjs
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * @author joostrijneveld [joost@joostrijneveld.nl]
- * @copyright Crown Copyright 2024
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import OperationError from "../errors/OperationError.mjs";
-import Utils from "../Utils.mjs";
-import { toHex } from "../lib/Hex.mjs";
-import { salsa20Block, hsalsa20 } from "../lib/Salsa20.mjs";
-
-/**
- * XSalsa20 operation
- */
-class XSalsa20 extends Operation {
-
- /**
- * XSalsa20 constructor
- */
- constructor() {
- super();
-
- this.name = "XSalsa20";
- this.module = "Ciphers";
- this.description = "XSalsa20 is a variant of the Salsa20 stream cipher designed by Daniel J. Bernstein; XSalsa uses longer nonces.
Key: XSalsa20 uses a key of 16 or 32 bytes (128 or 256 bits).
Nonce: XSalsa20 uses a nonce of 24 bytes (192 bits).
Counter: XSalsa uses a counter of 8 bytes (64 bits). The counter starts at zero at the start of the keystream, and is incremented at every 64 bytes.";
- this.infoURL = "https://en.wikipedia.org/wiki/Salsa20#XSalsa20_with_192-bit_nonce";
- this.inputType = "string";
- this.outputType = "string";
- this.args = [
- {
- "name": "Key",
- "type": "toggleString",
- "value": "",
- "toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
- },
- {
- "name": "Nonce",
- "type": "toggleString",
- "value": "",
- "toggleValues": ["Hex", "UTF8", "Latin1", "Base64", "Integer"]
- },
- {
- "name": "Counter",
- "type": "number",
- "value": 0,
- "min": 0
- },
- {
- "name": "Rounds",
- "type": "option",
- "value": ["20", "12", "8"]
- },
- {
- "name": "Input",
- "type": "option",
- "value": ["Hex", "Raw"]
- },
- {
- "name": "Output",
- "type": "option",
- "value": ["Raw", "Hex"]
- }
- ];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- const key = Utils.convertToByteArray(args[0].string, args[0].option),
- nonceType = args[1].option,
- rounds = parseInt(args[3], 10),
- inputType = args[4],
- outputType = args[5];
-
- if (key.length !== 16 && key.length !== 32) {
- throw new OperationError(`Invalid key length: ${key.length} bytes.
-
-XSalsa20 uses a key of 16 or 32 bytes (128 or 256 bits).`);
- }
-
- let counter, nonce;
- if (nonceType === "Integer") {
- nonce = Utils.intToByteArray(parseInt(args[1].string, 10), 8, "little");
- } else {
- nonce = Utils.convertToByteArray(args[1].string, args[1].option);
- if (!(nonce.length === 24)) {
- throw new OperationError(`Invalid nonce length: ${nonce.length} bytes.
-
-XSalsa20 uses a nonce of 24 bytes (192 bits).`);
- }
- }
- counter = Utils.intToByteArray(args[2], 8, "little");
-
- const xsalsaKey = hsalsa20(key, nonce.slice(0, 16), rounds);
-
- const output = [];
- input = Utils.convertToByteArray(input, inputType);
-
- let counterAsInt = Utils.byteArrayToInt(counter, "little");
- for (let i = 0; i < input.length; i += 64) {
- counter = Utils.intToByteArray(counterAsInt, 8, "little");
- const stream = salsa20Block(xsalsaKey, nonce.slice(16, 24), counter, rounds);
- for (let j = 0; j < 64 && i + j < input.length; j++) {
- output.push(input[i + j] ^ stream[j]);
- }
- counterAsInt++;
- }
-
- if (outputType === "Hex") {
- return toHex(output);
- } else {
- return Utils.arrayBufferToStr(Uint8Array.from(output).buffer);
- }
- }
-
- /**
- * Highlight XSalsa20
- *
- * @param {Object[]} pos
- * @param {number} pos[].start
- * @param {number} pos[].end
- * @param {Object[]} args
- * @returns {Object[]} pos
- */
- highlight(pos, args) {
- const inputType = args[4],
- outputType = args[5];
- if (inputType === "Raw" && outputType === "Raw") {
- return pos;
- }
- }
-
- /**
- * Highlight XSalsa20 in reverse
- *
- * @param {Object[]} pos
- * @param {number} pos[].start
- * @param {number} pos[].end
- * @param {Object[]} args
- * @returns {Object[]} pos
- */
- highlightReverse(pos, args) {
- const inputType = args[4],
- outputType = args[5];
- if (inputType === "Raw" && outputType === "Raw") {
- return pos;
- }
- }
-
-}
-
-export default XSalsa20;
diff --git a/src/core/operations/XXTEADecrypt.mjs b/src/core/operations/XXTEADecrypt.mjs
deleted file mode 100644
index 496e5409..00000000
--- a/src/core/operations/XXTEADecrypt.mjs
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * @author devcydo [devcydo@gmail.com]
- * @author Ma Bingyao [mabingyao@gmail.com]
- * @author n1474335 [n1474335@gmail.com]
- * @copyright Crown Copyright 2024
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import Utils from "../Utils.mjs";
-import OperationError from "../errors/OperationError.mjs";
-import {decrypt} from "../lib/XXTEA.mjs";
-
-/**
- * XXTEA Decrypt operation
- */
-class XXTEADecrypt extends Operation {
-
- /**
- * XXTEADecrypt constructor
- */
- constructor() {
- super();
-
- this.name = "XXTEA Decrypt";
- this.module = "Ciphers";
- this.description = "Corrected Block TEA (often referred to as XXTEA) is a block cipher designed to correct weaknesses in the original Block TEA. XXTEA operates on variable-length blocks that are some arbitrary multiple of 32 bits in size (minimum 64 bits). The number of full cycles depends on the block size, but there are at least six (rising to 32 for small block sizes). The original Block TEA applies the XTEA round function to each word in the block and combines it additively with its leftmost neighbour. Slow diffusion rate of the decryption process was immediately exploited to break the cipher. Corrected Block TEA uses a more involved round function which makes use of both immediate neighbours in processing each word in the block.";
- this.infoURL = "https://wikipedia.org/wiki/XXTEA";
- this.inputType = "ArrayBuffer";
- this.outputType = "ArrayBuffer";
- this.args = [
- {
- "name": "Key",
- "type": "toggleString",
- "value": "",
- "toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
- },
- ];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- const key = new Uint8Array(Utils.convertToByteArray(args[0].string, args[0].option));
- try {
- return decrypt(new Uint8Array(input), key).buffer;
- } catch (err) {
- throw new OperationError("Unable to decrypt using this key");
- }
- }
-
-}
-
-export default XXTEADecrypt;
diff --git a/src/core/operations/XXTEAEncrypt.mjs b/src/core/operations/XXTEAEncrypt.mjs
deleted file mode 100644
index 85379de0..00000000
--- a/src/core/operations/XXTEAEncrypt.mjs
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * @author devcydo [devcydo@gmail.com]
- * @author Ma Bingyao [mabingyao@gmail.com]
- * @author n1474335 [n1474335@gmail.com]
- * @copyright Crown Copyright 2024
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import Utils from "../Utils.mjs";
-import {encrypt} from "../lib/XXTEA.mjs";
-
-/**
- * XXTEA Encrypt operation
- */
-class XXTEAEncrypt extends Operation {
-
- /**
- * XXTEAEncrypt constructor
- */
- constructor() {
- super();
-
- this.name = "XXTEA Encrypt";
- this.module = "Ciphers";
- this.description = "Corrected Block TEA (often referred to as XXTEA) is a block cipher designed to correct weaknesses in the original Block TEA. XXTEA operates on variable-length blocks that are some arbitrary multiple of 32 bits in size (minimum 64 bits). The number of full cycles depends on the block size, but there are at least six (rising to 32 for small block sizes). The original Block TEA applies the XTEA round function to each word in the block and combines it additively with its leftmost neighbour. Slow diffusion rate of the decryption process was immediately exploited to break the cipher. Corrected Block TEA uses a more involved round function which makes use of both immediate neighbours in processing each word in the block.";
- this.infoURL = "https://wikipedia.org/wiki/XXTEA";
- this.inputType = "ArrayBuffer";
- this.outputType = "ArrayBuffer";
- this.args = [
- {
- "name": "Key",
- "type": "toggleString",
- "value": "",
- "toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
- },
- ];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {string}
- */
- run(input, args) {
- const key = new Uint8Array(Utils.convertToByteArray(args[0].string, args[0].option));
- return encrypt(new Uint8Array(input), key).buffer;
- }
-
-}
-
-export default XXTEAEncrypt;
diff --git a/src/core/operations/YAMLToJSON.mjs b/src/core/operations/YAMLToJSON.mjs
deleted file mode 100644
index 5b986575..00000000
--- a/src/core/operations/YAMLToJSON.mjs
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * @author ccarpo [ccarpo@gmx.net]
- * @copyright Crown Copyright 2021
- * @license Apache-2.0
- */
-
-import Operation from "../Operation.mjs";
-import OperationError from "../errors/OperationError.mjs";
-import jsYaml from "js-yaml";
-/**
- * YAML to JSON operation
- */
-class YAMLToJSON extends Operation {
-
- /**
- * YAMLToJSON constructor
- */
- constructor() {
- super();
-
- this.name = "YAML to JSON";
- this.module = "Default";
- this.description = "Convert YAML to JSON";
- this.infoURL = "https://en.wikipedia.org/wiki/YAML";
- this.inputType = "string";
- this.outputType = "JSON";
- this.args = [];
- }
-
- /**
- * @param {string} input
- * @param {Object[]} args
- * @returns {JSON}
- */
- run(input, args) {
- try {
- return jsYaml.load(input);
- } catch (err) {
- throw new OperationError("Unable to parse YAML: " + err);
- }
- }
-
-}
-
-export default YAMLToJSON;
diff --git a/src/core/vendor/DisassembleX86-64.mjs b/src/core/vendor/DisassembleX86-64.mjs
index aeae426b..5f0ac65d 100644
--- a/src/core/vendor/DisassembleX86-64.mjs
+++ b/src/core/vendor/DisassembleX86-64.mjs
@@ -3199,7 +3199,7 @@ const REG = [
REG index 10 Intel MM qword technology MMX vector instructions.
---------------------------------------------------------------------------------------------------------------------------
These can not be used with Vector length adjustment used in vector extensions. The MM register are the ST registers aliased
- to MM register. Instructions that use these registers use the SIMD vector unit registers (MM), these are called the old
+ to MM register. Instructions that use these registers use the the SIMD vector unit registers (MM), these are called the old
MMX vector instructions. When Intel added the SSE instructions to the SIMD math vector unit the new 128 bit XMM registers,
are added into the SIMD unit then they ware made longer in size 256, then 512 across in length, with 1024 (?MM Reserved)
In which the vector length setting was added to control there size though vector setting adjustment codes. Instruction
@@ -3784,7 +3784,7 @@ function GotoPosition( Address )
/*-------------------------------------------------------------------------------------------------------------------------
Finds bit positions to the Size attribute indexes in REG array, and the Pointer Array. For the Size Attribute variations.
---------------------------------------------------------------------------------------------------------------------------
-The SizeAttribute settings is 8 digits big consisting of 1, or 0 to specify the extended size that an operand can be made.
+The SizeAttribute settings is 8 digits big consisting of 1, or 0 to specify the the extended size that an operand can be made.
In which an value of 01100100 is decoded as "0 = 1024, 1 = 512, 1 = 256, 0 = 128, 0 = 64, 1 = 32, 0 = 16, 0 = 8".
In which the largest bit position is 512, and is the 6th number "0 = 7, 1 = 6, 1 = 5, 0 = 4, 0 = 3, 1 = 2, 0 = 1, 0 = 0".
In which 6 is the bit position for 512 as the returned Size . Each size is in order from 0 to 7, thus the size given back
@@ -4054,7 +4054,7 @@ function DecodeImmediate( type, BySize, SizeSetting )
//Sign bit adjust.
- if( V32 >= ( n / 2 ) ) { V32 -= n; }
+ if( V32 >= ( n >> 1 ) ) { V32 -= n; }
//Add position.
diff --git a/src/node/config/scripts/generateNodeIndex.mjs b/src/node/config/scripts/generateNodeIndex.mjs
index 981c9b79..4e16fbeb 100644
--- a/src/node/config/scripts/generateNodeIndex.mjs
+++ b/src/node/config/scripts/generateNodeIndex.mjs
@@ -23,7 +23,7 @@ const dir = path.join(`${process.cwd()}/src/node`);
if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd());
console.log("Error: generateNodeIndex.mjs should be run from the project root");
- console.log("Example> node --experimental-modules src/node/config/scripts/generateNodeIndex.mjs");
+ console.log("Example> node --experimental-modules src/core/config/scripts/generateNodeIndex.mjs");
process.exit(1);
}
diff --git a/src/web/App.mjs b/src/web/App.mjs
old mode 100644
new mode 100755
index 7071854a..cce91b1e
--- a/src/web/App.mjs
+++ b/src/web/App.mjs
@@ -39,14 +39,13 @@ class App {
this.baking = false;
this.autoBake_ = false;
+ this.autoBakePause = false;
this.progress = 0;
this.ingId = 0;
this.appLoaded = false;
this.workerLoaded = false;
this.waitersLoaded = false;
-
- this.snackbars = [];
}
@@ -60,7 +59,6 @@ class App {
this.initialiseSplitter();
this.loadLocalStorage();
- this.manager.options.applyPreferredColorScheme();
this.populateOperationsList();
this.manager.setup();
this.manager.output.saveBombe();
@@ -155,12 +153,12 @@ class App {
* Runs Auto Bake if it is set.
*/
autoBake() {
- if (this.baking) {
- this.manager.worker.cancelBakeForAutoBake();
- this.baking = false;
- }
+ // If autoBakePause is set, we are loading a full recipe (and potentially input), so there is no
+ // need to set the staleness indicator. Just exit and wait until auto bake is called after loading
+ // has completed.
+ if (this.autoBakePause) return false;
- if (this.autoBake_) {
+ if (this.autoBake_ && !this.baking) {
log.debug("Auto-baking");
this.manager.worker.bakeInputs({
nums: [this.manager.tabs.getActiveTab("input")],
@@ -473,6 +471,7 @@ class App {
* @fires Manager#statechange
*/
loadURIParams(params=this.getURIParams()) {
+ this.autoBakePause = true;
this.uriParams = params;
// Read in recipe from URI params
@@ -501,22 +500,22 @@ class App {
// Input Character Encoding
// Must be set before the input is loaded
if (this.uriParams.ienc) {
- this.manager.input.chrEncChange(parseInt(this.uriParams.ienc, 10), true, true);
+ this.manager.input.chrEncChange(parseInt(this.uriParams.ienc, 10));
}
// Output Character Encoding
if (this.uriParams.oenc) {
- this.manager.output.chrEncChange(parseInt(this.uriParams.oenc, 10), true);
+ this.manager.output.chrEncChange(parseInt(this.uriParams.oenc, 10));
}
// Input EOL sequence
if (this.uriParams.ieol) {
- this.manager.input.eolChange(this.uriParams.ieol, true);
+ this.manager.input.eolChange(this.uriParams.ieol);
}
// Output EOL sequence
if (this.uriParams.oeol) {
- this.manager.output.eolChange(this.uriParams.oeol, true);
+ this.manager.output.eolChange(this.uriParams.oeol);
}
// Read in input data from URI params
@@ -537,10 +536,9 @@ class App {
// Read in theme from URI params
if (this.uriParams.theme) {
this.manager.options.changeTheme(Utils.escapeHtml(this.uriParams.theme));
- } else {
- this.manager.options.applyPreferredColorScheme();
}
+ this.autoBakePause = false;
window.dispatchEvent(this.manager.statechange);
}
@@ -574,6 +572,10 @@ class App {
setRecipeConfig(recipeConfig) {
document.getElementById("rec-list").innerHTML = null;
+ // Pause auto-bake while loading but don't modify `this.autoBake_`
+ // otherwise `manualBake` cannot trigger.
+ this.autoBakePause = true;
+
for (let i = 0; i < recipeConfig.length; i++) {
const item = this.manager.recipe.addOperation(recipeConfig[i].op);
@@ -608,6 +610,9 @@ class App {
this.progress = 0;
}
+
+ // Unpause auto bake
+ this.autoBakePause = false;
}
@@ -703,14 +708,14 @@ class App {
log.info("[" + time.toLocaleString() + "] " + str);
if (silent) return;
- this.snackbars.push($.snackbar({
+ this.currentSnackbar = $.snackbar({
content: str,
timeout: timeout,
htmlAllowed: true,
onClose: () => {
- this.snackbars.shift().remove();
+ this.currentSnackbar.remove();
}
- }));
+ });
}
diff --git a/src/web/HTMLCategory.mjs b/src/web/HTMLCategory.mjs
index b61a6740..0414fd71 100755
--- a/src/web/HTMLCategory.mjs
+++ b/src/web/HTMLCategory.mjs
@@ -42,9 +42,6 @@ class HTMLCategory {
let html = `
${this.name}
-
`;
diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs
index 30cfd1d9..d0523aa4 100755
--- a/src/web/HTMLOperation.mjs
+++ b/src/web/HTMLOperation.mjs
@@ -85,7 +85,6 @@ class HTMLOperation {
`;
@@ -158,9 +157,9 @@ function titleFromWikiLink(urlStr) {
pageTitle = "";
switch (urlObj.host) {
- case "forensics.wiki":
+ case "forensicswiki.xyz":
wikiName = "Forensics Wiki";
- pageTitle = Utils.toTitleCase(urlObj.path.replace(/\//g, "").replace(/_/g, " "));
+ pageTitle = urlObj.query.substr(6).replace(/_/g, " "); // Chop off 'title='
break;
case "wikipedia.org":
wikiName = "Wikipedia";
diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs
index 2e87210c..b02a7eee 100755
--- a/src/web/Manager.mjs
+++ b/src/web/Manager.mjs
@@ -139,7 +139,6 @@ class Manager {
document.getElementById("load-delete-button").addEventListener("click", this.controls.loadDeleteClick.bind(this.controls));
document.getElementById("load-name").addEventListener("change", this.controls.loadNameChange.bind(this.controls));
document.getElementById("load-button").addEventListener("click", this.controls.loadButtonClick.bind(this.controls));
- document.getElementById("hide-icon").addEventListener("click", this.controls.hideRecipeArgsClick.bind(this.recipe));
document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls));
this.addMultiEventListeners("#save-texts textarea", "keyup paste", this.controls.saveTextChange, this.controls);
@@ -155,7 +154,6 @@ class Manager {
// Recipe
this.addDynamicListener(".arg:not(select)", "input", this.recipe.ingChange, this.recipe);
this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe);
- this.addDynamicListener(".hide-args-icon", "click", this.recipe.hideArgsClick, this.recipe);
this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe);
this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe);
this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe);
@@ -229,7 +227,6 @@ class Manager {
this.addDynamicListener(".option-item input[type=checkbox]", "change", this.options.switchChange, this.options);
this.addDynamicListener(".option-item input[type=checkbox]#wordWrap", "change", this.options.setWordWrap, this.options);
this.addDynamicListener(".option-item input[type=checkbox]#useMetaKey", "change", this.bindings.updateKeybList, this.bindings);
- this.addDynamicListener(".option-item input[type=checkbox]#showCatCount", "change", this.ops.setCatCount, this.ops);
this.addDynamicListener(".option-item input[type=number]", "keyup", this.options.numberChange, this.options);
this.addDynamicListener(".option-item input[type=number]", "change", this.options.numberChange, this.options);
this.addDynamicListener(".option-item select", "change", this.options.selectChange, this.options);
@@ -271,7 +268,7 @@ class Manager {
* @param {Object} [scope=this] - The object to bind to the callback function
*
* @example
- * // Calls the search function whenever the keyup, paste or search events are triggered on the
+ * // Calls the search function whenever the the keyup, paste or search events are triggered on the
* // search element
* this.addMultiEventListener("search", "keyup paste search", this.search, this);
*/
@@ -292,7 +289,7 @@ class Manager {
* @param {Object} [scope=this] - The object to bind to the callback function
*
* @example
- * // Calls the save function whenever the keyup or paste events are triggered on any element
+ * // Calls the save function whenever the the keyup or paste events are triggered on any element
* // with the .saveable class
* this.addMultiEventListener(".saveable", "keyup paste", this.save, this);
*/
diff --git a/src/web/html/index.html b/src/web/html/index.html
index 38bf7ccc..151fd178 100755
--- a/src/web/html/index.html
+++ b/src/web/html/index.html
@@ -1,10 +1,10 @@
-