Skip to content

Commit e2d9574

Browse files
crypto/x509: fix certificate policy marshaling
CL 520535 added the new OID type, and the Certificate field Policies to replace PolicyIdentifiers. During review I missed three problems: (1) the marshaling of Certificate didn't take into account the case where both fields were populated with the same OIDs (which would be the case if you parsed a certificate and used it as a template), (2) buildCertExtensions only generated the certificate policies extension if PolicyIdentifiers was populated, and (3) how we would marshal an empty OID (i.e. OID{}). This change makes marshaling a certificate with an empty OID an error, and only adds a single copy of any OID that appears in both Policies and PolicyIdentifiers to the certificate policies extension. This should make the round trip behavior for certificates reasonable. Additionally this change documents that CreateCertificate uses the Policies field from the template, and fixes buildCertExtensions to populate the certificate policies extension if _either_ PolicyIdentifiers or Policies is populated, not just PolicyIdentifiers. Fixes #63909 Change-Id: I0fcbd3ceaab7a376e7e991ff8b37e2145ffb4a61 Reviewed-on: https://go-review.googlesource.com/c/go/+/539297 Reviewed-by: Mateusz Poliwczak <[email protected]> Reviewed-by: Damien Neil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 1764da7 commit e2d9574

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

Diff for: src/crypto/x509/x509.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ func buildCertExtensions(template *Certificate, subjectIsEmpty bool, authorityKe
11861186
n++
11871187
}
11881188

1189-
if len(template.PolicyIdentifiers) > 0 &&
1189+
if (len(template.PolicyIdentifiers) > 0 || len(template.Policies) > 0) &&
11901190
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
11911191
ret[n], err = marshalCertificatePolicies(template.Policies, template.PolicyIdentifiers)
11921192
if err != nil {
@@ -1378,14 +1378,27 @@ func marshalCertificatePolicies(policies []OID, policyIdentifiers []asn1.ObjectI
13781378

13791379
b := cryptobyte.NewBuilder(make([]byte, 0, 128))
13801380
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
1381+
// added is used to track OIDs which are duplicated in both Policies and PolicyIdentifiers
1382+
// so they can be skipped. Note that this explicitly doesn't check for duplicate OIDs in
1383+
// Policies or in PolicyIdentifiers themselves, as this would be considered breaking behavior.
1384+
added := map[string]bool{}
13811385
for _, v := range policies {
13821386
child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
13831387
child.AddASN1(cryptobyte_asn1.OBJECT_IDENTIFIER, func(child *cryptobyte.Builder) {
1388+
oidStr := v.String()
1389+
added[oidStr] = true
1390+
if len(v.der) == 0 {
1391+
child.SetError(errors.New("invalid policy object identifier"))
1392+
return
1393+
}
13841394
child.AddBytes(v.der)
13851395
})
13861396
})
13871397
}
13881398
for _, v := range policyIdentifiers {
1399+
if added[v.String()] {
1400+
continue
1401+
}
13891402
child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
13901403
child.AddASN1ObjectIdentifier(v)
13911404
})
@@ -1534,6 +1547,7 @@ var emptyASN1Subject = []byte{0x30, 0}
15341547
// - PermittedIPRanges
15351548
// - PermittedURIDomains
15361549
// - PolicyIdentifiers
1550+
// - Policies
15371551
// - SerialNumber
15381552
// - SignatureAlgorithm
15391553
// - Subject
@@ -1557,6 +1571,9 @@ var emptyASN1Subject = []byte{0x30, 0}
15571571
//
15581572
// If SubjectKeyId from template is empty and the template is a CA, SubjectKeyId
15591573
// will be generated from the hash of the public key.
1574+
//
1575+
// If both PolicyIdentifiers and Policies are populated, any OID which appears
1576+
// in both slices will only be added to the certificate policies extension once.
15601577
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv any) ([]byte, error) {
15611578
key, ok := priv.(crypto.Signer)
15621579
if !ok {

Diff for: src/crypto/x509/x509_test.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -3929,23 +3929,24 @@ func TestCertificateOIDPolicies(t *testing.T) {
39293929
NotAfter: time.Unix(100000, 0),
39303930
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
39313931
Policies: []OID{
3932+
mustNewOIDFromInts(t, []uint64{1, 2, 3}),
39323933
mustNewOIDFromInts(t, []uint64{1, 2, 3, 4, 5}),
39333934
mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxInt32}),
39343935
mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxUint32, math.MaxUint64}),
39353936
},
39363937
}
39373938

39383939
var expectPolicyIdentifiers = []asn1.ObjectIdentifier{
3940+
[]int{1, 2, 3},
39393941
[]int{1, 2, 3, 4, 5},
39403942
[]int{1, 2, 3, math.MaxInt32},
3941-
[]int{1, 2, 3},
39423943
}
39433944

39443945
var expectPolicies = []OID{
3946+
mustNewOIDFromInts(t, []uint64{1, 2, 3}),
39453947
mustNewOIDFromInts(t, []uint64{1, 2, 3, 4, 5}),
39463948
mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxInt32}),
39473949
mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxUint32, math.MaxUint64}),
3948-
mustNewOIDFromInts(t, []uint64{1, 2, 3}),
39493950
}
39503951

39513952
certDER, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey)
@@ -3966,3 +3967,19 @@ func TestCertificateOIDPolicies(t *testing.T) {
39663967
t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies)
39673968
}
39683969
}
3970+
3971+
func TestInvalidPolicyOID(t *testing.T) {
3972+
template := Certificate{
3973+
SerialNumber: big.NewInt(1),
3974+
Subject: pkix.Name{CommonName: "Cert"},
3975+
NotBefore: time.Now(),
3976+
NotAfter: time.Now().Add(time.Hour),
3977+
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
3978+
Policies: []OID{OID{}},
3979+
}
3980+
_, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey)
3981+
expected := "invalid policy object identifier"
3982+
if err.Error() != expected {
3983+
t.Fatalf("CreateCertificate() unexpected error: %v, want: %v", err, expected)
3984+
}
3985+
}

0 commit comments

Comments
 (0)