Skip to content

Commit 5413c5e

Browse files
committedFeb 18, 2025
Merge branch 'feature/TLSv1.3' into refactor/tls13-merge-attempt-3
2 parents 03c45d0 + 90dd94c commit 5413c5e

File tree

222 files changed

+29246
-10886
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

222 files changed

+29246
-10886
lines changed
 

‎ct/scanner/scanner.go

+120-25
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,20 @@ import (
1313
"github.com/zmap/zcrypto/ct"
1414
"github.com/zmap/zcrypto/ct/client"
1515
"github.com/zmap/zcrypto/ct/x509"
16+
"github.com/zmap/zcrypto/encoding/asn1"
17+
"github.com/zmap/zcrypto/x509/pkix"
1618
)
1719

20+
// ASN1Certificate holds the top-level asn1 fields in a certificate.
21+
//
22+
// It is used to determine if a certificate contains well-formed asn1 data or is corrupted.
23+
type ASN1Certificate struct {
24+
Raw asn1.RawContent
25+
TBSCertificate asn1.RawValue
26+
SignatureAlgorithm pkix.AlgorithmIdentifier
27+
SignatureValue asn1.BitString
28+
}
29+
1830
// Clients wishing to implement their own Matchers should implement this interface:
1931
type Matcher interface {
2032
// CertificateMatches is called by the scanner for each X509 Certificate found in the log.
@@ -137,6 +149,9 @@ type ScannerOptions struct {
137149
Name string
138150

139151
MaximumIndex int64
152+
153+
// Always output encountered certificates, so long as they are valid ASN.1
154+
IgnoreParsingErrors bool
140155
}
141156

142157
// Creates a new ScannerOptions struct with sensible defaults
@@ -188,30 +203,87 @@ type fetchRange struct {
188203
end int64
189204
}
190205

191-
// Takes the error returned by either x509.ParseCertificate() or
192-
// x509.ParseTBSCertificate() and determines if it's non-fatal or otherwise.
193-
// In the case of non-fatal errors, the error will be logged,
194-
// entriesWithNonFatalErrors will be incremented, and the return value will be
195-
// nil.
196-
// Fatal errors will be logged, unparsableEntires will be incremented, and the
206+
// parseCertificate takes a raw certificate, parses it, and if there is an error,
207+
// determines if it is fatal. In the case of non-fatal errors, the error will be logged,
208+
// entriesWithNonFatalErrors will be incremented, and the returned error will be nil.
209+
//
210+
// Fatal parse errors will be logged, unparsableEntries will be incremented, and the
197211
// fatal error itself will be returned.
198-
// When |err| is nil, this method does nothing.
199-
func (s *Scanner) handleParseEntryError(err error, entryType ct.LogEntryType, index int64) error {
212+
//
213+
// This function does NOT promise that the returned certificate will be non-nil
214+
// whenever err is non-nil.
215+
func (s *Scanner) parseCertificate(
216+
precert bool,
217+
entryType ct.LogEntryType,
218+
index int64,
219+
raw []byte,
220+
) (*x509.Certificate, error) {
221+
var cert *x509.Certificate
222+
var err error
223+
if precert {
224+
cert, err = x509.ParseTBSCertificate(raw)
225+
} else {
226+
cert, err = x509.ParseCertificate(raw)
227+
}
228+
200229
if err == nil {
201230
// No error to handle
202-
return nil
231+
return cert, nil
203232
}
233+
234+
var isFatal bool
235+
204236
switch err.(type) {
205237
case x509.NonFatalErrors:
206238
s.entriesWithNonFatalErrors++
207-
// We'll make a note, but continue.
208-
s.logger.Warnf("Non-fatal error in %+v at index %d of log at %s: %s", entryType, index, s.logClient.Uri, err)
209239
default:
210240
s.unparsableEntries++
211-
s.logger.Warnf("Failed to parse in %+v at index %d of log at %s: %s", entryType, index, s.logClient.Uri, err)
212-
return err
241+
isFatal = true
242+
}
243+
244+
if !isFatal {
245+
s.logger.Warnf(
246+
"Ignored non-fatal error in %+v at index %d of log at %s: %s",
247+
entryType,
248+
index,
249+
s.logClient.Uri,
250+
err,
251+
)
252+
return cert, nil
253+
}
254+
255+
if !s.opts.IgnoreParsingErrors {
256+
s.logger.Errorf(
257+
"Fatal parse error in %+v at index %d of log at %s: %s",
258+
entryType,
259+
index,
260+
s.logClient.Uri,
261+
err,
262+
)
263+
return nil, err
213264
}
214-
return nil
265+
266+
var asn1cert ASN1Certificate
267+
if _, perr := asn1.Unmarshal(raw, &asn1cert); perr != nil {
268+
s.logger.Warnf(
269+
"Corrupted cert ASN.1 in %+v at index %d of log %s: %s",
270+
entryType,
271+
index,
272+
s.logClient.Uri,
273+
perr,
274+
)
275+
return nil, perr
276+
}
277+
278+
s.logger.Warnf(
279+
"Ignored fatal parse error in %+v at index %d of log %s: %s",
280+
entryType,
281+
index,
282+
s.logClient.Uri,
283+
err,
284+
)
285+
286+
return cert, nil
215287
}
216288

217289
// Processes the given |entry| in the specified log.
@@ -223,29 +295,52 @@ func (s *Scanner) processEntry(entry ct.LogEntry, foundCert func(*ct.LogEntry, s
223295
// Only interested in precerts and this is an X.509 cert, early-out.
224296
return
225297
}
226-
cert, err := x509.ParseCertificate(entry.Leaf.TimestampedEntry.X509Entry)
227-
if err = s.handleParseEntryError(err, entry.Leaf.TimestampedEntry.EntryType, entry.Index); err != nil {
228-
// We hit an unparseable entry, already logged inside handleParseEntryError()
298+
cert, err := s.parseCertificate(
299+
false,
300+
entry.Leaf.TimestampedEntry.EntryType,
301+
entry.Index,
302+
entry.Leaf.TimestampedEntry.X509Entry,
303+
)
304+
if err != nil {
229305
return
230306
}
231-
if s.opts.Matcher.CertificateMatches(cert) {
232-
entry.X509Cert = cert
307+
if cert != nil {
308+
if s.opts.Matcher.CertificateMatches(cert) {
309+
entry.RawCert = entry.Leaf.TimestampedEntry.X509Entry
310+
entry.X509Cert = cert
311+
foundCert(&entry, s.opts.Name)
312+
}
313+
} else {
314+
entry.RawCert = entry.Leaf.TimestampedEntry.X509Entry
233315
foundCert(&entry, s.opts.Name)
234316
}
235317
case ct.PrecertLogEntryType:
236-
c, err := x509.ParseTBSCertificate(entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate)
237-
if err = s.handleParseEntryError(err, entry.Leaf.TimestampedEntry.EntryType, entry.Index); err != nil {
238-
// We hit an unparseable entry, already logged inside handleParseEntryError()
318+
cert, err := s.parseCertificate(
319+
true,
320+
entry.Leaf.TimestampedEntry.EntryType,
321+
entry.Index,
322+
entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate,
323+
)
324+
if err != nil {
239325
return
240326
}
241327
precert := &ct.Precertificate{
242328
Raw: entry.Chain[0],
243-
TBSCertificate: *c,
329+
TBSCertificate: cert,
244330
IssuerKeyHash: entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash}
245-
if s.opts.Matcher.PrecertificateMatches(precert) {
246-
entry.Precert = precert
331+
332+
entry.IsPrecert = true
333+
entry.RawCert = entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate
334+
entry.Precert = precert
335+
336+
if cert != nil {
337+
if s.opts.Matcher.PrecertificateMatches(precert) {
338+
foundPrecert(&entry, s.opts.Name)
339+
}
340+
} else {
247341
foundPrecert(&entry, s.opts.Name)
248342
}
343+
249344
s.precertsSeen++
250345
}
251346
}

‎ct/types.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,16 @@ func (d *DigitallySigned) UnmarshalJSON(b []byte) error {
237237

238238
// LogEntry represents the contents of an entry in a CT log, see section 3.1.
239239
type LogEntry struct {
240-
Index int64
241-
Leaf MerkleTreeLeaf
240+
Index int64
241+
Leaf MerkleTreeLeaf
242+
RawCert []byte
243+
IsPrecert bool
244+
Chain []ASN1Cert
245+
Server string
246+
// When the raw cert is parseable, and not a precert,
247+
// X509Cert contains the parsed certificate for convenience.
242248
X509Cert *x509.Certificate
243249
Precert *Precertificate
244-
Chain []ASN1Cert
245-
Server string
246250
}
247251

248252
// SHA256Hash represents the output from the SHA256 hash function.
@@ -348,8 +352,8 @@ type Precertificate struct {
348352
// SHA256 hash of the issuing key
349353
IssuerKeyHash [issuerKeyHashLength]byte
350354
// Parsed TBSCertificate structure (held in an x509.Certificate for ease of
351-
// access.
352-
TBSCertificate x509.Certificate
355+
// access), when possible.
356+
TBSCertificate *x509.Certificate
353357
}
354358

355359
// X509Certificate returns the X.509 Certificate contained within the

0 commit comments

Comments
 (0)