{"record":{"id":"488d7f48bc8528e7","repo":"n0-computer/iroh","slug":"mismatchedsuffix","errorCode":"MismatchedSuffix","errorMessage":"Client didn't extract the same keying material, the suffix mismatched: expected {expected:X?} but got {actual:X?}","messagePattern":"Client didn't extract the same keying material, the suffix mismatched: expected (.+?) but got (.+?)","errorType":"error_code","errorClass":"VerificationError","httpStatus":null,"severity":"error","filePath":"iroh-relay/src/protos/handshake.rs","lineNumber":309,"sourceCode":"    ) -> Result<(), Box<VerificationError>> {\n        let key_material = io\n            .export_keying_material(\n                [0u8; 32],\n                DOMAIN_SEP_TLS_EXPORT_LABEL,\n                Some(self.public_key.as_bytes()),\n            )\n            .ok_or_else(|| e!(VerificationError::NoKeyingMaterial))?;\n        // We split the export and only sign the first 16 bytes, and\n        // pass through the last 16 bytes.\n        // Passing on the suffix helps the verifying end figure out what\n        // went wrong: If there's a suffix mismatch, then the exported keying\n        // material on both ends wasn't the same - so perhaps there was a\n        // TLS proxy in between or similar.\n        // If the suffix does match, but the signature doesn't verify, then\n        // there must be something wrong with the client's secret key or signature.\n        let (message, suffix) = key_material.split_at(16);\n        let suffix: [u8; 16] = suffix.try_into().expect(\"hardcoded length\");\n        ensure!(\n            suffix == self.key_material_suffix,\n            VerificationError::MismatchedSuffix {\n                expected: self.key_material_suffix,\n                actual: suffix\n            }\n        );\n        // NOTE: We don't blake3-hash here as we do it in [`ServerChallenge::message_to_sign`],\n        // because we already have a domain separation string and keyed hashing step in\n        // the TLS export keying material above.\n        self.public_key\n            .verify(message, &Signature::from_bytes(&self.signature))\n            .map_err(|err| {\n                e!(VerificationError::SignatureInvalid {\n                    source: err,\n                    message: message.to_vec(),\n                    public_key: self.public_key,\n                    signature: self.signature\n                })","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/n0-computer/iroh/blob/2b4de030ce5e0133f272871a76f0c685c63f552a/iroh-relay/src/protos/handshake.rs#L291-L327","documentation":"VerificationError::MismatchedSuffix is returned by the server side of the relay handshake when the client's derived keying material has a different 16-byte suffix than the server's. This means both ends did not derive the same shared secret from the TLS channel, typically because a middlebox (TLS proxy/MITM) altered the channel. It distinguishes this failure from a bad client signature.","triggerScenarios":"Client connects to the relay through a TLS-intercepting proxy or corporate MITM appliance; mismatched TLS exporter configuration between client and server during handshake verification (serverside::verify).","commonSituations":"Corporate networks with TLS inspection proxies; VPNs or firewalls that re-terminate TLS; version mismatches where client and relay derive keying material differently.","solutions":["Bypass any TLS-intercepting proxy for the relay endpoint (allowlist the relay domain/port).","Verify client and iroh-relay versions both use the same handshake/keying-material derivation.","Check that the relay serves the correct TLS certificate chain and is not fronted by a re-terminating load balancer.","Retry from a different network to confirm the middlebox is the cause."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Rust\nmatch client.connect(relay_url).await {\n    Err(err) if matches!(err, ConnectError::Handshake(VerificationError::MismatchedSuffix { .. })) => {\n        eprintln!(\"TLS interception detected: keying material mismatch — bypass proxy or use a trusted network\");\n    }\n    other => other?,\n}","preventionTips":["Allowlist iroh relay endpoints in TLS-inspection proxies.","Pin relay TLS certificates where possible.","Keep iroh client and relay versions in sync.","Test connectivity from networks without TLS interception before deploying."],"tags":["network","tls","handshake","security","iroh"],"backgroundTag":"checksum-mismatch","analyzedSha":"2b4de030ce5e0133f272871a76f0c685c63f552a","analyzedAt":"2026-09-08T04:26:47.755Z","contentChangedAt":"2026-09-08T04:26:47.755Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}