golang/go · error

tls: FIPS 140-3 requires the use of Extended Master Secret

Error message

tls: FIPS 140-3 requires the use of Extended Master Secret

What it means

When the FIPS 140-3 module is required (fips140tls.Required() returns true), the server declining Extended Master Secret (RFC 7627) on a TLS 1.2 handshake is a hard violation unless the GODEBUG switch fips140ems is explicitly set to "0". Without EMS the master secret uses the legacy derivation, which FIPS 140-3 disallows.

Source

Thrown at src/crypto/tls/handshake_client.go:778

	if err != nil {
		c.sendAlert(alertInternalError)
		return err
	}
	if ckx != nil {
		if _, err := hs.c.writeHandshakeRecord(ckx, &hs.finishedHash); err != nil {
			return err
		}
	}

	if hs.serverHello.extendedMasterSecret {
		c.extMasterSecret = true
		hs.masterSecret = extMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
			hs.finishedHash.Sum())
	} else {
		if fips140tls.Required() {
			if fips140ems.Value() != "0" {
				c.sendAlert(alertHandshakeFailure)
				return errors.New("tls: FIPS 140-3 requires the use of Extended Master Secret")
			}
			fips140ems.IncNonDefault()
		}
		hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
			hs.hello.random, hs.serverHello.random)
	}
	if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil {
		c.sendAlert(alertInternalError)
		return errors.New("tls: failed to write to key log: " + err.Error())
	}

	if chainToSend != nil && len(chainToSend.Certificate) > 0 {
		certVerify := &certificateVerifyMsg{}

		key, ok := chainToSend.PrivateKey.(crypto.Signer)
		if !ok {
			c.sendAlert(alertInternalError)
			return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Upgrade/patch the server to support Extended Master Secret (RFC 7627) — almost all modern servers do.
  2. If site policy permits the relaxation, set GODEBUG=tlsems=0 (fips140ems=0) — but this breaks FIPS 140-3 compliance of the connection.
  3. Confirm GODEBUG=fips140=on is actually required for this workload; remove it if not.
  4. Prefer TLS 1.3, where EMS semantics are mandatory.

Example fix

// Prefer fixing the server. If policy allows relaxation, document the trade-off:
//   GODEBUG=tlsems=0 go run ./cmd
// Otherwise move the peer to TLS 1.3.
cfg := &tls.Config{MinVersion: tls.VersionTLS13}
Defensive patterns

Strategy: validation

Validate before calling

// Confirm FIPS posture is intentional and that you are not over-restricting.
// If the peer lacks EMS, either fix the peer or explicitly opt out (non-FIPS).
// GODEBUG=tlsems=0 disables the requirement but breaks FIPS 140-3 compliance.

Type guard

func isFIPSEMSRequired(err error) bool {
    return err != nil && strings.Contains(err.Error(), "FIPS 140-3 requires the use of Extended Master Secret")
}

Try / catch

if _, err := tls.Dial("tcp", addr, cfg); err != nil {
    if isFIPSEMSRequired(err) {
        // Do not silently relax: decide whether FIPS is truly required here.
        log.Printf("compliance: peer %s lacks EMS; refusing under FIPS policy", addr)
    }
}

Prevention

When it happens

Trigger: Connecting from a FIPS-required Go build (GOEXPERIMENT=fips140 / GODEBUG=fips140=on) to a TLS 1.2 server whose ServerHello did not include the extended_master_session extension; the else-branch fires.

Common situations: FIPS-compliance-enforced deployments (government, finance, healthcare) talking to legacy or non-compliant servers; misconfigured GODEBUG FIPS settings.

Understand the failure class

Related errors


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