1 package linter
2
3 import (
4 "crypto"
5 "crypto/ecdsa"
6 "crypto/rand"
7 "crypto/rsa"
8 "crypto/x509"
9 "fmt"
10 "strings"
11
12 zlintx509 "github.com/zmap/zcrypto/x509"
13 "github.com/zmap/zlint/v3"
14 "github.com/zmap/zlint/v3/lint"
15
16 "github.com/letsencrypt/boulder/core"
17
18 _ "github.com/letsencrypt/boulder/linter/lints/cabf_br"
19 _ "github.com/letsencrypt/boulder/linter/lints/chrome"
20 _ "github.com/letsencrypt/boulder/linter/lints/cpcps"
21 _ "github.com/letsencrypt/boulder/linter/lints/rfc"
22 )
23
24 var ErrLinting = fmt.Errorf("failed lint(s)")
25
26
27
28
29
30
31
32
33
34 func Check(tbs *x509.Certificate, subjectPubKey crypto.PublicKey, realIssuer *x509.Certificate, realSigner crypto.Signer, skipLints []string) ([]byte, error) {
35 linter, err := New(realIssuer, realSigner, skipLints)
36 if err != nil {
37 return nil, err
38 }
39
40 lintCertBytes, err := linter.Check(tbs, subjectPubKey)
41 if err != nil {
42 return nil, err
43 }
44
45 return lintCertBytes, nil
46 }
47
48
49 func CheckCRL(tbs *x509.RevocationList, realIssuer *x509.Certificate, realSigner crypto.Signer, skipLints []string) error {
50 linter, err := New(realIssuer, realSigner, skipLints)
51 if err != nil {
52 return err
53 }
54 return linter.CheckCRL(tbs)
55 }
56
57
58
59
60
61 type Linter struct {
62 issuer *x509.Certificate
63 signer crypto.Signer
64 registry lint.Registry
65 realPubKey crypto.PublicKey
66 }
67
68
69
70
71
72
73 func New(realIssuer *x509.Certificate, realSigner crypto.Signer, skipLints []string) (*Linter, error) {
74 lintSigner, err := makeSigner(realSigner)
75 if err != nil {
76 return nil, err
77 }
78 lintIssuer, err := makeIssuer(realIssuer, lintSigner)
79 if err != nil {
80 return nil, err
81 }
82 reg, err := makeRegistry(skipLints)
83 if err != nil {
84 return nil, err
85 }
86 return &Linter{lintIssuer, lintSigner, reg, realSigner.Public()}, nil
87 }
88
89
90
91
92
93
94
95
96 func (l Linter) Check(tbs *x509.Certificate, subjectPubKey crypto.PublicKey) ([]byte, error) {
97 lintPubKey := subjectPubKey
98 selfSigned, err := core.PublicKeysEqual(subjectPubKey, l.realPubKey)
99 if err != nil {
100 return nil, err
101 }
102 if selfSigned {
103 lintPubKey = l.signer.Public()
104 }
105
106 lintCertBytes, cert, err := makeLintCert(tbs, lintPubKey, l.issuer, l.signer)
107 if err != nil {
108 return nil, err
109 }
110
111 lintRes := zlint.LintCertificateEx(cert, l.registry)
112 err = ProcessResultSet(lintRes)
113 if err != nil {
114 return nil, err
115 }
116
117 return lintCertBytes, nil
118 }
119
120
121
122
123 func (l Linter) CheckCRL(tbs *x509.RevocationList) error {
124 crl, err := makeLintCRL(tbs, l.issuer, l.signer)
125 if err != nil {
126 return err
127 }
128 lintRes := zlint.LintRevocationListEx(crl, l.registry)
129 return ProcessResultSet(lintRes)
130 }
131
132 func makeSigner(realSigner crypto.Signer) (crypto.Signer, error) {
133 var lintSigner crypto.Signer
134 var err error
135 switch k := realSigner.Public().(type) {
136 case *rsa.PublicKey:
137 lintSigner, err = rsa.GenerateKey(rand.Reader, k.Size()*8)
138 if err != nil {
139 return nil, fmt.Errorf("failed to create RSA lint signer: %w", err)
140 }
141 case *ecdsa.PublicKey:
142 lintSigner, err = ecdsa.GenerateKey(k.Curve, rand.Reader)
143 if err != nil {
144 return nil, fmt.Errorf("failed to create ECDSA lint signer: %w", err)
145 }
146 default:
147 return nil, fmt.Errorf("unsupported lint signer type: %T", k)
148 }
149 return lintSigner, nil
150 }
151
152 func makeIssuer(realIssuer *x509.Certificate, lintSigner crypto.Signer) (*x509.Certificate, error) {
153 lintIssuerTBS := &x509.Certificate{
154
155
156
157
158
159
160
161
162
163
164
165 AuthorityKeyId: realIssuer.AuthorityKeyId,
166 BasicConstraintsValid: realIssuer.BasicConstraintsValid,
167 CRLDistributionPoints: realIssuer.CRLDistributionPoints,
168 DNSNames: realIssuer.DNSNames,
169 EmailAddresses: realIssuer.EmailAddresses,
170 ExcludedDNSDomains: realIssuer.ExcludedDNSDomains,
171 ExcludedEmailAddresses: realIssuer.ExcludedEmailAddresses,
172 ExcludedIPRanges: realIssuer.ExcludedIPRanges,
173 ExcludedURIDomains: realIssuer.ExcludedURIDomains,
174 ExtKeyUsage: realIssuer.ExtKeyUsage,
175 ExtraExtensions: realIssuer.ExtraExtensions,
176 IPAddresses: realIssuer.IPAddresses,
177 IsCA: realIssuer.IsCA,
178 IssuingCertificateURL: realIssuer.IssuingCertificateURL,
179 KeyUsage: realIssuer.KeyUsage,
180 MaxPathLen: realIssuer.MaxPathLen,
181 MaxPathLenZero: realIssuer.MaxPathLenZero,
182 NotAfter: realIssuer.NotAfter,
183 NotBefore: realIssuer.NotBefore,
184 OCSPServer: realIssuer.OCSPServer,
185 PermittedDNSDomains: realIssuer.PermittedDNSDomains,
186 PermittedDNSDomainsCritical: realIssuer.PermittedDNSDomainsCritical,
187 PermittedEmailAddresses: realIssuer.PermittedEmailAddresses,
188 PermittedIPRanges: realIssuer.PermittedIPRanges,
189 PermittedURIDomains: realIssuer.PermittedURIDomains,
190 PolicyIdentifiers: realIssuer.PolicyIdentifiers,
191 SerialNumber: realIssuer.SerialNumber,
192 Subject: realIssuer.Subject,
193 SubjectKeyId: realIssuer.SubjectKeyId,
194 URIs: realIssuer.URIs,
195 UnknownExtKeyUsage: realIssuer.UnknownExtKeyUsage,
196 }
197 lintIssuerBytes, err := x509.CreateCertificate(rand.Reader, lintIssuerTBS, lintIssuerTBS, lintSigner.Public(), lintSigner)
198 if err != nil {
199 return nil, fmt.Errorf("failed to create lint issuer: %w", err)
200 }
201 lintIssuer, err := x509.ParseCertificate(lintIssuerBytes)
202 if err != nil {
203 return nil, fmt.Errorf("failed to parse lint issuer: %w", err)
204 }
205 return lintIssuer, nil
206 }
207
208 func makeRegistry(skipLints []string) (lint.Registry, error) {
209 reg, err := lint.GlobalRegistry().Filter(lint.FilterOptions{
210 ExcludeNames: skipLints,
211 ExcludeSources: []lint.LintSource{
212
213 lint.CABFEVGuidelines,
214
215
216 lint.EtsiEsi,
217 },
218 })
219 if err != nil {
220 return nil, fmt.Errorf("failed to create lint registry: %w", err)
221 }
222 return reg, nil
223 }
224
225 func makeLintCert(tbs *x509.Certificate, subjectPubKey crypto.PublicKey, issuer *x509.Certificate, signer crypto.Signer) ([]byte, *zlintx509.Certificate, error) {
226 lintCertBytes, err := x509.CreateCertificate(rand.Reader, tbs, issuer, subjectPubKey, signer)
227 if err != nil {
228 return nil, nil, fmt.Errorf("failed to create lint certificate: %w", err)
229 }
230 lintCert, err := zlintx509.ParseCertificate(lintCertBytes)
231 if err != nil {
232 return nil, nil, fmt.Errorf("failed to parse lint certificate: %w", err)
233 }
234 return lintCertBytes, lintCert, nil
235 }
236
237 func ProcessResultSet(lintRes *zlint.ResultSet) error {
238 if lintRes.NoticesPresent || lintRes.WarningsPresent || lintRes.ErrorsPresent || lintRes.FatalsPresent {
239 var failedLints []string
240 for lintName, result := range lintRes.Results {
241 if result.Status > lint.Pass {
242 failedLints = append(failedLints, fmt.Sprintf("%s (%s)", lintName, result.Details))
243 }
244 }
245 return fmt.Errorf("%w: %s", ErrLinting, strings.Join(failedLints, ", "))
246 }
247 return nil
248 }
249
250 func makeLintCRL(tbs *x509.RevocationList, issuer *x509.Certificate, signer crypto.Signer) (*zlintx509.RevocationList, error) {
251 lintCRLBytes, err := x509.CreateRevocationList(rand.Reader, tbs, issuer, signer)
252 if err != nil {
253 return nil, err
254 }
255 lintCRL, err := zlintx509.ParseRevocationList(lintCRLBytes)
256 if err != nil {
257 return nil, err
258 }
259 return lintCRL, nil
260 }
261
View as plain text