aboutsummaryrefslogtreecommitdiff
path: root/src/crypto
diff options
context:
space:
mode:
authorRoland Shoemaker <roland@golang.org>2023-12-04 10:17:34 -0800
committerRoland Shoemaker <roland@golang.org>2023-12-11 23:29:45 +0000
commitb18b05881691861c4279a50010829150f1684fa9 (patch)
tree5b1096b76342a088dcc3b9250edb547560f51d42 /src/crypto
parentf4ff0e25054d1159396cc1d9252f1e94021ce46c (diff)
downloadgo-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.go42
-rw-r--r--src/crypto/x509/x509_test.go43
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)
+ }
+}