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) {
|
if !bytes.Equal(candidate.RawSubject, cert.RawSubject) {
|
||||||
continue
|
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
|
continue
|
||||||
}
|
}
|
||||||
var certSAN *pkix.Extension
|
var certSAN *pkix.Extension
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package x509
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/dsa"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
|
@ -3048,3 +3049,129 @@ func TestInvalidPolicyWithAnyKeyUsage(t *testing.T) {
|
||||||
t.Fatalf("unexpected error, got %q, want %q", err, expectedErr)
|
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