golang/go · error
tls: ML-DSA certificates require TLS 1.3
Error message
tls: ML-DSA certificates require TLS 1.3
What it means
Thrown by unsupportedCertificateError when the certificate key is *mldsa.PublicKey (post-quantum ML-DSA) but the negotiated TLS version is below 1.3. ML-DSA is only defined for TLS 1.3 signatures; the TLS 1.2 signature-scheme table has no ML-DSA entries, so such a cert cannot be used with older versions.
Source
Thrown at src/crypto/tls/auth.go:330
if !ok {
return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
cert.PrivateKey)
}
switch pub := signer.Public().(type) {
case *ecdsa.PublicKey:
switch pub.Curve {
case elliptic.P256():
case elliptic.P384():
case elliptic.P521():
default:
return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
}
case *rsa.PublicKey:
return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
case ed25519.PublicKey:
case *mldsa.PublicKey:
return errors.New("tls: ML-DSA certificates require TLS 1.3")
default:
return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
}
if cert.SupportedSignatureAlgorithms != nil {
return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
}
return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Set MinVersion and MaxVersion to TLS 1.3 on both sides when using ML-DSA certs.
- Provide a fallback classical (RSA/ECDSA/Ed25519) certificate for TLS 1.2 peers.
- Confirm the peer supports TLS 1.3 and ML-DSA signature schemes before presenting the PQ cert.
- Use Config.GetCertificate to choose the ML-DSA cert only for TLS 1.3 ClientHellos.
Example fix
// before
cfg.MinVersion = tls.VersionTLS12 // ML-DSA needs 1.3
// after
cfg.MinVersion = tls.VersionTLS13
cfg.Certificates = []tls.Certificate{mldsaCert} Defensive patterns
Strategy: validation
Validate before calling
// Only present an ML-DSA cert when TLS 1.3 is possible.
func maybeMLDSA(chi *tls.ClientHelloInfo, mldsaCert, classicCert *tls.Certificate) (*tls.Certificate, error) {
supports13 := false
for _, v := range chi.SupportedVersions {
if v == tls.VersionTLS13 { supports13 = true; break }
}
if supports13 { return mldsaCert, nil }
return classicCert, nil
} Prevention
- Set MinVersion=TLS 1.3 when deploying ML-DSA certs.
- Keep a classical cert as fallback for TLS 1.2 peers.
- Use GetCertificate to gate the PQ cert on ClientHello SupportedVersions.
- Test ML-DSA interop with a peer known to support TLS 1.3 PQ schemes.
When it happens
Trigger: Configuring a certificate with an ML-DSA (module-lattice signature) private key while the connection negotiates TLS 1.2 or earlier. Reached in the unsupportedCertificateError branch for *mldsa.PublicKey.
Common situations: Mixing a post-quantum ML-DSA certificate with a peer/config that caps at TLS 1.2; MinVersion left at default while testing PQ certs; a client that only offers TLS 1.2.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server's certificate uses ML-DSA, which requires TLS 1.
- tls: server sent an unnecessary HelloRetryRequest key_share
- tls: internal error: supportsCurve accepted unimplemented cu
- mldsa: unavailable in FIPS 140-3 Go Cryptographic Module v1.
- mldsa: invalid parameters
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/ccba0cecf8538347.
Report an issue: GitHub.