diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index 9fd6d68d..8a3aff54 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -19,6 +19,7 @@ jobs:
- name: Install
run: |
+ export DETECT_CHROMEDRIVER_VERSION=true
npm install
npm run setheapsize
diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml
index daa59490..296e60b9 100644
--- a/.github/workflows/pull_requests.yml
+++ b/.github/workflows/pull_requests.yml
@@ -18,6 +18,7 @@ jobs:
- name: Install
run: |
+ export DETECT_CHROMEDRIVER_VERSION=true
npm install
npm run setheapsize
diff --git a/package-lock.json b/package-lock.json
index af5fd6dd..beee3309 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -118,6 +118,7 @@
"chromedriver": "^130.0.0",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
+ "compression-webpack-plugin": "^11.1.0",
"copy-webpack-plugin": "^12.0.2",
"core-js": "^3.37.1",
"css-loader": "7.1.2",
@@ -5344,6 +5345,27 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/compression-webpack-plugin": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-11.1.0.tgz",
+ "integrity": "sha512-zDOQYp10+upzLxW+VRSjEpRRwBXJdsb5lBMlRxx1g8hckIFBpe3DTI0en2w7h+beuq89576RVzfiXrkdPGrHhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "schema-utils": "^4.2.0",
+ "serialize-javascript": "^6.0.2"
+ },
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ }
+ },
"node_modules/compression/node_modules/bytes": {
"version": "3.0.0",
"dev": true,
diff --git a/package.json b/package.json
index 54664eb1..7901c8a4 100644
--- a/package.json
+++ b/package.json
@@ -58,6 +58,7 @@
"chromedriver": "^130.0.0",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
+ "compression-webpack-plugin": "^11.1.0",
"copy-webpack-plugin": "^12.0.2",
"core-js": "^3.37.1",
"css-loader": "7.1.2",
diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index 2345280f..ff00fd36 100644
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -164,6 +164,7 @@
"name": "Public Key",
"ops": [
"Parse X.509 certificate",
+ "Parse X.509 CRL",
"Parse ASN.1 hex string",
"PEM to Hex",
"Hex to PEM",
@@ -234,8 +235,12 @@
"Parse IP range",
"Parse IPv6 address",
"Parse IPv4 header",
+ "Strip IPv4 header",
"Parse TCP",
+ "Strip TCP header",
+ "Parse TLS record",
"Parse UDP",
+ "Strip UDP header",
"Parse SSH Host Key",
"Parse URI",
"URL Encode",
diff --git a/src/core/lib/Protocol.mjs b/src/core/lib/Protocol.mjs
index 57d2374a..dfb8b197 100644
--- a/src/core/lib/Protocol.mjs
+++ b/src/core/lib/Protocol.mjs
@@ -26,6 +26,9 @@ export function objToTable(obj, nested=false) {
`;
for (const key in obj) {
+ if (typeof obj[key] === "function")
+ continue;
+
html += `
${key} | `;
if (typeof obj[key] === "object")
html += `${objToTable(obj[key], true)} | `;
diff --git a/src/core/operations/AddLineNumbers.mjs b/src/core/operations/AddLineNumbers.mjs
index c1c6159a..3eee6544 100644
--- a/src/core/operations/AddLineNumbers.mjs
+++ b/src/core/operations/AddLineNumbers.mjs
@@ -22,7 +22,13 @@ class AddLineNumbers extends Operation {
this.description = "Adds line numbers to the output.";
this.inputType = "string";
this.outputType = "string";
- this.args = [];
+ this.args = [
+ {
+ "name": "Offset",
+ "type": "number",
+ "value": 0
+ }
+ ];
}
/**
@@ -33,10 +39,11 @@ class AddLineNumbers extends Operation {
run(input, args) {
const lines = input.split("\n"),
width = lines.length.toString().length;
+ const offset = args[0] ? parseInt(args[0], 10) : 0;
let output = "";
for (let n = 0; n < lines.length; n++) {
- output += (n+1).toString().padStart(width, " ") + " " + lines[n] + "\n";
+ output += (n+1+offset).toString().padStart(width, " ") + " " + lines[n] + "\n";
}
return output.slice(0, output.length-1);
}
diff --git a/src/core/operations/JWTSign.mjs b/src/core/operations/JWTSign.mjs
index af46908e..66831efa 100644
--- a/src/core/operations/JWTSign.mjs
+++ b/src/core/operations/JWTSign.mjs
@@ -36,6 +36,11 @@ class JWTSign extends Operation {
name: "Signing algorithm",
type: "option",
value: JWT_ALGORITHMS
+ },
+ {
+ name: "Header",
+ type: "text",
+ value: "{}"
}
];
}
@@ -46,11 +51,12 @@ class JWTSign extends Operation {
* @returns {string}
*/
run(input, args) {
- const [key, algorithm] = args;
+ const [key, algorithm, header] = args;
try {
return jwt.sign(input, key, {
- algorithm: algorithm === "None" ? "none" : algorithm
+ algorithm: algorithm === "None" ? "none" : algorithm,
+ header: JSON.parse(header || "{}")
});
} catch (err) {
throw new OperationError(`Error: Have you entered the key correctly? The key should be either the secret for HMAC algorithms or the PEM-encoded private key for RSA and ECDSA.
diff --git a/src/core/operations/JWTVerify.mjs b/src/core/operations/JWTVerify.mjs
index 604edc9c..451b82ab 100644
--- a/src/core/operations/JWTVerify.mjs
+++ b/src/core/operations/JWTVerify.mjs
@@ -22,7 +22,7 @@ class JWTVerify extends Operation {
this.name = "JWT Verify";
this.module = "Crypto";
- this.description = "Verifies that a JSON Web Token is valid and has been signed with the provided secret / private key.
The key should be either the secret for HMAC algorithms or the PEM-encoded private key for RSA and ECDSA.";
+ this.description = "Verifies that a JSON Web Token is valid and has been signed with the provided secret / private key.
The key should be either the secret for HMAC algorithms or the PEM-encoded public key for RSA and ECDSA.";
this.infoURL = "https://wikipedia.org/wiki/JSON_Web_Token";
this.inputType = "string";
this.outputType = "JSON";
diff --git a/src/core/operations/ParseTLSRecord.mjs b/src/core/operations/ParseTLSRecord.mjs
new file mode 100644
index 00000000..57a339a8
--- /dev/null
+++ b/src/core/operations/ParseTLSRecord.mjs
@@ -0,0 +1,884 @@
+/**
+ * @author c65722 []
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import {toHexFast} from "../lib/Hex.mjs";
+import {objToTable} from "../lib/Protocol.mjs";
+import Stream from "../lib/Stream.mjs";
+
+/**
+ * Parse TLS record operation.
+ */
+class ParseTLSRecord extends Operation {
+
+ /**
+ * ParseTLSRecord constructor.
+ */
+ constructor() {
+ super();
+
+ this.name = "Parse TLS record";
+ this.module = "Default";
+ this.description = "Parses one or more TLS records";
+ this.infoURL = "https://wikipedia.org/wiki/Transport_Layer_Security";
+ this.inputType = "ArrayBuffer";
+ this.outputType = "json";
+ this.presentType = "html";
+ this.args = [];
+ this._handshakeParser = new HandshakeParser();
+ this._contentTypes = new Map();
+
+ for (const key in ContentType) {
+ this._contentTypes[ContentType[key]] = key.toString().toLocaleLowerCase();
+ }
+ }
+
+ /**
+ * @param {ArrayBuffer} input - Stream, containing one or more raw TLS Records.
+ * @param {Object[]} args
+ * @returns {Object[]} Array of Object representations of TLS Records contained within input.
+ */
+ run(input, args) {
+ const s = new Stream(new Uint8Array(input));
+
+ const output = [];
+
+ while (s.hasMore()) {
+ const record = this._readRecord(s);
+ if (record) {
+ output.push(record);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads a TLS Record from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw TLS Record.
+ * @returns {Object} Object representation of TLS Record.
+ */
+ _readRecord(input) {
+ const RECORD_HEADER_LEN = 5;
+
+ if (input.position + RECORD_HEADER_LEN > input.length) {
+ input.moveTo(input.length);
+
+ return null;
+ }
+
+ const type = input.readInt(1);
+ const typeString = this._contentTypes[type] ?? type.toString();
+ const version = "0x" + toHexFast(input.getBytes(2));
+ const length = input.readInt(2);
+ const content = input.getBytes(length);
+ const truncated = content.length < length;
+
+ const recordHeader = new RecordHeader(typeString, version, length, truncated);
+
+ if (!content.length) {
+ return {...recordHeader};
+ }
+
+ if (type === ContentType.HANDSHAKE) {
+ return this._handshakeParser.parse(new Stream(content), recordHeader);
+ }
+
+ const record = {...recordHeader};
+ record.value = "0x" + toHexFast(content);
+
+ return record;
+ }
+
+ /**
+ * Displays the parsed TLS Records in a tabular style.
+ *
+ * @param {Object[]} data - Array of Object representations of the TLS Records.
+ * @returns {html} HTML representation of TLS Records contained within data.
+ */
+ present(data) {
+ return data.map(r => objToTable(r)).join("\n\n");
+ }
+}
+
+export default ParseTLSRecord;
+
+/**
+ * Repesents the known values of type field of a TLS Record header.
+ */
+const ContentType = Object.freeze({
+ CHANGE_CIPHER_SPEC: 20,
+ ALERT: 21,
+ HANDSHAKE: 22,
+ APPLICATION_DATA: 23,
+});
+
+/**
+ * Represents a TLS Record header
+ */
+class RecordHeader {
+ /**
+ * RecordHeader cosntructor.
+ *
+ * @param {string} type - String representation of TLS Record type field.
+ * @param {string} version - Hex representation of TLS Record version field.
+ * @param {int} length - Length of TLS Record.
+ * @param {bool} truncated - Is TLS Record truncated.
+ */
+ constructor(type, version, length, truncated) {
+ this.type = type;
+ this.version = version;
+ this.length = length;
+
+ if (truncated) {
+ this.truncated = true;
+ }
+ }
+}
+
+/**
+ * Parses TLS Handshake messages.
+ */
+class HandshakeParser {
+
+ /**
+ * HandshakeParser constructor.
+ */
+ constructor() {
+ this._clientHelloParser = new ClientHelloParser();
+ this._serverHelloParser = new ServerHelloParser();
+ this._newSessionTicketParser = new NewSessionTicketParser();
+ this._certificateParser = new CertificateParser();
+ this._certificateRequestParser = new CertificateRequestParser();
+ this._certificateVerifyParser = new CertificateVerifyParser();
+ this._handshakeTypes = new Map();
+
+ for (const key in HandshakeType) {
+ this._handshakeTypes[HandshakeType[key]] = key.toString().toLowerCase();
+ }
+ }
+
+ /**
+ * Parses a single TLS handshake message.
+ *
+ * @param {Stream} input - Stream, containing a raw Handshake message.
+ * @param {RecordHeader} recordHeader - TLS Record header.
+ * @returns {Object} Object representation of Handshake.
+ */
+ parse(input, recordHeader) {
+ const output = {...recordHeader};
+
+ if (!input.hasMore()) {
+ return output;
+ }
+
+ const handshakeType = input.readInt(1);
+ output.handshakeType = this._handshakeTypes[handshakeType] ?? handshakeType.toString();
+
+ if (input.position + 3 > input.length) {
+ input.moveTo(input.length);
+
+ return output;
+ }
+
+ const handshakeLength = input.readInt(3);
+
+ if (handshakeLength + 4 !== recordHeader.length) {
+ input.moveTo(0);
+
+ output.handshakeType = this._handshakeTypes[HandshakeType.FINISHED];
+ output.handshakeValue = "0x" + toHexFast(input.bytes);
+
+ return output;
+ }
+
+ const content = input.getBytes(handshakeLength);
+ if (!content.length) {
+ return output;
+ }
+
+ switch (handshakeType) {
+ case HandshakeType.CLIENT_HELLO:
+ return {...output, ...this._clientHelloParser.parse(new Stream(content))};
+ case HandshakeType.SERVER_HELLO:
+ return {...output, ...this._serverHelloParser.parse(new Stream(content))};
+ case HandshakeType.NEW_SESSION_TICKET:
+ return {...output, ...this._newSessionTicketParser.parse(new Stream(content))};
+ case HandshakeType.CERTIFICATE:
+ return {...output, ...this._certificateParser.parse(new Stream(content))};
+ case HandshakeType.CERTIFICATE_REQUEST:
+ return {...output, ...this._certificateRequestParser.parse(new Stream(content))};
+ case HandshakeType.CERTIFICATE_VERIFY:
+ return {...output, ...this._certificateVerifyParser.parse(new Stream(content))};
+ default:
+ output.handshakeValue = "0x" + toHexFast(content);
+ }
+
+ return output;
+ }
+}
+
+/**
+ * Represents the known values of the msg_type field of a TLS Handshake message.
+ */
+const HandshakeType = Object.freeze({
+ HELLO_REQUEST: 0,
+ CLIENT_HELLO: 1,
+ SERVER_HELLO: 2,
+ NEW_SESSION_TICKET: 4,
+ CERTIFICATE: 11,
+ SERVER_KEY_EXCHANGE: 12,
+ CERTIFICATE_REQUEST: 13,
+ SERVER_HELLO_DONE: 14,
+ CERTIFICATE_VERIFY: 15,
+ CLIENT_KEY_EXCHANGE: 16,
+ FINISHED: 20,
+});
+
+/**
+ * Parses TLS Handshake ClientHello messages.
+ */
+class ClientHelloParser {
+
+ /**
+ * ClientHelloParser constructor.
+ */
+ constructor() {
+ this._extensionsParser = new ExtensionsParser();
+ }
+
+ /**
+ * Parses a single TLS Handshake ClientHello message.
+ *
+ * @param {Stream} input - Stream, containing a raw ClientHello message.
+ * @returns {Object} Object representation of ClientHello.
+ */
+ parse(input) {
+ const output = {};
+
+ output.clientVersion = this._readClientVersion(input);
+ output.random = this._readRandom(input);
+
+ const sessionID = this._readSessionID(input);
+ if (sessionID) {
+ output.sessionID = sessionID;
+ }
+
+ output.cipherSuites = this._readCipherSuites(input);
+ output.compressionMethods = this._readCompressionMethods(input);
+ output.extensions = this._readExtensions(input);
+
+ return output;
+ }
+
+ /**
+ * Reads the client_version field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ClientHello message, with position before client_version field.
+ * @returns {string} Hex representation of client_version.
+ */
+ _readClientVersion(input) {
+ return readBytesAsHex(input, 2);
+ }
+
+ /**
+ * Reads the random field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ClientHello message, with position before random field.
+ * @returns {string} Hex representation of random.
+ */
+ _readRandom(input) {
+ return readBytesAsHex(input, 32);
+ }
+
+ /**
+ * Reads the session_id field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ClientHello message, with position before session_id length field.
+ * @returns {string} Hex representation of session_id, or empty string if session_id not present.
+ */
+ _readSessionID(input) {
+ return readSizePrefixedBytesAsHex(input, 1);
+ }
+
+ /**
+ * Reads the cipher_suites field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ClientHello message, with position before cipher_suites length field.
+ * @returns {Object} Object represention of cipher_suites field.
+ */
+ _readCipherSuites(input) {
+ const output = {};
+
+ output.length = input.readInt(2);
+ if (!output.length) {
+ return {};
+ }
+
+ const cipherSuites = new Stream(input.getBytes(output.length));
+ if (cipherSuites.length < output.length) {
+ output.truncated = true;
+ }
+
+ output.values = [];
+
+ while (cipherSuites.hasMore()) {
+ const cipherSuite = readBytesAsHex(cipherSuites, 2);
+ if (cipherSuite) {
+ output.values.push(cipherSuite);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads the compression_methods field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ClientHello message, with position before compression_methods length field.
+ * @returns {Object} Object representation of compression_methods field.
+ */
+ _readCompressionMethods(input) {
+ const output = {};
+
+ output.length = input.readInt(1);
+ if (!output.length) {
+ return {};
+ }
+
+ const compressionMethods = new Stream(input.getBytes(output.length));
+ if (compressionMethods.length < output.length) {
+ output.truncated = true;
+ }
+
+ output.values = [];
+
+ while (compressionMethods.hasMore()) {
+ const compressionMethod = readBytesAsHex(compressionMethods, 1);
+ if (compressionMethod) {
+ output.values.push(compressionMethod);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads the extensions field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ClientHello message, with position before extensions length field.
+ * @returns {Object} Object representations of extensions field.
+ */
+ _readExtensions(input) {
+ const output = {};
+
+ output.length = input.readInt(2);
+ if (!output.length) {
+ return {};
+ }
+
+ const extensions = new Stream(input.getBytes(output.length));
+ if (extensions.length < output.length) {
+ output.truncated = true;
+ }
+
+ output.values = this._extensionsParser.parse(extensions);
+
+ return output;
+ }
+}
+
+/**
+ * Parses TLS Handshake ServeHello messages.
+ */
+class ServerHelloParser {
+
+ /**
+ * ServerHelloParser constructor.
+ */
+ constructor() {
+ this._extensionsParser = new ExtensionsParser();
+ }
+
+ /**
+ * Parses a single TLS Handshake ServerHello message.
+ *
+ * @param {Stream} input - Stream, containing a raw ServerHello message.
+ * @return {Object} Object representation of ServerHello.
+ */
+ parse(input) {
+ const output = {};
+
+ output.serverVersion = this._readServerVersion(input);
+ output.random = this._readRandom(input);
+
+ const sessionID = this._readSessionID(input);
+ if (sessionID) {
+ output.sessionID = sessionID;
+ }
+
+ output.cipherSuite = this._readCipherSuite(input);
+ output.compressionMethod = this._readCompressionMethod(input);
+ output.extensions = this._readExtensions(input);
+
+ return output;
+ }
+
+ /**
+ * Reads the server_version field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ServerHello message, with position before server_version field.
+ * @returns {string} Hex representation of server_version.
+ */
+ _readServerVersion(input) {
+ return readBytesAsHex(input, 2);
+ }
+
+ /**
+ * Reads the random field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ServerHello message, with position before random field.
+ * @returns {string} Hex representation of random.
+ */
+ _readRandom(input) {
+ return readBytesAsHex(input, 32);
+ }
+
+ /**
+ * Reads the session_id field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ServertHello message, with position before session_id length field.
+ * @returns {string} Hex representation of session_id, or empty string if session_id not present.
+ */
+ _readSessionID(input) {
+ return readSizePrefixedBytesAsHex(input, 1);
+ }
+
+ /**
+ * Reads the cipher_suite field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ServerHello message, with position before cipher_suite field.
+ * @returns {string} Hex represention of cipher_suite.
+ */
+ _readCipherSuite(input) {
+ return readBytesAsHex(input, 2);
+ }
+
+ /**
+ * Reads the compression_method field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ServerHello message, with position before compression_method field.
+ * @returns {string} Hex represention of compression_method.
+ */
+ _readCompressionMethod(input) {
+ return readBytesAsHex(input, 1);
+ }
+
+ /**
+ * Reads the extensions field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw ServerHello message, with position before extensions length field.
+ * @returns {Object} Object representation of extensions field.
+ */
+ _readExtensions(input) {
+ const output = {};
+
+ output.length = input.readInt(2);
+ if (!output.length) {
+ return {};
+ }
+
+ const extensions = new Stream(input.getBytes(output.length));
+ if (extensions.length < output.length) {
+ output.truncated = true;
+ }
+
+ output.values = this._extensionsParser.parse(extensions);
+
+ return output;
+ }
+}
+
+/**
+ * Parses TLS Handshake Hello Extensions.
+ */
+class ExtensionsParser {
+
+ /**
+ * Parses a stream of TLS Handshake Hello Extensions.
+ *
+ * @param {Stream} input - Stream, containing multiple raw Extensions, with position before first extension length field.
+ * @returns {Object[]} Array of Object representations of Extensions contained within input.
+ */
+ parse(input) {
+ const output = [];
+
+ while (input.hasMore()) {
+ const extension = this._readExtension(input);
+ if (extension) {
+ output.push(extension);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads a single Extension from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a list of Extensions, with position before the length field of the next Extension.
+ * @returns {Object} Object representation of Extension.
+ */
+ _readExtension(input) {
+ const output = {};
+
+ if (input.position + 4 > input.length) {
+ input.moveTo(input.length);
+ return null;
+ }
+
+ output.type = "0x" + toHexFast(input.getBytes(2));
+ output.length = input.readInt(2);
+ if (!output.length) {
+ return output;
+ }
+
+ const value = input.getBytes(output.length);
+ if (!value || value.length !== output.length) {
+ output.truncated = true;
+ }
+
+ if (value && value.length) {
+ output.value = "0x" + toHexFast(value);
+ }
+
+ return output;
+ }
+}
+
+/**
+ * Parses TLS Handshake NewSessionTicket messages.
+ */
+class NewSessionTicketParser {
+
+ /**
+ * Parses a single TLS Handshake NewSessionTicket message.
+ *
+ * @param {Stream} input - Stream, containing a raw NewSessionTicket message.
+ * @returns {Object} Object representation of NewSessionTicket.
+ */
+ parse(input) {
+ return {
+ ticketLifetimeHint: this._readTicketLifetimeHint(input),
+ ticket: this._readTicket(input),
+ };
+ }
+
+ /**
+ * Reads the ticket_lifetime_hint field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw NewSessionTicket message, with position before ticket_lifetime_hint field.
+ * @returns {string} Lifetime hint, in seconds.
+ */
+ _readTicketLifetimeHint(input) {
+ if (input.position + 4 > input.length) {
+ input.moveTo(input.length);
+ return "";
+ }
+
+ return input.readInt(4) + "s";
+ }
+
+ /**
+ * Reads the ticket field fromt the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw NewSessionTicket message, with position before ticket length field.
+ * @returns {string} Hex representation of ticket.
+ */
+ _readTicket(input) {
+ return readSizePrefixedBytesAsHex(input, 2);
+ }
+}
+
+/**
+ * Parses TLS Handshake Certificate messages.
+ */
+class CertificateParser {
+
+ /**
+ * Parses a single TLS Handshake Certificate message.
+ *
+ * @param {Stream} input - Stream, containing a raw Certificate message.
+ * @returns {Object} Object representation of Certificate.
+ */
+ parse(input) {
+ const output = {};
+
+ output.certificateList = this._readCertificateList(input);
+
+ return output;
+ }
+
+ /**
+ * Reads the certificate_list field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw Certificate message, with position before certificate_list length field.
+ * @returns {string[]} Array of strings, each containing a hex representation of a value within the certificate_list field.
+ */
+ _readCertificateList(input) {
+ const output = {};
+
+ if (input.position + 3 > input.length) {
+ input.moveTo(input.length);
+ return output;
+ }
+
+ output.length = input.readInt(3);
+ if (!output.length) {
+ return output;
+ }
+
+ const certificates = new Stream(input.getBytes(output.length));
+ if (certificates.length < output.length) {
+ output.truncated = true;
+ }
+
+ output.values = [];
+
+ while (certificates.hasMore()) {
+ const certificate = this._readCertificate(certificates);
+ if (certificate) {
+ output.values.push(certificate);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads a single certificate from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a list of certificicates, with position before the length field of the next certificate.
+ * @returns {string} Hex representation of certificate.
+ */
+ _readCertificate(input) {
+ return readSizePrefixedBytesAsHex(input, 3);
+ }
+}
+
+/**
+ * Parses TLS Handshake CertificateRequest messages.
+ */
+class CertificateRequestParser {
+
+ /**
+ * Parses a single TLS Handshake CertificateRequest message.
+ *
+ * @param {Stream} input - Stream, containing a raw CertificateRequest message.
+ * @return {Object} Object representation of CertificateRequest.
+ */
+ parse(input) {
+ const output = {};
+
+ output.certificateTypes = this._readCertificateTypes(input);
+ output.supportedSignatureAlgorithms = this._readSupportedSignatureAlgorithms(input);
+
+ const certificateAuthorities = this._readCertificateAuthorities(input);
+ if (certificateAuthorities.length) {
+ output.certificateAuthorities = certificateAuthorities;
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads the certificate_types field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw CertificateRequest message, with position before certificate_types length field.
+ * @return {string[]} Array of strings, each containing a hex representation of a value within the certificate_types field.
+ */
+ _readCertificateTypes(input) {
+ const output = {};
+
+ output.length = input.readInt(1);
+ if (!output.length) {
+ return {};
+ }
+
+ const certificateTypes = new Stream(input.getBytes(output.length));
+ if (certificateTypes.length < output.length) {
+ output.truncated = true;
+ }
+
+ output.values = [];
+
+ while (certificateTypes.hasMore()) {
+ const certificateType = readBytesAsHex(certificateTypes, 1);
+ if (certificateType) {
+ output.values.push(certificateType);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads the supported_signature_algorithms field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw CertificateRequest message, with position before supported_signature_algorithms length field.
+ * @returns {string[]} Array of strings, each containing a hex representation of a value within the supported_signature_algorithms field.
+ */
+ _readSupportedSignatureAlgorithms(input) {
+ const output = {};
+
+ output.length = input.readInt(2);
+ if (!output.length) {
+ return {};
+ }
+
+ const signatureAlgorithms = new Stream(input.getBytes(output.length));
+ if (signatureAlgorithms.length < output.length) {
+ output.truncated = true;
+ }
+
+ output.values = [];
+
+ while (signatureAlgorithms.hasMore()) {
+ const signatureAlgorithm = readBytesAsHex(signatureAlgorithms, 2);
+ if (signatureAlgorithm) {
+ output.values.push(signatureAlgorithm);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads the certificate_authorities field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw CertificateRequest message, with position before certificate_authorities length field.
+ * @returns {string[]} Array of strings, each containing a hex representation of a value within the certificate_authorities field.
+ */
+ _readCertificateAuthorities(input) {
+ const output = {};
+
+ output.length = input.readInt(2);
+ if (!output.length) {
+ return {};
+ }
+
+ const certificateAuthorities = new Stream(input.getBytes(output.length));
+ if (certificateAuthorities.length < output.length) {
+ output.truncated = true;
+ }
+
+ output.values = [];
+
+ while (certificateAuthorities.hasMore()) {
+ const certificateAuthority = this._readCertificateAuthority(certificateAuthorities);
+ if (certificateAuthority) {
+ output.values.push(certificateAuthority);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Reads a single certificate authority from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a list of raw certificate authorities, with position before the length field of the next certificate authority.
+ * @returns {string} Hex representation of certificate authority.
+ */
+ _readCertificateAuthority(input) {
+ return readSizePrefixedBytesAsHex(input, 2);
+ }
+}
+
+/**
+ * Parses TLS Handshake CertificateVerify messages.
+ */
+class CertificateVerifyParser {
+
+ /**
+ * Parses a single CertificateVerify Message.
+ *
+ * @param {Stream} input - Stream, containing a raw CertificateVerify message.
+ * @returns {Object} Object representation of CertificateVerify.
+ */
+ parse(input) {
+ return {
+ algorithmHash: this._readAlgorithmHash(input),
+ algorithmSignature: this._readAlgorithmSignature(input),
+ signature: this._readSignature(input),
+ };
+ }
+
+ /**
+ * Reads the algorithm.hash field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw CertificateVerify message, with position before algorithm.hash field.
+ * @return {string} Hex representation of hash algorithm.
+ */
+ _readAlgorithmHash(input) {
+ return readBytesAsHex(input, 1);
+ }
+
+ /**
+ * Reads the algorithm.signature field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw CertificateVerify message, with position before algorithm.signature field.
+ * @return {string} Hex representation of signature algorithm.
+ */
+ _readAlgorithmSignature(input) {
+ return readBytesAsHex(input, 1);
+ }
+
+ /**
+ * Reads the signature field from the following bytes in the provided Stream.
+ *
+ * @param {Stream} input - Stream, containing a raw CertificateVerify message, with position before signature field.
+ * @return {string} Hex representation of signature.
+ */
+ _readSignature(input) {
+ return readSizePrefixedBytesAsHex(input, 2);
+ }
+}
+
+/**
+ * Read the following size prefixed bytes from the provided Stream, and reuturn as a hex string.
+ *
+ * @param {Stream} input - Stream to read from.
+ * @param {int} sizePrefixLength - Length of the size prefix field.
+ * @returns {string} Hex representation of bytes read from Stream, empty string is returned if
+ * field cannot be read in full.
+ */
+function readSizePrefixedBytesAsHex(input, sizePrefixLength) {
+ const length = input.readInt(sizePrefixLength);
+ if (!length) {
+ return "";
+ }
+
+ return readBytesAsHex(input, length);
+}
+
+/**
+ * Read n bytes from the provided Stream, and return as a hex string.
+ *
+ * @param {Stream} input - Stream to read from.
+ * @param {int} n - Number of bytes to read.
+ * @returns {string} Hex representation of bytes read from Stream, or empty string if field cannot
+ * be read in full.
+ */
+function readBytesAsHex(input, n) {
+ const bytes = input.getBytes(n);
+ if (!bytes || bytes.length !== n) {
+ return "";
+ }
+
+ return "0x" + toHexFast(bytes);
+}
diff --git a/src/core/operations/ParseX509CRL.mjs b/src/core/operations/ParseX509CRL.mjs
new file mode 100644
index 00000000..f498375d
--- /dev/null
+++ b/src/core/operations/ParseX509CRL.mjs
@@ -0,0 +1,391 @@
+/**
+ * @author robinsandhu
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import r from "jsrsasign";
+import Operation from "../Operation.mjs";
+import { fromBase64 } from "../lib/Base64.mjs";
+import { toHex } from "../lib/Hex.mjs";
+import { formatDnObj } from "../lib/PublicKey.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import Utils from "../Utils.mjs";
+
+/**
+ * Parse X.509 CRL operation
+ */
+class ParseX509CRL extends Operation {
+
+ /**
+ * ParseX509CRL constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Parse X.509 CRL";
+ this.module = "PublicKey";
+ this.description = "Parse Certificate Revocation List (CRL)";
+ this.infoURL = "https://wikipedia.org/wiki/Certificate_revocation_list";
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [
+ {
+ "name": "Input format",
+ "type": "option",
+ "value": ["PEM", "DER Hex", "Base64", "Raw"]
+ }
+ ];
+ this.checks = [
+ {
+ "pattern": "^-+BEGIN X509 CRL-+\\r?\\n[\\da-z+/\\n\\r]+-+END X509 CRL-+\\r?\\n?$",
+ "flags": "i",
+ "args": ["PEM"]
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string} Human-readable description of a Certificate Revocation List (CRL).
+ */
+ run(input, args) {
+ if (!input.length) {
+ return "No input";
+ }
+
+ const inputFormat = args[0];
+
+ let undefinedInputFormat = false;
+ try {
+ switch (inputFormat) {
+ case "DER Hex":
+ input = input.replace(/\s/g, "").toLowerCase();
+ break;
+ case "PEM":
+ break;
+ case "Base64":
+ input = toHex(fromBase64(input, null, "byteArray"), "");
+ break;
+ case "Raw":
+ input = toHex(Utils.strToArrayBuffer(input), "");
+ break;
+ default:
+ undefinedInputFormat = true;
+ }
+ } catch (e) {
+ throw "Certificate load error (non-certificate input?)";
+ }
+ if (undefinedInputFormat) throw "Undefined input format";
+
+ const crl = new r.X509CRL(input);
+
+ let out = `Certificate Revocation List (CRL):
+ Version: ${crl.getVersion() === null ? "1 (0x0)" : "2 (0x1)"}
+ Signature Algorithm: ${crl.getSignatureAlgorithmField()}
+ Issuer:\n${formatDnObj(crl.getIssuer(), 8)}
+ Last Update: ${generalizedDateTimeToUTC(crl.getThisUpdate())}
+ Next Update: ${generalizedDateTimeToUTC(crl.getNextUpdate())}\n`;
+
+ if (crl.getParam().ext !== undefined) {
+ out += `\tCRL extensions:\n${formatCRLExtensions(crl.getParam().ext, 8)}\n`;
+ }
+
+ out += `Revoked Certificates:\n${formatRevokedCertificates(crl.getRevCertArray(), 4)}
+Signature Value:\n${formatCRLSignature(crl.getSignatureValueHex(), 8)}`;
+
+ return out;
+ }
+}
+
+/**
+ * Generalized date time string to UTC.
+ * @param {string} datetime
+ * @returns UTC datetime string.
+ */
+function generalizedDateTimeToUTC(datetime) {
+ // Ensure the string is in the correct format
+ if (!/^\d{12,14}Z$/.test(datetime)) {
+ throw new OperationError(`failed to format datetime string ${datetime}`);
+ }
+
+ // Extract components
+ let centuary = "20";
+ if (datetime.length === 15) {
+ centuary = datetime.substring(0, 2);
+ datetime = datetime.slice(2);
+ }
+ const year = centuary + datetime.substring(0, 2);
+ const month = datetime.substring(2, 4);
+ const day = datetime.substring(4, 6);
+ const hour = datetime.substring(6, 8);
+ const minute = datetime.substring(8, 10);
+ const second = datetime.substring(10, 12);
+
+ // Construct ISO 8601 format string
+ const isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}Z`;
+
+ // Parse using standard Date object
+ const isoDateTime = new Date(isoString);
+
+ return isoDateTime.toUTCString();
+}
+
+/**
+ * Format CRL extensions.
+ * @param {r.ExtParam[] | undefined} extensions
+ * @param {Number} indent
+ * @returns Formatted string detailing CRL extensions.
+ */
+function formatCRLExtensions(extensions, indent) {
+ if (Array.isArray(extensions) === false || extensions.length === 0) {
+ return indentString(`No CRL extensions.`, indent);
+ }
+
+ let out = ``;
+
+ extensions.sort((a, b) => {
+ if (!Object.hasOwn(a, "extname") || !Object.hasOwn(b, "extname")) {
+ return 0;
+ }
+ if (a.extname < b.extname) {
+ return -1;
+ } else if (a.extname === b.extname) {
+ return 0;
+ } else {
+ return 1;
+ }
+ });
+
+ extensions.forEach((ext) => {
+ if (!Object.hasOwn(ext, "extname")) {
+ throw new OperationError(`CRL entry extension object missing 'extname' key: ${ext}`);
+ }
+ switch (ext.extname) {
+ case "authorityKeyIdentifier":
+ out += `X509v3 Authority Key Identifier:\n`;
+ if (Object.hasOwn(ext, "kid")) {
+ out += `\tkeyid:${colonDelimitedHexFormatString(ext.kid.hex.toUpperCase())}\n`;
+ }
+ if (Object.hasOwn(ext, "issuer")) {
+ out += `\tDirName:${ext.issuer.str}\n`;
+ }
+ if (Object.hasOwn(ext, "sn")) {
+ out += `\tserial:${colonDelimitedHexFormatString(ext.sn.hex.toUpperCase())}\n`;
+ }
+ break;
+ case "cRLDistributionPoints":
+ out += `X509v3 CRL Distribution Points:\n`;
+ ext.array.forEach((distPoint) => {
+ const fullName = `Full Name:\n${formatGeneralNames(distPoint.dpname.full, 4)}`;
+ out += indentString(fullName, 4) + "\n";
+ });
+ break;
+ case "cRLNumber":
+ if (!Object.hasOwn(ext, "num")) {
+ throw new OperationError(`'cRLNumber' CRL entry extension missing 'num' key: ${ext}`);
+ }
+ out += `X509v3 CRL Number:\n\t${ext.num.hex.toUpperCase()}\n`;
+ break;
+ case "issuerAltName":
+ out += `X509v3 Issuer Alternative Name:\n${formatGeneralNames(ext.array, 4)}\n`;
+ break;
+ default:
+ out += `${ext.extname}:\n`;
+ out += `\tUnsupported CRL extension. Try openssl CLI.\n`;
+ break;
+ }
+ });
+
+ return indentString(chop(out), indent);
+}
+
+/**
+ * Format general names array.
+ * @param {Object[]} names
+ * @returns Multi-line formatted string describing all supported general name types.
+ */
+function formatGeneralNames(names, indent) {
+ let out = ``;
+
+ names.forEach((name) => {
+ const key = Object.keys(name)[0];
+
+ switch (key) {
+ case "ip":
+ out += `IP:${name.ip}\n`;
+ break;
+ case "dns":
+ out += `DNS:${name.dns}\n`;
+ break;
+ case "uri":
+ out += `URI:${name.uri}\n`;
+ break;
+ case "rfc822":
+ out += `EMAIL:${name.rfc822}\n`;
+ break;
+ case "dn":
+ out += `DIR:${name.dn.str}\n`;
+ break;
+ case "other":
+ out += `OtherName:${name.other.oid}::${Object.values(name.other.value)[0].str}\n`;
+ break;
+ default:
+ out += `${key}: unsupported general name type`;
+ break;
+ }
+ });
+
+ return indentString(chop(out), indent);
+}
+
+/**
+ * Colon-delimited hex formatted output.
+ * @param {string} hexString Hex String
+ * @returns String representing input hex string with colon delimiter.
+ */
+function colonDelimitedHexFormatString(hexString) {
+ if (hexString.length % 2 !== 0) {
+ hexString = "0" + hexString;
+ }
+
+ return chop(hexString.replace(/(..)/g, "$&:"));
+}
+
+/**
+ * Format revoked certificates array
+ * @param {r.RevokedCertificate[] | null} revokedCertificates
+ * @param {Number} indent
+ * @returns Multi-line formatted string output of revoked certificates array
+ */
+function formatRevokedCertificates(revokedCertificates, indent) {
+ if (Array.isArray(revokedCertificates) === false || revokedCertificates.length === 0) {
+ return indentString("No Revoked Certificates.", indent);
+ }
+
+ let out=``;
+
+ revokedCertificates.forEach((revCert) => {
+ if (!Object.hasOwn(revCert, "sn") || !Object.hasOwn(revCert, "date")) {
+ throw new OperationError("invalid revoked certificate object, missing either serial number or date");
+ }
+
+ out += `Serial Number: ${revCert.sn.hex.toUpperCase()}
+ Revocation Date: ${generalizedDateTimeToUTC(revCert.date)}\n`;
+ if (Object.hasOwn(revCert, "ext") && Array.isArray(revCert.ext) && revCert.ext.length !== 0) {
+ out += `\tCRL entry extensions:\n${indentString(formatCRLEntryExtensions(revCert.ext), 2*indent)}\n`;
+ }
+ });
+
+ return indentString(chop(out), indent);
+}
+
+/**
+ * Format CRL entry extensions.
+ * @param {Object[]} exts
+ * @returns Formatted multi-line string describing CRL entry extensions.
+ */
+function formatCRLEntryExtensions(exts) {
+ let out = ``;
+
+ const crlReasonCodeToReasonMessage = {
+ 0: "Unspecified",
+ 1: "Key Compromise",
+ 2: "CA Compromise",
+ 3: "Affiliation Changed",
+ 4: "Superseded",
+ 5: "Cessation Of Operation",
+ 6: "Certificate Hold",
+ 8: "Remove From CRL",
+ 9: "Privilege Withdrawn",
+ 10: "AA Compromise",
+ };
+
+ const holdInstructionOIDToName = {
+ "1.2.840.10040.2.1": "Hold Instruction None",
+ "1.2.840.10040.2.2": "Hold Instruction Call Issuer",
+ "1.2.840.10040.2.3": "Hold Instruction Reject",
+ };
+
+ exts.forEach((ext) => {
+ if (!Object.hasOwn(ext, "extname")) {
+ throw new OperationError(`CRL entry extension object missing 'extname' key: ${ext}`);
+ }
+ switch (ext.extname) {
+ case "cRLReason":
+ if (!Object.hasOwn(ext, "code")) {
+ throw new OperationError(`'cRLReason' CRL entry extension missing 'code' key: ${ext}`);
+ }
+ out += `X509v3 CRL Reason Code:
+ ${Object.hasOwn(crlReasonCodeToReasonMessage, ext.code) ? crlReasonCodeToReasonMessage[ext.code] : `invalid reason code: ${ext.code}`}\n`;
+ break;
+ case "2.5.29.23": // Hold instruction
+ out += `Hold Instruction Code:\n\t${Object.hasOwn(holdInstructionOIDToName, ext.extn.oid) ? holdInstructionOIDToName[ext.extn.oid] : `${ext.extn.oid}: unknown hold instruction OID`}\n`;
+ break;
+ case "2.5.29.24": // Invalidity Date
+ out += `Invalidity Date:\n\t${generalizedDateTimeToUTC(ext.extn.gentime.str)}\n`;
+ break;
+ default:
+ out += `${ext.extname}:\n`;
+ out += `\tUnsupported CRL entry extension. Try openssl CLI.\n`;
+ break;
+ }
+ });
+
+ return chop(out);
+}
+
+/**
+ * Format CRL signature.
+ * @param {String} sigHex
+ * @param {Number} indent
+ * @returns String representing hex signature value formatted on multiple lines.
+ */
+function formatCRLSignature(sigHex, indent) {
+ if (sigHex.length % 2 !== 0) {
+ sigHex = "0" + sigHex;
+ }
+
+ return indentString(formatMultiLine(chop(sigHex.replace(/(..)/g, "$&:"))), indent);
+}
+
+/**
+ * Format string onto multiple lines.
+ * @param {string} longStr
+ * @returns String as a multi-line string.
+ */
+function formatMultiLine(longStr) {
+ const lines = [];
+
+ for (let remain = longStr ; remain !== "" ; remain = remain.substring(54)) {
+ lines.push(remain.substring(0, 54));
+ }
+
+ return lines.join("\n");
+}
+
+/**
+ * Indent a multi-line string by n spaces.
+ * @param {string} input String
+ * @param {number} spaces How many leading spaces
+ * @returns Indented string.
+ */
+function indentString(input, spaces) {
+ const indent = " ".repeat(spaces);
+ return input.replace(/^/gm, indent);
+}
+
+/**
+ * Remove last character from a string.
+ * @param {string} s String
+ * @returns Chopped string.
+ */
+function chop(s) {
+ if (s.length < 1) {
+ return s;
+ }
+ return s.substring(0, s.length - 1);
+}
+
+export default ParseX509CRL;
diff --git a/src/core/operations/StripIPv4Header.mjs b/src/core/operations/StripIPv4Header.mjs
new file mode 100644
index 00000000..4b6ef1af
--- /dev/null
+++ b/src/core/operations/StripIPv4Header.mjs
@@ -0,0 +1,57 @@
+/**
+ * @author c65722 []
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import Stream from "../lib/Stream.mjs";
+
+/**
+ * Strip IPv4 header operation
+ */
+class StripIPv4Header extends Operation {
+
+ /**
+ * StripIPv4Header constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Strip IPv4 header";
+ this.module = "Default";
+ this.description = "Strips the IPv4 header from an IPv4 packet, outputting the payload.";
+ this.infoURL = "https://wikipedia.org/wiki/IPv4";
+ this.inputType = "ArrayBuffer";
+ this.outputType = "ArrayBuffer";
+ this.args = [];
+ }
+
+ /**
+ * @param {ArrayBuffer} input
+ * @param {Object[]} args
+ * @returns {ArrayBuffer}
+ */
+ run(input, args) {
+ const MIN_HEADER_LEN = 20;
+
+ const s = new Stream(new Uint8Array(input));
+ if (s.length < MIN_HEADER_LEN) {
+ throw new OperationError("Input length is less than minimum IPv4 header length");
+ }
+
+ const ihl = s.readInt(1) & 0x0f;
+ const dataOffsetBytes = ihl * 4;
+ if (s.length < dataOffsetBytes) {
+ throw new OperationError("Input length is less than IHL");
+ }
+
+ s.moveTo(dataOffsetBytes);
+
+ return s.getBytes().buffer;
+ }
+
+}
+
+export default StripIPv4Header;
diff --git a/src/core/operations/StripTCPHeader.mjs b/src/core/operations/StripTCPHeader.mjs
new file mode 100644
index 00000000..747744b2
--- /dev/null
+++ b/src/core/operations/StripTCPHeader.mjs
@@ -0,0 +1,60 @@
+/**
+ * @author c65722 []
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import Stream from "../lib/Stream.mjs";
+
+/**
+ * Strip TCP header operation
+ */
+class StripTCPHeader extends Operation {
+
+ /**
+ * StripTCPHeader constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Strip TCP header";
+ this.module = "Default";
+ this.description = "Strips the TCP header from a TCP segment, outputting the payload.";
+ this.infoURL = "https://wikipedia.org/wiki/Transmission_Control_Protocol";
+ this.inputType = "ArrayBuffer";
+ this.outputType = "ArrayBuffer";
+ this.args = [];
+ }
+
+ /**
+ * @param {ArrayBuffer} input
+ * @param {Object[]} args
+ * @returns {ArrayBuffer}
+ */
+ run(input, args) {
+ const MIN_HEADER_LEN = 20;
+ const DATA_OFFSET_OFFSET = 12;
+ const DATA_OFFSET_LEN_BITS = 4;
+
+ const s = new Stream(new Uint8Array(input));
+ if (s.length < MIN_HEADER_LEN) {
+ throw new OperationError("Need at least 20 bytes for a TCP Header");
+ }
+
+ s.moveTo(DATA_OFFSET_OFFSET);
+ const dataOffsetWords = s.readBits(DATA_OFFSET_LEN_BITS);
+ const dataOffsetBytes = dataOffsetWords * 4;
+ if (s.length < dataOffsetBytes) {
+ throw new OperationError("Input length is less than data offset");
+ }
+
+ s.moveTo(dataOffsetBytes);
+
+ return s.getBytes().buffer;
+ }
+
+}
+
+export default StripTCPHeader;
diff --git a/src/core/operations/StripUDPHeader.mjs b/src/core/operations/StripUDPHeader.mjs
new file mode 100644
index 00000000..0847c58f
--- /dev/null
+++ b/src/core/operations/StripUDPHeader.mjs
@@ -0,0 +1,51 @@
+/**
+ * @author c65722 []
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import Stream from "../lib/Stream.mjs";
+import OperationError from "../errors/OperationError.mjs";
+
+/**
+ * Strip UDP header operation
+ */
+class StripUDPHeader extends Operation {
+
+ /**
+ * StripUDPHeader constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Strip UDP header";
+ this.module = "Default";
+ this.description = "Strips the UDP header from a UDP datagram, outputting the payload.";
+ this.infoURL = "https://wikipedia.org/wiki/User_Datagram_Protocol";
+ this.inputType = "ArrayBuffer";
+ this.outputType = "ArrayBuffer";
+ this.args = [];
+ }
+
+ /**
+ * @param {ArrayBuffer} input
+ * @param {Object[]} args
+ * @returns {ArrayBuffer}
+ */
+ run(input, args) {
+ const HEADER_LEN = 8;
+
+ const s = new Stream(new Uint8Array(input));
+ if (s.length < HEADER_LEN) {
+ throw new OperationError("Need 8 bytes for a UDP Header");
+ }
+
+ s.moveTo(HEADER_LEN);
+
+ return s.getBytes().buffer;
+ }
+
+}
+
+export default StripUDPHeader;
diff --git a/src/node/config/scripts/generateNodeIndex.mjs b/src/node/config/scripts/generateNodeIndex.mjs
index 4e16fbeb..981c9b79 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/core/config/scripts/generateNodeIndex.mjs");
+ console.log("Example> node --experimental-modules src/node/config/scripts/generateNodeIndex.mjs");
process.exit(1);
}
diff --git a/tests/browser/01_io.js b/tests/browser/01_io.js
index f8f7f9e1..cbfec083 100644
--- a/tests/browser/01_io.js
+++ b/tests/browser/01_io.js
@@ -675,42 +675,42 @@ module.exports = {
}
},
- "Loading from URL": browser => {
- utils.clear(browser);
+ // "Loading from URL": browser => {
+ // utils.clear(browser);
- /* Side panel displays correct info */
- utils.uploadFile(browser, "files/TowelDay.jpeg");
+ // /* Side panel displays correct info */
+ // utils.uploadFile(browser, "files/TowelDay.jpeg");
- browser
- .waitForElementVisible("#input-text .cm-file-details")
- .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
- .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
- .waitForElementVisible("#input-text .cm-file-details .file-details-name")
- .waitForElementVisible("#input-text .cm-file-details .file-details-size")
- .waitForElementVisible("#input-text .cm-file-details .file-details-type")
- .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
+ // browser
+ // .waitForElementVisible("#input-text .cm-file-details")
+ // .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
+ // .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
+ // .waitForElementVisible("#input-text .cm-file-details .file-details-name")
+ // .waitForElementVisible("#input-text .cm-file-details .file-details-size")
+ // .waitForElementVisible("#input-text .cm-file-details .file-details-type")
+ // .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
- /* Complex deep link populates the input correctly (encoding, eol, input) */
- browser
- .urlHash("recipe=To_Base64('A-Za-z0-9%2B/%3D')&input=VGhlIHNoaXBzIGh1bmcgaW4gdGhlIHNreSBpbiBtdWNoIHRoZSBzYW1lIHdheSB0aGF0IGJyaWNrcyBkb24ndC4M&ienc=21866&oenc=1201&ieol=FF&oeol=PS")
- .waitForElementVisible("#rec-list li.operation");
+ // /* Complex deep link populates the input correctly (encoding, eol, input) */
+ // browser
+ // .urlHash("recipe=To_Base64('A-Za-z0-9%2B/%3D')&input=VGhlIHNoaXBzIGh1bmcgaW4gdGhlIHNreSBpbiBtdWNoIHRoZSBzYW1lIHdheSB0aGF0IGJyaWNrcyBkb24ndC4M&ienc=21866&oenc=1201&ieol=FF&oeol=PS")
+ // .waitForElementVisible("#rec-list li.operation");
- browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^.{65}$/);
- browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("66");
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
+ // browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^.{65}$/);
+ // browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("66");
+ // browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
- browser.expect.element("#input-text .chr-enc-value").text.that.equals("KOI8-U Ukrainian Cyrillic");
- browser.expect.element("#output-text .chr-enc-value").text.that.equals("UTF-16BE");
+ // browser.expect.element("#input-text .chr-enc-value").text.that.equals("KOI8-U Ukrainian Cyrillic");
+ // browser.expect.element("#output-text .chr-enc-value").text.that.equals("UTF-16BE");
- browser.expect.element("#input-text .eol-value").text.that.equals("FF");
- browser.expect.element("#output-text .eol-value").text.that.equals("PS");
+ // browser.expect.element("#input-text .eol-value").text.that.equals("FF");
+ // browser.expect.element("#output-text .eol-value").text.that.equals("PS");
- utils.bake(browser);
+ // utils.bake(browser);
- browser.expect.element(`#output-text .cm-content`).to.have.property("textContent").match(/^.{44}$/);
- browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("44");
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
- },
+ // browser.expect.element(`#output-text .cm-content`).to.have.property("textContent").match(/^.{44}$/);
+ // browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("44");
+ // browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
+ // },
"Replace input with output": browser => {
/* Input is correctly populated */
diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs
index 7c2ce20d..69f5b5f0 100644
--- a/tests/operations/index.mjs
+++ b/tests/operations/index.mjs
@@ -116,6 +116,7 @@ import "./tests/ParseObjectIDTimestamp.mjs";
import "./tests/ParseQRCode.mjs";
import "./tests/ParseSSHHostKey.mjs";
import "./tests/ParseTCP.mjs";
+import "./tests/ParseTLSRecord.mjs";
import "./tests/ParseTLV.mjs";
import "./tests/ParseUDP.mjs";
import "./tests/PEMtoHex.mjs";
@@ -143,6 +144,9 @@ import "./tests/SIGABA.mjs";
import "./tests/SM4.mjs";
// import "./tests/SplitColourChannels.mjs"; // Cannot test operations that use the File type yet
import "./tests/StrUtils.mjs";
+import "./tests/StripIPv4Header.mjs";
+import "./tests/StripTCPHeader.mjs";
+import "./tests/StripUDPHeader.mjs";
import "./tests/Subsection.mjs";
import "./tests/SwapCase.mjs";
import "./tests/SymmetricDifference.mjs";
diff --git a/tests/operations/tests/JWTSign.mjs b/tests/operations/tests/JWTSign.mjs
index 3970a5e5..a7752138 100644
--- a/tests/operations/tests/JWTSign.mjs
+++ b/tests/operations/tests/JWTSign.mjs
@@ -44,7 +44,18 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [hsKey, "HS256"],
+ args: [hsKey, "HS256", "{}"],
+ }
+ ],
+ },
+ {
+ name: "JWT Sign: HS256 with custom header",
+ input: inputObject,
+ expectedOutput: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImN1c3RvbS5rZXkifQ.eyJTdHJpbmciOiJTb21lU3RyaW5nIiwiTnVtYmVyIjo0MiwiaWF0IjoxfQ.kXln8btJburfRlND8IDZAQ8NZGFFZhvHyooHa6N9za8",
+ recipeConfig: [
+ {
+ op: "JWT Sign",
+ args: [hsKey, "HS256", `{"kid":"custom.key"}`],
}
],
},
@@ -55,7 +66,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [hsKey, "HS384"],
+ args: [hsKey, "HS384", "{}"],
}
],
},
@@ -66,7 +77,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [hsKey, "HS512"],
+ args: [hsKey, "HS512", "{}"],
}
],
},
@@ -77,7 +88,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [esKey, "ES256"],
+ args: [esKey, "ES256", "{}"],
},
{
op: "JWT Decode",
@@ -92,7 +103,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [esKey, "ES384"],
+ args: [esKey, "ES384", "{}"],
},
{
op: "JWT Decode",
@@ -107,7 +118,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [esKey, "ES512"],
+ args: [esKey, "ES512", "{}"],
},
{
op: "JWT Decode",
@@ -122,7 +133,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [rsKey, "RS256"],
+ args: [rsKey, "RS256", "{}"],
},
{
op: "JWT Decode",
@@ -137,7 +148,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [rsKey, "RS384"],
+ args: [rsKey, "RS384", "{}"],
},
{
op: "JWT Decode",
@@ -152,7 +163,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "JWT Sign",
- args: [esKey, "RS512"],
+ args: [esKey, "RS512", "{}"],
},
{
op: "JWT Decode",
diff --git a/tests/operations/tests/ParseTLSRecord.mjs b/tests/operations/tests/ParseTLSRecord.mjs
new file mode 100644
index 00000000..86db2f2c
--- /dev/null
+++ b/tests/operations/tests/ParseTLSRecord.mjs
@@ -0,0 +1,2045 @@
+/**
+ * Parse TLS record tests.
+ *
+ * @auther c65722
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "Parse TLS record: Truncated header",
+ input: "16030300",
+ expectedOutput: "[]",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Change Cipher Spec",
+ input: "140303000101",
+ expectedOutput: '[{"type":"change_cipher_spec","version":"0x0303","length":1,"value":"0x01"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Change Cipher Spec - Truncated before content",
+ input: "1403030001",
+ expectedOutput: '[{"type":"change_cipher_spec","version":"0x0303","length":1,"truncated":true}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Alert",
+ input: "150303001411770b5b5d11078535823266ec79671ed402bced",
+ expectedOutput: '[{"type":"alert","version":"0x0303","length":20,"value":"0x11770b5b5d11078535823266ec79671ed402bced"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Alert - Truncated within content",
+ input: "150303001411770b5b5d1107853582",
+ expectedOutput: '[{"type":"alert","version":"0x0303","length":20,"truncated":true,"value":"0x11770b5b5d1107853582"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Alert - Truncated before content",
+ input: "1503030014",
+ expectedOutput: '[{"type":"alert","version":"0x0303","length":20,"truncated":true}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Truncated within length",
+ input: "1603030032010000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Truncated before length",
+ input: "160303003201",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Truncated before msg type",
+ input: "1603030032",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Hello Request",
+ input: "160303000400000000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":4,"handshakeType":"hello_request"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076000004123443210200010000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated before extensions length",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107600000412344321020001",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated within compression methods",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076000004123443210200",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"truncated":true,"values":["0x00"]},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated before compression methods",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd0510760000041234432102",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"truncated":true,"values":[]},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated before compression methods length",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107600000412344321",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated within cipher suite value",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076000004123443",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{"length":4,"truncated":true,"values":["0x1234"]},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated within cipher suites",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd0510760000041234",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{"length":4,"truncated":true,"values":["0x1234"]},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated before cipher suites",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076000004",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{"length":4,"truncated":true,"values":[]},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated before cipher suites length",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd0510760000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated before session id length",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107600",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated within random",
+ input: "16030300320100002e030345cd3a31beaebd2934dd4ec2a151d7a0",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"","cipherSuites":{},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated before random",
+ input: "16030300320100002e0303",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"","cipherSuites":{},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated within client version",
+ input: "16030300320100002e03",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello","clientVersion":"","random":"","cipherSuites":{},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, No session ID, No extensions - Truncated before client version",
+ input: "16030300320100002e",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":50,"truncated":true,"handshakeType":"client_hello"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, No extensions",
+ input: "16030300520100004e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae0004123443210200010000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":82,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","sessionID":"0xdc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, No extensions - Truncated within session id",
+ input: "16030300520100004e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":82,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, No extensions - Truncated before session id",
+ input: "16030300520100004e030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":82,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","cipherSuites":{},"compressionMethods":{},"extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, Extensions",
+ input: "160303006f0100006b030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae000412344321020001001d00000010000e00000b6578616d706c652e636f6d00170000ff01000100",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":111,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","sessionID":"0xdc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{"length":29,"values":[{"type":"0x0000","length":16,"value":"0x000e00000b6578616d706c652e636f6d"},{"type":"0x0017","length":0},{"type":"0xff01","length":1,"value":"0x00"}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, Extensions - Truncated within extension value",
+ input: "160303006f0100006b030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae000412344321020001001d00000010000e00000b657861",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":111,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","sessionID":"0xdc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{"length":29,"truncated":true,"values":[{"type":"0x0000","length":16,"truncated":true,"value":"0x000e00000b657861"}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, Extensions - Truncated before extension value",
+ input: "160303006f0100006b030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae000412344321020001001d00000010",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":111,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","sessionID":"0xdc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{"length":29,"truncated":true,"values":[{"type":"0x0000","length":16,"truncated":true}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, Extensions - Truncated within extension length",
+ input: "160303006f0100006b030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae000412344321020001001d000000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":111,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","sessionID":"0xdc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{"length":29,"truncated":true,"values":[]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, Extensions - Truncated before extension length",
+ input: "160303006f0100006b030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae000412344321020001001d0000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":111,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","sessionID":"0xdc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{"length":29,"truncated":true,"values":[]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, Extensions - Truncated within extension type",
+ input: "160303006f0100006b030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae000412344321020001001d00",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":111,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","sessionID":"0xdc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{"length":29,"truncated":true,"values":[]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Hello, Session ID, Extensions - Truncated before extension type",
+ input: "160303006f0100006b030345cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd05107620dc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae000412344321020001001d",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":111,"truncated":true,"handshakeType":"client_hello","clientVersion":"0x0303","random":"0x45cd3a31beaebd2934dd4ec2a151d7a054eab8bc0e4e5b9d4b9abdaacd051076","sessionID":"0xdc78c85fdcee405ebb7963543771005a3d1b7dbf88fb9f8df12e4f7ea525e1ae","cipherSuites":{"length":4,"values":["0x1234","0x4321"]},"compressionMethods":{"length":2,"values":["0x00","0x01"]},"extensions":{"length":29,"truncated":true,"values":[]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions",
+ input: "160303002c02000028030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132004321010000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated before extensions length",
+ input: "160303002c02000028030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113200432101",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated before compression method",
+ input: "160303002c02000028030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132004321",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","cipherSuite":"0x4321","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated within cipher suite",
+ input: "160303002c02000028030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b011320043",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","cipherSuite":"","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated before cipher suite",
+ input: "160303002c02000028030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113200",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","cipherSuite":"","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated before session id length",
+ input: "160303002c02000028030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","cipherSuite":"","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated within random",
+ input: "160303002c02000028030309684ab9c0f6e739e308cd42a18a73d9a",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"","cipherSuite":"","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated before random",
+ input: "160303002c0200002803030",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"","cipherSuite":"","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated within server version",
+ input: "160303002c0200002803",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello","serverVersion":"","random":"","cipherSuite":"","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, No session ID, No extensions - Truncated before server version",
+ input: "160303002c02000028",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":44,"truncated":true,"handshakeType":"server_hello"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, No extension",
+ input: "160303004c02000048030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220a4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b95984321010000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":76,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","sessionID":"0xa4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, No extension - Truncated within session id",
+ input: "160303004c02000048030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220a4fe3d1e9a7dc5ce3d9341b4d48a2df7",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":76,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","cipherSuite":"","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, No extension - Truncated before session id",
+ input: "160303004c02000048030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":76,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","cipherSuite":"","compressionMethod":"","extensions":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, Extensions",
+ input: "160303005902000055030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220a4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598432101000d00000000ff0100010000170000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":89,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","sessionID":"0xa4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{"length":13,"values":[{"type":"0x0000","length":0},{"type":"0xff01","length":1,"value":"0x00"},{"type":"0x0017","length":0}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, Extensions - Truncated before extension value",
+ input: "160303005902000055030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220a4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598432101000d00000000ff010001",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":89,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","sessionID":"0xa4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{"length":13,"truncated":true,"values":[{"type":"0x0000","length":0},{"type":"0xff01","length":1,"truncated":true}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, Extensions - Truncated within extension length",
+ input: "160303005902000055030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220a4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598432101000d00000000ff0100",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":89,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","sessionID":"0xa4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{"length":13,"truncated":true,"values":[{"type":"0x0000","length":0}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, Extensions - Truncated before extension length",
+ input: "160303005902000055030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220a4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598432101000d00000000ff01",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":89,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","sessionID":"0xa4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{"length":13,"truncated":true,"values":[{"type":"0x0000","length":0}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, Extensions - Truncated within extension type",
+ input: "160303005902000055030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220a4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598432101000d00000000ff",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":89,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","sessionID":"0xa4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{"length":13,"truncated":true,"values":[{"type":"0x0000","length":0}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello, Session ID, Extensions - Truncated before extension type",
+ input: "160303005902000055030309684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b0113220a4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598432101000d00000000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":89,"truncated":true,"handshakeType":"server_hello","serverVersion":"0x0303","random":"0x09684ab9c0f6e739e308cd42a18a73d9adc579378aa6b4228df7ecc422b01132","sessionID":"0xa4fe3d1e9a7dc5ce3d9341b4d48a2df755a0fd83876d0330018306707c9b9598","cipherSuite":"0x4321","compressionMethod":"0x01","extensions":{"length":13,"truncated":true,"values":[{"type":"0x0000","length":0}]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - New Session Ticket",
+ input: "16030300ca040000c60000070800c0626f6889ce97edae08b0870505f9251e1d0713438ed014ac8f5e6969cf9e500aaba6080dfed5474ec85ff48d882d526cdae7f21d51b4beeb0be83fb822f18d22d2086b7519b29114364af034ac9a6915562ba686b81917bcb89fc4a750284470e7d67d8d33647e245e5e789f547d6a1be91ef0985bbfcf3b88760586b8f02570e0b7e8547fdad75530bc0261756ec994dfc725c8551c762f26e105e62290cd43773ea9e8a42ac8ac21467053240a29ef93c2e34c2f13ce8ff494d8c64f727248",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":202,"handshakeType":"new_session_ticket","ticketLifetimeHint":"1800s","ticket":"0x626f6889ce97edae08b0870505f9251e1d0713438ed014ac8f5e6969cf9e500aaba6080dfed5474ec85ff48d882d526cdae7f21d51b4beeb0be83fb822f18d22d2086b7519b29114364af034ac9a6915562ba686b81917bcb89fc4a750284470e7d67d8d33647e245e5e789f547d6a1be91ef0985bbfcf3b88760586b8f02570e0b7e8547fdad75530bc0261756ec994dfc725c8551c762f26e105e62290cd43773ea9e8a42ac8ac21467053240a29ef93c2e34c2f13ce8ff494d8c64f727248"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - New Session Ticket - Truncated within ticket",
+ input: "16030300ca040000c60000070800c0626f6889ce97edae08b0870505f9251e1d0713438ed014ac8f5e6969cf9e500aaba6080dfed5474ec85ff48d882d526cdae7f21d51b4beeb0be83fb822f18d22d2086b7519b29114364af034ac9a6915562ba686b81917bcb89fc4a750284470",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":202,"truncated":true,"handshakeType":"new_session_ticket","ticketLifetimeHint":"1800s","ticket":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - New Session Ticket - Truncated before ticket",
+ input: "16030300ca040000c60000070800c0",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":202,"truncated":true,"handshakeType":"new_session_ticket","ticketLifetimeHint":"1800s","ticket":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - New Session Ticket - Truncated within ticket length",
+ input: "16030300ca040000c60000070800",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":202,"truncated":true,"handshakeType":"new_session_ticket","ticketLifetimeHint":"1800s","ticket":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - New Session Ticket - Truncated before ticket length",
+ input: "16030300ca040000c600000708",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":202,"truncated":true,"handshakeType":"new_session_ticket","ticketLifetimeHint":"1800s","ticket":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - New Session Ticket - Truncated within ticket lifetime hint",
+ input: "16030300ca040000c6000007",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":202,"truncated":true,"handshakeType":"new_session_ticket","ticketLifetimeHint":"","ticket":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - New Session Ticket - Truncated before ticket lifetime hint",
+ input: "16030300ca040000c6",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":202,"truncated":true,"handshakeType":"new_session_ticket"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate",
+ input: "1603030acf0b000acb000ac80002923082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf00029430820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d000599308205953082037da00302010202141521d02e945395325d99051e616ad01c97627ee2300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65332e636f6d3020170d3234303932323130303232325a180f32313234303832393130303232325a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65332e636f6d30820222300d06092a864886f70d01010105000382020f003082020a0282020100bd7c65b5c2c7027e4eb77722f84d7dc9b45f9fae45c59dd0035340b3d8fec5ea644ac4563c4260b2c078880bf81ffec0e4cd9193b708ded6431c0e7d9e8f45d595712b733262f8f62f1b4c3ae69f1f39bc68a39b1b5699adddfd7c51b83f59479fe5ffe0faef6376b1c5cea434aa9db85e792f989b5977c6fda87f7c00f79e67e417d826c1ab1fa304163414fc6321790f07cffede43170718536e5fe3128f6d101de82a7b1de37f89e61d822f09eef7304213d41998a49e5ab6b1a7eb1ab4ece21f005061828567047aaf640cff2f87c85eefc2d3a91ebf48aaa893e59451acbea894975df2587b203302fb39755f2e21e012d1fc89df86ec53723df497318d8b44eee9334a2699ad403a7df6719747bc37429d3c47ada354308380b09bb6d76e21dc1735a1479470c94c0282bbbdf5e2e6af60cf1f2e9b8dad20e45307729813eaaf584b31984e036d5452dfae47a4b8640bdf4c02ecf4ce4240d64d2ab895cbf512558712533cd3fc6838bfd24a2a588b9f1b1848bb0d6b1cd77345add6e9dc547a7b95b027bb18e96f30c4f9cd780c96984472b70ea39a7acdff9c649ac4a59e12a5a72d436036b31fa130f6a72c717b3df403113ee3b3d1605f76e57e96b83e501ed5fe9200e2ea9aefa797fa0c8b6c5d8f12e4bea7359be03d3ca35d3e22e20639fc7e03c990a494402268a08fb1589dc086995b0ba3c9ffe255b6b7cf0203010001a3533051301d0603551d0e041604143e8bac5b946c2eff6a8cb337081fa4fe6ce07312301f0603551d230418301680143e8bac5b946c2eff6a8cb337081fa4fe6ce07312300f0603551d130101ff040530030101ff300d06092a864886f70d01010b0500038202010081ad7acd39e5cc60682c962d367a84d32191e5b465ed531f617daf5fd33394a3ac9a42116d34211708ada0d9bd2cbf1d4a4175d67c87116c7495ed372c585ae6bdfe0bc713aa1afd0cc3f025c322dc45be0c3be982918dea938deaaa9e5bfd1fccb3eb8a111aec0498f64bdb16f6cb07bcecd85f6b9e445cf596d85596b4f0d7147d73cbc26000d374085e9c69f56262827fa3d5a037cf1d2cfe0f0eca779b101da08a8d732ecf584a193d93449697ee24ed6f41f9735ea3a3f206f8e6b5bf0b0ff3488a31d0feaccd701a144d35c265dc32d2e650f855debbfa5bd2d9dc2d80a1b8f81013f8049bd7be83a3ec5ae554c19fd4241a6686d4094ff073022d1f16afa5a0297e54a9b56fd469b44c6904d2b542f83ff0cf6af3b649f408f72f7cb49be5583ec4b1d912a677ae1fd81779506af9b688d8b753fdb0451925752fba8efcdaedf935f2a264caa1f4fe746ac6c339cca647b25f0bd2139205e67b6e90987da8b993b85037931443a6652426ab779db090cf08b28fed862a0ccdde1568bd930bf2d39ab7b850f97925e9bda13a6ee5166e48959711065c054bdf5ff04e4b8d5120caabca40c7707da3bb10f2ae7a00a6e56b012a6c00daaec5ddf0b63f61622aeeb81a71a5aa17508e5471e777bed8d09023c24280495adc38ffc3615dd20b139d32d7cc30b0690ab7f3e47a0131fa3d81929e64c6b9c6b363da410f6e5e",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":2767,"handshakeType":"certificate","certificateList":{"length":2760,"values":["0x3082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf","0x30820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d","0x308205953082037da00302010202141521d02e945395325d99051e616ad01c97627ee2300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65332e636f6d3020170d3234303932323130303232325a180f32313234303832393130303232325a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65332e636f6d30820222300d06092a864886f70d01010105000382020f003082020a0282020100bd7c65b5c2c7027e4eb77722f84d7dc9b45f9fae45c59dd0035340b3d8fec5ea644ac4563c4260b2c078880bf81ffec0e4cd9193b708ded6431c0e7d9e8f45d595712b733262f8f62f1b4c3ae69f1f39bc68a39b1b5699adddfd7c51b83f59479fe5ffe0faef6376b1c5cea434aa9db85e792f989b5977c6fda87f7c00f79e67e417d826c1ab1fa304163414fc6321790f07cffede43170718536e5fe3128f6d101de82a7b1de37f89e61d822f09eef7304213d41998a49e5ab6b1a7eb1ab4ece21f005061828567047aaf640cff2f87c85eefc2d3a91ebf48aaa893e59451acbea894975df2587b203302fb39755f2e21e012d1fc89df86ec53723df497318d8b44eee9334a2699ad403a7df6719747bc37429d3c47ada354308380b09bb6d76e21dc1735a1479470c94c0282bbbdf5e2e6af60cf1f2e9b8dad20e45307729813eaaf584b31984e036d5452dfae47a4b8640bdf4c02ecf4ce4240d64d2ab895cbf512558712533cd3fc6838bfd24a2a588b9f1b1848bb0d6b1cd77345add6e9dc547a7b95b027bb18e96f30c4f9cd780c96984472b70ea39a7acdff9c649ac4a59e12a5a72d436036b31fa130f6a72c717b3df403113ee3b3d1605f76e57e96b83e501ed5fe9200e2ea9aefa797fa0c8b6c5d8f12e4bea7359be03d3ca35d3e22e20639fc7e03c990a494402268a08fb1589dc086995b0ba3c9ffe255b6b7cf0203010001a3533051301d0603551d0e041604143e8bac5b946c2eff6a8cb337081fa4fe6ce07312301f0603551d230418301680143e8bac5b946c2eff6a8cb337081fa4fe6ce07312300f0603551d130101ff040530030101ff300d06092a864886f70d01010b0500038202010081ad7acd39e5cc60682c962d367a84d32191e5b465ed531f617daf5fd33394a3ac9a42116d34211708ada0d9bd2cbf1d4a4175d67c87116c7495ed372c585ae6bdfe0bc713aa1afd0cc3f025c322dc45be0c3be982918dea938deaaa9e5bfd1fccb3eb8a111aec0498f64bdb16f6cb07bcecd85f6b9e445cf596d85596b4f0d7147d73cbc26000d374085e9c69f56262827fa3d5a037cf1d2cfe0f0eca779b101da08a8d732ecf584a193d93449697ee24ed6f41f9735ea3a3f206f8e6b5bf0b0ff3488a31d0feaccd701a144d35c265dc32d2e650f855debbfa5bd2d9dc2d80a1b8f81013f8049bd7be83a3ec5ae554c19fd4241a6686d4094ff073022d1f16afa5a0297e54a9b56fd469b44c6904d2b542f83ff0cf6af3b649f408f72f7cb49be5583ec4b1d912a677ae1fd81779506af9b688d8b753fdb0451925752fba8efcdaedf935f2a264caa1f4fe746ac6c339cca647b25f0bd2139205e67b6e90987da8b993b85037931443a6652426ab779db090cf08b28fed862a0ccdde1568bd930bf2d39ab7b850f97925e9bda13a6ee5166e48959711065c054bdf5ff04e4b8d5120caabca40c7707da3bb10f2ae7a00a6e56b012a6c00daaec5ddf0b63f61622aeeb81a71a5aa17508e5471e777bed8d09023c24280495adc38ffc3615dd20b139d32d7cc30b0690ab7f3e47a0131fa3d81929e64c6b9c6b363da410f6e5e"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate - Truncated within certificate",
+ input: "1603030acf0b000acb000ac80002923082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf00029430820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d0005990x308205953082037da00302010202141521d02e945395325d99051e616ad01c97627ee2300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65332e636f6d3020170d3234303932323130303232325a180f32313234303832393130303232325a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65332e636f6d30820222300d06092a864886f70d01010105000382020f003082020a0282020100bd7c65b5c2c7027e4eb77722f84d7dc9b45f9fae45c59dd0035340b3d8fec5ea644ac4563c4260b2c078880bf81ffec0e4cd9193b708ded6431c0e7d9e8f45d595712b733262f8f62f1b4c3ae69f1f39bc68a39b1b5699adddfd7c51b83f59479fe5ffe0faef6376b1c5cea434aa9db85e792f989b5977c6fda87f7c00f79e67e417d826c1ab1fa304163414fc6321790f07cffede43170718536e5fe3128f6d101de82a7b1de37f89e61d822f09eef7304213d41998a49e5ab6b1a7eb1ab4ece21f005061828567047aaf640cff2f87c85eefc2d3a91ebf48aaa893e59451acbea894975df2587b203302fb39755f2e21e012d1fc89df86ec53723df497318d8b44eee9334a2699ad403a7df6719747bc37429d3c47ada354308380b09bb6d76e21dc1735a1479470c94c0282bbbdf5e2e6af60cf1f2e9b8dad20e45307729813eaaf584b31984e036d5452dfae47a4b8640bdf4c02ecf4ce4240d64d2ab895cbf512558712533cd3fc6838bfd24a2a588b9f1b1848bb0d6b1cd77345add6e9dc547a7b95b027bb18e96f30c4f9cd780c96984472b70ea39a7acdff9c649ac4a5",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":2767,"truncated":true,"handshakeType":"certificate","certificateList":{"length":2760,"truncated":true,"values":["0x3082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf","0x30820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate - Truncated before certificate",
+ input: "1603030acf0b000acb000ac80002923082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf00029430820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d000599",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":2767,"truncated":true,"handshakeType":"certificate","certificateList":{"length":2760,"truncated":true,"values":["0x3082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf","0x30820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate - Truncated within certificate length",
+ input: "1603030acf0b000acb000ac80002923082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf00029430820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d0005",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":2767,"truncated":true,"handshakeType":"certificate","certificateList":{"length":2760,"truncated":true,"values":["0x3082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf","0x30820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate - Truncated before certificate length",
+ input: "1603030acf0b000acb000ac80002923082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf00029430820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":2767,"truncated":true,"handshakeType":"certificate","certificateList":{"length":2760,"truncated":true,"values":["0x3082028e308201f7a003020102021468f6f88ecf1bf3d14e7503ef2e1b789cb77b86c3300d06092a864886f70d01010b05003058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d3020170d3234303932323039353335385a180f32313234303832393039353335385a3058310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643114301206035504030c0b6578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100c3df3e5745f05b3aa220ce4108838107653c3ae9584ace27d7088506ebdc3531afbe6265719278682eaa4fec7ae1f319395d356be79477bc62edbe7207d96f5717e9bd9083fdcc797c1b8e38bcf9fd08df6f101bc2a06101ddce6be2f5a0de80ebc8fdce2538867c1d6a84acef26b2068c5d27771abcee071bcf378899cb32730203010001a3533051301d0603551d0e041604144c9b134c1575c51ae9d03c4020da7541278ad928301f0603551d230418301680144c9b134c1575c51ae9d03c4020da7541278ad928300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000381810012a06cced33d721b1d7912ff0b190b74524ddfdeca103aba0f168f4f15f57212ba7d66328e48b021f32cfec84f65d79821bc1fe9f472f60c094e537160708a48a0898dbf613cece86892cf48fcd598757aa4379e18673626be2f048e35f585086ea7a3766ce50a14ca6f691b369c965e062f40619cde6262ed8019b522e76eaf","0x30820290308201f9a00302010202142a3329f5e2e92940318cecd036ff135525b1d491300d06092a864886f70d01010b05003059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d3020170d3234303932323039353531375a180f32313234303832393039353531375a3059310b30090603550406130258583115301306035504070c0c44656661756c742043697479311c301a060355040a0c1344656661756c7420436f6d70616e79204c74643115301306035504030c0c6578616d706c65322e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100b27c861d957c49111b4f37f65bc142da564429c74a925e3de6d9add55ccfccf1316a5002b3ed2d35ec9822499e7256f9caaa2191010df354185c63a32c8d080ba49510953d7ec2210685030564be69a9f2262a9da22f3623b2a9b032f3a82b1c31ce11336c288fc3d5f63565aacc8c0f85ebaad6af2cd3505a7cf3945ca2ca690203010001a3533051301d0603551d0e0416041485478b7936ecd417647e9d8582d3f68fc670d839301f0603551d2304183016801485478b7936ecd417647e9d8582d3f68fc670d839300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003818100652656aef44c7a507a376de248cd1b36028fb1b0292593f88eb36b429f7de4c668aef7b0d862c9314e5d870f7c28353022657a7de07ec69505a54e48337ab6ba425bfd8865b720f1f2e86c92edaa261fd73e44856ac45c4d9378c86adb96b6f999f61e5f651cb885e06a3d909b5fa79458941bea36785ea585aeb5025032a18d"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate - Truncated within certificate list length",
+ input: "1603030acf0b000acb000a",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":2767,"truncated":true,"handshakeType":"certificate","certificateList":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate - Truncated before certificate list length",
+ input: "1603030acf0b000acb",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":2767,"truncated":true,"handshakeType":"certificate"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Key Exchange",
+ input: "16030300840c000080a90c12174921d7044303107b6e37523957439b436e57904e82702784bfc261a8f0a7e4143a77144357d29ee322f25e4fce393ac7570ee26c378298a6ad18fd8b87175e472c7c07b97699f72958e0af489df00d34e5e03dde2e09dfe06d448651ee45c07fadc05e0d1585589e3715a04b935e72bc28c34593712acef7883ed69a",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":132,"handshakeType":"server_key_exchange","handshakeValue":"0xa90c12174921d7044303107b6e37523957439b436e57904e82702784bfc261a8f0a7e4143a77144357d29ee322f25e4fce393ac7570ee26c378298a6ad18fd8b87175e472c7c07b97699f72958e0af489df00d34e5e03dde2e09dfe06d448651ee45c07fadc05e0d1585589e3715a04b935e72bc28c34593712acef7883ed69a"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Key Exchange - Truncated within content",
+ input: "16030300840c000080a90c12174921d7044303107b6e37523957439b436e57904e82702784bfc261a8f0a7e4143a77144357d29ee322f25e4fce393ac7570ee26c378298a6ad18fd8b",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":132,"truncated":true,"handshakeType":"server_key_exchange","handshakeValue":"0xa90c12174921d7044303107b6e37523957439b436e57904e82702784bfc261a8f0a7e4143a77144357d29ee322f25e4fce393ac7570ee26c378298a6ad18fd8b"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Key Exchange - Truncated before content",
+ input: "16030300840c000080",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":132,"truncated":true,"handshakeType":"server_key_exchange"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, No certificate authorities",
+ input: "160303001f0d00001b040102030400120601060206030301030203030201020202030000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":31,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202","0x0203"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities",
+ input: "16030300470d000043040102030400120601060206030301030203030201020202030028000c546bf13f358cf3ddc1eef77d001813b3cdd60a34fc74f2e4ef2344cfd2156924d8d2810e2c86",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202","0x0203"]},"certificateAuthorities":{"length":40,"values":["0x546bf13f358cf3ddc1eef77d","0x13b3cdd60a34fc74f2e4ef2344cfd2156924d8d2810e2c86"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated within certificate authority",
+ input: "16030300470d000043040102030400120601060206030301030203030201020202030028000c546bf13f358cf3ddc1eef77d001813b3cdd60a34fc74f2e4ef23",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202","0x0203"]},"certificateAuthorities":{"length":40,"truncated":true,"values":["0x546bf13f358cf3ddc1eef77d"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated before certificate authority",
+ input: "16030300470d000043040102030400120601060206030301030203030201020202030028000c546bf13f358cf3ddc1eef77d0018",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202","0x0203"]},"certificateAuthorities":{"length":40,"truncated":true,"values":["0x546bf13f358cf3ddc1eef77d"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated within certificate authority length",
+ input: "16030300470d000043040102030400120601060206030301030203030201020202030028000c546bf13f358cf3ddc1eef77d00",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202","0x0203"]},"certificateAuthorities":{"length":40,"truncated":true,"values":["0x546bf13f358cf3ddc1eef77d"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated before certificate authority length",
+ input: "16030300470d000043040102030400120601060206030301030203030201020202030028000c546bf13f358cf3ddc1eef77d",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202","0x0203"]},"certificateAuthorities":{"length":40,"truncated":true,"values":["0x546bf13f358cf3ddc1eef77d"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated within certificate authorities length",
+ input: "16030300470d0000430401020304001206010602060303010302030302010202020300",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202","0x0203"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated before certificate authorities length",
+ input: "16030300470d00004304010203040012060106020603030103020303020102020203",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202","0x0203"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated within supported signature algorithm",
+ input: "16030300470d000043040102030400120601060206030301030203030201020202",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"truncated":true,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated before supported signature algorithm",
+ input: "16030300470d0000430401020304001206010602060303010302030302010202",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{"length":18,"truncated":true,"values":["0x0601","0x0602","0x0603","0x0301","0x0302","0x0303","0x0201","0x0202"]}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated within supported signature algorithms length",
+ input: "16030300470d000043040102030400",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated before supported signature algorithms length",
+ input: "16030300470d0000430401020304",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"values":["0x01","0x02","0x03","0x04"]},"supportedSignatureAlgorithms":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated within certificate types",
+ input: "16030300470d00004304010203",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"truncated":true,"values":["0x01","0x02","0x03"]},"supportedSignatureAlgorithms":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated before certificate types",
+ input: "16030300470d00004304",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request","certificateTypes":{"length":4,"truncated":true,"values":[]},"supportedSignatureAlgorithms":{}}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Request, Certificate authorities - Truncated before certificate types length",
+ input: "16030300470d000043",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":71,"truncated":true,"handshakeType":"certificate_request"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Server Hello Done",
+ input: "16030300040e000000",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":4,"handshakeType":"server_hello_done"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Verify",
+ input: "16030301080f000104040101009310d3dda84b149a00258f0bb4501e710f7ed70a45cf4f0bab39dac1a456027f0f6167924f08a8221613bcf46c27e91458d05163200fd1bf3673351d74693c08c6640635d4e9f84e9568e39d3346e3ff2f3eacf9887d738935d8b07e42659dd3b212662bf028bcefe98b686a1a83fb2f24aead94cccd3f6b26c9d42ba43254d2a93d1b85ae2d0ee7c7170aac3397fa6de77183d30c99e6bb0e81f925793f64d8b490cb74d051896ebee9086c7606905b21bab6ebd9866a451958f7d839134aeb335b2ad5f9ce89a69321a099c081b5166332cf2bb231dd135b79cf94218e6ada94644eaa09ae6c0ec0164e3cca631c0f4b7b9a2d59fb40909ec88805e61b5917",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":264,"handshakeType":"certificate_verify","algorithmHash":"0x04","algorithmSignature":"0x01","signature":"0x9310d3dda84b149a00258f0bb4501e710f7ed70a45cf4f0bab39dac1a456027f0f6167924f08a8221613bcf46c27e91458d05163200fd1bf3673351d74693c08c6640635d4e9f84e9568e39d3346e3ff2f3eacf9887d738935d8b07e42659dd3b212662bf028bcefe98b686a1a83fb2f24aead94cccd3f6b26c9d42ba43254d2a93d1b85ae2d0ee7c7170aac3397fa6de77183d30c99e6bb0e81f925793f64d8b490cb74d051896ebee9086c7606905b21bab6ebd9866a451958f7d839134aeb335b2ad5f9ce89a69321a099c081b5166332cf2bb231dd135b79cf94218e6ada94644eaa09ae6c0ec0164e3cca631c0f4b7b9a2d59fb40909ec88805e61b5917"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Verify - Truncated within signature",
+ input: "16030301080f000104040101009310d3dda84b149a00258f0bb4501e710f7ed70a45cf4f0bab39dac1a456027f0f6167924f08a8221613bcf46c27e91458d05163200fd1bf3673351d74693c08c6640635d4e9f84e9568e39d3346e3ff2f3eacf9887d738935d8b07e42659dd3b212662bf028bcefe98b686a1a83fb2f24aead94cccd3f6b26c9d42ba43254d2",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":264,"truncated":true,"handshakeType":"certificate_verify","algorithmHash":"0x04","algorithmSignature":"0x01","signature":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Verify - Truncated before signature",
+ input: "16030301080f00010404010100",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":264,"truncated":true,"handshakeType":"certificate_verify","algorithmHash":"0x04","algorithmSignature":"0x01","signature":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Verify - Truncated within signature length",
+ input: "16030301080f000104040101",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":264,"truncated":true,"handshakeType":"certificate_verify","algorithmHash":"0x04","algorithmSignature":"0x01","signature":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Verify - Truncated before signature length",
+ input: "16030301080f0001040401",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":264,"truncated":true,"handshakeType":"certificate_verify","algorithmHash":"0x04","algorithmSignature":"0x01","signature":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Verify - Truncated before algorithm.signature",
+ input: "16030301080f00010404",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":264,"truncated":true,"handshakeType":"certificate_verify","algorithmHash":"0x04","algorithmSignature":"","signature":""}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Certificate Verify - Truncated before algorithm.hash",
+ input: "16030301080f000104",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":264,"truncated":true,"handshakeType":"certificate_verify"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Key Exchange",
+ input: "1603030084100000802b45af77539975e975c9389030193bb6d7841d870e058850a5aac5f8ded75d243ae8bec2bc8ba4e683eba22d5820b555c69f97001aa7d56cba1839588e7f1602ad0b4cb7319fc52694a67f1e381b4d8a581823410920717ee85ef352dea39097e6b131bdfeb3913f0f7eaa3b3882abe4615cc13e2a133558adff159771dfdc8d",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":132,"handshakeType":"client_key_exchange","handshakeValue":"0x2b45af77539975e975c9389030193bb6d7841d870e058850a5aac5f8ded75d243ae8bec2bc8ba4e683eba22d5820b555c69f97001aa7d56cba1839588e7f1602ad0b4cb7319fc52694a67f1e381b4d8a581823410920717ee85ef352dea39097e6b131bdfeb3913f0f7eaa3b3882abe4615cc13e2a133558adff159771dfdc8d"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Key Exchange - Truncated within content",
+ input: "1603030084100000802b45af77539975e975c9389030193bb6d7841d870e058850a5aac5f8ded75d243ae8bec2bc8ba4e683eba22d5820b555c69f97001aa7d56cba1839588e7f1602",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":132,"truncated":true,"handshakeType":"client_key_exchange","handshakeValue":"0x2b45af77539975e975c9389030193bb6d7841d870e058850a5aac5f8ded75d243ae8bec2bc8ba4e683eba22d5820b555c69f97001aa7d56cba1839588e7f1602"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Client Key Exchange - Truncated before content",
+ input: "160303008410000080",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":132,"truncated":true,"handshakeType":"client_key_exchange"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Finished",
+ input: "1603030028ed83078db91b046358065ca3f7ea4494af3deb59bf72f522e15ef9071c52becb0069a093b23994c1",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":40,"handshakeType":"finished","handshakeValue":"0xed83078db91b046358065ca3f7ea4494af3deb59bf72f522e15ef9071c52becb0069a093b23994c1"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Finished - Truncated within ciphertext",
+ input: "1603030028ed83078db91b046358065ca3f7ea4494af3deb59",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":40,"truncated":true,"handshakeType":"finished","handshakeValue":"0xed83078db91b046358065ca3f7ea4494af3deb59"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Unknown",
+ input: "1603030024120000203c210cd33fd2a7379ae02700b208ae7357f98b46a1dea566c4061acfb6e188bc",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":36,"handshakeType":"18","handshakeValue":"0x3c210cd33fd2a7379ae02700b208ae7357f98b46a1dea566c4061acfb6e188bc"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Unknown - Truncated within content",
+ input: "1603030024120000203c210cd33fd2a7379ae02700b208",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":36,"truncated":true,"handshakeType":"18","handshakeValue":"0x3c210cd33fd2a7379ae02700b208"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Handshake - Unknown - Truncated before content",
+ input: "160303002412000020",
+ expectedOutput: '[{"type":"handshake","version":"0x0303","length":36,"truncated":true,"handshakeType":"18"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Application Data",
+ input: "1703030064bbfd70f5d2ae0fe62262830040c264fa578bf2000ea50bb2c92d4837727f5db06b580e43896eaa1a0042b4fc3eb5aca6731705f5d957c481bade800cf1cd066dfd997851af09e820e84ee0b531b4eaccfd8b5f28b74d756a8aeadf78eefb2d26e46b5b69",
+ expectedOutput: '[{"type":"application_data","version":"0x0303","length":100,"value":"0xbbfd70f5d2ae0fe62262830040c264fa578bf2000ea50bb2c92d4837727f5db06b580e43896eaa1a0042b4fc3eb5aca6731705f5d957c481bade800cf1cd066dfd997851af09e820e84ee0b531b4eaccfd8b5f28b74d756a8aeadf78eefb2d26e46b5b69"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Application Data - Truncated within content",
+ input: "1703030064bbfd70f5d2ae0fe62262830040c264fa578bf2000ea50bb2c92d4837727f5db06b580e43896eaa1a0042b4fc3eb5aca67317",
+ expectedOutput: '[{"type":"application_data","version":"0x0303","length":100,"truncated":true,"value":"0xbbfd70f5d2ae0fe62262830040c264fa578bf2000ea50bb2c92d4837727f5db06b580e43896eaa1a0042b4fc3eb5aca67317"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Application Data - Truncated before content",
+ input: "1703030064",
+ expectedOutput: '[{"type":"application_data","version":"0x0303","length":100,"truncated":true}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Unknown",
+ input: "1c03030020c02beaae1dd2e9ec46c4d201d72105457af1f8e92d56ad95f339398e5774cb6f",
+ expectedOutput: '[{"type":"28","version":"0x0303","length":32,"value":"0xc02beaae1dd2e9ec46c4d201d72105457af1f8e92d56ad95f339398e5774cb6f"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Unknown - Truncated within content",
+ input: "1c03030020c02beaae1dd2e9ec46c4d201d7210545",
+ expectedOutput: '[{"type":"28","version":"0x0303","length":32,"truncated":true,"value":"0xc02beaae1dd2e9ec46c4d201d7210545"}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ },
+ {
+ name: "Parse TLS record: Unknown - Truncated before content",
+ input: "1c03030020",
+ expectedOutput: '[{"type":"28","version":"0x0303","length":32,"truncated":true}]',
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Parse TLS record",
+ args: []
+ },
+ {
+ op: "JSON Minify",
+ args: []
+ }
+ ]
+ }
+]);
diff --git a/tests/operations/tests/ParseX509CRL.mjs b/tests/operations/tests/ParseX509CRL.mjs
new file mode 100644
index 00000000..33de5e38
--- /dev/null
+++ b/tests/operations/tests/ParseX509CRL.mjs
@@ -0,0 +1,331 @@
+/**
+ * Parse X.509 CRL tests.
+ *
+ * @author robinsandhu
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../lib/TestRegister.mjs";
+
+const IN_CRL_PEM_RSA = `-----BEGIN X509 CRL-----
+MIID7jCCAdYCAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVUsxDzANBgNV
+BAgMBkxvbmRvbjELMAkGA1UECgwCQkIxFTATBgNVBAMMDFRlc3QgUm9vdCBDQRcN
+MjQwODI1MTE0OTEwWhcNMjQwOTI0MTE0OTEwWjA1MDMCAhAAFw0yNDA4MjUwMzIz
+MDhaMB4wCgYDVR0VBAMKAQYwEAYDVR0XBAkGByqGSM44AgOgggEnMIIBIzAJBgNV
+HRIEAjAAMH0GA1UdIwR2MHSAFLjJrf2oUFTVhW40i0xgL7BJtodGoUakRDBCMQsw
+CQYDVQQGEwJVSzEPMA0GA1UECAwGTG9uZG9uMQswCQYDVQQKDAJCQjEVMBMGA1UE
+AwwMVGVzdCBSb290IENBghQ3XUv2vXwRfMxGGv/XLywm+B5LPTAtBgNVHS4EJjAk
+MCKgIKAehhxodHRwOi8vZXhhbXBsZS5jb20vZGVsdGEtY3JsMFsGA1UdHwRUMFIw
+IaAfoB2GG2h0dHA6Ly9leGFtcGxlLmNvbS9mdWxsLWNybDAhoB+gHYYbbGRhcDov
+L2V4YW1wbGUuY29tL2Z1bGwtY3JsMAqgCKAGhwR/AAABMAsGA1UdFAQEAgIePDAN
+BgkqhkiG9w0BAQsFAAOCAgEAAxsr+9nELUVWhFekwy6GsqH8xOf6EqGjRaEdX49W
+mB40m2VajOkK8UHGoVyZzoDI2r/c8OPXUtbpK0fpvEl3SZU5j/C8JbZaZFFrEGeH
+fSEqdVHFjohpawNcG41Qs+YT21TBqH1hD5yVI7gjVvfKICRfxDpl5oGClxBCVOSV
+gVtLbe9q44uCBJ1kUkoc9Vz47Hv7JyckgqVXkORWHt2SFNALxlMEzOEQTpuC5Kcb
+4i7hTCUF+kpkIvr02LJImq0Aaqzs6cC/DcdJiRPPyfaN8fQryFv76gg9i8zZcb6c
+W42rvumiyw+7nnZfmq53webr5fCHaXhZk47ASOJD6GC5cX9rje1qGRgULXRhqcvK
+n319s2iXj3FStDDorKGgsCV2zYmotX17ExB98CcCgBE52zMtRZilwhOGeh8mx3qT
+l0W2B8uKKAq5BMmiziSBzQt700JPiruURZXbQ1fH1n7pKP6wGEh2e9TfQMlN20hE
+I+CMt+1bG0Bpt5AfiwE8UykQ/WvpVxdJrgj0JM0yA37KfC8XD+cmavJ5/grorbj3
+t0zBdK7bl+Y45VU/5/mX5ZR3O3ea1RclPM3hKMREfPneOlpan6r3dVwFqEN/TeTu
+46vuDeKaEr3yJkOFfy0lSYPhPhzhU5vDR5ibxqvwxZNznI2AdTnZLEf8LRqnTVo1
+qx0=
+-----END X509 CRL-----`;
+
+const OUT_CRL_PEM_RSA = `Certificate Revocation List (CRL):
+ Version: 2 (0x1)
+ Signature Algorithm: SHA256withRSA
+ Issuer:
+ C = UK
+ ST = London
+ O = BB
+ CN = Test Root CA
+ Last Update: Sun, 25 Aug 2024 11:49:10 GMT
+ Next Update: Tue, 24 Sep 2024 11:49:10 GMT
+ CRL extensions:
+ 2.5.29.46:
+ Unsupported CRL extension. Try openssl CLI.
+ X509v3 Authority Key Identifier:
+ keyid:B8:C9:AD:FD:A8:50:54:D5:85:6E:34:8B:4C:60:2F:B0:49:B6:87:46
+ DirName:/C=UK/ST=London/O=BB/CN=Test Root CA
+ serial:37:5D:4B:F6:BD:7C:11:7C:CC:46:1A:FF:D7:2F:2C:26:F8:1E:4B:3D
+ X509v3 CRL Distribution Points:
+ Full Name:
+ URI:http://example.com/full-crl
+ Full Name:
+ URI:ldap://example.com/full-crl
+ Full Name:
+ IP:127.0.0.1
+ X509v3 CRL Number:
+ 1E3C
+ issuerAltName:
+ Unsupported CRL extension. Try openssl CLI.
+Revoked Certificates:
+ Serial Number: 1000
+ Revocation Date: Sun, 25 Aug 2024 03:23:08 GMT
+ CRL entry extensions:
+ X509v3 CRL Reason Code:
+ Certificate Hold
+ Hold Instruction Code:
+ Hold Instruction Reject
+Signature Value:
+ 03:1b:2b:fb:d9:c4:2d:45:56:84:57:a4:c3:2e:86:b2:a1:fc:
+ c4:e7:fa:12:a1:a3:45:a1:1d:5f:8f:56:98:1e:34:9b:65:5a:
+ 8c:e9:0a:f1:41:c6:a1:5c:99:ce:80:c8:da:bf:dc:f0:e3:d7:
+ 52:d6:e9:2b:47:e9:bc:49:77:49:95:39:8f:f0:bc:25:b6:5a:
+ 64:51:6b:10:67:87:7d:21:2a:75:51:c5:8e:88:69:6b:03:5c:
+ 1b:8d:50:b3:e6:13:db:54:c1:a8:7d:61:0f:9c:95:23:b8:23:
+ 56:f7:ca:20:24:5f:c4:3a:65:e6:81:82:97:10:42:54:e4:95:
+ 81:5b:4b:6d:ef:6a:e3:8b:82:04:9d:64:52:4a:1c:f5:5c:f8:
+ ec:7b:fb:27:27:24:82:a5:57:90:e4:56:1e:dd:92:14:d0:0b:
+ c6:53:04:cc:e1:10:4e:9b:82:e4:a7:1b:e2:2e:e1:4c:25:05:
+ fa:4a:64:22:fa:f4:d8:b2:48:9a:ad:00:6a:ac:ec:e9:c0:bf:
+ 0d:c7:49:89:13:cf:c9:f6:8d:f1:f4:2b:c8:5b:fb:ea:08:3d:
+ 8b:cc:d9:71:be:9c:5b:8d:ab:be:e9:a2:cb:0f:bb:9e:76:5f:
+ 9a:ae:77:c1:e6:eb:e5:f0:87:69:78:59:93:8e:c0:48:e2:43:
+ e8:60:b9:71:7f:6b:8d:ed:6a:19:18:14:2d:74:61:a9:cb:ca:
+ 9f:7d:7d:b3:68:97:8f:71:52:b4:30:e8:ac:a1:a0:b0:25:76:
+ cd:89:a8:b5:7d:7b:13:10:7d:f0:27:02:80:11:39:db:33:2d:
+ 45:98:a5:c2:13:86:7a:1f:26:c7:7a:93:97:45:b6:07:cb:8a:
+ 28:0a:b9:04:c9:a2:ce:24:81:cd:0b:7b:d3:42:4f:8a:bb:94:
+ 45:95:db:43:57:c7:d6:7e:e9:28:fe:b0:18:48:76:7b:d4:df:
+ 40:c9:4d:db:48:44:23:e0:8c:b7:ed:5b:1b:40:69:b7:90:1f:
+ 8b:01:3c:53:29:10:fd:6b:e9:57:17:49:ae:08:f4:24:cd:32:
+ 03:7e:ca:7c:2f:17:0f:e7:26:6a:f2:79:fe:0a:e8:ad:b8:f7:
+ b7:4c:c1:74:ae:db:97:e6:38:e5:55:3f:e7:f9:97:e5:94:77:
+ 3b:77:9a:d5:17:25:3c:cd:e1:28:c4:44:7c:f9:de:3a:5a:5a:
+ 9f:aa:f7:75:5c:05:a8:43:7f:4d:e4:ee:e3:ab:ee:0d:e2:9a:
+ 12:bd:f2:26:43:85:7f:2d:25:49:83:e1:3e:1c:e1:53:9b:c3:
+ 47:98:9b:c6:ab:f0:c5:93:73:9c:8d:80:75:39:d9:2c:47:fc:
+ 2d:1a:a7:4d:5a:35:ab:1d`;
+
+const IN_CRL_PEM_RSA_CRL_REASON_AND_INVALIDITY_DATE = `-----BEGIN X509 CRL-----
+MIID9jCCAd4CAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVUsxDzANBgNV
+BAgMBkxvbmRvbjELMAkGA1UECgwCQkIxFTATBgNVBAMMDFRlc3QgUm9vdCBDQRcN
+MjQwODI1MTIwODU2WhcNMjQwOTI0MTIwODU2WjA9MDsCAhAAFw0yNDA4MjUxMjA4
+NDhaMCYwCgYDVR0VBAMKAQEwGAYDVR0YBBEYDzIwMjQwODI1MDAwMDAwWqCCAScw
+ggEjMAkGA1UdEgQCMAAwfQYDVR0jBHYwdIAUuMmt/ahQVNWFbjSLTGAvsEm2h0ah
+RqREMEIxCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xCzAJBgNVBAoMAkJC
+MRUwEwYDVQQDDAxUZXN0IFJvb3QgQ0GCFDddS/a9fBF8zEYa/9cvLCb4Hks9MC0G
+A1UdLgQmMCQwIqAgoB6GHGh0dHA6Ly9leGFtcGxlLmNvbS9kZWx0YS1jcmwwWwYD
+VR0fBFQwUjAhoB+gHYYbaHR0cDovL2V4YW1wbGUuY29tL2Z1bGwtY3JsMCGgH6Ad
+hhtsZGFwOi8vZXhhbXBsZS5jb20vZnVsbC1jcmwwCqAIoAaHBH8AAAEwCwYDVR0U
+BAQCAh49MA0GCSqGSIb3DQEBCwUAA4ICAQByLp7JWQmB1NhlLACH6zFOe31yCTVy
+xJQtgujtSri1LNu6IwzBGsKBQIl3ucwMxPvoZzlujNLmshUT3nSogV0/5n1q0Gyj
+5Yiz2iw8mmKJLmGZ9Oz3QoGxgFww0/0x/VwRHuS2hw+A7JB8tO/2nW3oTclvS55l
+R+VtkDjUN58+Yl2SQksvb3qD6bHHJTCaP7Dskls0fdBIoYIDvZejrTYSSzTX/Kw4
+735P0GBMhj7zVF8azGz2PFpSISg4huJMyp7EDKZf2c2dnkuwmEUlPQEBLX25j/Il
+81OxfVVFja+wUagaGtjEPGy5gsU8zFwkWhjaD5PGBbZvnT+EDsOtJPU7Ot/sBHfz
+XqUtMrfmz/S/GsQ+QCpnBvarBy9QYuk9M0ePBGy33CUQpjPULxuJJVAHxNoetHCv
+7udng2Pi4D8vDNfzbMwHt7HurMo0CsSju+cL4rnIfsz02RrD9WC84KxBLWkqC7Hi
+IKGIpF740Yc4BliVE1HDaOKyI6FEft5asj3OgXwmBw7pVlxSNWACaA2vOFkdN/V5
+XZZjVJdRJxkgEfCvsJVenFp8ND6gmJmWum7tqM5ytmiXjPtejsPpVq4IclG+Yhnr
+tFQ9TDEuCrNsRIGGGDodyXq1+kGXY0w8RqGEb7J4Og/M6r4LMAKPkO7e0nEibTqX
+d2igvR2e5p+yKw==
+-----END X509 CRL-----`;
+
+const OUT_CRL_PEM_RSA_CRL_REASON_AND_INVALIDITY_DATE = `Certificate Revocation List (CRL):
+ Version: 2 (0x1)
+ Signature Algorithm: SHA256withRSA
+ Issuer:
+ C = UK
+ ST = London
+ O = BB
+ CN = Test Root CA
+ Last Update: Sun, 25 Aug 2024 12:08:56 GMT
+ Next Update: Tue, 24 Sep 2024 12:08:56 GMT
+ CRL extensions:
+ 2.5.29.46:
+ Unsupported CRL extension. Try openssl CLI.
+ X509v3 Authority Key Identifier:
+ keyid:B8:C9:AD:FD:A8:50:54:D5:85:6E:34:8B:4C:60:2F:B0:49:B6:87:46
+ DirName:/C=UK/ST=London/O=BB/CN=Test Root CA
+ serial:37:5D:4B:F6:BD:7C:11:7C:CC:46:1A:FF:D7:2F:2C:26:F8:1E:4B:3D
+ X509v3 CRL Distribution Points:
+ Full Name:
+ URI:http://example.com/full-crl
+ Full Name:
+ URI:ldap://example.com/full-crl
+ Full Name:
+ IP:127.0.0.1
+ X509v3 CRL Number:
+ 1E3D
+ issuerAltName:
+ Unsupported CRL extension. Try openssl CLI.
+Revoked Certificates:
+ Serial Number: 1000
+ Revocation Date: Sun, 25 Aug 2024 12:08:48 GMT
+ CRL entry extensions:
+ X509v3 CRL Reason Code:
+ Key Compromise
+ Invalidity Date:
+ Sun, 25 Aug 2024 00:00:00 GMT
+Signature Value:
+ 72:2e:9e:c9:59:09:81:d4:d8:65:2c:00:87:eb:31:4e:7b:7d:
+ 72:09:35:72:c4:94:2d:82:e8:ed:4a:b8:b5:2c:db:ba:23:0c:
+ c1:1a:c2:81:40:89:77:b9:cc:0c:c4:fb:e8:67:39:6e:8c:d2:
+ e6:b2:15:13:de:74:a8:81:5d:3f:e6:7d:6a:d0:6c:a3:e5:88:
+ b3:da:2c:3c:9a:62:89:2e:61:99:f4:ec:f7:42:81:b1:80:5c:
+ 30:d3:fd:31:fd:5c:11:1e:e4:b6:87:0f:80:ec:90:7c:b4:ef:
+ f6:9d:6d:e8:4d:c9:6f:4b:9e:65:47:e5:6d:90:38:d4:37:9f:
+ 3e:62:5d:92:42:4b:2f:6f:7a:83:e9:b1:c7:25:30:9a:3f:b0:
+ ec:92:5b:34:7d:d0:48:a1:82:03:bd:97:a3:ad:36:12:4b:34:
+ d7:fc:ac:38:ef:7e:4f:d0:60:4c:86:3e:f3:54:5f:1a:cc:6c:
+ f6:3c:5a:52:21:28:38:86:e2:4c:ca:9e:c4:0c:a6:5f:d9:cd:
+ 9d:9e:4b:b0:98:45:25:3d:01:01:2d:7d:b9:8f:f2:25:f3:53:
+ b1:7d:55:45:8d:af:b0:51:a8:1a:1a:d8:c4:3c:6c:b9:82:c5:
+ 3c:cc:5c:24:5a:18:da:0f:93:c6:05:b6:6f:9d:3f:84:0e:c3:
+ ad:24:f5:3b:3a:df:ec:04:77:f3:5e:a5:2d:32:b7:e6:cf:f4:
+ bf:1a:c4:3e:40:2a:67:06:f6:ab:07:2f:50:62:e9:3d:33:47:
+ 8f:04:6c:b7:dc:25:10:a6:33:d4:2f:1b:89:25:50:07:c4:da:
+ 1e:b4:70:af:ee:e7:67:83:63:e2:e0:3f:2f:0c:d7:f3:6c:cc:
+ 07:b7:b1:ee:ac:ca:34:0a:c4:a3:bb:e7:0b:e2:b9:c8:7e:cc:
+ f4:d9:1a:c3:f5:60:bc:e0:ac:41:2d:69:2a:0b:b1:e2:20:a1:
+ 88:a4:5e:f8:d1:87:38:06:58:95:13:51:c3:68:e2:b2:23:a1:
+ 44:7e:de:5a:b2:3d:ce:81:7c:26:07:0e:e9:56:5c:52:35:60:
+ 02:68:0d:af:38:59:1d:37:f5:79:5d:96:63:54:97:51:27:19:
+ 20:11:f0:af:b0:95:5e:9c:5a:7c:34:3e:a0:98:99:96:ba:6e:
+ ed:a8:ce:72:b6:68:97:8c:fb:5e:8e:c3:e9:56:ae:08:72:51:
+ be:62:19:eb:b4:54:3d:4c:31:2e:0a:b3:6c:44:81:86:18:3a:
+ 1d:c9:7a:b5:fa:41:97:63:4c:3c:46:a1:84:6f:b2:78:3a:0f:
+ cc:ea:be:0b:30:02:8f:90:ee:de:d2:71:22:6d:3a:97:77:68:
+ a0:bd:1d:9e:e6:9f:b2:2b`;
+
+const IN_CRL_PEM_RSA_CRL_EXTENSIONS = `-----BEGIN X509 CRL-----
+MIIE0DCCArgCAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVUsxDzANBgNV
+BAgMBkxvbmRvbjELMAkGA1UECgwCQkIxFTATBgNVBAMMDFRlc3QgUm9vdCBDQRcN
+MjQwODI1MTIzNzEwWhcNMjQwOTI0MTIzNzEwWjA9MDsCAhAAFw0yNDA4MjUxMjA4
+NDhaMCYwCgYDVR0VBAMKAQEwGAYDVR0YBBEYDzIwMjQwODI1MDAwMDAwWqCCAgEw
+ggH9MIHiBgNVHRIEgdowgdegFAYEKgMEBaAMFgpDdXN0b21OYW1lgQ5jYUBleGFt
+cGxlLmNvbYYSaHR0cDovL2V4YW1wbGUuY29tgg5jYS5leGFtcGxlLmNvbYcEwKgB
+AaSBhDCBgTELMAkGA1UEBhMCVVMxFTATBgNVBAgMDEV4YW1wbGVTdGF0ZTEUMBIG
+A1UEBwwLRXhhbXBsZUNpdHkxEzARBgNVBAoMCkV4YW1wbGVPcmcxFDASBgNVBAsM
+C0V4YW1wbGVVbml0MRowGAYDVQQDDBFFeGFtcGxlQ29tbW9uTmFtZTB9BgNVHSME
+djB0gBS4ya39qFBU1YVuNItMYC+wSbaHRqFGpEQwQjELMAkGA1UEBhMCVUsxDzAN
+BgNVBAgMBkxvbmRvbjELMAkGA1UECgwCQkIxFTATBgNVBAMMDFRlc3QgUm9vdCBD
+QYIUN11L9r18EXzMRhr/1y8sJvgeSz0wLQYDVR0uBCYwJDAioCCgHoYcaHR0cDov
+L2V4YW1wbGUuY29tL2RlbHRhLWNybDBbBgNVHR8EVDBSMCGgH6AdhhtodHRwOi8v
+ZXhhbXBsZS5jb20vZnVsbC1jcmwwIaAfoB2GG2xkYXA6Ly9leGFtcGxlLmNvbS9m
+dWxsLWNybDAKoAigBocEfwAAATALBgNVHRQEBAICHkIwDQYJKoZIhvcNAQELBQAD
+ggIBAF/9L4aGmId2igw7+MfDxokevIJkJX/MkmHpXBl1b4hL85FGD7OPCmn47VzC
+Wejlc/AQB7mWyUugvrVEq/FiCO8a8Fieyjw5uCYz0eiNnuvHVRGM2mOEkiA0I/rn
+F5AFB1YfCFGXPyRkXNRbOBE91mhOzh1H9PX2qVnj5l3KsPE/7YuteacR0TkfkRJa
+BXLic+5F/CCV/J/iYR7LncuLUlhBfsosG/ucHL70EytlfX6CBWY3kBbmj7nd497T
+QG392+m9xp7MIsJAS+3qEzwJAfni6zUV0fWh/ucOl8BIjHEh97VqI3+8yzhdXfkF
+2gkfpkqJQY0+5OO1VSRYTlQNld3QjN/VVJjatfHyaXfPCx4VEKW1kWYo+0zxO4SL
+SB/+S/o99bCeNy1MXqEvy5HoDwFHePXGsAEPHWPdj7EWm7g9T/Fl1iSR6hpohvDD
+K4LaGdVhzvCraLIh8H7XW3KztvZvDQejYQAgADW0UO0rFHJ1XXhKYSqXNGnfDt+3
+cRpt2XxSxt5HJtHlatiI25PuBMNWV2Zod4RHB/8UEvs1KC7dcwkAiCEY+E3o/zkC
+rdZ/8XtNf5a4WSN/D7pPsfsO6SE+7lxkJ+UQcZLXAz8b5ArPTlWt2HdJIBEVs25K
+FAkizyldhnAcNHFk7XN94eTLNeD6hUbFL9pNHiSmKu5A9YW0
+-----END X509 CRL-----`;
+
+const OUT_CRL_PEM_RSA_CRL_EXTENSIONS = `Certificate Revocation List (CRL):
+ Version: 2 (0x1)
+ Signature Algorithm: SHA256withRSA
+ Issuer:
+ C = UK
+ ST = London
+ O = BB
+ CN = Test Root CA
+ Last Update: Sun, 25 Aug 2024 12:37:10 GMT
+ Next Update: Tue, 24 Sep 2024 12:37:10 GMT
+ CRL extensions:
+ 2.5.29.46:
+ Unsupported CRL extension. Try openssl CLI.
+ X509v3 Authority Key Identifier:
+ keyid:B8:C9:AD:FD:A8:50:54:D5:85:6E:34:8B:4C:60:2F:B0:49:B6:87:46
+ DirName:/C=UK/ST=London/O=BB/CN=Test Root CA
+ serial:37:5D:4B:F6:BD:7C:11:7C:CC:46:1A:FF:D7:2F:2C:26:F8:1E:4B:3D
+ X509v3 CRL Distribution Points:
+ Full Name:
+ URI:http://example.com/full-crl
+ Full Name:
+ URI:ldap://example.com/full-crl
+ Full Name:
+ IP:127.0.0.1
+ X509v3 CRL Number:
+ 1E42
+ X509v3 Issuer Alternative Name:
+ OtherName:1.2.3.4.5::CustomName
+ EMAIL:ca@example.com
+ URI:http://example.com
+ DNS:ca.example.com
+ IP:192.168.1.1
+ DIR:/C=US/ST=ExampleState/L=ExampleCity/O=ExampleOrg/OU=ExampleUnit/CN=ExampleCommonName
+Revoked Certificates:
+ Serial Number: 1000
+ Revocation Date: Sun, 25 Aug 2024 12:08:48 GMT
+ CRL entry extensions:
+ X509v3 CRL Reason Code:
+ Key Compromise
+ Invalidity Date:
+ Sun, 25 Aug 2024 00:00:00 GMT
+Signature Value:
+ 5f:fd:2f:86:86:98:87:76:8a:0c:3b:f8:c7:c3:c6:89:1e:bc:
+ 82:64:25:7f:cc:92:61:e9:5c:19:75:6f:88:4b:f3:91:46:0f:
+ b3:8f:0a:69:f8:ed:5c:c2:59:e8:e5:73:f0:10:07:b9:96:c9:
+ 4b:a0:be:b5:44:ab:f1:62:08:ef:1a:f0:58:9e:ca:3c:39:b8:
+ 26:33:d1:e8:8d:9e:eb:c7:55:11:8c:da:63:84:92:20:34:23:
+ fa:e7:17:90:05:07:56:1f:08:51:97:3f:24:64:5c:d4:5b:38:
+ 11:3d:d6:68:4e:ce:1d:47:f4:f5:f6:a9:59:e3:e6:5d:ca:b0:
+ f1:3f:ed:8b:ad:79:a7:11:d1:39:1f:91:12:5a:05:72:e2:73:
+ ee:45:fc:20:95:fc:9f:e2:61:1e:cb:9d:cb:8b:52:58:41:7e:
+ ca:2c:1b:fb:9c:1c:be:f4:13:2b:65:7d:7e:82:05:66:37:90:
+ 16:e6:8f:b9:dd:e3:de:d3:40:6d:fd:db:e9:bd:c6:9e:cc:22:
+ c2:40:4b:ed:ea:13:3c:09:01:f9:e2:eb:35:15:d1:f5:a1:fe:
+ e7:0e:97:c0:48:8c:71:21:f7:b5:6a:23:7f:bc:cb:38:5d:5d:
+ f9:05:da:09:1f:a6:4a:89:41:8d:3e:e4:e3:b5:55:24:58:4e:
+ 54:0d:95:dd:d0:8c:df:d5:54:98:da:b5:f1:f2:69:77:cf:0b:
+ 1e:15:10:a5:b5:91:66:28:fb:4c:f1:3b:84:8b:48:1f:fe:4b:
+ fa:3d:f5:b0:9e:37:2d:4c:5e:a1:2f:cb:91:e8:0f:01:47:78:
+ f5:c6:b0:01:0f:1d:63:dd:8f:b1:16:9b:b8:3d:4f:f1:65:d6:
+ 24:91:ea:1a:68:86:f0:c3:2b:82:da:19:d5:61:ce:f0:ab:68:
+ b2:21:f0:7e:d7:5b:72:b3:b6:f6:6f:0d:07:a3:61:00:20:00:
+ 35:b4:50:ed:2b:14:72:75:5d:78:4a:61:2a:97:34:69:df:0e:
+ df:b7:71:1a:6d:d9:7c:52:c6:de:47:26:d1:e5:6a:d8:88:db:
+ 93:ee:04:c3:56:57:66:68:77:84:47:07:ff:14:12:fb:35:28:
+ 2e:dd:73:09:00:88:21:18:f8:4d:e8:ff:39:02:ad:d6:7f:f1:
+ 7b:4d:7f:96:b8:59:23:7f:0f:ba:4f:b1:fb:0e:e9:21:3e:ee:
+ 5c:64:27:e5:10:71:92:d7:03:3f:1b:e4:0a:cf:4e:55:ad:d8:
+ 77:49:20:11:15:b3:6e:4a:14:09:22:cf:29:5d:86:70:1c:34:
+ 71:64:ed:73:7d:e1:e4:cb:35:e0:fa:85:46:c5:2f:da:4d:1e:
+ 24:a6:2a:ee:40:f5:85:b4`;
+
+
+TestRegister.addTests([
+ {
+ name: "Parse X.509 CRL: Example PEM encoded CRL with RSA signature",
+ input: IN_CRL_PEM_RSA,
+ expectedOutput: OUT_CRL_PEM_RSA,
+ recipeConfig: [
+ {
+ "op": "Parse X.509 CRL",
+ "args": ["PEM"]
+ }
+ ]
+ },
+ {
+ name: "Parse X.509 CRL: Example PEM encoded CRL with RSA signature, CRL Reason and Invalidity Date",
+ input: IN_CRL_PEM_RSA_CRL_REASON_AND_INVALIDITY_DATE,
+ expectedOutput: OUT_CRL_PEM_RSA_CRL_REASON_AND_INVALIDITY_DATE,
+ recipeConfig: [
+ {
+ "op": "Parse X.509 CRL",
+ "args": ["PEM"]
+ }
+ ]
+ },
+ {
+ name: "Parse X.509 CRL: Example PEM encoded CRL with RSA signature and CRL Extensions",
+ input: IN_CRL_PEM_RSA_CRL_EXTENSIONS,
+ expectedOutput: OUT_CRL_PEM_RSA_CRL_EXTENSIONS,
+ recipeConfig: [
+ {
+ "op": "Parse X.509 CRL",
+ "args": ["PEM"]
+ }
+ ]
+ },
+]);
diff --git a/tests/operations/tests/StripIPv4Header.mjs b/tests/operations/tests/StripIPv4Header.mjs
new file mode 100644
index 00000000..739bc70d
--- /dev/null
+++ b/tests/operations/tests/StripIPv4Header.mjs
@@ -0,0 +1,126 @@
+/**
+ * Strip IPv4 header tests.
+ *
+ * @author c65722 []
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "Strip IPv4 header: No options, No payload",
+ input: "450000140005400080060000c0a80001c0a80002",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip IPv4 header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip IPv4 header: No options, Payload",
+ input: "450000140005400080060000c0a80001c0a80002ffffffffffffffff",
+ expectedOutput: "ffffffffffffffff",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip IPv4 header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip IPv4 header: Options, No payload",
+ input: "460000140005400080060000c0a80001c0a8000207000000",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip IPv4 header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip IPv4 header: Options, Payload",
+ input: "460000140005400080060000c0a80001c0a8000207000000ffffffffffffffff",
+ expectedOutput: "ffffffffffffffff",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip IPv4 header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip IPv4 header: Input length lesss than minimum header length",
+ input: "450000140005400080060000c0a80001c0a800",
+ expectedOutput: "Input length is less than minimum IPv4 header length",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip IPv4 header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip IPv4 header: Input length less than IHL",
+ input: "460000140005400080060000c0a80001c0a80000",
+ expectedOutput: "Input length is less than IHL",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip IPv4 header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ }
+]);
diff --git a/tests/operations/tests/StripTCPHeader.mjs b/tests/operations/tests/StripTCPHeader.mjs
new file mode 100644
index 00000000..1d98d411
--- /dev/null
+++ b/tests/operations/tests/StripTCPHeader.mjs
@@ -0,0 +1,126 @@
+/**
+ * Strip TCP header tests.
+ *
+ * @author c65722 []
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "Strip TCP header: No options, No payload",
+ input: "7f900050000fa4b2000cb2a45010bff100000000",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip TCP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip TCP header: No options, Payload",
+ input: "7f900050000fa4b2000cb2a45010bff100000000ffffffffffffffff",
+ expectedOutput: "ffffffffffffffff",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip TCP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip TCP header: Options, No payload",
+ input: "7f900050000fa4b2000cb2a47010bff100000000020405b404020000",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip TCP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip TCP header: Options, Payload",
+ input: "7f900050000fa4b2000cb2a47010bff100000000020405b404020000ffffffffffffffff",
+ expectedOutput: "ffffffffffffffff",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip TCP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip TCP header: Input length less than minimum header length",
+ input: "7f900050000fa4b2000cb2a45010bff1000000",
+ expectedOutput: "Need at least 20 bytes for a TCP Header",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip TCP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip TCP header: Input length less than data offset",
+ input: "7f900050000fa4b2000cb2a47010bff100000000",
+ expectedOutput: "Input length is less than data offset",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip TCP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ }
+]);
diff --git a/tests/operations/tests/StripUDPHeader.mjs b/tests/operations/tests/StripUDPHeader.mjs
new file mode 100644
index 00000000..d753ec50
--- /dev/null
+++ b/tests/operations/tests/StripUDPHeader.mjs
@@ -0,0 +1,69 @@
+/**
+ * Strip UDP header tests.
+ *
+ * @author c65722 []
+ * @copyright Crown Copyright 2024
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "Strip UDP header: No payload",
+ input: "8111003500000000",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip UDP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip UDP header: Payload",
+ input: "8111003500080000ffffffffffffffff",
+ expectedOutput: "ffffffffffffffff",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip UDP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ },
+ {
+ name: "Strip UDP header: Input length less than header length",
+ input: "81110035000000",
+ expectedOutput: "Need 8 bytes for a UDP Header",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["None"]
+ },
+ {
+ op: "Strip UDP header",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["None", 0]
+ }
+ ]
+ }
+]);
diff --git a/webpack.config.js b/webpack.config.js
index e77f2ab9..f23fab0e 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,8 +1,10 @@
const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const CompressionPlugin = require("compression-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { ModifySourcePlugin, ReplaceOperation } = require("modify-source-webpack-plugin");
const path = require("path");
+const zlib = require("zlib");
/**
* Webpack configuration details for use with Grunt.
@@ -64,6 +66,21 @@ module.exports = {
new MiniCssExtractPlugin({
filename: "assets/[name].css"
}),
+ new CompressionPlugin({
+ filename: "[path][base].gz",
+ algorithm: "gzip",
+ test: /\.(js|css|html)$/,
+ }),
+ new CompressionPlugin({
+ filename: "[path][base].br",
+ algorithm: "brotliCompress",
+ test: /\.(js|css|html)$/,
+ compressionOptions: {
+ params: {
+ [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
+ },
+ },
+ }),
new CopyWebpackPlugin({
patterns: [
{