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:
		
							parent
							
								
									4c581436a3
								
							
						
					
					
						commit
						bb210a2a28
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue