golang/go · critical

tls: client's Finished message is incorrect

Error message

tls: client's Finished message is incorrect

What it means

The server recomputed the client's Finished verify_data from the master secret and transcript, and it did not match the value sent by the client. A wrong Finished message means the client's view of the handshake diverges from the server's — usually a sign of tampering, an attacker injecting messages, or a buggy peer.

Source

Thrown at src/crypto/tls/handshake_server.go:873

	// finishedMsg is included in the transcript, but not until after we
	// check the client version, since the state before this message was
	// sent is used during verification.
	msg, err := c.readHandshake(nil)
	if err != nil {
		return err
	}
	clientFinished, ok := msg.(*finishedMsg)
	if !ok {
		c.sendAlert(alertUnexpectedMessage)
		return unexpectedMessageError(clientFinished, msg)
	}

	verify := hs.finishedHash.clientSum(hs.masterSecret)
	if len(verify) != len(clientFinished.verifyData) ||
		subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
		c.sendAlert(alertHandshakeFailure)
		return errors.New("tls: client's Finished message is incorrect")
	}

	if err := transcriptMsg(clientFinished, &hs.finishedHash); err != nil {
		return err
	}

	copy(out, verify)
	return nil
}

func (hs *serverHandshakeState) sendSessionTicket() error {
	if !hs.hello.ticketSupported {
		return nil
	}

	c := hs.c
	m := new(newSessionTicketMsg)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Check for middleboxes, proxies, or TLS-terminating load balancers that might alter handshake bytes — they break the transcript.
  2. If using session resumption, ensure the ticket and PSK derivation are consistent between client and server.
  3. Verify both peers run compatible TLS implementations (avoid ancient versions on either side).
  4. Inspect cipher-suite negotiation for a mismatch that could cause divergent master-secret computation.
Defensive patterns

Strategy: try-catch

Validate before calling

// No caller-side validation possible; Finished mismatch is a peer/transcript
// integrity problem. Ensure no middlebox alters handshake bytes.

Try / catch

// Server: treat as a potential MITM or bug; do not silently retry.
if err != nil && strings.Contains(err.Error(), "Finished message is incorrect") {
    log.Error("Finished verification failed — possible tampering",
        "remote", conn.RemoteAddr(), "err", err)
    return err
}

Prevention

When it happens

Trigger: In the server's finished-message processing: subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 (or length mismatch). The client computed verify_data with a different master secret or transcript hash.

Common situations: A MITM altering handshake records, an inconsistent master-secret derivation between client and server, a buggy client TLS library, or use of an incorrect PSK/session resumption. The handshake is aborted with handshake_failure.

Understand the failure class

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/b5697c2c92c0dbb9. Report an issue: GitHub.