{"record":{"id":"ccba0cecf8538347","repo":"golang/go","slug":"tls-ml-dsa-certificates-require-tls-1-3","errorCode":null,"errorMessage":"tls: ML-DSA certificates require TLS 1.3","messagePattern":"tls: ML-DSA certificates require TLS 1\\.3","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/auth.go","lineNumber":330,"sourceCode":"\tif !ok {\n\t\treturn fmt.Errorf(\"tls: certificate private key (%T) does not implement crypto.Signer\",\n\t\t\tcert.PrivateKey)\n\t}\n\n\tswitch pub := signer.Public().(type) {\n\tcase *ecdsa.PublicKey:\n\t\tswitch pub.Curve {\n\t\tcase elliptic.P256():\n\t\tcase elliptic.P384():\n\t\tcase elliptic.P521():\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"tls: unsupported certificate curve (%s)\", pub.Curve.Params().Name)\n\t\t}\n\tcase *rsa.PublicKey:\n\t\treturn fmt.Errorf(\"tls: certificate RSA key size too small for supported signature algorithms\")\n\tcase ed25519.PublicKey:\n\tcase *mldsa.PublicKey:\n\t\treturn errors.New(\"tls: ML-DSA certificates require TLS 1.3\")\n\tdefault:\n\t\treturn fmt.Errorf(\"tls: unsupported certificate key (%T)\", pub)\n\t}\n\n\tif cert.SupportedSignatureAlgorithms != nil {\n\t\treturn fmt.Errorf(\"tls: peer doesn't support the certificate custom signature algorithms\")\n\t}\n\n\treturn fmt.Errorf(\"tls: internal error: unsupported key (%T)\", cert.PrivateKey)\n}\n","sourceCodeStart":312,"sourceCodeEnd":341,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/auth.go#L312-L341","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncfg.MinVersion = tls.VersionTLS12 // ML-DSA needs 1.3\n\n// after\ncfg.MinVersion = tls.VersionTLS13\ncfg.Certificates = []tls.Certificate{mldsaCert}","handlingStrategy":"validation","validationCode":"// Only present an ML-DSA cert when TLS 1.3 is possible.\nfunc maybeMLDSA(chi *tls.ClientHelloInfo, mldsaCert, classicCert *tls.Certificate) (*tls.Certificate, error) {\n    supports13 := false\n    for _, v := range chi.SupportedVersions {\n        if v == tls.VersionTLS13 { supports13 = true; break }\n    }\n    if supports13 { return mldsaCert, nil }\n    return classicCert, nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["crypto","tls","mldsa","post-quantum","tls13","go"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}