Skip to content

Commit b18b058

Browse files
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 <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent f4ff0e2 commit b18b058

File tree

5 files changed

+92
-8
lines changed

5 files changed

+92
-8
lines changed

doc/godebug.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ runtime locks can be enabled with the [`runtimecontentionstacks`
185185
setting](/pkg/runtime#hdr-Environment_Variable). These stack traces have
186186
non-standard semantics, see setting documentation for details.
187187

188+
Go 1.22 added a new [`crypto/x509.Certificate`](/pkg/crypto/x509/#Certificate)
189+
field, [`Policies`](/pkg/crypto/x509/#Certificate.Policies), which supports
190+
certificate policy OIDs with components larger than 31 bits. By default this
191+
field is only used during parsing, when it is populated with policy OIDs, but
192+
not used during marshaling. It can be used to marshal these larger OIDs, instead
193+
of the existing PolicyIdentifiers field, by using the
194+
[`x509usepolicies` setting.](/pkg/crypto/x509/#CreateCertificate).
195+
196+
188197
### Go 1.21
189198

190199
Go 1.21 made it a run-time error to call `panic` with a nil interface value,

src/crypto/x509/x509.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,8 @@ func isIA5String(s string) error {
11011101
return nil
11021102
}
11031103

1104+
var usePoliciesField = godebug.New("x509usepolicies")
1105+
11041106
func buildCertExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId []byte, subjectKeyId []byte) (ret []pkix.Extension, err error) {
11051107
ret = make([]pkix.Extension, 10 /* maximum number of elements. */)
11061108
n := 0
@@ -1186,9 +1188,10 @@ func buildCertExtensions(template *Certificate, subjectIsEmpty bool, authorityKe
11861188
n++
11871189
}
11881190

1189-
if len(template.PolicyIdentifiers) > 0 &&
1191+
usePolicies := usePoliciesField.Value() == "1"
1192+
if ((!usePolicies && len(template.PolicyIdentifiers) > 0) || (usePolicies && len(template.Policies) > 0)) &&
11901193
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
1191-
ret[n], err = marshalCertificatePolicies(template.PolicyIdentifiers)
1194+
ret[n], err = marshalCertificatePolicies(template.Policies, template.PolicyIdentifiers)
11921195
if err != nil {
11931196
return nil, err
11941197
}
@@ -1373,15 +1376,30 @@ func marshalBasicConstraints(isCA bool, maxPathLen int, maxPathLenZero bool) (pk
13731376
return ext, err
13741377
}
13751378

1376-
func marshalCertificatePolicies(policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) {
1379+
func marshalCertificatePolicies(policies []OID, policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) {
13771380
ext := pkix.Extension{Id: oidExtensionCertificatePolicies}
13781381

13791382
b := cryptobyte.NewBuilder(make([]byte, 0, 128))
13801383
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
1381-
for _, v := range policyIdentifiers {
1382-
child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
1383-
child.AddASN1ObjectIdentifier(v)
1384-
})
1384+
if usePoliciesField.Value() == "1" {
1385+
usePoliciesField.IncNonDefault()
1386+
for _, v := range policies {
1387+
child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
1388+
child.AddASN1(cryptobyte_asn1.OBJECT_IDENTIFIER, func(child *cryptobyte.Builder) {
1389+
if len(v.der) == 0 {
1390+
child.SetError(errors.New("invalid policy object identifier"))
1391+
return
1392+
}
1393+
child.AddBytes(v.der)
1394+
})
1395+
})
1396+
}
1397+
} else {
1398+
for _, v := range policyIdentifiers {
1399+
child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
1400+
child.AddASN1ObjectIdentifier(v)
1401+
})
1402+
}
13851403
}
13861404
})
13871405

