golang/go · error

tls: server's certificate uses ML-DSA, which requires TLS 1.

Error message

tls: server's certificate uses ML-DSA, which requires TLS 1.3

What it means

Thrown in verifyServerCertificate() when the server's leaf certificate uses an ML-DSA (Module-Lattice-Based Digital Signature Algorithm) public key but the negotiated TLS version is below 1.3. ML-DSA is a post-quantum signature algorithm only defined for TLS 1.3 per the relevant specifications.

Source

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

		c.verifiedChains, err = fipsAllowedChains(chains)
		if err != nil {
			c.sendAlert(alertBadCertificate)
			return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
		}
	}

	if fips140tls.Required() && !isCertificateAllowedFIPS(certs[0]) {
		c.sendAlert(alertBadCertificate)
		err := errors.New("server's certificate is not allowed in FIPS 140-3 mode")
		return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
	}

	switch certs[0].PublicKey.(type) {
	case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
	case *mldsa.PublicKey:
		if c.vers < VersionTLS13 {
			c.sendAlert(alertIllegalParameter)
			return errors.New("tls: server's certificate uses ML-DSA, which requires TLS 1.3")
		}
	default:
		c.sendAlert(alertUnsupportedCertificate)
		return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
	}

	c.peerCertificates = certs

	if c.config.VerifyPeerCertificate != nil && !echRejected {
		if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
			c.sendAlert(alertBadCertificate)
			return err
		}
	}

	if c.config.VerifyConnection != nil && !echRejected {
		if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
			c.sendAlert(alertBadCertificate)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Remove or raise the MaxVersion restriction in tls.Config to allow TLS 1.3 negotiation.
  2. Ensure MinVersion is not set above VersionTLS13 and MaxVersion defaults to VersionTLS13.
  3. If TLS 1.3 is unavailable on the client, replace the server certificate with a classical algorithm (RSA, ECDSA, or Ed25519).
  4. Verify TLS 1.3 support end-to-end: openssl s_client -connect host:443 -tls1_3

Example fix

// before — forces TLS 1.2, incompatible with ML-DSA certs
config := &tls.Config{
    MaxVersion: tls.VersionTLS12,
}

// after — allow TLS 1.3
config := &tls.Config{
    MinVersion: tls.VersionTLS12,
    MaxVersion: tls.VersionTLS13,
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate TLS version config before connecting when ML-DSA certs may be used
func validateConfigForPostQuantum(config *tls.Config) error {
    if config.MaxVersion != 0 && config.MaxVersion < tls.VersionTLS13 {
        return fmt.Errorf("MaxVersion is below TLS 1.3; servers with ML-DSA certs will fail")
    }
    return nil
}

Try / catch

conn, err := tls.Dial("tcp", addr, config)
if err != nil {
    if strings.Contains(err.Error(), "ML-DSA, which requires TLS 1.3") {
        // Raise MaxVersion to allow TLS 1.3 and retry
        config.MaxVersion = tls.VersionTLS13
        conn, err = tls.Dial("tcp", addr, config)
    }
}

Prevention

When it happens

Trigger: Triggered when certs[0].PublicKey is *mldsa.PublicKey and c.vers < VersionTLS13 (0x0304). The client sends alertIllegalParameter.

Common situations: Client tls.Config restricts MaxVersion to TLS 1.2 while connecting to a server with a post-quantum ML-DSA certificate. Client in a restricted environment that downgrades the TLS version. Server admin deployed ML-DSA certificates without ensuring all clients support TLS 1.3.

Understand the failure class

Related errors


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