]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.25] crypto/x509: mitigate DoS vector when intermediate certificat...
authorNeal Patel <nealpatel@google.com>
Thu, 11 Sep 2025 20:27:04 +0000 (16:27 -0400)
committerGopher Robot <gobot@golang.org>
Tue, 7 Oct 2025 18:02:06 +0000 (11:02 -0700)
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
For #75675
Fixes #75703

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-internal-review.googlesource.com/c/go/+/2963
Commit-Queue: Roland Shoemaker <bracewell@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/709845
TryBot-Bypass: Michael Pratt <mpratt@google.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Auto-Submit: Michael Pratt <mpratt@google.com>

src/crypto/x509/verify.go
src/crypto/x509/verify_test.go

index 7cc0fb2e3e0385c65f607e0c42f11a466a77e7a4..755c1db96c1edbfa83323427446ecd619485e976 100644 (file)
@@ -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
index 7991f49946d587b45360d86112b7291e3c8c31e9..5595f99ea5e43a7715fe8cf2c622693751ad7b04 100644 (file)
@@ -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(&params, 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
+}