github.com/zmap/zlint@v1.1.0/README.md (about) 1 ZLint 2 ===== 3 4 [](https://travis-ci.org/zmap/zlint) 5 [](https://goreportcard.com/report/github.com/zmap/zlint) 6 7 ZLint is a X.509 certificate linter written in Go that checks for consistency 8 with [RFC 5280](https://www.ietf.org/rfc/rfc5280.txt) and the CA/Browser Forum 9 Baseline Requirements 10 ([v.1.4.8](https://cabforum.org/wp-content/uploads/CA-Browser-Forum-BR-1.4.8.pdf)). 11 12 A detailed list of BR coverage can be found here: 13 https://docs.google.com/spreadsheets/d/1ywp0op9mkTaggigpdF2YMTubepowJ50KQBhc_b00e-Y. 14 15 Requirements 16 ------------ 17 18 ZLint requires [Go 1.13.x or newer](https://golang.org/doc/install) be 19 installed. The command line setup instructions assume the `go` command is in 20 your `$PATH`. 21 22 Versioning 23 ---------- 24 25 ZLint aims to follow [semantic versioning](https://semver.org/). The addition of 26 new lints will generally result in a MINOR version revision. Since downstream 27 projects depend on lint results and names for policy decisions changes of this 28 nature will result in MAJOR version revision. 29 30 Command Line Usage 31 ------------------ 32 33 ZLint can be used on the command-line through a simple bundled executable 34 _ZLint_ as well as through 35 [ZCertificate](https://github.com/zmap/zcertificate), a more full-fledged 36 command-line certificate parser that links against ZLint. 37 38 Example ZLint CLI usage: 39 40 go get github.com/zmap/zlint/cmd/zlint 41 zlint mycert.pem 42 43 44 Library Usage 45 ------------- 46 47 ZLint can also be used as a library: 48 49 ```go 50 import ( 51 "github.com/zmap/zcrypto/x509" 52 "github.com/zmap/zlint" 53 ) 54 55 parsed, err := x509.ParseCertificate(raw) 56 if err != nil { 57 // The certificate could not be parsed. Either error or halt. 58 log.Fatalf("could not parse certificate: %s", err) 59 } 60 zlintResultSet := zlint.LintCertificate(parsed) 61 ``` 62 63 64 See https://github.com/zmap/zlint/blob/master/cmd/zlint/main.go for an example. 65 66 67 Adding New Lints 68 ---------------- 69 70 **Generating Lint Scaffolding.** The scaffolding for a new lints can be created 71 by running `./newLint.sh <lint_name> <structName>`. Lint names are generally of 72 the form `e_subject_common_name_not_from_san` where the first letter is one of: 73 `e`, `w`, or `n` (error, warning, or notice respectively). Struct names 74 following Go conventions, e.g., `subjectCommonNameNotFromSAN`. Example: 75 `./newLint.sh e_subject_common_name_not_from_san subjectCommonNameNotFromSAN`. 76 This will generate a new lint in the `lints` directory with the necessary 77 fields filled out. 78 79 **Choosing a Lint Result Level.** When choosing what `lints.LintStatus` your new 80 lint should return (e.g. `Notice`,`Warn`, `Error`, or `Fatal`) the following 81 general guidance may help. `Error` should be used for clear violations of RFC/BR 82 `MUST` or `MUST NOT` requirements and include strong citations. `Warn` should be 83 used for violations of RFC/BR `SHOULD` or `SHOULD NOT` requirements and again 84 should include strong citations. `Notice` should be used for more general "FYI" 85 statements that violate non-codified community standards or for cases where 86 citations are unclear. Lastly `Fatal` should be used when there is an 87 unresolvable error in `zlint`, `zcrypto` or some other part of the certificate 88 processing. 89 90 **Scoping a Lint.** Lints are executed in three steps. First, the ZLint 91 framework determines whether a certificate falls within the scope of a given 92 lint by calling `CheckApplies`. This is often used to scope lints to only check 93 subscriber, intermediate CA, or root CAs. This function commonly calls one of a 94 select number of helper functions: `IsCA`, `IsSubscriber`, `IsExtInCert`, or 95 `DNSNamesExist`. Example: 96 97 ```go 98 func (l *caCRLSignNotSet) CheckApplies(c *x509.Certificate) bool { 99 return c.IsCA && util.IsExtInCert(c, util.KeyUsageOID) 100 } 101 ``` 102 103 Next, the framework determines whether the certificate was issued after the 104 effective date of a Lint by checking whether the certificate was issued prior 105 to the lint's `EffectiveDate`. You'll also need to fill out the source and 106 description of what the lint is checking. We encourage you to copy text 107 directly from the BR or RFC here. Example: 108 109 ```go 110 func init() { 111 RegisterLint(&Lint{ 112 Name: "e_ca_country_name_missing", 113 Description: "Root and Subordinate CA certificates MUST have a countryName present in subject information", 114 Citation: "BRs: 7.1.2.1", 115 EffectiveDate: util.CABEffectiveDate, 116 Test: &caCountryNameMissing{}, 117 }) 118 } 119 ``` 120 121 The meat of the lint is contained within the `RunTest` function, which is 122 passed `x509.Certificate`. **Note:** This is an X.509 object from 123 [ZCrypto](https://github.com/zmap/zcrypto) not the Go standard library. Lints 124 should perform their described test and then return a `ResultStruct` that 125 contains a Result and optionally a `Details` string, e.g., 126 `ResultStruct{Result: Pass}`. If you encounter a situation in which you 127 typically would return a Go `error` object, instead return 128 `ResultStruct{Result: Fatal}`. 129 130 Example: 131 132 ```go 133 func (l *caCRLSignNotSet) RunTest(c *x509.Certificate) *ResultStruct { 134 if c.KeyUsage&x509.KeyUsageCRLSign != 0 { 135 return &ResultStruct{Result: Pass} 136 } 137 return &ResultStruct{Result: Error} 138 } 139 ``` 140 141 **Creating Unit Tests.** Every lint should also have two corresponding unit 142 tests for a success and failure condition. We have typically generated test 143 certificates using Go (see https://golang.org/pkg/crypto/x509/#CreateCertificate 144 for details), but OpenSSL could also be used. Test certificates should be placed 145 in `testlint/testCerts` and called from the test file created by `newLint.sh`. 146 Prepend the PEM with the output of `openssl x509 -text`. 147 148 Example: 149 150 ```go 151 func TestBasicConstNotCritical(t *testing.T) { 152 // Only need to change these two values and the lint name 153 inputPath := "../testlint/testCerts/caBasicConstNotCrit.pem" 154 expected := Error 155 out, _ := Lints["e_basic_constraints_not_critical"].ExecuteTest(ReadCertificate(inputPath)) 156 if out.Result != expected { 157 t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status) 158 } 159 } 160 161 ``` 162 163 **Integration Tests.** ZLint's [continuous 164 integration](https://travis-ci.org/zmap/zlint) includes an integration test 165 phase where all lints are run against a large corpus of certificates. The number 166 of notice, warning, error and fatal results for each lint are captured and 167 compared to a set of expected values in a configuration file. You may need to 168 update these expected values when you add/change lints. Please see the 169 [integration tests 170 README](https://github.com/zmap/zlint/blob/master/integration/README.md) for 171 more information. 172 173 Updating the TLD Map 174 -------------------- 175 176 ZLint maintains [a map of 177 top-level-domains](https://github.com/zmap/zlint/blob/master/util/gtld_map.go) 178 and their validity periods that is referenced by linters. As ICANN adds and 179 removes TLDs this map need to be updated. To do so, ensure the 180 `zlint-gtld-update` command is installed and in your `$PATH` and run `go 181 generate`: 182 183 go get github.com/zmap/zlint/cmd/zlint-gtld-update 184 go generate github.com/zmap/zlint/... 185 186 Zlint Users/Integrations 187 ------------------------- 188 189 Pre-issuance linting is **strongly recommended** by the [Mozilla root 190 program](https://wiki.allizom.org/CA/Required_or_Recommended_Practices#Pre-Issuance_Linting). 191 Here are some projects/CAs known to integrate with ZLint in some fashion: 192 193 * [Camerfirma](https://bugzilla.mozilla.org/show_bug.cgi?id=1556806#c5) 194 * [CFSSL](https://github.com/cloudflare/cfssl/pull/1015) 195 * [Sectigo and crt.sh](https://groups.google.com/forum/#!msg/mozilla.dev.security.policy/sjXswrcsvrE/Nl3OLd4PAAAJ) 196 * [Digicert](https://bugzilla.mozilla.org/show_bug.cgi?id=1550645#c9) 197 * [EJBCA](https://download.primekey.com/docs/EJBCA-Enterprise/6_11_1/adminguide.html#Post%20Processing%20Validators%20(Pre-Certificate%20or%20Certificate%20Validation)) 198 * [Government of Spain, FNMT](https://bugzilla.mozilla.org/show_bug.cgi?id=1495507#c8) 199 * [Globalsign](https://cabforum.org/pipermail/public/2018-April/013233.html) 200 * [GoDaddy](https://bugzilla.mozilla.org/show_bug.cgi?id=1462844#c6) 201 * [Izenpe](https://bugzilla.mozilla.org/show_bug.cgi?id=1528290#c5) 202 * [Let's Encrypt](https://letsencrypt.org) and [Boulder](https://github.com/letsencrypt/boulder) 203 * [Siemens](https://bugzilla.mozilla.org/show_bug.cgi?id=1391063#c32) 204 * [QuoVadis](https://bugzilla.mozilla.org/show_bug.cgi?id=1521950#c3) 205 206 Please submit a pull request to update the README if you are aware of 207 another CA/project that uses zlint. 208 209 License and Copyright 210 --------------------- 211 212 ZMap Copyright 2019 Regents of the University of Michigan 213 214 Licensed under the Apache License, Version 2.0 (the "License"); you may not use 215 this file except in compliance with the License. You may obtain a copy of the 216 License at http://www.apache.org/licenses/LICENSE-2.0 217 218 Unless required by applicable law or agreed to in writing, software distributed 219 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 220 CONDITIONS OF ANY KIND, either express or implied. See LICENSE for the specific 221 language governing permissions and limitations under the License.