gchq/CyberChef · error · OperationError

Not a Key Exchange Init.

Error message

Not a Key Exchange Init.

What it means

Thrown by HASSH Client Fingerprint after the length/padding bytes are consumed. The SSH message code byte is read and compared against 20 (SSH_MSG_KEXINIT). Any other value means the record is not the key-exchange init message, so the HASSH algorithm string cannot be extracted.

Source

Thrown at src/core/operations/HASSHClientFingerprint.mjs:76

     */
    run(input, args) {
        const [inputFormat, outputFormat] = args;

        input = Utils.convertToByteArray(input, inputFormat);
        const s = new Stream(new Uint8Array(input));

        // Length
        const length = s.readInt(4);
        if (s.length !== length + 4)
            throw new OperationError("Incorrect packet length.");

        // Padding length
        const paddingLength = s.readInt(1);

        // Message code
        const messageCode = s.readInt(1);
        if (messageCode !== 20)
            throw new OperationError("Not a Key Exchange Init.");

        // Cookie
        s.moveForwardsBy(16);

        // KEX Algorithms
        const kexAlgosLength = s.readInt(4);
        const kexAlgos = s.readString(kexAlgosLength);

        // Server Host Key Algorithms
        const serverHostKeyAlgosLength = s.readInt(4);
        s.moveForwardsBy(serverHostKeyAlgosLength);

        // Encryption Algorithms Client to Server
        const encAlgosC2SLength = s.readInt(4);
        const encAlgosC2S = s.readString(encAlgosC2SLength);

        // Encryption Algorithms Server to Client
        const encAlgosS2CLength = s.readInt(4);

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Confirm the packet is the client's SSH_MSG_KEXINIT (5th byte after the 4-byte length is decimal 20 / 0x14).
  2. Use HASSH Server Fingerprint if the bytes came from the server direction.
  3. Re-capture and pick the earliest KEX_INIT record in the TCP stream.
  4. Check byte 5 (0-indexed offset 5) equals 0x14 before invoking.

Example fix

// before: bytes are from a NEWKEYS (0x15) packet
run(newkeysHex, ["Hex", "Hash"]);
// after: select the record whose 5th byte is 0x14
const kexInit = records.find(r => r[5] === 0x14);
run(kexInit, ["Hex", "Hash"]);
Defensive patterns

Strategy: validation

Validate before calling

function isKexInit(bytes) {
  // message code sits at offset 5 (4-byte length + 1 padding-length byte)
  return bytes.length >= 6 && bytes[5] === 20;
}

Type guard

function isClientKexInit(bytes) {
  return isCompleteSshPacket(bytes) && bytes[5] === 20;
}

Try / catch

try {
  hash = hasshClient.run(hexInput, args);
} catch (e) {
  if (e instanceof OperationError && /Key Exchange Init/i.test(e.message)) {
    // wrong message type - try the next record
    hash = await nextRecord();
  } else throw e;
}

Prevention

When it happens

Trigger: Feeding a valid SSH packet that is a different message type (NEWKEYS=21, KEX_DH_INIT=30, DISCONNECT=1, service request, etc.); feeding the server's KEX_INIT to the client fingerprint operation; feeding a later-stage packet.

Common situations: Captured the wrong handshake message; used server-direction bytes in the client operation; reordered a multi-packet capture; the connection failed before KEXINIT and only a DISCONNECT was sent.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/ed0f844b3a4f61af. Report an issue: GitHub.