mirror of
https://github.com/golang/go.git
synced 2026-06-27 19:30:52 +00:00
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:
parent
d80de8f117
commit
4e51025e3e
10 changed files with 1337 additions and 23 deletions
8
api/next/78888.txt
Normal file
8
api/next/78888.txt
Normal 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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
1
doc/next/6-stdlib/99-minor/crypto/x509/78888.md
Normal file
1
doc/next/6-stdlib/99-minor/crypto/x509/78888.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
<!-- crypto/x509 ML-DSA support is documented in doc/next/6-stdlib/70-mldsa.md. -->
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
73
src/crypto/x509/parser_fips140v1.0_test.go
Normal file
73
src/crypto/x509/parser_fips140v1.0_test.go
Normal 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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue