Add lints for our own CPS requirements (#5512)

Add a collection of custom lints to enforce that our issuance of
Subscriber (via normal Boulder operation) and Root and Intermediate CA
Certificates (via the Ceremony tool) abides by the requirements we
place on ourselves via our CPS. Provide a small collection of useful
constants for these lints to share. Import all of these lints from our
lint package, so that they are automatically registered with zlint's
`GlobalRegistry` and are automatically included in all of our lint
checks.

At this time, only three lints are included, checking that the validity
periods of our various certificate types do not exceed their CPS-set
maximums. Additional lints for key sizes, distinguished names, key
usages, policy OIDs, AIA URLs, and more will be added in the future.

Part of #5492
This commit is contained in:
Aaron Gable 2021-07-13 10:16:15 -07:00 committed by GitHub
parent 4c581436a3
commit bb210a2a28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 169 additions and 0 deletions

View File

@ -12,6 +12,10 @@ import (
zlintx509 "github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3"
"github.com/zmap/zlint/v3/lint"
_ "github.com/letsencrypt/boulder/linter/lints/intermediate"
_ "github.com/letsencrypt/boulder/linter/lints/root"
_ "github.com/letsencrypt/boulder/linter/lints/subscriber"
)
// Check accomplishes the entire process of linting: it generates a throwaway

24
linter/lints/common.go Normal file
View File

@ -0,0 +1,24 @@
package lints
import (
"time"
"github.com/zmap/zlint/v3/lint"
)
const (
// CABF Baseline Requirements 6.3.2 Certificate operational periods:
// For the purpose of calculations, a day is measured as 86,400 seconds.
// Any amount of time greater than this, including fractional seconds and/or
// leap seconds, shall represent an additional day.
DaySeconds time.Duration = 86400 * time.Second
// Declare our own Sources for use in zlint registry filtering.
LetsEncryptCPSIntermediate lint.LintSource = "LECPSIntermediate"
LetsEncryptCPSRoot lint.LintSource = "LECPSRoot"
LetsEncryptCPSSubscriber lint.LintSource = "LECPSSubscriber"
)
var (
CPSV33Date = time.Date(2021, time.June, 8, 0, 0, 0, 0, time.UTC)
)

View File

@ -0,0 +1,47 @@
package subscriber
import (
"time"
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
"github.com/letsencrypt/boulder/linter/lints"
)
type intermediateCertValidityTooLong struct{}
func init() {
lint.RegisterLint(&lint.Lint{
Name: "e_validity_period_greater_than_8_years",
Description: "Let's Encrypt Intermediate CA Certificates have Validity Periods of up to 8 years",
Citation: "CPS: 7.1",
Source: lints.LetsEncryptCPSIntermediate,
EffectiveDate: lints.CPSV33Date,
Lint: &intermediateCertValidityTooLong{},
})
}
func (l *intermediateCertValidityTooLong) Initialize() error {
return nil
}
func (l *intermediateCertValidityTooLong) CheckApplies(c *x509.Certificate) bool {
return util.IsSubCA(c)
}
func (l *intermediateCertValidityTooLong) Execute(c *x509.Certificate) *lint.LintResult {
// CPS 7.1: "Intermediate CA Certificate Validity Period: Up to 8 years."
maxValidity := 8 * 365 * lints.DaySeconds
// RFC 5280 4.1.2.5: "The validity period for a certificate is the period
// of time from notBefore through notAfter, inclusive."
certValidity := c.NotAfter.Add(time.Second).Sub(c.NotBefore)
if certValidity > maxValidity {
return &lint.LintResult{Status: lint.Error}
}
return &lint.LintResult{Status: lint.Pass}
}

View File

@ -0,0 +1,47 @@
package subscriber
import (
"time"
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
"github.com/letsencrypt/boulder/linter/lints"
)
type rootCertValidityTooLong struct{}
func init() {
lint.RegisterLint(&lint.Lint{
Name: "e_validity_period_greater_than_25_years",
Description: "Let's Encrypt Root CA Certificates have Validity Periods of up to 25 years",
Citation: "CPS: 7.1",
Source: lints.LetsEncryptCPSRoot,
EffectiveDate: lints.CPSV33Date,
Lint: &rootCertValidityTooLong{},
})
}
func (l *rootCertValidityTooLong) Initialize() error {
return nil
}
func (l *rootCertValidityTooLong) CheckApplies(c *x509.Certificate) bool {
return util.IsRootCA(c)
}
func (l *rootCertValidityTooLong) Execute(c *x509.Certificate) *lint.LintResult {
// CPS 7.1: "Root CA Certificate Validity Period: Up to 25 years."
maxValidity := 25 * 365 * lints.DaySeconds
// RFC 5280 4.1.2.5: "The validity period for a certificate is the period
// of time from notBefore through notAfter, inclusive."
certValidity := c.NotAfter.Add(time.Second).Sub(c.NotBefore)
if certValidity > maxValidity {
return &lint.LintResult{Status: lint.Error}
}
return &lint.LintResult{Status: lint.Pass}
}

View File

@ -0,0 +1,47 @@
package subscriber
import (
"time"
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
"github.com/letsencrypt/boulder/linter/lints"
)
type subscriberCertValidityTooLong struct{}
func init() {
lint.RegisterLint(&lint.Lint{
Name: "e_validity_period_greater_than_100_days",
Description: "Let's Encrypt Subscriber Certificates have Validity Periods of up to 100 days",
Citation: "CPS: 7.1",
Source: lints.LetsEncryptCPSSubscriber,
EffectiveDate: lints.CPSV33Date,
Lint: &subscriberCertValidityTooLong{},
})
}
func (l *subscriberCertValidityTooLong) Initialize() error {
return nil
}
func (l *subscriberCertValidityTooLong) CheckApplies(c *x509.Certificate) bool {
return util.IsServerAuthCert(c) && !c.IsCA
}
func (l *subscriberCertValidityTooLong) Execute(c *x509.Certificate) *lint.LintResult {
// CPS 7.1: "DV SSL End Entity Certificate Validity Period: Up to 100 days."
maxValidity := 100 * lints.DaySeconds
// RFC 5280 4.1.2.5: "The validity period for a certificate is the period
// of time from notBefore through notAfter, inclusive."
certValidity := c.NotAfter.Add(time.Second).Sub(c.NotBefore)
if certValidity > maxValidity {
return &lint.LintResult{Status: lint.Error}
}
return &lint.LintResult{Status: lint.Pass}
}