golang/go · critical
tls: incorrect renegotiation extension contents
Error message
tls: incorrect renegotiation extension contents
What it means
On a renegotiation (c.handshakes > 0) where secure renegotiation was previously established, RFC 5746 requires the server's renegotiation_info to equal clientFinished[0:12] concatenated with serverFinished[12:24] from the prior handshake. The code constructs expectedSecureRenegotiation and compares with bytes.Equal; a mismatch indicates tampering or a broken channel and is aborted with alertHandshakeFailure.
Source
Thrown at src/crypto/tls/handshake_client.go:920
if !supportsPointFormat && offeredNonCompressedFormat {
return false, errors.New("tls: server offered only incompatible point formats")
}
if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported {
c.secureRenegotiation = true
if len(hs.serverHello.secureRenegotiation) != 0 {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
}
}
if c.handshakes > 0 && c.secureRenegotiation {
var expectedSecureRenegotiation [24]byte
copy(expectedSecureRenegotiation[:], c.clientFinished[:])
copy(expectedSecureRenegotiation[12:], c.serverFinished[:])
if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: incorrect renegotiation extension contents")
}
}
if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol, false); err != nil {
c.sendAlert(alertUnsupportedExtension)
return false, err
}
c.clientProtocol = hs.serverHello.alpnProtocol
c.scts = hs.serverHello.scts
if !hs.serverResumedSession() {
return false, nil
}
if hs.session.version != c.vers {
c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: server resumed a session with a different version")View on GitHub (pinned to b6b368adc5)
Solutions
- Move to TLS 1.3 which has no renegotiation and removes this attack surface (MinVersion = VersionTLS13).
- Remove any TLS-intercepting appliance from the path of renegotiating connections.
- If renegotiation is required, ensure the path is transparent.
Example fix
// before: permissive renegotiation policy
cfg := &tls.Config{Renegotiation: tls.RenegotiateFreelyAsClient}
// after: TLS 1.3 eliminates renegotiation entirely
cfg := &tls.Config{MinVersion: tls.VersionTLS13} Defensive patterns
Strategy: validation
Validate before calling
// Eliminate the renegotiation attack surface entirely.
func disableRenegotiation(cfg *tls.Config) {
cfg.MinVersion = tls.VersionTLS13
} Type guard
func isIncorrectRenegotiationContents(err error) bool {
return err != nil && strings.Contains(err.Error(), "incorrect renegotiation extension contents")
} Try / catch
if _, err := tls.Dial("tcp", addr, cfg); err != nil {
if isIncorrectRenegotiationContents(err) {
// Likely interception on a renegotiating connection; do not retry silently.
security.ReportInterception(addr, err)
}
} Prevention
- Use TLS 1.3 to remove renegotiation entirely.
- Audit paths for TLS-intercepting appliances.
- Disable renegotiation where possible.
When it happens
Trigger: MitM attempting to splice or synchronize renegotiation state; buggy middlebox that corrupts renegotiation_info; certificate rotation that breaks state continuity across renegotiations.
Common situations: TLS interception appliances on long-lived renegotiating connections; rarely legitimate servers.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: initial handshake had non-empty renegotiation extension
- tls: server echoed TLS 1.3 compatibility session ID in TLS 1
- tls: server's identity changed during renegotiation
- tls: server selected unsupported compression format
- tls: invalid outer extensions
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/d8e3a074ac4e40e9.
Report an issue: GitHub.