crypto/x509: add ML-DSA support

Updates #78888

Change-Id: I33e2d386fa911c67f062e24f52891a3c6a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/776708
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
Filippo Valsorda 2026-05-09 13:36:09 +02:00 committed by Gopher Robot
parent d80de8f117
commit 4e51025e3e
10 changed files with 1337 additions and 23 deletions

8
api/next/78888.txt Normal file
View file

@ -0,0 +1,8 @@
pkg crypto/x509, const MLDSA = 5 #78888
pkg crypto/x509, const MLDSA PublicKeyAlgorithm #78888
pkg crypto/x509, const MLDSA44 = 17 #78888
pkg crypto/x509, const MLDSA44 SignatureAlgorithm #78888
pkg crypto/x509, const MLDSA65 = 18 #78888
pkg crypto/x509, const MLDSA65 SignatureAlgorithm #78888
pkg crypto/x509, const MLDSA87 = 19 #78888
pkg crypto/x509, const MLDSA87 SignatureAlgorithm #78888

View file

@ -1,6 +1,8 @@
### crypto/mldsa
<!-- https://go.dev/issue/77626 --->
<!-- https://go.dev/issue/77626, https://go.dev/issue/78888 --->
The new [crypto/mldsa] package implements the post-quantum ML-DSA signature
scheme specified in FIPS 204.
[crypto/x509] now supports ML-DSA private keys, public keys, and signatures.

View file

@ -0,0 +1 @@
<!-- crypto/x509 ML-DSA support is documented in doc/next/6-stdlib/70-mldsa.md. -->

View file

@ -8,6 +8,8 @@ import (
"crypto/internal/fips140"
"internal/testenv"
"regexp"
"strconv"
"strings"
"testing"
)
@ -18,6 +20,35 @@ func MustSupportFIPS140(t *testing.T) {
}
}
// MustMinimumFIPS140ModuleVersion skips the test if compiled against a lower
// minor version of the FIPS 140-3 module than min (such as "v1.26.0").
func MustMinimumFIPS140ModuleVersion(t *testing.T, min string) {
t.Helper()
if fips140.Version() == "latest" {
return
}
if parseFIPS140MinorVersion(t, fips140.Version()) < parseFIPS140MinorVersion(t, min) {
t.Skipf("test requires FIPS 140-3 module %s or later", min)
}
}
func parseFIPS140MinorVersion(t *testing.T, version string) int {
t.Helper()
v, ok := strings.CutPrefix(version, "v1.")
if !ok {
t.Fatalf("unexpected FIPS 140 version format: %q", version)
}
v, _, ok = strings.Cut(v, ".")
if !ok {
t.Fatalf("unexpected FIPS 140 version format: %q", version)
}
i, err := strconv.Atoi(v)
if err != nil {
t.Fatalf("unexpected FIPS 140 version format %q: %v", version, err)
}
return i
}
func RerunWithFIPS140Enabled(t *testing.T) {
t.Helper()
MustSupportFIPS140(t)

View file

@ -8,6 +8,7 @@ import (
"crypto/dsa"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/mldsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
@ -131,7 +132,42 @@ AIU+2GKjyT3iMuzZxxFxPFMCAwEAAQ==
fmt.Println("pub is of type ECDSA:", pub)
case ed25519.PublicKey:
fmt.Println("pub is of type Ed25519:", pub)
case *mldsa.PublicKey:
fmt.Println("pub is of type ML-DSA:", pub)
default:
panic("unknown type of public key")
}
}
func ExampleMarshalPKCS8PrivateKey_mlDSA() {
// Generate an ML-DSA-44 key, marshal it to PKCS #8, and PEM-encode it.
// ML-DSA private keys are encoded in their seed-only representation per
// RFC 9881.
priv, err := mldsa.GenerateKey(mldsa.MLDSA44())
if err != nil {
panic(err)
}
der, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
panic(err)
}
pemBytes := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: der,
})
// Round-trip the PEM-encoded key back into an *mldsa.PrivateKey.
block, _ := pem.Decode(pemBytes)
if block == nil {
panic("failed to decode PEM block")
}
parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
if !parsed.(*mldsa.PrivateKey).Equal(priv) {
panic("round-tripped key does not match original")
}
}

