mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
crypto/x509: mitigate DoS vector when intermediate certificate contains DSA public key
An attacker could craft an intermediate X.509 certificate containing a DSA public key and can crash a remote host with an unauthenticated call to any endpoint that verifies the certificate chain. Thank you to Jakub Ciolek for reporting this issue. Fixes CVE-2025-58188 Fixes #75675 Change-Id: I2ecbb87b9b8268dbc55c8795891e596ab60f0088 Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2780 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Roland Shoemaker <bracewell@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/709853 Reviewed-by: Carlos Amedee <carlos@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
6f7926589d
commit
6e4007e8cf
2 changed files with 131 additions and 1 deletions
|
|
@ -927,7 +927,10 @@ func alreadyInChain(candidate *Certificate, chain []*Certificate) bool {
|
|||
if !bytes.Equal(candidate.RawSubject, cert.RawSubject) {
|
||||
continue
|
||||
}
|
||||
if !candidate.PublicKey.(pubKeyEqual).Equal(cert.PublicKey) {
|
||||
// We enforce the canonical encoding of SPKI (by only allowing the
|
||||
// correct AI paremeter encodings in parseCertificate), so it's safe to
|
||||
// directly compare the raw bytes.
|
||||
if !bytes.Equal(candidate.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) {
|
||||
continue
|
||||
}
|
||||
var certSAN *pkix.Extension
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package x509
|
|||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
|
|
@ -3048,3 +3049,129 @@ func TestInvalidPolicyWithAnyKeyUsage(t *testing.T) {
|
|||
t.Fatalf("unexpected error, got %q, want %q", err, expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateChainSignedByECDSA(t *testing.T) {
|
||||
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root := &Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: "X"},
|
||||
NotBefore: time.Now().Add(-time.Hour),
|
||||
NotAfter: time.Now().Add(365 * 24 * time.Hour),
|
||||
IsCA: true,
|
||||
KeyUsage: KeyUsageCertSign | KeyUsageCRLSign,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
caDER, err := CreateCertificate(rand.Reader, root, root, &caKey.PublicKey, caKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root, err = ParseCertificate(caDER)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
leafKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
leaf := &Certificate{
|
||||
SerialNumber: big.NewInt(42),
|
||||
Subject: pkix.Name{CommonName: "leaf"},
|
||||
NotBefore: time.Now().Add(-10 * time.Minute),
|
||||
NotAfter: time.Now().Add(24 * time.Hour),
|
||||
KeyUsage: KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []ExtKeyUsage{ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
leafDER, err := CreateCertificate(rand.Reader, leaf, root, &leafKey.PublicKey, caKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
leaf, err = ParseCertificate(leafDER)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
inter, err := ParseCertificate(dsaSelfSignedCNX(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
inters := NewCertPool()
|
||||
inters.AddCert(root)
|
||||
inters.AddCert(inter)
|
||||
|
||||
wantErr := "certificate signed by unknown authority"
|
||||
_, err = leaf.Verify(VerifyOptions{Intermediates: inters, Roots: NewCertPool()})
|
||||
if !strings.Contains(err.Error(), wantErr) {
|
||||
t.Errorf("got %v, want %q", err, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
// dsaSelfSignedCNX produces DER-encoded
|
||||
// certificate with the properties:
|
||||
//
|
||||
// Subject=Issuer=CN=X
|
||||
// DSA SPKI
|
||||
// Matching inner/outer signature OIDs
|
||||
// Dummy ECDSA signature
|
||||
func dsaSelfSignedCNX(t *testing.T) []byte {
|
||||
t.Helper()
|
||||
var params dsa.Parameters
|
||||
if err := dsa.GenerateParameters(¶ms, rand.Reader, dsa.L1024N160); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var dsaPriv dsa.PrivateKey
|
||||
dsaPriv.Parameters = params
|
||||
if err := dsa.GenerateKey(&dsaPriv, rand.Reader); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dsaPub := &dsaPriv.PublicKey
|
||||
|
||||
type dsaParams struct{ P, Q, G *big.Int }
|
||||
paramDER, err := asn1.Marshal(dsaParams{dsaPub.P, dsaPub.Q, dsaPub.G})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
yDER, err := asn1.Marshal(dsaPub.Y)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
spki := publicKeyInfo{
|
||||
Algorithm: pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidPublicKeyDSA,
|
||||
Parameters: asn1.RawValue{FullBytes: paramDER},
|
||||
},
|
||||
PublicKey: asn1.BitString{Bytes: yDER, BitLength: 8 * len(yDER)},
|
||||
}
|
||||
|
||||
rdn := pkix.Name{CommonName: "X"}.ToRDNSequence()
|
||||
b, err := asn1.Marshal(rdn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rawName := asn1.RawValue{FullBytes: b}
|
||||
|
||||
algoIdent := pkix.AlgorithmIdentifier{Algorithm: oidSignatureDSAWithSHA256}
|
||||
tbs := tbsCertificate{
|
||||
Version: 0,
|
||||
SerialNumber: big.NewInt(1002),
|
||||
SignatureAlgorithm: algoIdent,
|
||||
Issuer: rawName,
|
||||
Validity: validity{NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(24 * time.Hour)},
|
||||
Subject: rawName,
|
||||
PublicKey: spki,
|
||||
}
|
||||
c := certificate{
|
||||
TBSCertificate: tbs,
|
||||
SignatureAlgorithm: algoIdent,
|
||||
SignatureValue: asn1.BitString{Bytes: []byte{0}, BitLength: 8},
|
||||
}
|
||||
dsaDER, err := asn1.Marshal(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return dsaDER
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue