github.com/letsencrypt/boulder@v0.20251208.0/linter/lints/cabf_br/lint_crl_validity_period.go (about) 1 package cabfbr 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/letsencrypt/boulder/linter/lints" 8 "github.com/zmap/zcrypto/encoding/asn1" 9 "github.com/zmap/zcrypto/x509" 10 "github.com/zmap/zlint/v3/lint" 11 "github.com/zmap/zlint/v3/util" 12 "golang.org/x/crypto/cryptobyte" 13 14 cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" 15 ) 16 17 type crlValidityPeriod struct{} 18 19 /************************************************ 20 Baseline Requirements, Section 4.9.7: 21 * For the status of Subscriber Certificates [...] the value of the nextUpdate 22 field MUST NOT be more than ten days beyond the value of the thisUpdate field. 23 * For the status of Subordinate CA Certificates [...]. The value of the 24 nextUpdate field MUST NOT be more than twelve months beyond the value of the 25 thisUpdatefield. 26 ************************************************/ 27 28 func init() { 29 lint.RegisterRevocationListLint(&lint.RevocationListLint{ 30 LintMetadata: lint.LintMetadata{ 31 Name: "e_crl_validity_period", 32 Description: "Let's Encrypt CRLs must have an acceptable validity period", 33 Citation: "BRs: 4.9.7", 34 Source: lint.CABFBaselineRequirements, 35 EffectiveDate: util.CABFBRs_1_2_1_Date, 36 }, 37 Lint: NewCrlValidityPeriod, 38 }) 39 } 40 41 func NewCrlValidityPeriod() lint.RevocationListLintInterface { 42 return &crlValidityPeriod{} 43 } 44 45 func (l *crlValidityPeriod) CheckApplies(c *x509.RevocationList) bool { 46 return true 47 } 48 49 func (l *crlValidityPeriod) Execute(c *x509.RevocationList) *lint.LintResult { 50 /* 51 Let's Encrypt issues two kinds of CRLs: 52 53 1) CRLs containing subscriber certificates, created by crl-updater. 54 These assert the distributionPoint and onlyContainsUserCerts 55 boolean. 56 2) CRLs containing issuer CRLs, created by the ceremony tool. These 57 assert the onlyContainsCACerts boolean. 58 59 We use the presence of these booleans to determine which BR-mandated 60 lifetime to enforce. 61 */ 62 63 // The only way to determine which type of CRL we're dealing with. The 64 // issuingDistributionPoint must be parsed and the internal fields 65 // inspected. 66 idpOID := asn1.ObjectIdentifier{2, 5, 29, 28} // id-ce-issuingDistributionPoint 67 idpe := lints.GetExtWithOID(c.Extensions, idpOID) 68 if idpe == nil { 69 return &lint.LintResult{ 70 Status: lint.Warn, 71 Details: "CRL missing IssuingDistributionPoint", 72 } 73 } 74 75 // Step inside the outer issuingDistributionPoint sequence to get access to 76 // its constituent fields. 77 idpv := cryptobyte.String(idpe.Value) 78 if !idpv.ReadASN1(&idpv, cryptobyte_asn1.SEQUENCE) { 79 return &lint.LintResult{ 80 Status: lint.Warn, 81 Details: "Failed to read IssuingDistributionPoint distributionPoint", 82 } 83 } 84 85 // Throw distributionPoint away. 86 distributionPointTag := cryptobyte_asn1.Tag(0).ContextSpecific().Constructed() 87 _ = idpv.SkipOptionalASN1(distributionPointTag) 88 89 // Parse IssuingDistributionPoint OPTIONAL BOOLEANS to eventually perform 90 // sanity checks. 91 idp := lints.NewIssuingDistributionPoint() 92 onlyContainsUserCertsTag := cryptobyte_asn1.Tag(1).ContextSpecific() 93 if !lints.ReadOptionalASN1BooleanWithTag(&idpv, &idp.OnlyContainsUserCerts, onlyContainsUserCertsTag, false) { 94 return &lint.LintResult{ 95 Status: lint.Warn, 96 Details: "Failed to read IssuingDistributionPoint onlyContainsUserCerts", 97 } 98 } 99 100 onlyContainsCACertsTag := cryptobyte_asn1.Tag(2).ContextSpecific() 101 if !lints.ReadOptionalASN1BooleanWithTag(&idpv, &idp.OnlyContainsCACerts, onlyContainsCACertsTag, false) { 102 return &lint.LintResult{ 103 Status: lint.Warn, 104 Details: "Failed to read IssuingDistributionPoint onlyContainsCACerts", 105 } 106 } 107 108 // Basic sanity check so that later on we can determine what type of CRL we 109 // issued based on the presence of one of these fields. If both fields exist 110 // then 1) it's a problem and 2) the real validity period is unknown. 111 if idp.OnlyContainsUserCerts && idp.OnlyContainsCACerts { 112 return &lint.LintResult{ 113 Status: lint.Error, 114 Details: "IssuingDistributionPoint should not have both onlyContainsUserCerts: TRUE and onlyContainsCACerts: TRUE", 115 } 116 } 117 118 // Default to subscriber cert CRL. 119 var BRValidity = 10 * 24 * time.Hour 120 var validityString = "10 days" 121 if idp.OnlyContainsCACerts { 122 BRValidity = 365 * lints.BRDay 123 validityString = "365 days" 124 } 125 126 parsedValidity := c.NextUpdate.Sub(c.ThisUpdate) 127 if parsedValidity <= 0 { 128 return &lint.LintResult{ 129 Status: lint.Error, 130 Details: "CRL has NextUpdate at or before ThisUpdate", 131 } 132 } 133 134 if parsedValidity > BRValidity { 135 return &lint.LintResult{ 136 Status: lint.Error, 137 Details: fmt.Sprintf("CRL has validity period greater than %s", validityString), 138 } 139 } 140 return &lint.LintResult{Status: lint.Pass} 141 }