View file

@ -10,6 +10,7 @@ import (
"crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/mldsa"
"crypto/rsa"
"crypto/x509/pkix"
"encoding/asn1"
@ -362,6 +363,15 @@ func parsePublicKey(keyData *publicKeyInfo) (any, error) {
return nil, errors.New("x509: wrong Ed25519 public key size")
}
return ed25519.PublicKey(data), nil
case oid.Equal(oidPublicKeyMLDSA44), oid.Equal(oidPublicKeyMLDSA65), oid.Equal(oidPublicKeyMLDSA87):
if len(params.FullBytes) != 0 {
return nil, errors.New("x509: ML-DSA key encoded with illegal parameters")
}
params, ok := mldsaParametersFromOID(oid)
if !ok {
return nil, errors.New("x509: unsupported ML-DSA parameters")
}
return mldsa.NewPublicKey(params, data)
case oid.Equal(oidPublicKeyX25519):
// RFC 8410, Section 3
// > For all of the OIDs, the parameters MUST be absent.

View file

@ -0,0 +1,73 @@
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build fips140v1.0
package x509
import "testing"
// TestParseMLDSACertificateFIPS140v1_0 verifies that ML-DSA certificates can
// still be parsed under the FIPS 140-3 module v1.0.0, which doesn't support
// ML-DSA. The parsed certificate has PublicKeyAlgorithm set to
// UnknownPublicKeyAlgorithm and a nil PublicKey, so callers can inspect the
// rest of the certificate without erroring.
func TestParseMLDSACertificateFIPS140v1_0(t *testing.T) {
for _, tt := range []struct {
name string
pem string
}{
{"ML-DSA-44", rfc9881ExampleCertificateMLDSA44},
{"ML-DSA-65", rfc9881ExampleCertificateMLDSA65},
{"ML-DSA-87", rfc9881ExampleCertificateMLDSA87},
} {
t.Run(tt.name, func(t *testing.T) {
cert, err := ParseCertificate(pemDecode(t, tt.pem))
if err != nil {
t.Fatalf("ParseCertificate failed: %v", err)
}
if cert.PublicKeyAlgorithm != UnknownPublicKeyAlgorithm {
t.Errorf("PublicKeyAlgorithm = %v, want UnknownPublicKeyAlgorithm", cert.PublicKeyAlgorithm)
}
if cert.PublicKey != nil {
t.Errorf("PublicKey = %v, want nil", cert.PublicKey)
}
// The rest of the certificate should still be inspectable.
if cert.Subject.CommonName == "" {
t.Error("Subject.CommonName is empty; expected the certificate to be parsed")
}
})
}
}
// TestMLDSAUnavailableErrorsNotPanics asserts that the public x509 entry
// points return errors (rather than panicking) when ML-DSA is unavailable.
// The mldsa package documents that "methods are unreachable" on v1.0.0; this
// test ensures x509 callers stay on the error path.
func TestMLDSAUnavailableErrorsNotPanics(t *testing.T) {
// ParsePKIXPublicKey: extracts the raw SPKI from a parsed cert and parses
// the public key directly. Should return an error, not panic.
cert, err := ParseCertificate(pemDecode(t, rfc9881ExampleCertificateMLDSA44))
if err != nil {
t.Fatalf("ParseCertificate failed: %v", err)
}
if _, err := ParsePKIXPublicKey(cert.RawSubjectPublicKeyInfo); err == nil {
t.Error("ParsePKIXPublicKey: expected error, got nil")
}
// ParsePKCS8PrivateKey: ML-DSA seed-only private keys.
for _, tt := range []struct {
name string
pem string
}{
{"ML-DSA-44", rfc9881ExamplePrivateKeyMLDSA44},
{"ML-DSA-65", rfc9881ExamplePrivateKeyMLDSA65},
{"ML-DSA-87", rfc9881ExamplePrivateKeyMLDSA87},
} {
t.Run(tt.name, func(t *testing.T) {
if _, err := ParsePKCS8PrivateKey(pemDecode(t, tt.pem)); err == nil {
t.Error("ParsePKCS8PrivateKey: expected error, got nil")
}
})
}
}

View file

@ -8,6 +8,7 @@ import (
"crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/mldsa"
"crypto/rsa"
"crypto/x509/pkix"
"encoding/asn1"
@ -28,8 +29,8 @@ type pkcs8 struct {
// ParsePKCS8PrivateKey parses an unencrypted private key in PKCS #8, ASN.1 DER form.
//
// It returns a *[rsa.PrivateKey], an *[ecdsa.PrivateKey], an [ed25519.PrivateKey] (not
// a pointer), or an *[ecdh.PrivateKey] (for X25519). More types might be supported
// in the future.
// a pointer), a *[mldsa.PrivateKey], or an *[ecdh.PrivateKey] (for X25519).
// More types might be supported in the future.
//
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
//
@ -79,6 +80,36 @@ func ParsePKCS8PrivateKey(der []byte) (key any, err error) {
}
return ed25519.NewKeyFromSeed(curvePrivateKey), nil
case privKey.Algo.Algorithm.Equal(oidPublicKeyMLDSA44),
privKey.Algo.Algorithm.Equal(oidPublicKeyMLDSA65),
privKey.Algo.Algorithm.Equal(oidPublicKeyMLDSA87):
if l := len(privKey.Algo.Parameters.FullBytes); l != 0 {
return nil, errors.New("x509: invalid ML-DSA private key parameters")
}
if l := len(privKey.PrivateKey); l == 0 {
return nil, fmt.Errorf("x509: invalid ML-DSA private key length: %d", l)
}
switch privKey.PrivateKey[0] {
case 0x80: // IMPLICIT [0] OCTET STRING (seed)
case 0x04: // OCTET STRING (expandedKey)
return nil, errors.New("x509: semi-expanded ML-DSA private keys without seed are not supported")
case 0x30: // SEQUENCE (both)
return nil, errors.New(`x509: ML-DSA private keys with both seed and expanded key are not supported, use e.g. "openssl pkey -provparam ml-dsa.output_formats=seed-only" to convert to a seed-only key`)
default:
return nil, fmt.Errorf("x509: invalid ML-DSA private key: invalid ASN.1 tag %02x", privKey.PrivateKey[0])
}
if l := len(privKey.PrivateKey); l != 2+mldsa.PrivateKeySize {
return nil, fmt.Errorf("x509: invalid ML-DSA private key length: %d", l)
}
if privKey.PrivateKey[1] != mldsa.PrivateKeySize {
return nil, fmt.Errorf("x509: invalid ML-DSA private key ASN.1 encoding")
}
params, ok := mldsaParametersFromOID(privKey.Algo.Algorithm)
if !ok {
return nil, errors.New("x509: unknown ML-DSA parameters")
}
return mldsa.NewPrivateKey(params, privKey.PrivateKey[2:])
case privKey.Algo.Algorithm.Equal(oidPublicKeyX25519):
if l := len(privKey.Algo.Parameters.FullBytes); l != 0 {
return nil, errors.New("x509: invalid X25519 private key parameters")
@ -97,8 +128,8 @@ func ParsePKCS8PrivateKey(der []byte) (key any, err error) {
// MarshalPKCS8PrivateKey converts a private key to PKCS #8, ASN.1 DER form.
//
// The following key types are currently supported: *[rsa.PrivateKey],
// *[ecdsa.PrivateKey], [ed25519.PrivateKey] (not a pointer), and *[ecdh.PrivateKey].
// Unsupported key types result in an error.
// *[ecdsa.PrivateKey], [ed25519.PrivateKey] (not a pointer), *[mldsa.PrivateKey],
// and *[ecdh.PrivateKey]. Unsupported key types result in an error.
//
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
//
@ -147,6 +178,16 @@ func MarshalPKCS8PrivateKey(key any) ([]byte, error) {
}
privKey.PrivateKey = curvePrivateKey
case *mldsa.PrivateKey:
oid, ok := oidFromMLDSAParameters(k.PublicKey().Parameters())
if !ok {
return nil, errors.New("x509: unknown ML-DSA parameters while marshaling to PKCS#8")
}
privKey.Algo = pkix.AlgorithmIdentifier{
Algorithm: oid,
}
privKey.PrivateKey = append([]byte{0x80, mldsa.PrivateKeySize}, k.Bytes()...)
case *ecdh.PrivateKey:
if k.Curve() == ecdh.X25519() {
privKey.Algo = pkix.AlgorithmIdentifier{

View file

@ -27,6 +27,8 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/fips140"
"crypto/mldsa"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
@ -65,8 +67,8 @@ type pkixPublicKey struct {
// public key is a SubjectPublicKeyInfo structure (see RFC 5280, Section 4.1).
//
// It returns a *[rsa.PublicKey], *[dsa.PublicKey], *[ecdsa.PublicKey],
// [ed25519.PublicKey] (not a pointer), or *[ecdh.PublicKey] (for X25519).
// More types might be supported in the future.
// [ed25519.PublicKey] (not a pointer), *[mldsa.PublicKey], or *[ecdh.PublicKey]
// (for X25519). More types might be supported in the future.
//
// This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
func ParsePKIXPublicKey(derBytes []byte) (pub any, err error) {
@ -115,6 +117,13 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
case ed25519.PublicKey:
publicKeyBytes = pub
publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
case *mldsa.PublicKey:
oid, ok := oidFromMLDSAParameters(pub.Parameters())
if !ok {
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported ML-DSA parameters")
}
publicKeyBytes = pub.Bytes()
publicKeyAlgorithm.Algorithm = oid
case *ecdh.PublicKey:
publicKeyBytes = pub.Bytes()
if pub.Curve() == ecdh.X25519() {
@ -144,8 +153,8 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
// (see RFC 5280, Section 4.1).
//
// The following key types are currently supported: *[rsa.PublicKey],
// *[ecdsa.PublicKey], [ed25519.PublicKey] (not a pointer), and *[ecdh.PublicKey].
// Unsupported key types result in an error.
// *[ecdsa.PublicKey], [ed25519.PublicKey] (not a pointer), *[mldsa.PublicKey],
// and *[ecdh.PublicKey]. Unsupported key types result in an error.
//
// This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
func MarshalPKIXPublicKey(pub any) ([]byte, error) {
@ -231,6 +240,9 @@ const (
SHA384WithRSAPSS
SHA512WithRSAPSS
PureEd25519
MLDSA44
MLDSA65
MLDSA87
)
func (algo SignatureAlgorithm) isRSAPSS() bool {
@ -268,6 +280,7 @@ const (
DSA // Only supported for parsing.
ECDSA
Ed25519
MLDSA
)
var publicKeyAlgoName = [...]string{
@ -275,6 +288,7 @@ var publicKeyAlgoName = [...]string{
DSA: "DSA",
ECDSA: "ECDSA",
Ed25519: "Ed25519",
MLDSA: "ML-DSA",
}
func (algo PublicKeyAlgorithm) String() string {
@ -384,6 +398,9 @@ var signatureAlgorithmDetails = []struct {
{ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, emptyRawValue, ECDSA, crypto.SHA384, false},
{ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, emptyRawValue, ECDSA, crypto.SHA512, false},
{PureEd25519, "Ed25519", oidSignatureEd25519, emptyRawValue, Ed25519, crypto.Hash(0) /* no pre-hashing */, false},
{MLDSA44, "ML-DSA-44", oidPublicKeyMLDSA44, emptyRawValue, MLDSA, crypto.Hash(0) /* no pre-hashing */, false},
{MLDSA65, "ML-DSA-65", oidPublicKeyMLDSA65, emptyRawValue, MLDSA, crypto.Hash(0) /* no pre-hashing */, false},
{MLDSA87, "ML-DSA-87", oidPublicKeyMLDSA87, emptyRawValue, MLDSA, crypto.Hash(0) /* no pre-hashing */, false},
}
var emptyRawValue = asn1.RawValue{}
@ -414,9 +431,14 @@ type pssParameters struct {
}
func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm {
if ai.Algorithm.Equal(oidSignatureEd25519) {
if ai.Algorithm.Equal(oidSignatureEd25519) ||
ai.Algorithm.Equal(oidPublicKeyMLDSA44) ||
ai.Algorithm.Equal(oidPublicKeyMLDSA65) ||
ai.Algorithm.Equal(oidPublicKeyMLDSA87) {
// RFC 8410, Section 3
// > For all of the OIDs, the parameters MUST be absent.
// RFC 9881, Section 2
// > The contents of the parameters component for each algorithm MUST be absent.
if len(ai.Parameters.FullBytes) != 0 {
return UnknownSignatureAlgorithm
}
@ -492,6 +514,22 @@ var (
// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
oidPublicKeyX25519 = asn1.ObjectIdentifier{1, 3, 101, 110}
oidPublicKeyEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
// RFC 9881, Section 2
//
// id-ml-dsa-44 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2)
// country(16) us(840) organization(1) gov(101) csor(3)
// nistAlgorithm(4) sigAlgs(3) id-ml-dsa-44(17) }
//
// id-ml-dsa-65 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2)
// country(16) us(840) organization(1) gov(101) csor(3)
// nistAlgorithm(4) sigAlgs(3) id-ml-dsa-65(18) }
//
// id-ml-dsa-87 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2)
// country(16) us(840) organization(1) gov(101) csor(3)
// nistAlgorithm(4) sigAlgs(3) id-ml-dsa-87(19) }
oidPublicKeyMLDSA44 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 17}
oidPublicKeyMLDSA65 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 18}
oidPublicKeyMLDSA87 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 19}
)
// getPublicKeyAlgorithmFromOID returns the exposed PublicKeyAlgorithm
@ -507,6 +545,14 @@ func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm
return ECDSA
case oid.Equal(oidPublicKeyEd25519):
return Ed25519
case oid.Equal(oidPublicKeyMLDSA44),
oid.Equal(oidPublicKeyMLDSA65),
oid.Equal(oidPublicKeyMLDSA87):
// ML-DSA is not available in FIPS 140-3 module v1.0.0.
if fips140.Version() == "v1.0.0" {
return UnknownPublicKeyAlgorithm
}
return MLDSA
}
return UnknownPublicKeyAlgorithm
}
@ -578,6 +624,30 @@ func oidFromECDHCurve(curve ecdh.Curve) (asn1.ObjectIdentifier, bool) {
return nil, false
}
func mldsaParametersFromOID(oid asn1.ObjectIdentifier) (mldsa.Parameters, bool) {
switch {
case oid.Equal(oidPublicKeyMLDSA44):
return mldsa.MLDSA44(), true
case oid.Equal(oidPublicKeyMLDSA65):
return mldsa.MLDSA65(), true
case oid.Equal(oidPublicKeyMLDSA87):
return mldsa.MLDSA87(), true
}
return mldsa.Parameters{}, false
}
func oidFromMLDSAParameters(params mldsa.Parameters) (asn1.ObjectIdentifier, bool) {
switch {
case params == mldsa.MLDSA44():
return oidPublicKeyMLDSA44, true
case params == mldsa.MLDSA65():
return oidPublicKeyMLDSA65, true
case params == mldsa.MLDSA87():
return oidPublicKeyMLDSA87, true
}
return nil, false
}
// KeyUsage represents the set of actions that are valid for a given key. It's
// a bitmap of the KeyUsage* constants.
type KeyUsage int
@ -976,6 +1046,10 @@ func signaturePublicKeyAlgoMismatchError(expectedPubKeyAlgo PublicKeyAlgorithm,
return fmt.Errorf("x509: signature algorithm specifies an %s public key, but have public key of type %T", expectedPubKeyAlgo.String(), pubKey)
}
func signatureMLDSAParametersMismatchError(expectedSigAlgo SignatureAlgorithm, pubKey *mldsa.PublicKey) error {
return fmt.Errorf("x509: signature algorithm specifies an ML-DSA public key with %s parameters, but have a public key with %s parameters", expectedSigAlgo, pubKey.Parameters())
}
// checkSignature verifies that signature is a valid signature over signed from
// a crypto.PublicKey.
func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey, allowSHA1 bool) (err error) {
@ -992,7 +1066,7 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
switch hashType {
case crypto.Hash(0):
if pubKeyAlgo != Ed25519 {
if pubKeyAlgo != Ed25519 && pubKeyAlgo != MLDSA {
return ErrUnsupportedAlgorithm
}
case crypto.MD5:
@ -1038,6 +1112,30 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
return errors.New("x509: Ed25519 verification failure")
}
return
case *mldsa.PublicKey:
if pubKeyAlgo != MLDSA {
return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
}
switch pub.Parameters() {
case mldsa.MLDSA44():
if algo != MLDSA44 {
return signatureMLDSAParametersMismatchError(algo, pub)
}
case mldsa.MLDSA65():
if algo != MLDSA65 {
return signatureMLDSAParametersMismatchError(algo, pub)
}
case mldsa.MLDSA87():
if algo != MLDSA87 {
return signatureMLDSAParametersMismatchError(algo, pub)
}
default:
return fmt.Errorf("x509: unknown ML-DSA parameters: %s", pub.Parameters())
}
if err := mldsa.Verify(pub, signed, signature, nil); err != nil {
return fmt.Errorf("x509: ML-DSA verification failure: %w", err)
}
return
}
return ErrUnsupportedAlgorithm
}
@ -1559,8 +1657,21 @@ func signingParamsForKey(key crypto.Signer, sigAlgo SignatureAlgorithm) (Signatu
pubType = Ed25519
defaultAlgo = PureEd25519
case *mldsa.PublicKey:
pubType = MLDSA
switch pub.Parameters() {
case mldsa.MLDSA44():
defaultAlgo = MLDSA44
case mldsa.MLDSA65():
defaultAlgo = MLDSA65
case mldsa.MLDSA87():
defaultAlgo = MLDSA87
default:
return 0, ai, fmt.Errorf("x509: unsupported ML-DSA parameters: %s", pub.Parameters())
}
default:
return 0, ai, errors.New("x509: only RSA, ECDSA and Ed25519 keys supported")
return 0, ai, errors.New("x509: only RSA, ECDSA, ML-DSA and Ed25519 keys supported")
}
if sigAlgo == 0 {
@ -1572,6 +1683,9 @@ func signingParamsForKey(key crypto.Signer, sigAlgo SignatureAlgorithm) (Signatu
if details.pubKeyAlgo != pubType {
return 0, ai, errors.New("x509: requested SignatureAlgorithm does not match private key type")
}
if pubType == MLDSA && sigAlgo != defaultAlgo {
return 0, ai, errors.New("x509: requested SignatureAlgorithm does not match ML-DSA parameters")
}
if details.hash == crypto.MD5 {
return 0, ai, errors.New("x509: signing with MD5 is not supported")
}
@ -1657,9 +1771,10 @@ var emptyASN1Subject = []byte{0x30, 0}
//
// The returned slice is the certificate in DER encoding.
//
// The currently supported key types are *rsa.PublicKey, *ecdsa.PublicKey and
// ed25519.PublicKey. pub must be a supported key type, and priv must be a
// crypto.Signer or crypto.MessageSigner with a supported public key.
// The currently supported key types are *rsa.PublicKey, *ecdsa.PublicKey,
// ed25519.PublicKey, and *mldsa.PublicKey. pub must be a supported key type,
// and priv must be a crypto.Signer or crypto.MessageSigner with a supported
// public key.
//
// The AuthorityKeyId will be taken from the SubjectKeyId of parent, if any,
// unless the resulting certificate is self-signed. Otherwise the value from
@ -2067,8 +2182,9 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
// priv is the private key to sign the CSR with, and the corresponding public
// key will be included in the CSR. It must implement crypto.Signer or
// crypto.MessageSigner and its Public() method must return a *rsa.PublicKey or
// a *ecdsa.PublicKey or a ed25519.PublicKey. (A *rsa.PrivateKey,
// *ecdsa.PrivateKey or ed25519.PrivateKey satisfies this.)
// a *ecdsa.PublicKey or a ed25519.PublicKey or a *mldsa.PublicKey.
// (A *rsa.PrivateKey, *ecdsa.PrivateKey or ed25519.PrivateKey or
// *mldsa.PrivateKey satisfies this.)
//
// The returned slice is the certificate request in DER encoding.
func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv any) (csr []byte, err error) {

File diff suppressed because it is too large Load diff