...

Source file src/github.com/letsencrypt/boulder/linter/lints/cabf_br/lint_crl_validity_period.go

Documentation: github.com/letsencrypt/boulder/linter/lints/cabf_br

     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  }
   142  

View as plain text