crypto/tls: add ML-DSA support

Tested against Google Chrome Canary 150.0.7834.0.

The Client-TLSv10, Client-TLSv11, and Client-TLSv12 recordings need to
change because they are configured with a version range that includes
TLS 1.3 (even if the OpenSSL server selects the lower one), so they
include ML-DSA signature algorithms in the Client Hello. The
Client-TLSv13 and Server-TLSv13-ClientAuthRequested* recordings need to
change because they now advertise ML-DSA support.

Because of #79481, we need to regenerate all recordings anyway, so defer
doing so to the next CL 779662, to avoid churning twice and bloating the
git history.

Fixes #78888

Change-Id: I14e6a89b459166155261aa8fc0edb83c6a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/776709
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Bypass: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
This commit is contained in:
yuhan6665
2026-09-08 22:24:17 -04:00
parent ac7d996924
commit 04a9e83d29
12 changed files with 138 additions and 21 deletions
+28
View File
@@ -10,6 +10,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/mldsa"
"crypto/rsa"
"errors"
"fmt"
@@ -45,6 +46,14 @@ func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc c
if !ed25519.Verify(pubKey, signed, sig) {
return errors.New("Ed25519 verification failure")
}
case signatureMLDSA:
pubKey, ok := pubkey.(*mldsa.PublicKey)
if !ok {
return fmt.Errorf("expected an ML-DSA public key, got %T", pubkey)
}
if err := mldsa.Verify(pubKey, signed, sig, nil); err != nil {
return fmt.Errorf("ML-DSA verification failure: %w", err)
}
case signaturePKCS1v15:
pubKey, ok := pubkey.(*rsa.PublicKey)
if !ok {
@@ -133,6 +142,8 @@ func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType
sigType = signatureECDSA
case Ed25519:
sigType = signatureEd25519
case MLDSA44, MLDSA65, MLDSA87:
sigType = signatureMLDSA
default:
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
}
@@ -147,6 +158,8 @@ func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType
hash = crypto.SHA512
case Ed25519:
hash = directSigning
case MLDSA44, MLDSA65, MLDSA87:
hash = directSigning
default:
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
}
@@ -168,6 +181,8 @@ func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash c
// full signature, and not even OpenSSL bothers with the
// complexity, so we can't even test it properly.
return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
case *mldsa.PublicKey:
return 0, 0, fmt.Errorf("tls: ML-DSA public keys are not supported before TLS 1.3")
default:
return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
}
@@ -224,6 +239,17 @@ func signatureSchemesForPublicKey(version uint16, pub crypto.PublicKey) []Signat
return sigAlgs
case ed25519.PublicKey:
return []SignatureScheme{Ed25519}
case *mldsa.PublicKey:
switch pub.Parameters() {
case mldsa.MLDSA44():
return []SignatureScheme{MLDSA44}
case mldsa.MLDSA65():
return []SignatureScheme{MLDSA65}
case mldsa.MLDSA87():
return []SignatureScheme{MLDSA87}
default:
panic("tls: internal error: unknown ML-DSA parameter set: " + pub.Parameters().String())
}
default:
return nil
}
@@ -300,6 +326,8 @@ func unsupportedCertificateError(cert *Certificate) error {
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)
}
+43 -9
View File
@@ -12,6 +12,8 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/fips140"
"crypto/mldsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha512"
@@ -216,11 +218,12 @@ const (
signatureRSAPSS
signatureECDSA
signatureEd25519
signatureMLDSA
)
// directSigning is a standard Hash value that signals that no pre-hashing
// should be performed, and that the input should be signed directly. It is the
// hash function associated with the Ed25519 signature scheme.
// hash function associated with the Ed25519 and ML-DSA signature schemes.
var directSigning crypto.Hash = 0
// helloRetryRequestRandom is set as the Random value of a ServerHello
@@ -425,6 +428,11 @@ const (
// EdDSA algorithms.
Ed25519 SignatureScheme = 0x0807
// ML-DSA algorithms.
MLDSA44 SignatureScheme = 0x0904
MLDSA65 SignatureScheme = 0x0905
MLDSA87 SignatureScheme = 0x0906
// Legacy signature and hash algorithms for TLS 1.2.
PKCS1WithSHA1 SignatureScheme = 0x0201
ECDSAWithSHA1 SignatureScheme = 0x0203
@@ -1520,6 +1528,9 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error {
return errors.New("connection doesn't support Ed25519")
}
ecdsaCipherSuite = true
case *mldsa.PublicKey:
// ML-DSA requires TLS 1.3, which we already excluded above.
return errors.New("connection doesn't support ML-DSA")
case *rsa.PublicKey:
default:
return supportsRSAFallback(unsupportedCertificateError(c))
@@ -1644,8 +1655,8 @@ var writerMutex sync.Mutex
type Certificate struct {
Certificate [][]byte
// PrivateKey contains the private key corresponding to the public key in
// Leaf. This must implement [crypto.Signer] with an RSA, ECDSA or Ed25519
// PublicKey.
// Leaf. This must implement [crypto.Signer] with an RSA, ECDSA, Ed25519
// (TLS 1.2+), or ML-DSA (TLS 1.3) PublicKey.
//
// For a server up to TLS 1.2, it can also implement crypto.Decrypter with
// an RSA PublicKey.
@@ -1781,15 +1792,21 @@ func unexpectedMessageError(wanted, got any) error {
var testingOnlySupportedSignatureAlgorithms []SignatureScheme
// supportedSignatureAlgorithms returns the supported signature algorithms for
// the given minimum TLS version, to advertise in ClientHello and
// CertificateRequest messages.
func supportedSignatureAlgorithms(minVers uint16) []SignatureScheme {
// the given range of TLS versions, to advertise in ClientHello and
// CertificateRequest messages. An algorithm is included if it is enabled at any
// version in the range.
func supportedSignatureAlgorithms(minVers, maxVers uint16) []SignatureScheme {
sigAlgs := defaultSupportedSignatureAlgorithms()
if testingOnlySupportedSignatureAlgorithms != nil {
sigAlgs = slices.Clone(testingOnlySupportedSignatureAlgorithms)
}
return slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
return isDisabledSignatureAlgorithm(minVers, s, false)
for v := minVers; v <= maxVers; v++ {
if !isDisabledSignatureAlgorithm(v, s, false) {
return false
}
}
return true
})
}
@@ -1800,6 +1817,18 @@ func isDisabledSignatureAlgorithm(version uint16, s SignatureScheme, isCert bool
return true
}
switch s {
case MLDSA44, MLDSA65, MLDSA87:
// ML-DSA is not available in FIPS 140-3 module v1.0.0.
if fips140.Version() == "v1.0.0" {
return true
}
// ML-DSA codepoints are only defined for TLS 1.3.
if version < VersionTLS13 {
return true
}
}
// For the _cert extension we include all algorithms, including SHA-1 and
// PKCS#1 v1.5, because it's more likely that something on our side will be
// willing to accept a *-with-SHA1 certificate (e.g. with a custom
@@ -1829,10 +1858,15 @@ func isDisabledSignatureAlgorithm(version uint16, s SignatureScheme, isCert bool
// supportedSignatureAlgorithmsCert returns the supported algorithms for
// signatures in certificates.
func supportedSignatureAlgorithmsCert() []SignatureScheme {
func supportedSignatureAlgorithmsCert(minVers, maxVers uint16) []SignatureScheme {
sigAlgs := defaultSupportedSignatureAlgorithms()
return slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
return isDisabledSignatureAlgorithm(0, s, true)
for v := minVers; v <= maxVers; v++ {
if !isDisabledSignatureAlgorithm(v, s, true) {
return false
}
}
return true
})
}
+11 -2
View File
@@ -18,6 +18,9 @@ func _() {
_ = x[ECDSAWithP384AndSHA384-1283]
_ = x[ECDSAWithP521AndSHA512-1539]
_ = x[Ed25519-2055]
_ = x[MLDSA44-2308]
_ = x[MLDSA65-2309]
_ = x[MLDSA87-2310]
_ = x[PKCS1WithSHA1-513]
_ = x[ECDSAWithSHA1-515]
}
@@ -32,10 +35,12 @@ const (
_SignatureScheme_name_6 = "PKCS1WithSHA512"
_SignatureScheme_name_7 = "ECDSAWithP521AndSHA512"
_SignatureScheme_name_8 = "PSSWithSHA256PSSWithSHA384PSSWithSHA512Ed25519"
_SignatureScheme_name_9 = "MLDSA44MLDSA65MLDSA87"
)
var (
_SignatureScheme_index_8 = [...]uint8{0, 13, 26, 39, 46}
_SignatureScheme_index_9 = [...]uint8{0, 7, 14, 21}
)
func (i SignatureScheme) String() string {
@@ -59,6 +64,9 @@ func (i SignatureScheme) String() string {
case 2052 <= i && i <= 2055:
i -= 2052
return _SignatureScheme_name_8[_SignatureScheme_index_8[i]:_SignatureScheme_index_8[i+1]]
case 2308 <= i && i <= 2310:
i -= 2308
return _SignatureScheme_name_9[_SignatureScheme_index_9[i]:_SignatureScheme_index_9[i+1]]
default:
return "SignatureScheme(" + strconv.FormatInt(int64(i), 10) + ")"
}
@@ -117,8 +125,9 @@ const _ClientAuthType_name = "NoClientCertRequestClientCertRequireAnyClientCertV
var _ClientAuthType_index = [...]uint8{0, 12, 29, 49, 72, 98}
func (i ClientAuthType) String() string {
if i < 0 || i >= ClientAuthType(len(_ClientAuthType_index)-1) {
idx := int(i) - 0
if i < 0 || idx >= len(_ClientAuthType_index)-1 {
return "ClientAuthType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ClientAuthType_name[_ClientAuthType_index[i]:_ClientAuthType_index[i+1]]
return _ClientAuthType_name[_ClientAuthType_index[idx]:_ClientAuthType_index[idx+1]]
}
+3
View File
@@ -39,6 +39,9 @@ func defaultCurvePreferences() []CurveID {
// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
func defaultSupportedSignatureAlgorithms() []SignatureScheme {
return []SignatureScheme{
MLDSA44,
MLDSA65,
MLDSA87,
PSSWithSHA256,
ECDSAWithP256AndSHA256,
Ed25519,
+7
View File
@@ -10,6 +10,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/mldsa"
"crypto/rsa"
"crypto/x509"
)
@@ -42,6 +43,9 @@ var (
PSSWithSHA256,
ECDSAWithP256AndSHA256,
Ed25519,
MLDSA44,
MLDSA65,
MLDSA87,
PSSWithSHA384,
PSSWithSHA512,
PKCS1WithSHA256,
@@ -72,6 +76,9 @@ func isCertificateAllowedFIPS(c *x509.Certificate) bool {
return k.Curve == elliptic.P256() || k.Curve == elliptic.P384() || k.Curve == elliptic.P521()
case ed25519.PublicKey:
return true
case *mldsa.PublicKey:
// Only for the native module.
return true //!boring.Enabled
default:
return false
}
+8 -2
View File
@@ -13,6 +13,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/mldsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
@@ -35,6 +36,7 @@ var (
rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
ed25519Key = flag.Bool("ed25519", false, "Generate an Ed25519 key")
mldsaKey = flag.Bool("mldsa", false, "Generate an ML-DSA-44 key")
)
func publicKey(priv any) any {
@@ -45,6 +47,8 @@ func publicKey(priv any) any {
return &k.PublicKey
case ed25519.PrivateKey:
return k.Public().(ed25519.PublicKey)
case *mldsa.PrivateKey:
return k.PublicKey()
default:
return nil
}
@@ -63,6 +67,8 @@ func main() {
case "":
if *ed25519Key {
_, priv, err = ed25519.GenerateKey(rand.Reader)
} else if *mldsaKey {
priv, err = mldsa.GenerateKey(mldsa.MLDSA44())
} else {
priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
}
@@ -81,8 +87,8 @@ func main() {
log.Fatalf("Failed to generate private key: %v", err)
}
// ECDSA, ED25519 and RSA subject keys should have the DigitalSignature
// KeyUsage bits set in the x509.Certificate template
// ECDSA, ED25519, ML-DSA, and RSA subject keys should have the
// DigitalSignature KeyUsage bits set in the x509.Certificate template
keyUsage := x509.KeyUsageDigitalSignature
// Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In
// the context of TLS this KeyUsage is particular to RSA key exchange and
+8 -2
View File
@@ -11,6 +11,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/hpke"
"crypto/mldsa"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
@@ -119,8 +120,8 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
}
if maxVersion >= VersionTLS12 {
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms(minVersion)
hello.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert()
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms(minVersion, maxVersion)
hello.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert(minVersion, maxVersion)
}
var keyShareKeys *keySharePrivateKeys
@@ -1160,6 +1161,11 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
switch certs[0].PublicKey.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
break
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)
+1 -1
View File
@@ -653,7 +653,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
// See RFC 8446, Section 4.4.3.
// We don't use hs.hello.supportedSignatureAlgorithms because it might
// include PKCS#1 v1.5 and SHA-1 if the ClientHello also supported TLS 1.2.
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers)) ||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers, c.vers)) ||
!isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, signatureSchemesForPublicKey(c.vers, c.peerCertificates[0].PublicKey)) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: certificate used with invalid signature algorithm")
+12 -1
View File
@@ -9,6 +9,7 @@ import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/mldsa"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
@@ -306,6 +307,11 @@ func (hs *serverHandshakeState) processClientHello() error {
hs.ecSignOk = true
case *rsa.PublicKey:
hs.rsaSignOk = true
case *mldsa.PublicKey:
// ML-DSA can only be used with TLS 1.3.
c.sendAlert(alertInternalError)
return fmt.Errorf("tls: ML-DSA certificates require TLS 1.3, but client negotiated %s",
VersionName(c.vers))
default:
c.sendAlert(alertInternalError)
return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
@@ -646,7 +652,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
}
if c.vers >= VersionTLS12 {
certReq.hasSignatureAlgorithm = true
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms(c.vers)
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms(c.vers, c.vers)
}
// An empty list of certificateAuthorities signals to
@@ -985,6 +991,11 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error {
if len(certs) > 0 {
switch certs[0].PublicKey.(type) {
case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
case *mldsa.PublicKey:
if c.vers < VersionTLS13 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: client certificate uses ML-DSA, which requires TLS 1.3")
}
default:
c.sendAlert(alertUnsupportedCertificate)
return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
+3 -3
View File
@@ -934,8 +934,8 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
certReq := new(certificateRequestMsgTLS13)
certReq.ocspStapling = true
certReq.scts = true
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms(c.vers)
certReq.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert()
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms(c.vers, c.vers)
certReq.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert(c.vers, c.vers)
if c.config.ClientCAs != nil {
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
}
@@ -1184,7 +1184,7 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
// See RFC 8446, Section 4.4.3.
// We don't use certReq.supportedSignatureAlgorithms because it would
// require keeping the certificateRequestMsgTLS13 around in the hs.
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers)) ||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms(c.vers, c.vers)) ||
!isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, signatureSchemesForPublicKey(c.vers, c.peerCertificates[0].PublicKey)) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: client certificate used with invalid signature algorithm")
+4
View File
@@ -290,6 +290,10 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
if len(sig) < 2 {
return errServerKeyExchange
}
switch ka.signatureAlgorithm {
case MLDSA44, MLDSA65, MLDSA87:
return errors.New("tls: server selected ML-DSA with TLS version < 1.3")
}
}
sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) {
+10 -1
View File
@@ -33,6 +33,7 @@ import (
"crypto/cipher"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/mldsa"
"crypto/mlkem"
"crypto/rsa"
"crypto/sha256"
@@ -817,6 +818,14 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
if !priv.Public().(ed25519.PublicKey).Equal(pub) {
return fail(errors.New("tls: private key does not match public key"))
}
case *mldsa.PublicKey:
priv, ok := cert.PrivateKey.(*mldsa.PrivateKey)
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if !priv.PublicKey().Equal(pub) {
return fail(errors.New("tls: private key does not match public key"))
}
default:
return fail(errors.New("tls: unknown public key algorithm"))
}
@@ -833,7 +842,7 @@ func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
}
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, *mldsa.PrivateKey:
return key, nil
default:
return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")