{"record":{"id":"2c2977af6e8c2b59","repo":"golang/go","slug":"tls-invalid-client-finished-hash","errorCode":null,"errorMessage":"tls: invalid client finished hash","messagePattern":"tls: invalid client finished hash","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/crypto/tls/handshake_server_tls13.go","lineNumber":1139,"sourceCode":"\nfunc (hs *serverHandshakeStateTLS13) readClientFinished() error {\n\tc := hs.c\n\n\t// finishedMsg is not included in the transcript.\n\tmsg, err := c.readHandshake(nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfinished, ok := msg.(*finishedMsg)\n\tif !ok {\n\t\tc.sendAlert(alertUnexpectedMessage)\n\t\treturn unexpectedMessageError(finished, msg)\n\t}\n\n\tif !hmac.Equal(hs.clientFinished, finished.verifyData) {\n\t\tc.sendAlert(alertDecryptError)\n\t\treturn errors.New(\"tls: invalid client finished hash\")\n\t}\n\n\tif err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret, false); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":1121,"sourceCodeEnd":1148,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_server_tls13.go#L1121-L1148","documentation":"The server received the client's Finished message but the HMAC verifyData didn't match the server's independently computed hs.clientFinished value. The Finished message (RFC 8446 §4.4.4) is an HMAC over the entire handshake transcript using the finished_key derived from the handshake traffic secret. A mismatch means the client and server disagree on the transcript or derived keys.","triggerScenarios":"Server calls hmac.Equal(hs.clientFinished, finished.verifyData) and they differ. This means the client computed a different Finished MAC than expected, indicating the two sides have divergent handshake transcripts or traffic secrets.","commonSituations":"A buggy MITM or TLS-terminating proxy that alters handshake messages; a client TLS library bug in transcript hashing or key derivation; memory corruption or a data race affecting the transcript hash; a network device that silently modifies handshake records; an extremely rare implementation bug in the client's HKDF or HMAC computation.","solutions":["Check for any TLS-intercepting proxy, WAF, or load balancer that modifies handshake messages.","Test with a reference TLS 1.3 client to rule out client-side bugs.","If using a custom crypto/tls fork, verify transcript hash and key schedule implementations.","Run with go test -race to detect data races on the transcript or traffic secret.","Capture a full packet trace and verify handshake message integrity end-to-end."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// No caller-side validation can prevent this — it's a transcript/key divergence.\n// Ensure no proxy modifies handshake messages.\n// Verify the TLS configuration is standard:\nfunc validateTLSConfig(cfg *tls.Config) error {\n    // Ensure no custom/incompatible settings that could cause transcript divergence\n    if cfg.InsecureSkipVerify {\n        // Not directly related but indicates non-standard config\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := conn.Handshake(); err != nil {\n    if strings.Contains(err.Error(), \"invalid client finished hash\") {\n        log.Printf(\"handshake transcript divergence detected: %v\", err)\n        // This often indicates a MITM or a serious bug — investigate\n    }\n}","preventionTips":["Ensure no TLS-intercepting proxy or WAF modifies handshake messages.","Use standard TLS libraries on both sides.","Run go test -race to catch data races on transcript state.","Monitor this error — it can indicate active MITM attacks."],"tags":["tls","tls13","handshake","finished","hmac","transcript","server-side"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}