Skip to content

Commit cd6597e

Browse files
authored
ca: add per-profile MaxCertificateSize (#8806)
1 parent 8af69ff commit cd6597e

3 files changed

Lines changed: 53 additions & 0 deletions

File tree

issuance/cert.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ type ProfileConfig struct {
4646
MaxValidityPeriod config.Duration
4747
MaxValidityBackdate config.Duration
4848

49+
// MaxCertificateSize causes rejection at the linting stage of any certificate
50+
// that would be bigger than this many bytes. This should be considered a backstop
51+
// and should be set higher than the corresponding limits at the WFE
52+
// (MaxCumulativeIdentifierLength) and the RA (MaxNames * 253, per-profile),
53+
// plus the longest possible signature size, plus some extra for certificate
54+
// fields.
55+
MaxCertificateSize int
56+
4957
// LintConfig is a path to a zlint config file, which can be used to control
5058
// the behavior of zlint's "customizable lints".
5159
LintConfig string
@@ -64,6 +72,8 @@ type Profile struct {
6472
maxBackdate time.Duration
6573
maxValidity time.Duration
6674

75+
maxCertificateSize int
76+
6777
lints lint.Registry
6878
}
6979

@@ -96,6 +106,7 @@ func NewProfile(profileConfig ProfileConfig) (*Profile, error) {
96106
omitSKID: profileConfig.OmitSKID,
97107
maxBackdate: profileConfig.MaxValidityBackdate.Duration,
98108
maxValidity: profileConfig.MaxValidityPeriod.Duration,
109+
maxCertificateSize: profileConfig.MaxCertificateSize,
99110
lints: lints,
100111
}
101112

@@ -361,6 +372,10 @@ func (i *Issuer) Prepare(prof *Profile, req *IssuanceRequest) ([]byte, *issuance
361372
return nil, nil, fmt.Errorf("tbsCertificate linting failed: %w", err)
362373
}
363374

375+
if prof.maxCertificateSize > 0 && len(lintCertBytes) > prof.maxCertificateSize {
376+
return nil, nil, fmt.Errorf("linting certificate too big (%d > %d)", len(lintCertBytes), prof.maxCertificateSize)
377+
}
378+
364379
if len(req.precertDER) > 0 {
365380
err = precert.Correspond(req.precertDER, lintCertBytes)
366381
if err != nil {

issuance/cert_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"crypto/x509"
1111
"crypto/x509/pkix"
1212
"encoding/base64"
13+
"fmt"
1314
"net"
1415
"reflect"
1516
"strings"
@@ -395,6 +396,40 @@ func TestIssue(t *testing.T) {
395396
}
396397
}
397398

399+
func TestIssueCertTooBig(t *testing.T) {
400+
fc := clock.NewFake()
401+
signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
402+
if err != nil {
403+
t.Fatalf("newIssuer: %s", err)
404+
}
405+
pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
406+
if err != nil {
407+
t.Fatalf("ecdsa.GenerateKey: %s", err)
408+
}
409+
var dnsNames []string
410+
for i := 0; i < 1000; i++ {
411+
dnsNames = append(dnsNames, fmt.Sprintf("%d.example.com", i))
412+
}
413+
profile := defaultProfile()
414+
profile.maxCertificateSize = 1000
415+
_, _, err = signer.Prepare(profile, &IssuanceRequest{
416+
PublicKey: MarshalablePublicKey{pk.Public()},
417+
SubjectKeyId: goodSKID,
418+
Serial: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
419+
DNSNames: dnsNames,
420+
NotBefore: fc.Now(),
421+
NotAfter: fc.Now().Add(time.Hour - time.Second),
422+
IncludeCTPoison: true,
423+
})
424+
if err == nil {
425+
t.Errorf("signer.Prepare of big cert: got nil error, want an error")
426+
}
427+
expected := "linting certificate too big"
428+
if !strings.Contains(err.Error(), expected) {
429+
t.Errorf("signer.Prepare of big cert: got %q, want %q", err, expected)
430+
}
431+
}
432+
398433
func TestIssueDNSNamesOnly(t *testing.T) {
399434
fc := clock.NewFake()
400435
signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)

test/config-next/ca.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"omitSKID": false,
5656
"maxValidityPeriod": "7776000s",
5757
"maxValidityBackdate": "1h5m",
58+
"maxCertificateSize": 10000,
5859
"lintConfig": "test/config-next/zlint.toml",
5960
"ignoredLints": [
6061
"w_subject_common_name_included",
@@ -69,6 +70,7 @@
6970
"omitSKID": true,
7071
"maxValidityPeriod": "160h",
7172
"maxValidityBackdate": "1h5m",
73+
"maxCertificateSize": 10000,
7274
"lintConfig": "test/config-next/zlint.toml",
7375
"ignoredLints": [
7476
"w_ext_subject_key_identifier_missing_sub_cert",
@@ -82,6 +84,7 @@
8284
"omitSKID": true,
8385
"maxValidityPeriod": "583200s",
8486
"maxValidityBackdate": "1h5m",
87+
"maxCertificateSize": 10000,
8588
"lintConfig": "test/config-next/zlint.toml",
8689
"ignoredLints": [
8790
"w_ext_subject_key_identifier_missing_sub_cert",

0 commit comments

Comments
 (0)