From f9f198ab05e3282cbf6b13251d47d9141981e401 Mon Sep 17 00:00:00 2001
From: Neal Patel <nealpatel@google.com>
Date: Thu, 11 Sep 2025 16:27:04 -0400
Subject: [PATCH] [release-branch.go1.24] 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
For #75675
Fixes #75702

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/+/2964
Reviewed-on: https://go-review.googlesource.com/c/go/+/709836
TryBot-Bypass: Michael Pratt <mpratt@google.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Auto-Submit: Michael Pratt <mpratt@google.com>

CVE: CVE-2025-58188

Upstream-Status: Backport [https://github.com/golang/go/commit/f9f198ab05e3282cbf6b13251d47d9141981e401]

Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
---
 src/crypto/x509/verify.go      |   5 +-
 src/crypto/x509/verify_test.go | 126 +++++++++++++++++++++++++++++++++
 2 files changed, 130 insertions(+), 1 deletion(-)

diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
index 4502d4c..14cd23f 100644
--- a/src/crypto/x509/verify.go
+++ b/src/crypto/x509/verify.go
@@ -868,7 +868,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
diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
index 8a7a5f6..4a7d8da 100644
--- a/src/crypto/x509/verify_test.go
+++ b/src/crypto/x509/verify_test.go
@@ -6,6 +6,7 @@ package x509

 import (
	"crypto"
+	"crypto/dsa"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
@@ -2811,3 +2812,128 @@ func TestVerifyNilPubKey(t *testing.T) {
		t.Fatalf("buildChains returned unexpected error, got: %v, want %v", err, UnknownAuthorityError{})
	}
 }
+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
+}
--
2.40.0
