From 9ac85891bb3cfba4ecb45c8cf9fff3e639e2126a Mon Sep 17 00:00:00 2001 From: Aaron Gable Date: Thu, 30 Sep 2021 11:00:56 -0700 Subject: [PATCH] lint: check that cert doesn't have extra +1s (#5677) Add a new lint, which applies to all certs, not just one kind of root/intermediate/subscriber cert, which checks that the validity period is a round number of minutes; i.e. that it doesn't have an extra second of validity, like 90d+1s. In general, if all certs are issued with validities much less than the limits imposed by root program requirements, being off by one second one way or another shouldn't matter much. But since it is easy to mistakenly configure a cert to have notBefore and notAfter timestamps with the exact same second value, this lint will force people configuring profiles to think critically about their cert lifetimes. Fixes #5669 --- linter/linter.go | 1 + .../all/w_validity_period_has_extra_second.go | 43 +++++++++++++++++++ linter/lints/common.go | 1 + 3 files changed, 45 insertions(+) create mode 100644 linter/lints/all/w_validity_period_has_extra_second.go diff --git a/linter/linter.go b/linter/linter.go index 2ae3c6e5a..1bf04fef2 100644 --- a/linter/linter.go +++ b/linter/linter.go @@ -13,6 +13,7 @@ import ( "github.com/zmap/zlint/v3" "github.com/zmap/zlint/v3/lint" + _ "github.com/letsencrypt/boulder/linter/lints/all" _ "github.com/letsencrypt/boulder/linter/lints/intermediate" _ "github.com/letsencrypt/boulder/linter/lints/root" _ "github.com/letsencrypt/boulder/linter/lints/subscriber" diff --git a/linter/lints/all/w_validity_period_has_extra_second.go b/linter/lints/all/w_validity_period_has_extra_second.go new file mode 100644 index 000000000..188ce1456 --- /dev/null +++ b/linter/lints/all/w_validity_period_has_extra_second.go @@ -0,0 +1,43 @@ +package subscriber + +import ( + "time" + + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v3/lint" + + "github.com/letsencrypt/boulder/linter/lints" +) + +type anyCertValidityNotRound struct{} + +func init() { + lint.RegisterLint(&lint.Lint{ + Name: "w_validity_period_has_extra_second", + Description: "Let's Encrypt Certificates have Validity Periods that are a round number of seconds", + Citation: "CPS: 7.1", + Source: lints.LetsEncryptCPSAll, + EffectiveDate: lints.CPSV33Date, + Lint: &anyCertValidityNotRound{}, + }) +} + +func (l *anyCertValidityNotRound) Initialize() error { + return nil +} + +func (l *anyCertValidityNotRound) CheckApplies(c *x509.Certificate) bool { + return true +} + +func (l *anyCertValidityNotRound) Execute(c *x509.Certificate) *lint.LintResult { + // 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%60 == 0 { + return &lint.LintResult{Status: lint.Pass} + } + + return &lint.LintResult{Status: lint.Error} +} diff --git a/linter/lints/common.go b/linter/lints/common.go index 54e963821..87dc43390 100644 --- a/linter/lints/common.go +++ b/linter/lints/common.go @@ -14,6 +14,7 @@ const ( DaySeconds time.Duration = 86400 * time.Second // Declare our own Sources for use in zlint registry filtering. + LetsEncryptCPSAll lint.LintSource = "LECPSAll" LetsEncryptCPSIntermediate lint.LintSource = "LECPSIntermediate" LetsEncryptCPSRoot lint.LintSource = "LECPSRoot" LetsEncryptCPSSubscriber lint.LintSource = "LECPSSubscriber"