From e65869a10b2fd4dd414447aeb8bbeac184ade8f0 Mon Sep 17 00:00:00 2001 From: Robin Sandhu Date: Sun, 25 Aug 2024 04:52:51 +0100 Subject: [PATCH 01/12] Add operation for parsing X.509 CRLs Signed-off-by: Robin Sandhu --- src/core/config/Categories.json | 1 + src/core/operations/ParseX509CRL.mjs | 335 +++++++++++++++++++++++++++ 2 files changed, 336 insertions(+) create mode 100644 src/core/operations/ParseX509CRL.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index bebdd6a5..2ddc8b23 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", diff --git a/src/core/operations/ParseX509CRL.mjs b/src/core/operations/ParseX509CRL.mjs new file mode 100644 index 00000000..aa084282 --- /dev/null +++ b/src/core/operations/ParseX509CRL.mjs @@ -0,0 +1,335 @@ +/** + * @author robinsandhu + * @copyright Crown Copyright 2024 + * @license Apache-2.0 + */ + +import r from "jsrsasign"; +import Operation from "../Operation.mjs"; +import { formatDnObj } from "../lib/PublicKey.mjs"; +import OperationError from "../errors/OperationError.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"] + } + ]; + 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 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) => { + out += `\tFull Name:\n`; + distPoint.dpname.full.forEach((name) => { + if (Object.hasOwn(name, "ip")) { + out += `\t\tIP:${name.ip}\n`; + } + if (Object.hasOwn(name, "dns")) { + out += `\t\tDNS:${name.dns}\n`; + } + if (Object.hasOwn(name, "uri")) { + out += `\t\tURI:${name.uri}\n`; + } + if (Object.hasOwn(name, "rfc822")) { + out += `\t\tEMAIL:${name.rfc822}\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; + default: + out += `${ext.extname}:\n`; + out += `\tUnsupported CRL extension. Try openssl CLI.\n`; + 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; From dbc90090cf790382f9bb7b6978903150f7fe44fd Mon Sep 17 00:00:00 2001 From: Robin Sandhu Date: Sun, 25 Aug 2024 05:44:45 +0100 Subject: [PATCH 02/12] Add support for multiple input format i.e. DER Hex, Base64, Raw --- src/core/operations/ParseX509CRL.mjs | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/core/operations/ParseX509CRL.mjs b/src/core/operations/ParseX509CRL.mjs index aa084282..91509ff1 100644 --- a/src/core/operations/ParseX509CRL.mjs +++ b/src/core/operations/ParseX509CRL.mjs @@ -6,8 +6,11 @@ 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 @@ -30,7 +33,7 @@ class ParseX509CRL extends Operation { { "name": "Input format", "type": "option", - "value": ["PEM"] + "value": ["PEM", "DER Hex", "Base64", "Raw"] } ]; this.checks = [ @@ -52,6 +55,32 @@ class ParseX509CRL extends Operation { 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"; + + console.log(input); + const crl = new r.X509CRL(input); let out = `Certificate Revocation List (CRL): From a50d4d63eb2b424a23fac7502c7104dce30866c4 Mon Sep 17 00:00:00 2001 From: Robin Sandhu Date: Sun, 25 Aug 2024 14:15:00 +0100 Subject: [PATCH 03/12] Format issuerAltName CRL extension --- src/core/operations/ParseX509CRL.mjs | 64 ++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/core/operations/ParseX509CRL.mjs b/src/core/operations/ParseX509CRL.mjs index 91509ff1..8681f66a 100644 --- a/src/core/operations/ParseX509CRL.mjs +++ b/src/core/operations/ParseX509CRL.mjs @@ -79,8 +79,6 @@ class ParseX509CRL extends Operation { } if (undefinedInputFormat) throw "Undefined input format"; - console.log(input); - const crl = new r.X509CRL(input); let out = `Certificate Revocation List (CRL): @@ -178,24 +176,12 @@ function formatCRLExtensions(extensions, indent) { } break; case "cRLDistributionPoints": - out += `X509v3 CRL Distribution Points:\n`; + out += `X509v3 CRL Distribution Points:`; ext.array.forEach((distPoint) => { - out += `\tFull Name:\n`; - distPoint.dpname.full.forEach((name) => { - if (Object.hasOwn(name, "ip")) { - out += `\t\tIP:${name.ip}\n`; - } - if (Object.hasOwn(name, "dns")) { - out += `\t\tDNS:${name.dns}\n`; - } - if (Object.hasOwn(name, "uri")) { - out += `\t\tURI:${name.uri}\n`; - } - if (Object.hasOwn(name, "rfc822")) { - out += `\t\tEMAIL:${name.rfc822}\n`; - } - }); + const fullName = `\nFull Name:\n${formatGeneralNames(distPoint.dpname.full, 4)}`; + out += indentString(fullName, 4); }); + out += `\n`; break; case "cRLNumber": if (!Object.hasOwn(ext, "num")) { @@ -203,6 +189,9 @@ function formatCRLExtensions(extensions, indent) { } 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`; @@ -213,6 +202,45 @@ function formatCRLExtensions(extensions, indent) { 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 From 1fde2fba29f4076567e312ee0b2e26bf63077619 Mon Sep 17 00:00:00 2001 From: Robin Sandhu Date: Sun, 25 Aug 2024 14:26:14 +0100 Subject: [PATCH 04/12] Add basic tests for Parse X.509 CRL operations --- src/core/operations/ParseX509CRL.mjs | 7 +- tests/operations/tests/ParseX509CRL.mjs | 331 ++++++++++++++++++++++++ 2 files changed, 334 insertions(+), 4 deletions(-) create mode 100644 tests/operations/tests/ParseX509CRL.mjs diff --git a/src/core/operations/ParseX509CRL.mjs b/src/core/operations/ParseX509CRL.mjs index 8681f66a..f498375d 100644 --- a/src/core/operations/ParseX509CRL.mjs +++ b/src/core/operations/ParseX509CRL.mjs @@ -176,12 +176,11 @@ function formatCRLExtensions(extensions, indent) { } break; case "cRLDistributionPoints": - out += `X509v3 CRL Distribution Points:`; + out += `X509v3 CRL Distribution Points:\n`; ext.array.forEach((distPoint) => { - const fullName = `\nFull Name:\n${formatGeneralNames(distPoint.dpname.full, 4)}`; - out += indentString(fullName, 4); + const fullName = `Full Name:\n${formatGeneralNames(distPoint.dpname.full, 4)}`; + out += indentString(fullName, 4) + "\n"; }); - out += `\n`; break; case "cRLNumber": if (!Object.hasOwn(ext, "num")) { 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"] + } + ] + }, +]); From da74d9b22d23d915f85dba54c2dcdc61cca68de8 Mon Sep 17 00:00:00 2001 From: c65722 <53181351+c65722@users.noreply.github.com> Date: Sun, 15 Sep 2024 18:14:21 +0100 Subject: [PATCH 05/12] Add Strip UDP header operation --- src/core/config/Categories.json | 1 + src/core/operations/StripUDPHeader.mjs | 51 +++++++++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/StripUDPHeader.mjs | 69 +++++++++++++++++++++++ 4 files changed, 122 insertions(+) create mode 100644 src/core/operations/StripUDPHeader.mjs create mode 100644 tests/operations/tests/StripUDPHeader.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index bebdd6a5..e9599b7c 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -236,6 +236,7 @@ "Parse IPv4 header", "Parse TCP", "Parse UDP", + "Strip UDP header", "Parse SSH Host Key", "Parse URI", "URL Encode", 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/tests/operations/index.mjs b/tests/operations/index.mjs index 40ce7a2e..468c1481 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -142,6 +142,7 @@ 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/StripUDPHeader.mjs"; import "./tests/Subsection.mjs"; import "./tests/SwapCase.mjs"; import "./tests/SymmetricDifference.mjs"; 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] + } + ] + } +]); From 748379f0b07df948cf9074d2d500b5bc5484e1da Mon Sep 17 00:00:00 2001 From: c65722 <53181351+c65722@users.noreply.github.com> Date: Sun, 15 Sep 2024 14:29:35 +0100 Subject: [PATCH 06/12] Add Strip TCP header operation --- src/core/config/Categories.json | 1 + src/core/operations/StripTCPHeader.mjs | 60 +++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/StripTCPHeader.mjs | 126 ++++++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 src/core/operations/StripTCPHeader.mjs create mode 100644 tests/operations/tests/StripTCPHeader.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index bebdd6a5..f19f0a7d 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -235,6 +235,7 @@ "Parse IPv6 address", "Parse IPv4 header", "Parse TCP", + "Strip TCP header", "Parse UDP", "Parse SSH Host Key", "Parse URI", 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/tests/operations/index.mjs b/tests/operations/index.mjs index 40ce7a2e..7b762205 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -142,6 +142,7 @@ 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/StripTCPHeader.mjs"; import "./tests/Subsection.mjs"; import "./tests/SwapCase.mjs"; import "./tests/SymmetricDifference.mjs"; 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] + } + ] + } +]); From 1fcc365d9e04568235e7a0dd6ff249586c6ad818 Mon Sep 17 00:00:00 2001 From: c65722 <53181351+c65722@users.noreply.github.com> Date: Sat, 20 Jul 2019 07:58:33 -0700 Subject: [PATCH 07/12] Add Parse TLS record operation --- src/core/config/Categories.json | 1 + src/core/lib/Protocol.mjs | 3 + src/core/operations/ParseTLSRecord.mjs | 884 +++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/ParseTLSRecord.mjs | 2045 +++++++++++++++++++++ 5 files changed, 2934 insertions(+) create mode 100644 src/core/operations/ParseTLSRecord.mjs create mode 100644 tests/operations/tests/ParseTLSRecord.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index bebdd6a5..bd29dcb2 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -235,6 +235,7 @@ "Parse IPv6 address", "Parse IPv4 header", "Parse TCP", + "Parse TLS record", "Parse UDP", "Parse SSH Host Key", "Parse URI", 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/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/tests/operations/index.mjs b/tests/operations/index.mjs index 40ce7a2e..40b510a0 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -115,6 +115,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"; 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: [] + } + ] + } +]); From 71c8c8aac04b2c875f09796fad6229e684714086 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Sat, 4 Jan 2025 18:15:10 +0100 Subject: [PATCH 08/12] Add 'header' ingredient to JWT Sign operation Expose the 'header' option of the jsonwebtoken module [1] as an ingredient. This allows for adding and overwriting JWT header fields such as 'type' or 'kid'. [1]: https://github.com/auth0/node-jsonwebtoken?tab=readme-ov-file#usage --- src/core/operations/JWTSign.mjs | 10 ++++++++-- tests/operations/tests/JWTSign.mjs | 29 ++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 11 deletions(-) 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/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", From 6a92f922cb152c7af3e647dc01aea2c91e9b6d70 Mon Sep 17 00:00:00 2001 From: Simon Arnell <14029547+simonarnell@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:32:57 +0200 Subject: [PATCH 09/12] corrected path to generateNodeIndex.mjs --- src/node/config/scripts/generateNodeIndex.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } From cc7cc7f8fd67f9708408d960cce6940af55e3e7f Mon Sep 17 00:00:00 2001 From: Guilhem Radonde Date: Fri, 10 Jan 2025 09:32:41 +0100 Subject: [PATCH 10/12] misc: typo --- src/core/operations/JWTVerify.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"; From 5455061e152cc9ba20193d80e119226460f5ba4b Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 10 Feb 2025 23:53:55 +0000 Subject: [PATCH 11/12] Atomatically detect chrome driver version --- .github/workflows/master.yml | 1 + 1 file changed, 1 insertion(+) 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 From 3d017d5f84e5f9c13ae00c87a4b875037386f7dc Mon Sep 17 00:00:00 2001 From: a3957273 <89583054+a3957273@users.noreply.github.com> Date: Tue, 11 Feb 2025 00:02:39 +0000 Subject: [PATCH 12/12] Add it to the correct file. --- .github/workflows/pull_requests.yml | 1 + 1 file changed, 1 insertion(+) 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