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 /* 33 IssuingDistributionPoint stores the IA5STRING value of the optional 34 distribution point, and the (implied OPTIONAL) BOOLEAN values of 35 onlyContainsUserCerts and onlyContainsCACerts. 36 37 RFC 5280 38 * Section 5.2.5 39 IssuingDistributionPoint ::= SEQUENCE { 40 distributionPoint [0] DistributionPointName OPTIONAL, 41 onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, 42 onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, 43 ... 44 } 45 46 * Section 4.2.1.13 47 DistributionPointName ::= CHOICE { 48 fullName [0] GeneralNames, 49 ... } 50 51 * Appendix A.1, Page 128 52 GeneralName ::= CHOICE { 53 ... 54 uniformResourceIdentifier [6] IA5String, 55 ... } 56 */ 57 type IssuingDistributionPoint struct { 58 DistributionPointURI *url.URL 59 OnlyContainsUserCerts bool 60 OnlyContainsCACerts bool 61 } 62 63 // NewIssuingDistributionPoint is a constructor which returns an 64 // IssuingDistributionPoint with each field set to zero values. 65 func NewIssuingDistributionPoint() *IssuingDistributionPoint { 66 return &IssuingDistributionPoint{} 67 } 68 69 // GetExtWithOID is a helper for several of our custom lints. It returns the 70 // extension with the given OID if it exists, or nil otherwise. 71 func GetExtWithOID(exts []pkix.Extension, oid asn1.ObjectIdentifier) *pkix.Extension { 72 for _, ext := range exts { 73 if ext.Id.Equal(oid) { 74 return &ext 75 } 76 } 77 return nil 78 } 79 80 // ReadOptionalASN1BooleanWithTag attempts to read and advance incoming to 81 // search for an optional DER-encoded ASN.1 element tagged with the given tag. 82 // Unless out is nil, it stores whether an element with the tag was found in 83 // out, otherwise out will take the default value. It reports whether all reads 84 // were successful. 85 func ReadOptionalASN1BooleanWithTag(incoming *cryptobyte.String, out *bool, tag cryptobyte_asn1.Tag, defaultValue bool) bool { 86 // ReadOptionalASN1 performs a peek and will not advance if the tag is 87 // missing, meaning that incoming will retain bytes. 88 var valuePresent bool 89 var valueBytes cryptobyte.String 90 if !incoming.ReadOptionalASN1(&valueBytes, &valuePresent, tag) { 91 return false 92 } 93 val := defaultValue 94 if valuePresent { 95 /* 96 X.690 (07/2002) 97 https://www.itu.int/rec/T-REC-X.690-200207-S/en 98 99 Section 8.2.2: 100 If the boolean value is: 101 FALSE 102 the octet shall be zero. 103 If the boolean value is 104 TRUE 105 the octet shall have any non-zero value, as a sender's option. 106 107 Section 11.1 Boolean values: 108 If the encoding represents the boolean value TRUE, its single contents octet shall have all eight 109 bits set to one. (Contrast with 8.2.2.) 110 111 Succinctly, BER encoding states any nonzero value is TRUE. The DER 112 encoding restricts the value 0xFF as TRUE and any other: 0x01, 113 0x23, 0xFE, etc as invalid encoding. 114 */ 115 boolBytes := []byte(valueBytes) 116 if bytes.Equal(boolBytes, []byte{0xFF}) { 117 val = true 118 } else if bytes.Equal(boolBytes, []byte{0x00}) { 119 val = false 120 } else { 121 // Unrecognized DER encoding of boolean! 122 return false 123 } 124 } 125 if out != nil { 126 *out = val 127 } 128 129 // All reads were successful. 130 return true 131 } 132