...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package scanner
16
17 import (
18 "context"
19 "log"
20 "math/big"
21 "regexp"
22 "time"
23
24 ct "github.com/google/certificate-transparency-go"
25 "github.com/google/certificate-transparency-go/asn1"
26 "github.com/google/certificate-transparency-go/client"
27 "github.com/google/certificate-transparency-go/x509"
28 )
29
30
31
32 type Matcher interface {
33
34
35 CertificateMatches(*x509.Certificate) bool
36
37
38
39 PrecertificateMatches(*ct.Precertificate) bool
40 }
41
42
43 type MatchAll struct{}
44
45
46 func (m MatchAll) CertificateMatches(_ *x509.Certificate) bool {
47 return true
48 }
49
50
51 func (m MatchAll) PrecertificateMatches(_ *ct.Precertificate) bool {
52 return true
53 }
54
55
56 type MatchNone struct{}
57
58
59 func (m MatchNone) CertificateMatches(_ *x509.Certificate) bool {
60 return false
61 }
62
63
64 func (m MatchNone) PrecertificateMatches(_ *ct.Precertificate) bool {
65 return false
66 }
67
68
69 type MatchSerialNumber struct {
70 SerialNumber big.Int
71 }
72
73
74
75 func (m MatchSerialNumber) CertificateMatches(c *x509.Certificate) bool {
76 return c.SerialNumber.String() == m.SerialNumber.String()
77 }
78
79
80
81 func (m MatchSerialNumber) PrecertificateMatches(p *ct.Precertificate) bool {
82 return p.TBSCertificate.SerialNumber.String() == m.SerialNumber.String()
83 }
84
85
86
87
88
89 type MatchSubjectRegex struct {
90 CertificateSubjectRegex *regexp.Regexp
91 PrecertificateSubjectRegex *regexp.Regexp
92 }
93
94
95 func (m MatchSubjectRegex) CertificateMatches(c *x509.Certificate) bool {
96 if m.CertificateSubjectRegex.FindStringIndex(c.Subject.CommonName) != nil {
97 return true
98 }
99 for _, alt := range c.DNSNames {
100 if m.CertificateSubjectRegex.FindStringIndex(alt) != nil {
101 return true
102 }
103 }
104 return false
105 }
106
107
108 func (m MatchSubjectRegex) PrecertificateMatches(p *ct.Precertificate) bool {
109 if m.PrecertificateSubjectRegex.FindStringIndex(p.TBSCertificate.Subject.CommonName) != nil {
110 return true
111 }
112 for _, alt := range p.TBSCertificate.DNSNames {
113 if m.PrecertificateSubjectRegex.FindStringIndex(alt) != nil {
114 return true
115 }
116 }
117 return false
118 }
119
120
121 type MatchIssuerRegex struct {
122 CertificateIssuerRegex *regexp.Regexp
123 PrecertificateIssuerRegex *regexp.Regexp
124 }
125
126
127 func (m MatchIssuerRegex) CertificateMatches(c *x509.Certificate) bool {
128 return m.CertificateIssuerRegex.FindStringIndex(c.Issuer.CommonName) != nil
129 }
130
131
132 func (m MatchIssuerRegex) PrecertificateMatches(p *ct.Precertificate) bool {
133 return m.PrecertificateIssuerRegex.FindStringIndex(p.TBSCertificate.Issuer.CommonName) != nil
134 }
135
136
137 type MatchSCTTimestamp struct {
138 Timestamp uint64
139 }
140
141
142
143 func (m MatchSCTTimestamp) Matches(leaf *ct.LeafEntry) bool {
144 entry, _ := ct.LogEntryFromLeaf(1, leaf)
145 if entry == nil {
146
147 return false
148 }
149 return entry.Leaf.TimestampedEntry.Timestamp == m.Timestamp
150 }
151
152
153
154
155 type LeafMatcher interface {
156 Matches(*ct.LeafEntry) bool
157 }
158
159
160
161 type CertParseFailMatcher struct {
162 MatchNonFatalErrs bool
163 }
164
165
166 func (m CertParseFailMatcher) Matches(leaf *ct.LeafEntry) bool {
167 _, err := ct.LogEntryFromLeaf(1, leaf)
168 if err != nil {
169 if x509.IsFatal(err) {
170 return true
171 }
172 return m.MatchNonFatalErrs
173 }
174 return false
175 }
176
177
178
179 type CertVerifyFailMatcher struct {
180 roots *x509.CertPool
181 }
182
183
184 func (m *CertVerifyFailMatcher) PopulateRoots(ctx context.Context, logClient *client.LogClient) {
185 if m.roots != nil {
186 return
187 }
188 m.roots = x509.NewCertPool()
189 roots, err := logClient.GetAcceptedRoots(ctx)
190 if err != nil {
191 log.Fatal(err)
192 }
193 for _, root := range roots {
194 cert, _ := x509.ParseCertificate(root.Data)
195 if cert != nil {
196 m.roots.AddCert(cert)
197 } else {
198 log.Fatal(err)
199 }
200 }
201 }
202
203
204 func (m CertVerifyFailMatcher) Matches(leaf *ct.LeafEntry) bool {
205 entry, _ := ct.LogEntryFromLeaf(1, leaf)
206 if entry == nil {
207
208 return false
209 }
210
211 var notBefore time.Time
212 if entry.X509Cert != nil {
213 notBefore = entry.X509Cert.NotAfter
214 } else {
215 notBefore = entry.Precert.TBSCertificate.NotAfter
216 }
217 when := notBefore.Add(-1 * time.Second)
218 opts := x509.VerifyOptions{
219 KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
220 Roots: m.roots,
221 Intermediates: x509.NewCertPool(),
222 CurrentTime: when,
223 }
224 chain := make([]*x509.Certificate, len(entry.Chain))
225 for ii, cert := range entry.Chain {
226 intermediate, err := x509.ParseCertificate(cert.Data)
227 if intermediate == nil {
228 log.Printf("Intermediate %d fails to parse: %v", ii, err)
229 return true
230 }
231 chain[ii] = intermediate
232 opts.Intermediates.AddCert(intermediate)
233 }
234 if entry.X509Cert != nil {
235 if _, err := entry.X509Cert.Verify(opts); err != nil {
236 log.Printf("Cert fails to validate as of %v: %v", opts.CurrentTime, err)
237 return true
238 }
239 return false
240 }
241 if entry.Precert != nil {
242 precert, err := x509.ParseCertificate(entry.Precert.Submitted.Data)
243 if err != nil {
244 log.Printf("Precert fails to parse as of %v: %v", opts.CurrentTime, err)
245 return true
246 }
247
248 dropUnhandledExtension(precert, x509.OIDExtensionCTPoison)
249
250 for i := 1; i < len(chain); i++ {
251
252 dropUnhandledExtension(chain[i], x509.OIDExtensionPolicyConstraints)
253 }
254
255
256 if len(chain) > 0 {
257 for i, eku := range chain[0].ExtKeyUsage {
258 if eku == x509.ExtKeyUsageCertificateTransparency {
259 chain[0].ExtKeyUsage = append(chain[0].ExtKeyUsage[:i], chain[0].ExtKeyUsage[i+1:]...)
260 break
261 }
262 }
263 }
264
265 if _, err := precert.Verify(opts); err != nil {
266 log.Printf("Precert fails to validate as of %v: %v", opts.CurrentTime, err)
267 return true
268 }
269 return false
270 }
271 log.Printf("Neither cert nor precert present!")
272 return true
273 }
274
275 func dropUnhandledExtension(cert *x509.Certificate, oid asn1.ObjectIdentifier) {
276 for j, extOID := range cert.UnhandledCriticalExtensions {
277 if extOID.Equal(oid) {
278 cert.UnhandledCriticalExtensions = append(cert.UnhandledCriticalExtensions[:j], cert.UnhandledCriticalExtensions[j+1:]...)
279 return
280 }
281 }
282 }
283
View as plain text