github.com/letsencrypt/boulder@v0.20251208.0/linter/lints/common.go (about)

     1  package lints
     2  
     3  import (
     4  	"bytes"
     5  	"net/url"
     6  	"time"
     7  
     8  	"github.com/zmap/zcrypto/encoding/asn1"
     9  	"github.com/zmap/zcrypto/x509/pkix"
    10  	"github.com/zmap/zlint/v3/lint"
    11  	"golang.org/x/crypto/cryptobyte"
    12  	cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
    13  )
    14  
    15  const (
    16  	// CABF Baseline Requirements 6.3.2 Certificate operational periods:
    17  	// For the purpose of calculations, a day is measured as 86,400 seconds.
    18  	// Any amount of time greater than this, including fractional seconds and/or
    19  	// leap seconds, shall represent an additional day.
    20  	BRDay time.Duration = 86400 * time.Second
    21  
    22  	// Declare our own Sources for use in zlint registry filtering.
    23  	LetsEncryptCPS lint.LintSource = "LECPS"
    24  	ChromeCTPolicy lint.LintSource = "ChromeCT"
    25  )
    26  
    27  var (
    28  	CPSV33Date           = time.Date(2021, time.June, 8, 0, 0, 0, 0, time.UTC)
    29  	MozillaPolicy281Date = time.Date(2023, time.February, 15, 0, 0, 0, 0, time.UTC)
    30  )
    31  
    32  // IssuingDistributionPoint stores the IA5STRING value(s) of the optional
    33  // distributionPoint, and the (implied OPTIONAL) BOOLEAN values of
    34  // onlyContainsUserCerts and onlyContainsCACerts.
    35  //
    36  //	RFC 5280
    37  //	* Section 5.2.5
    38  //	  IssuingDistributionPoint ::= SEQUENCE {
    39  //	    distributionPoint          [0] DistributionPointName OPTIONAL,
    40  //	    onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
    41  //	    onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
    42  //	    ...
    43  //	  }
    44  //
    45  //	* Section 4.2.1.13
    46  //	  DistributionPointName ::= CHOICE {
    47  //	    fullName                [0]     GeneralNames,
    48  //	    ... }
    49  //
    50  //	* Appendix A.1, Page 128
    51  //	  GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
    52  //	  GeneralName ::= CHOICE {
    53  //	    ...
    54  //	        uniformResourceIdentifier [6]  IA5String,
    55  //	    ... }
    56  //
    57  // Because this struct is used by cryptobyte (not by encoding/asn1), and because
    58  // we only care about the uniformResourceIdentifier flavor of GeneralName, we
    59  // are able to flatten the DistributionPointName down into a slice of URIs.
    60  type IssuingDistributionPoint struct {
    61  	DistributionPointURIs []*url.URL
    62  	OnlyContainsUserCerts bool
    63  	OnlyContainsCACerts   bool
    64  }
    65  
    66  // NewIssuingDistributionPoint is a constructor which returns an
    67  // IssuingDistributionPoint with each field set to zero values.
    68  func NewIssuingDistributionPoint() *IssuingDistributionPoint {
    69  	return &IssuingDistributionPoint{}
    70  }
    71  
    72  // GetExtWithOID is a helper for several of our custom lints. It returns the
    73  // extension with the given OID if it exists, or nil otherwise.
    74  func GetExtWithOID(exts []pkix.Extension, oid asn1.ObjectIdentifier) *pkix.Extension {
    75  	for _, ext := range exts {
    76  		if ext.Id.Equal(oid) {
    77  			return &ext
    78  		}
    79  	}
    80  	return nil
    81  }
    82  
    83  // ReadOptionalASN1BooleanWithTag attempts to read and advance incoming to
    84  // search for an optional DER-encoded ASN.1 element tagged with the given tag.
    85  // Unless out is nil, it stores whether an element with the tag was found in
    86  // out, otherwise out will take the default value. It reports whether all reads
    87  // were successful.
    88  func ReadOptionalASN1BooleanWithTag(incoming *cryptobyte.String, out *bool, tag cryptobyte_asn1.Tag, defaultValue bool) bool {
    89  	// ReadOptionalASN1 performs a peek and will not advance if the tag is
    90  	// missing, meaning that incoming will retain bytes.
    91  	var valuePresent bool
    92  	var valueBytes cryptobyte.String
    93  	if !incoming.ReadOptionalASN1(&valueBytes, &valuePresent, tag) {
    94  		return false
    95  	}
    96  	val := defaultValue
    97  	if valuePresent {
    98  		/*
    99  			X.690 (07/2002)
   100  			https://www.itu.int/rec/T-REC-X.690-200207-S/en
   101  
   102  			Section 8.2.2:
   103  				If the boolean value is:
   104  				FALSE
   105  				the octet shall be zero.
   106  				If the boolean value is
   107  				TRUE
   108  				the octet shall have any non-zero value, as a sender's option.
   109  
   110  			Section 11.1 Boolean values:
   111  				If the encoding represents the boolean value TRUE, its single contents octet shall have all eight
   112  				bits set to one. (Contrast with 8.2.2.)
   113  
   114  			Succinctly, BER encoding states any nonzero value is TRUE. The DER
   115  			encoding restricts the value 0xFF as TRUE and any other: 0x01,
   116  			0x23, 0xFE, etc as invalid encoding.
   117  		*/
   118  		boolBytes := []byte(valueBytes)
   119  		if bytes.Equal(boolBytes, []byte{0xFF}) {
   120  			val = true
   121  		} else if bytes.Equal(boolBytes, []byte{0x00}) {
   122  			val = false
   123  		} else {
   124  			// Unrecognized DER encoding of boolean!
   125  			return false
   126  		}
   127  	}
   128  	if out != nil {
   129  		*out = val
   130  	}
   131  
   132  	// All reads were successful.
   133  	return true
   134  }