Skip to content

Commit 6e4007e

Browse files
thatnealpatelgopherbot
authored andcommitted
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 golang#75675 Change-Id: I2ecbb87b9b8268dbc55c8795891e596ab60f0088 Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2780 Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/go/+/709853 Reviewed-by: Carlos Amedee <[email protected]> Auto-Submit: Michael Pratt <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 6f79265 commit 6e4007e

File tree

2 files changed

+131
-1
lines changed

2 files changed

+131
-1
lines changed

src/crypto/x509/verify.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,10 @@ func alreadyInChain(candidate *Certificate, chain []*Certificate) bool {
927927
if !bytes.Equal(candidate.RawSubject, cert.RawSubject) {
928928
continue
929929
}
930-
if !candidate.PublicKey.(pubKeyEqual).Equal(cert.PublicKey) {
930+
// We enforce the canonical encoding of SPKI (by only allowing the
931+
// correct AI paremeter encodings in parseCertificate), so it's safe to
932+
// directly compare the raw bytes.
933+
if !bytes.Equal(candidate.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) {
931934
continue
932935
}
933936
var certSAN *pkix.Extension

src/crypto/x509/verify_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package x509
66

77
import (
88
"crypto"
9+
"crypto/dsa"
910
"crypto/ecdsa"
1011
"crypto/elliptic"
1112
"crypto/rand"
@@ -3048,3 +3049,129 @@ func TestInvalidPolicyWithAnyKeyUsage(t *testing.T) {
30483049
t.Fatalf("unexpected error, got %q, want %q", err, expectedErr)
30493050
}
30503051
}
3052+
3053+
func TestCertificateChainSignedByECDSA(t *testing.T) {
3054+
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
3055+
if err != nil {
3056+
t.Fatal(err)
3057+
}
3058+
root := &Certificate{
3059+
SerialNumber: big.NewInt(1),
3060+
Subject: pkix.Name{CommonName: "X"},
3061+
NotBefore: time.Now().Add(-time.Hour),
3062+
NotAfter: time.Now().Add(365 * 24 * time.Hour),
3063+
IsCA: true,
3064+
KeyUsage: KeyUsageCertSign | KeyUsageCRLSign,
3065+
BasicConstraintsValid: true,
3066+
}
3067+
caDER, err := CreateCertificate(rand.Reader, root, root, &caKey.PublicKey, caKey)
3068+
if err != nil {
3069+
t.Fatal(err)
3070+
}
3071+
root, err = ParseCertificate(caDER)
3072+
if err != nil {
3073+
t.Fatal(err)
3074+
}
3075+
3076+
leafKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
3077+
leaf := &Certificate{
3078+
SerialNumber: big.NewInt(42),
3079+
Subject: pkix.Name{CommonName: "leaf"},
3080+
NotBefore: time.Now().Add(-10 * time.Minute),
3081+
NotAfter: time.Now().Add(24 * time.Hour),
3082+
KeyUsage: KeyUsageDigitalSignature,
3083+
ExtKeyUsage: []ExtKeyUsage{ExtKeyUsageServerAuth},
3084+
BasicConstraintsValid: true,
3085+
}
3086+
leafDER, err := CreateCertificate(rand.Reader, leaf, root, &leafKey.PublicKey, caKey)
3087+
if err != nil {
3088+
t.Fatal(err)
3089+
}
3090+
leaf, err = ParseCertificate(leafDER)
3091+
if err != nil {
3092+
t.Fatal(err)
3093+
}
3094+
3095+
inter, err := ParseCertificate(dsaSelfSignedCNX(t))
3096+
if err != nil {
3097+
t.Fatal(err)
3098+
}
3099+
3100+
inters := NewCertPool()
3101+
inters.AddCert(root)
3102+
inters.AddCert(inter)
3103+
3104+
wantErr := "certificate signed by unknown authority"
3105+
_, err = leaf.Verify(VerifyOptions{Intermediates: inters, Roots: NewCertPool()})
3106+
if !strings.Contains(err.Error(), wantErr) {
3107+
t.Errorf("got %v, want %q", err, wantErr)
3108+
}
3109+
}
3110+
3111+
// dsaSelfSignedCNX produces DER-encoded
3112+
// certificate with the properties:
3113+
//
3114+
// Subject=Issuer=CN=X
3115+
// DSA SPKI
3116+
// Matching inner/outer signature OIDs
3117+
// Dummy ECDSA signature
3118+
func dsaSelfSignedCNX(t *testing.T) []byte {
3119+
t.Helper()
3120+
var params dsa.Parameters
3121+
if err := dsa.GenerateParameters(&params, rand.Reader, dsa.L1024N160); err != nil {
3122+
t.Fatal(err)
3123+
}
3124+
3125+
var dsaPriv dsa.PrivateKey
3126+
dsaPriv.Parameters = params
3127+
if err := dsa.GenerateKey(&dsaPriv, rand.Reader); err != nil {
3128+
t.Fatal(err)
3129+
}
3130+
dsaPub := &dsaPriv.PublicKey
3131+
3132+
type dsaParams struct{ P, Q, G *big.Int }
3133+
paramDER, err := asn1.Marshal(dsaParams{dsaPub.P, dsaPub.Q, dsaPub.G})
3134+
if err != nil {
3135+
t.Fatal(err)
3136+
}
3137+
yDER, err := asn1.Marshal(dsaPub.Y)
3138+
if err != nil {
3139+
t.Fatal(err)
3140+
}
3141+
3142+
spki := publicKeyInfo{
3143+
Algorithm: pkix.AlgorithmIdentifier{
3144+
Algorithm: oidPublicKeyDSA,
3145+
Parameters: asn1.RawValue{FullBytes: paramDER},
3146+
},
3147+
PublicKey: asn1.BitString{Bytes: yDER, BitLength: 8 * len(yDER)},
3148+
}
3149+
3150+
rdn := pkix.Name{CommonName: "X"}.ToRDNSequence()
3151+
b, err := asn1.Marshal(rdn)
3152+
if err != nil {
3153+
t.Fatal(err)
3154+
}
3155+
rawName := asn1.RawValue{FullBytes: b}
3156+
3157+
algoIdent := pkix.AlgorithmIdentifier{Algorithm: oidSignatureDSAWithSHA256}
3158+
tbs := tbsCertificate{
3159+
Version: 0,
3160+
SerialNumber: big.NewInt(1002),
3161+
SignatureAlgorithm: algoIdent,
3162+
Issuer: rawName,
3163+
Validity: validity{NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(24 * time.Hour)},
3164+
Subject: rawName,
3165+
PublicKey: spki,
3166+
}
3167+
c := certificate{
3168+
TBSCertificate: tbs,
3169+
SignatureAlgorithm: algoIdent,
3170+
SignatureValue: asn1.BitString{Bytes: []byte{0}, BitLength: 8},
3171+
}
3172+
dsaDER, err := asn1.Marshal(c)
3173+
if err != nil {
3174+
t.Fatal(err)
3175+
}
3176+
return dsaDER
3177+
}

0 commit comments

Comments
 (0)