diff options
author | Roland Shoemaker <roland@golang.org> | 2023-12-04 10:17:34 -0800 |
---|---|---|
committer | Roland Shoemaker <roland@golang.org> | 2023-12-11 23:29:45 +0000 |
commit | b18b05881691861c4279a50010829150f1684fa9 (patch) | |
tree | 5b1096b76342a088dcc3b9250edb547560f51d42 /src/crypto | |
parent | f4ff0e25054d1159396cc1d9252f1e94021ce46c (diff) | |
download | go-b18b05881691861c4279a50010829150f1684fa9.tar.gz go-b18b05881691861c4279a50010829150f1684fa9.zip |
crypto/x509: gate Policies marshaling with GODEBUG
Use a GODEBUG to choose which certificate policy field to use. If
x509usepolicies=1 is set, use the Policies field, otherwise use the
PolicyIdentifiers field.
Fixes #64248
Change-Id: I3f0b56102e0bac4ebe800497717c61c58ef3f092
Reviewed-on: https://go-review.googlesource.com/c/go/+/546916
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/crypto')
-rw-r--r-- | src/crypto/x509/x509.go | 42 | ||||
-rw-r--r-- | src/crypto/x509/x509_test.go | 43 |
2 files changed, 77 insertions, 8 deletions
diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index 29d8e6bff7..f33283b559 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -1101,6 +1101,8 @@ func isIA5String(s string) error { return nil } +var usePoliciesField = godebug.New("x509usepolicies") + func buildCertExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId []byte, subjectKeyId []byte) (ret []pkix.Extension, err error) { ret = make([]pkix.Extension, 10 /* maximum number of elements. */) n := 0 @@ -1186,9 +1188,10 @@ func buildCertExtensions(template *Certificate, subjectIsEmpty bool, authorityKe n++ } - if len(template.PolicyIdentifiers) > 0 && + usePolicies := usePoliciesField.Value() == "1" + if ((!usePolicies && len(template.PolicyIdentifiers) > 0) || (usePolicies && len(template.Policies) > 0)) && !oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) { - ret[n], err = marshalCertificatePolicies(template.PolicyIdentifiers) + ret[n], err = marshalCertificatePolicies(template.Policies, template.PolicyIdentifiers) if err != nil { return nil, err } @@ -1373,15 +1376,30 @@ func marshalBasicConstraints(isCA bool, maxPathLen int, maxPathLenZero bool) (pk return ext, err } -func marshalCertificatePolicies(policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) { +func marshalCertificatePolicies(policies []OID, policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) { ext := pkix.Extension{Id: oidExtensionCertificatePolicies} b := cryptobyte.NewBuilder(make([]byte, 0, 128)) b.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { - for _, v := range policyIdentifiers { - child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { - child.AddASN1ObjectIdentifier(v) - }) + if usePoliciesField.Value() == "1" { + usePoliciesField.IncNonDefault() + for _, v := range policies { + child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { + child.AddASN1(cryptobyte_asn1.OBJECT_IDENTIFIER, func(child *cryptobyte.Builder) { + if len(v.der) == 0 { + child.SetError(errors.New("invalid policy object identifier")) + return + } + child.AddBytes(v.der) + }) + }) + } + } else { + for _, v := range policyIdentifiers { + child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { + child.AddASN1ObjectIdentifier(v) + }) + } } }) @@ -1526,7 +1544,8 @@ var emptyASN1Subject = []byte{0x30, 0} // - PermittedEmailAddresses // - PermittedIPRanges // - PermittedURIDomains -// - PolicyIdentifiers +// - PolicyIdentifiers (see note below) +// - Policies (see note below) // - SerialNumber // - SignatureAlgorithm // - Subject @@ -1550,6 +1569,13 @@ var emptyASN1Subject = []byte{0x30, 0} // // If SubjectKeyId from template is empty and the template is a CA, SubjectKeyId // will be generated from the hash of the public key. +// +// The PolicyIdentifier and Policies fields are both used to marshal certificate +// policy OIDs. By default, only the PolicyIdentifier is marshaled, but if the +// GODEBUG setting "x509usepolicies" has the value "1", the Policies field will +// be marshalled instead of the PolicyIdentifier field. The Policies field can +// be used to marshal policy OIDs which have components that are larger than 31 +// bits. func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv any) ([]byte, error) { key, ok := priv.(crypto.Signer) if !ok { diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index 47cddceacf..ead0453f66 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -3956,3 +3956,46 @@ func TestCertificateOIDPolicies(t *testing.T) { t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies) } } + +func TestCertificatePoliciesGODEBUG(t *testing.T) { + template := Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "Cert"}, + NotBefore: time.Unix(1000, 0), + NotAfter: time.Unix(100000, 0), + PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, + Policies: []OID{mustNewOIDFromInts(t, []uint64{1, 2, math.MaxUint32 + 1})}, + } + + expectPolicies := []OID{mustNewOIDFromInts(t, []uint64{1, 2, 3})} + certDER, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey) + if err != nil { + t.Fatalf("CreateCertificate() unexpected error: %v", err) + } + + cert, err := ParseCertificate(certDER) + if err != nil { + t.Fatalf("ParseCertificate() unexpected error: %v", err) + } + + if !slices.EqualFunc(cert.Policies, expectPolicies, OID.Equal) { + t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies) + } + + t.Setenv("GODEBUG", "x509usepolicies=1") + expectPolicies = []OID{mustNewOIDFromInts(t, []uint64{1, 2, math.MaxUint32 + 1})} + + certDER, err = CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey) + if err != nil { + t.Fatalf("CreateCertificate() unexpected error: %v", err) + } + + cert, err = ParseCertificate(certDER) + if err != nil { + t.Fatalf("ParseCertificate() unexpected error: %v", err) + } + + if !slices.EqualFunc(cert.Policies, expectPolicies, OID.Equal) { + t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies) + } +} |