{"record":{"id":"b1b3eb7c29ba9731","repo":"gchq/CyberChef","slug":"not-handshake-data-b1b3eb","errorCode":null,"errorMessage":"Not handshake data.","messagePattern":"Not handshake data\\.","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/JA3SFingerprint.mjs","lineNumber":64,"sourceCode":"                value: [\"Hash digest\", \"JA3S string\", \"Full details\"]\n            }\n        ];\n    }\n\n    /**\n     * @param {string} input\n     * @param {Object[]} args\n     * @returns {string}\n     */\n    run(input, args) {\n        const [inputFormat, outputFormat] = args;\n\n        input = Utils.convertToByteArray(input, inputFormat);\n        const s = new Stream(new Uint8Array(input));\n\n        const handshake = s.readInt(1);\n        if (handshake !== 0x16)\n            throw new OperationError(\"Not handshake data.\");\n\n        // Version\n        s.moveForwardsBy(2);\n\n        // Length\n        const length = s.readInt(2);\n        if (s.length !== length + 5)\n            throw new OperationError(\"Incorrect handshake length.\");\n\n        // Handshake type\n        const handshakeType = s.readInt(1);\n        if (handshakeType !== 2)\n            throw new OperationError(\"Not a Server Hello.\");\n\n        // Handshake length\n        const handshakeLength = s.readInt(3);\n        if (s.length !== handshakeLength + 9)\n            throw new OperationError(\"Not enough data in Server Hello.\");","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/JA3SFingerprint.mjs#L46-L82","documentation":"JA3SFingerprint.run() reads the first byte of the TLS record and requires 0x16 (Handshake). Anything else (or undefined for empty input) throws. OperationError surfaced as step output. Mirror of the JA3 check but for the server side.","triggerScenarios":"Input is not a TLS record, inputFormat does not match the data (convertToByteArray yields wrong bytes), the record does not start at a record boundary, or an application-data/other content type was fed.","commonSituations":"Captured a full session instead of just the ServerHello; inputFormat selector mismatch; fed an HTTP response or random bytes; started reading mid-record.","solutions":["Verify the first byte is 0x16; re-slice to the record boundary if not.","Match inputFormat to the real data (Hex/Base64/Raw).","Extract only the ServerHello record.","If the byte is 0x17 (application data), you are past the handshake."],"exampleFix":"// before\nja3s.run('HTTP/1.1 200 OK\\r\\n', ['Latin1','Base64']); // 'H'=0x48 -> Not handshake data.\n// after\nconst rec = serverHelloStartingWith16;\nja3s.run(rec, ['Hex','Base64']);","handlingStrategy":"validation","validationCode":"import Utils from \"src/core/Utils.mjs\";\nfunction assertTlsHandshakeRecord(input, inputFormat) {\n  const bytes = Utils.convertToByteArray(input, inputFormat);\n  if (bytes.length < 1) throw new Error('empty input');\n  if (bytes[0] !== 0x16) {\n    throw new Error(`First byte 0x${bytes[0].toString(16)} is not a TLS Handshake (0x16). Check inputFormat and record boundary.`);\n  }\n  return bytes;\n}","typeGuard":"function looksLikeTlsHandshake(bytes) {\n  return bytes.length >= 5 && bytes[0] === 0x16 && bytes[1] === 0x03;\n}","tryCatchPattern":null,"preventionTips":["Verify the first byte is 0x16 before running JA3S.","Keep inputFormat in sync with the data.","Feed a single record, not a full session.","Capture exactly the ServerHello for JA3S."],"tags":["tls","network","parsing","cyberchef","input-validation","ja3s"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}