@@ -1526,7 +1544,8 @@ var emptyASN1Subject = []byte{0x30, 0}
15261544
// - PermittedEmailAddresses
15271545
// - PermittedIPRanges
15281546
// - PermittedURIDomains
1529-
// - PolicyIdentifiers
1547+
// - PolicyIdentifiers (see note below)
1548+
// - Policies (see note below)
15301549
// - SerialNumber
15311550
// - SignatureAlgorithm
15321551
// - Subject
@@ -1550,6 +1569,13 @@ var emptyASN1Subject = []byte{0x30, 0}
15501569
//
15511570
// If SubjectKeyId from template is empty and the template is a CA, SubjectKeyId
15521571
// will be generated from the hash of the public key.
1572+
//
1573+
// The PolicyIdentifier and Policies fields are both used to marshal certificate
1574+
// policy OIDs. By default, only the PolicyIdentifier is marshaled, but if the
1575+
// GODEBUG setting "x509usepolicies" has the value "1", the Policies field will
1576+
// be marshalled instead of the PolicyIdentifier field. The Policies field can
1577+
// be used to marshal policy OIDs which have components that are larger than 31
1578+
// bits.
15531579
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv any) ([]byte, error) {
15541580
key, ok := priv.(crypto.Signer)
15551581
if !ok {

src/crypto/x509/x509_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3956,3 +3956,46 @@ func TestCertificateOIDPolicies(t *testing.T) {
39563956
t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies)
39573957
}
39583958
}
3959+
3960+
func TestCertificatePoliciesGODEBUG(t *testing.T) {
3961+
template := Certificate{
3962+
SerialNumber: big.NewInt(1),
3963+
Subject: pkix.Name{CommonName: "Cert"},
3964+
NotBefore: time.Unix(1000, 0),
3965+
NotAfter: time.Unix(100000, 0),
3966+
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
3967+
Policies: []OID{mustNewOIDFromInts(t, []uint64{1, 2, math.MaxUint32 + 1})},
3968+
}
3969+
3970+
expectPolicies := []OID{mustNewOIDFromInts(t, []uint64{1, 2, 3})}
3971+
certDER, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey)
3972+
if err != nil {
3973+
t.Fatalf("CreateCertificate() unexpected error: %v", err)
3974+
}
3975+
3976+
cert, err := ParseCertificate(certDER)
3977+
if err != nil {
3978+
t.Fatalf("ParseCertificate() unexpected error: %v", err)
3979+
}
3980+
3981+
if !slices.EqualFunc(cert.Policies, expectPolicies, OID.Equal) {
3982+
t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies)
3983+
}
3984+
3985+
t.Setenv("GODEBUG", "x509usepolicies=1")
3986+
expectPolicies = []OID{mustNewOIDFromInts(t, []uint64{1, 2, math.MaxUint32 + 1})}
3987+
3988+
certDER, err = CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey)
3989+
if err != nil {
3990+
t.Fatalf("CreateCertificate() unexpected error: %v", err)
3991+
}
3992+
3993+
cert, err = ParseCertificate(certDER)
3994+
if err != nil {
3995+
t.Fatalf("ParseCertificate() unexpected error: %v", err)
3996+
}
3997+
3998+
if !slices.EqualFunc(cert.Policies, expectPolicies, OID.Equal) {
3999+
t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies)
4000+
}
4001+
}

src/internal/godebugs/table.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ var All = []Info{
5151
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
5252
{Name: "x509sha1", Package: "crypto/x509"},
5353
{Name: "x509usefallbackroots", Package: "crypto/x509"},
54+
{Name: "x509usepolicies", Package: "crypto/x509"},
5455
{Name: "zipinsecurepath", Package: "archive/zip"},
5556
}
5657

src/runtime/metrics/doc.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,11 @@ Below is the full list of supported metrics, ordered lexicographically.
327327
package due to a non-default GODEBUG=x509usefallbackroots=...
328328
setting.
329329
330+
/godebug/non-default-behavior/x509usepolicies:events
331+
The number of non-default behaviors executed by the crypto/x509
332+
package due to a non-default GODEBUG=x509usepolicies=...
333+
setting.
334+
330335
/godebug/non-default-behavior/zipinsecurepath:events
331336
The number of non-default behaviors executed by the archive/zip
332337
package due to a non-default GODEBUG=zipinsecurepath=...

0 commit comments

Comments
 (0)