golang/go · error

tls: session supported extended_master_secret but client doe

Error message

tls: session supported extended_master_secret but client does not

What it means

During session resumption the server found that the cached session used extended_master_secret (RFC 7627) but the current ClientHello does not advertise it. This is an EMS downgrade — the spec mandates aborting because silently accepting would weaken the security of the resumed session.

Source

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

	}
	opts := x509.VerifyOptions{
		CurrentTime: c.config.time(),
		Roots:       c.config.ClientCAs,
		KeyUsages:   []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
	}
	if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
		!anyValidVerifiedChain(sessionState.verifiedChains, opts) {
		return nil
	}

	// RFC 7627, Section 5.3
	if !sessionState.extMasterSecret && hs.clientHello.extendedMasterSecret {
		return nil
	}
	if sessionState.extMasterSecret && !hs.clientHello.extendedMasterSecret {
		// Aborting is somewhat harsh, but it's a MUST and it would indicate a
		// weird downgrade in client capabilities.
		return errors.New("tls: session supported extended_master_secret but client does not")
	}
	if !sessionState.extMasterSecret && fips140tls.Required() {
		if fips140ems.Value() != "0" {
			// FIPS 140-3 requires the use of Extended Master Secret.
			return nil
		}
		fips140ems.IncNonDefault()
	}

	c.peerCertificates = sessionState.peerCertificates
	c.ocspResponse = sessionState.ocspResponse
	c.scts = sessionState.scts
	c.verifiedChains = sessionState.verifiedChains
	c.extMasterSecret = sessionState.extMasterSecret
	hs.sessionState = sessionState
	hs.suite = suite
	c.curveID = sessionState.curveID
	c.didResume = true

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Ensure the client consistently advertises extended_master_secret on every connection (most modern stacks do by default).
  2. Investigate middleware/proxies that strip EMS — they create exactly this downgrade.
  3. Invalidate and rotate session tickets if the client legitimately changed capabilities, so stale EMS-flagged tickets are not reused.
  4. Update the client TLS library to a version that always offers EMS.

Example fix

// Most modern Go/Rust/OpenSSL clients always advertise EMS.
// If using a custom stack, ensure the extension is present:
//   - OpenSSL: enabled by default since 1.1.0
//   - Go: always sent for TLS 1.2
//   - Do not set any 'disable_extended_master_secret' flags
Defensive patterns

Strategy: validation

Validate before calling

// Client: always advertise extended_master_secret for TLS 1.2.
// Go clients do this automatically. If using a custom stack, include the
// extension and never disable it for sessions previously negotiated with EMS.

Try / catch

// Server: catch during resumption and force a full handshake instead.
if err != nil && strings.Contains(err.Error(), "session supported extended_master_secret but client does not") {
    // invalidate cached ticket and require full handshake
    sessionCache.Put(sessionKey, nil)
}

Prevention

When it happens

Trigger: In the server's session-resumption path: sessionState.extMasterSecret is true but hs.clientHello.extendedMasterSecret is false. The session was originally negotiated with EMS; the new ClientHello dropped the extension.

Common situations: A client that previously connected with EMS but is now configured (or downgraded) to omit it — possibly due to a library change, a middlebox stripping the extension, or a different client process reusing a cached ticket. Also a possible rollback attack vector.

Understand the failure class

Related errors


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