Skip to content

Commit 158f62b

Browse files
authored
Remove policy qualifiers from all issuance paths (#6980)
The inclusion of Policy Qualifiers inside Policy Information elements of a Certificate Policies extension is now NOT RECOMMENDED by the Baseline Requirements. We have already removed these fields from all of our Boulder configuration, and ceased issuing certificates with Policy Qualifiers. Remove all support for configuring and including Policy Qualifiers in our certificates, both in Boulder's main issuance path and in our ceremony tool. Switch from using the policyasn1 library to manually encode these extensions, to using the crypto/x509's Certificate.PolicyIdentifiers field. Delete the policyasn1 package as it is no longer necessary. Fixes #6880
1 parent 92d75a9 commit 158f62b

File tree

8 files changed

+41
-200
lines changed

8 files changed

+41
-200
lines changed

ca/ca_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func setup(t *testing.T) *testCtx {
192192
AllowCTPoison: true,
193193
AllowSCTList: true,
194194
AllowCommonName: true,
195-
Policies: []issuance.PolicyInformation{
195+
Policies: []issuance.PolicyConfig{
196196
{OID: "2.23.140.1.2.1"},
197197
},
198198
MaxValidityPeriod: config.Duration{Duration: time.Hour * 8760},

cmd/ceremony/cert.go

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import (
1313
"strconv"
1414
"strings"
1515
"time"
16-
17-
"github.com/letsencrypt/boulder/policyasn1"
1816
)
1917

2018
type policyInfoConfig struct {
21-
OID string
19+
OID string
20+
// Deprecated: we do not include the id-qt-cps policy qualifier in our
21+
// certificate policy extensions anymore.
2222
CPSURI string `yaml:"cps-uri"`
2323
}
2424

@@ -56,9 +56,7 @@ type certProfile struct {
5656
IssuerURL string `yaml:"issuer-url"`
5757

5858
// PolicyOIDs should contain any OIDs to be inserted in a certificate
59-
// policies extension. If the CPSURI field of a policyInfoConfig element
60-
// is set it will result in a PolicyInformation structure containing a
61-
// single id-qt-cps type qualifier indicating the CPS URI.
59+
// policies extension.
6260
Policies []policyInfoConfig `yaml:"policies"`
6361

6462
// KeyUsages should contain the set of key usage bits to set
@@ -183,33 +181,7 @@ var stringToKeyUsage = map[string]x509.KeyUsage{
183181
"Cert Sign": x509.KeyUsageCertSign,
184182
}
185183

186-
var (
187-
oidExtensionCertificatePolicies = asn1.ObjectIdentifier{2, 5, 29, 32}
188-
189-
oidOCSPNoCheck = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5}
190-
)
191-
192-
func buildPolicies(policies []policyInfoConfig) (pkix.Extension, error) {
193-
policyExt := pkix.Extension{Id: oidExtensionCertificatePolicies}
194-
var policyInfo []policyasn1.PolicyInformation
195-
for _, p := range policies {
196-
oid, err := parseOID(p.OID)
197-
if err != nil {
198-
return pkix.Extension{}, err
199-
}
200-
pi := policyasn1.PolicyInformation{Policy: oid}
201-
if p.CPSURI != "" {
202-
pi.Qualifiers = []policyasn1.PolicyQualifier{{OID: policyasn1.CPSQualifierOID, Value: p.CPSURI}}
203-
}
204-
policyInfo = append(policyInfo, pi)
205-
}
206-
v, err := asn1.Marshal(policyInfo)
207-
if err != nil {
208-
return pkix.Extension{}, err
209-
}
210-
policyExt.Value = v
211-
return policyExt, nil
212-
}
184+
var oidOCSPNoCheck = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5}
213185

214186
func generateSKID(pk []byte) ([]byte, error) {
215187
var pkixPublicKey struct {
@@ -316,12 +288,12 @@ func makeTemplate(randReader io.Reader, profile *certProfile, pubKey []byte, ct
316288
cert.MaxPathLenZero = true
317289
}
318290

319-
if len(profile.Policies) > 0 {
320-
policyExt, err := buildPolicies(profile.Policies)
291+
for _, policyConfig := range profile.Policies {
292+
oid, err := parseOID(policyConfig.OID)
321293
if err != nil {
322294
return nil, err
323295
}
324-
cert.ExtraExtensions = append(cert.ExtraExtensions, policyExt)
296+
cert.PolicyIdentifiers = append(cert.PolicyIdentifiers, oid)
325297
}
326298

327299
return cert, nil

cmd/ceremony/cert_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ func TestMakeTemplate(t *testing.T) {
9797
profile.KeyUsages = []string{"Digital Signature", "CRL Sign"}
9898
profile.Policies = []policyInfoConfig{{}}
9999
_, err = makeTemplate(randReader, profile, pubKey, rootCert)
100-
test.AssertError(t, err, "makeTemplate didn't fail with invalid policy OID")
100+
test.AssertError(t, err, "makeTemplate didn't fail with invalid (empty) policy OID")
101101

102-
profile.Policies = []policyInfoConfig{{OID: "1.2.3"}, {OID: "1.2.3.4", CPSURI: "hello"}}
102+
profile.Policies = []policyInfoConfig{{OID: "1.2.3"}, {OID: "1.2.3.4"}}
103103
profile.CommonName = "common name"
104104
profile.Organization = "organization"
105105
profile.Country = "country"
@@ -120,7 +120,7 @@ func TestMakeTemplate(t *testing.T) {
120120
test.AssertEquals(t, len(cert.IssuingCertificateURL), 1)
121121
test.AssertEquals(t, cert.IssuingCertificateURL[0], profile.IssuerURL)
122122
test.AssertEquals(t, cert.KeyUsage, x509.KeyUsageDigitalSignature|x509.KeyUsageCRLSign)
123-
test.AssertEquals(t, len(cert.ExtraExtensions), 1)
123+
test.AssertEquals(t, len(cert.PolicyIdentifiers), 2)
124124
test.AssertEquals(t, len(cert.ExtKeyUsage), 0)
125125

126126
cert, err = makeTemplate(randReader, profile, pubKey, intermediateCert)
@@ -469,8 +469,7 @@ func TestVerifyProfile(t *testing.T) {
469469
},
470470
{
471471
profile: certProfile{
472-
Policies: []policyInfoConfig{
473-
{OID: "1.2.3"}, {OID: "1.2.3.4", CPSURI: "hello"}},
472+
Policies: []policyInfoConfig{{OID: "1.2.3"}},
474473
},
475474
certType: requestCert,
476475
expectedErr: "policies cannot be set for a CSR",

issuance/issuance.go

Lines changed: 19 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131
"github.com/letsencrypt/boulder/config"
3232
"github.com/letsencrypt/boulder/core"
3333
"github.com/letsencrypt/boulder/linter"
34-
"github.com/letsencrypt/boulder/policyasn1"
3534
"github.com/letsencrypt/boulder/precert"
3635
"github.com/letsencrypt/boulder/privatekey"
3736
"github.com/letsencrypt/pkcs11key/v4"
@@ -44,21 +43,14 @@ type ProfileConfig struct {
4443
AllowSCTList bool
4544
AllowCommonName bool
4645

47-
Policies []PolicyInformation `validate:"omitempty,dive"`
46+
Policies []PolicyConfig `validate:"min=1,dive"`
4847
MaxValidityPeriod config.Duration
4948
MaxValidityBackdate config.Duration
5049
}
5150

52-
// PolicyInformation describes a policy
53-
type PolicyInformation struct {
54-
OID string
55-
Qualifiers []PolicyQualifier `validate:"excluded_without=OID,dive"`
56-
}
57-
58-
// PolicyQualifier describes a policy qualifier
59-
type PolicyQualifier struct {
60-
Type string `validate:"required"`
61-
Value string `validate:"required"`
51+
// PolicyConfig describes a policy
52+
type PolicyConfig struct {
53+
OID string `validate:"required"`
6254
}
6355

6456
// IssuerConfig describes the constraints on and URLs used by a single issuer.
@@ -143,7 +135,7 @@ func loadSigner(location IssuerLoc, cert *Certificate) (crypto.Signer, error) {
143135
if pkcs11Config.Module == "" ||
144136
pkcs11Config.TokenLabel == "" ||
145137
pkcs11Config.PIN == "" {
146-
return nil, fmt.Errorf("Missing a field in pkcs11Config %#v", pkcs11Config)
138+
return nil, fmt.Errorf("missing a field in pkcs11Config %#v", pkcs11Config)
147139
}
148140

149141
numSessions := location.NumSessions
@@ -169,7 +161,7 @@ type Profile struct {
169161
ocspURL string
170162
crlURL string
171163
issuerURL string
172-
policies *pkix.Extension
164+
policies []asn1.ObjectIdentifier
173165

174166
maxBackdate time.Duration
175167
maxValidity time.Duration
@@ -190,10 +182,6 @@ func parseOID(oidStr string) (asn1.ObjectIdentifier, error) {
190182
return oid, nil
191183
}
192184

193-
var stringToQualifierType = map[string]asn1.ObjectIdentifier{
194-
"id-qt-cps": policyasn1.CPSQualifierOID,
195-
}
196-
197185
// NewProfile synthesizes the profile config and issuer config into a single
198186
// object, and checks various aspects for correctness.
199187
func NewProfile(profileConfig ProfileConfig, issuerConfig IssuerConfig) (*Profile, error) {
@@ -203,6 +191,16 @@ func NewProfile(profileConfig ProfileConfig, issuerConfig IssuerConfig) (*Profil
203191
if issuerConfig.OCSPURL == "" {
204192
return nil, errors.New("OCSP URL is required")
205193
}
194+
195+
var policies []asn1.ObjectIdentifier
196+
for _, policyConfig := range profileConfig.Policies {
197+
oid, err := parseOID(policyConfig.OID)
198+
if err != nil {
199+
return nil, fmt.Errorf("failed parsing policy OID %q: %w", policyConfig.OID, err)
200+
}
201+
policies = append(policies, oid)
202+
}
203+
206204
sp := &Profile{
207205
useForRSALeaves: issuerConfig.UseForRSALeaves,
208206
useForECDSALeaves: issuerConfig.UseForECDSALeaves,
@@ -213,39 +211,11 @@ func NewProfile(profileConfig ProfileConfig, issuerConfig IssuerConfig) (*Profil
213211
issuerURL: issuerConfig.IssuerURL,
214212
crlURL: issuerConfig.CRLURL,
215213
ocspURL: issuerConfig.OCSPURL,
214+
policies: policies,
216215
maxBackdate: profileConfig.MaxValidityBackdate.Duration,
217216
maxValidity: profileConfig.MaxValidityPeriod.Duration,
218217
}
219-
if len(profileConfig.Policies) > 0 {
220-
var policies []policyasn1.PolicyInformation
221-
for _, policyConfig := range profileConfig.Policies {
222-
id, err := parseOID(policyConfig.OID)
223-
if err != nil {
224-
return nil, fmt.Errorf("failed parsing policy OID %q: %s", policyConfig.OID, err)
225-
}
226-
pi := policyasn1.PolicyInformation{Policy: id}
227-
for _, qualifierConfig := range policyConfig.Qualifiers {
228-
qt, ok := stringToQualifierType[qualifierConfig.Type]
229-
if !ok {
230-
return nil, fmt.Errorf("unknown qualifier type: %s", qualifierConfig.Type)
231-
}
232-
pq := policyasn1.PolicyQualifier{
233-
OID: qt,
234-
Value: qualifierConfig.Value,
235-
}
236-
pi.Qualifiers = append(pi.Qualifiers, pq)
237-
}
238-
policies = append(policies, pi)
239-
}
240-
policyExtBytes, err := asn1.Marshal(policies)
241-
if err != nil {
242-
return nil, err
243-
}
244-
sp.policies = &pkix.Extension{
245-
Id: asn1.ObjectIdentifier{2, 5, 29, 32},
246-
Value: policyExtBytes,
247-
}
248-
}
218+
249219
return sp, nil
250220
}
251221

@@ -324,16 +294,13 @@ func (p *Profile) generateTemplate() *x509.Certificate {
324294
OCSPServer: []string{p.ocspURL},
325295
IssuingCertificateURL: []string{p.issuerURL},
326296
BasicConstraintsValid: true,
297+
PolicyIdentifiers: p.policies,
327298
}
328299

329300
if p.crlURL != "" {
330301
template.CRLDistributionPoints = []string{p.crlURL}
331302
}
332303

333-
if p.policies != nil {
334-
template.ExtraExtensions = []pkix.Extension{*p.policies}
335-
}
336-
337304
return template
338305
}
339306

issuance/issuance_test.go

Lines changed: 9 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"github.com/letsencrypt/boulder/core"
2828
"github.com/letsencrypt/boulder/ctpolicy/loglist"
2929
"github.com/letsencrypt/boulder/linter"
30-
"github.com/letsencrypt/boulder/policyasn1"
3130
"github.com/letsencrypt/boulder/test"
3231
)
3332

@@ -37,7 +36,7 @@ func defaultProfileConfig() ProfileConfig {
3736
AllowCTPoison: true,
3837
AllowSCTList: true,
3938
AllowMustStaple: true,
40-
Policies: []PolicyInformation{
39+
Policies: []PolicyConfig{
4140
{OID: "1.2.3"},
4241
},
4342
MaxValidityPeriod: config.Duration{Duration: time.Hour},
@@ -85,47 +84,12 @@ func TestMain(m *testing.M) {
8584

8685
func TestNewProfilePolicies(t *testing.T) {
8786
config := defaultProfileConfig()
88-
config.Policies = append(config.Policies, PolicyInformation{
87+
config.Policies = append(config.Policies, PolicyConfig{
8988
OID: "1.2.3.4",
90-
Qualifiers: []PolicyQualifier{
91-
{
92-
Type: "id-qt-cps",
93-
Value: "cps-url",
94-
},
95-
},
9689
})
9790
profile, err := NewProfile(config, defaultIssuerConfig())
9891
test.AssertNotError(t, err, "NewProfile failed")
99-
test.AssertDeepEquals(t, *profile, Profile{
100-
useForRSALeaves: true,
101-
useForECDSALeaves: true,
102-
allowMustStaple: true,
103-
allowCTPoison: true,
104-
allowSCTList: true,
105-
allowCommonName: true,
106-
issuerURL: "http://issuer-url",
107-
ocspURL: "http://ocsp-url",
108-
policies: &pkix.Extension{
109-
Id: asn1.ObjectIdentifier{2, 5, 29, 32},
110-
Value: []byte{48, 36, 48, 4, 6, 2, 42, 3, 48, 28, 6, 3, 42, 3, 4, 48, 21, 48, 19, 6, 8, 43, 6, 1, 5, 5, 7, 2, 1, 22, 7, 99, 112, 115, 45, 117, 114, 108},
111-
},
112-
maxBackdate: time.Hour,
113-
maxValidity: time.Hour,
114-
})
115-
var policies []policyasn1.PolicyInformation
116-
_, err = asn1.Unmarshal(profile.policies.Value, &policies)
117-
test.AssertNotError(t, err, "failed to parse policies extension")
118-
test.AssertEquals(t, len(policies), 2)
119-
test.AssertDeepEquals(t, policies[0], policyasn1.PolicyInformation{
120-
Policy: asn1.ObjectIdentifier{1, 2, 3},
121-
})
122-
test.AssertDeepEquals(t, policies[1], policyasn1.PolicyInformation{
123-
Policy: asn1.ObjectIdentifier{1, 2, 3, 4},
124-
Qualifiers: []policyasn1.PolicyQualifier{{
125-
OID: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 1},
126-
Value: "cps-url",
127-
}},
128-
})
92+
test.AssertDeepEquals(t, profile.policies, []asn1.ObjectIdentifier{{1, 2, 3}, {1, 2, 3, 4}})
12993
}
13094

13195
func TestNewProfileNoIssuerURL(t *testing.T) {
@@ -142,28 +106,14 @@ func TestNewProfileNoOCSPURL(t *testing.T) {
142106

143107
func TestNewProfileInvalidOID(t *testing.T) {
144108
_, err := NewProfile(ProfileConfig{
145-
Policies: []PolicyInformation{{
109+
Policies: []PolicyConfig{{
146110
OID: "a.b.c",
147111
}},
148112
}, defaultIssuerConfig())
149-
test.AssertError(t, err, "NewProfile didn't fail with unknown policy qualifier type")
113+
test.AssertError(t, err, "NewProfile didn't fail with malformed policy OID")
150114
test.AssertEquals(t, err.Error(), "failed parsing policy OID \"a.b.c\": strconv.Atoi: parsing \"a\": invalid syntax")
151115
}
152116

153-
func TestNewProfileUnknownQualifierType(t *testing.T) {
154-
_, err := NewProfile(ProfileConfig{
155-
Policies: []PolicyInformation{{
156-
OID: "1.2.3",
157-
Qualifiers: []PolicyQualifier{{
158-
Type: "asd",
159-
Value: "bad",
160-
}},
161-
}},
162-
}, defaultIssuerConfig())
163-
test.AssertError(t, err, "NewProfile didn't fail with unknown policy qualifier type")
164-
test.AssertEquals(t, err.Error(), "unknown qualifier type: asd")
165-
}
166-
167117
func TestRequestValid(t *testing.T) {
168118
fc := clock.NewFake()
169119
fc.Add(time.Hour * 24)
@@ -398,24 +348,16 @@ func TestGenerateTemplate(t *testing.T) {
398348
{
399349
name: "include policies",
400350
profile: &Profile{
401-
sigAlg: x509.SHA256WithRSA,
402-
policies: &pkix.Extension{
403-
Id: asn1.ObjectIdentifier{1, 2, 3},
404-
Value: []byte{4, 5, 6},
405-
},
351+
sigAlg: x509.SHA256WithRSA,
352+
policies: []asn1.ObjectIdentifier{{4, 5, 6}},
406353
},
407354
expectedTemplate: &x509.Certificate{
408355
BasicConstraintsValid: true,
409356
SignatureAlgorithm: x509.SHA256WithRSA,
410357
ExtKeyUsage: defaultEKU,
411358
IssuingCertificateURL: []string{""},
412359
OCSPServer: []string{""},
413-
ExtraExtensions: []pkix.Extension{
414-
{
415-
Id: asn1.ObjectIdentifier{1, 2, 3},
416-
Value: []byte{4, 5, 6},
417-
},
418-
},
360+
PolicyIdentifiers: []asn1.ObjectIdentifier{{4, 5, 6}},
419361
},
420362
},
421363
}
@@ -1024,7 +966,7 @@ func TestMismatchedProfiles(t *testing.T) {
1024966

1025967
// Create a new profile that differs slightly (one more PolicyInformation than the precert)
1026968
profileConfig := defaultProfileConfig()
1027-
profileConfig.Policies = append(profileConfig.Policies, PolicyInformation{OID: "1.2.3.4", Qualifiers: nil})
969+
profileConfig.Policies = append(profileConfig.Policies, PolicyConfig{OID: "1.2.3.4"})
1028970
p, err := NewProfile(profileConfig, defaultIssuerConfig())
1029971
test.AssertNotError(t, err, "NewProfile failed")
1030972
issuer2, err := NewIssuer(issuerCert, issuerSigner, p, linter, fc)

0 commit comments

Comments
 (0)