Skip to content

Commit 7cf9ea0

Browse files
committed
Support pre-certs
1 parent 9d11a9a commit 7cf9ea0

2 files changed

Lines changed: 156 additions & 32 deletions

File tree

vindex/cmd/ct/main.go

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -347,19 +347,70 @@ func mapFn(data []byte) [][sha256.Size]byte {
347347
case 0:
348348
// x509
349349
isPreCert = false
350-
s.ReadUint24LengthPrefixed(&cert)
351-
case 1:
352-
if true {
353-
// Need to support parsing TBS certs
350+
if !s.ReadUint24LengthPrefixed(&cert) {
351+
klog.Warning("Failed to read x509 certificate")
354352
return nil
355353
}
354+
case 1:
356355
// precert
357356
isPreCert = true
358357
var ikh []byte
359-
s.ReadBytes(&ikh, sha256.Size)
360-
s.ReadUint24LengthPrefixed(&cert)
358+
if !s.ReadBytes(&ikh, sha256.Size) {
359+
klog.Warning("Failed to read issuer key hash")
360+
return nil
361+
}
362+
var tbsCert cryptobyte.String
363+
if !s.ReadUint24LengthPrefixed(&tbsCert) {
364+
klog.Warning("Failed to read precert TBSCertificate")
365+
return nil
366+
}
367+
368+
tbsDER := []byte(tbsCert)
369+
tbsCertCopy := tbsCert
370+
371+
var tbsSeq cryptobyte.String
372+
if !tbsCertCopy.ReadASN1(&tbsSeq, 0x30) { // SEQUENCE
373+
klog.Warning("failed to read TBSCertificate sequence")
374+
return nil
375+
}
376+
377+
if tbsSeq.PeekASN1Tag(0xA0) {
378+
var version cryptobyte.String
379+
if !tbsSeq.ReadASN1(&version, 0xA0) {
380+
klog.Warning("failed to read version")
381+
return nil
382+
}
383+
}
384+
385+
var serial cryptobyte.String
386+
if !tbsSeq.ReadASN1(&serial, 0x02) { // INTEGER
387+
klog.Warning("failed to read serial number")
388+
return nil
389+
}
390+
391+
var sigAlg cryptobyte.String
392+
if !tbsSeq.ReadASN1Element(&sigAlg, 0x30) { // SEQUENCE
393+
klog.Warning("failed to read signature algorithm")
394+
return nil
395+
}
396+
397+
var certBuilder cryptobyte.Builder
398+
certBuilder.AddASN1(0x30, func(b *cryptobyte.Builder) { // SEQUENCE
399+
b.AddBytes(tbsDER)
400+
b.AddBytes([]byte(sigAlg))
401+
b.AddASN1(3, func(b *cryptobyte.Builder) {
402+
b.AddBytes([]byte{0, 0})
403+
})
404+
})
405+
dummyCertDER, err := certBuilder.Bytes()
406+
if err != nil {
407+
klog.Warningf("failed to build dummy cert: %v", err)
408+
return nil
409+
}
410+
cert = cryptobyte.String(dummyCertDER)
361411
default:
362-
panic("unknown cert type")
412+
klog.Warningf("unknown cert type: %d", certType)
413+
return nil
363414
}
364415

365416
parsedCert, err := x509.ParseCertificate(cert)

vindex/cmd/ct/main_test.go

Lines changed: 98 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import (
3333
"golang.org/x/crypto/cryptobyte"
3434
)
3535

36-
func createTestCertBytes(dnsNames []string) ([]byte, error) {
36+
func createTestCertBytes(dnsNames []string, isPrecert bool) ([]byte, error) {
3737
priv, err := rsa.GenerateKey(rand.Reader, 2048)
3838
if err != nil {
3939
return nil, err
@@ -55,52 +55,126 @@ func createTestCertBytes(dnsNames []string) ([]byte, error) {
5555
return nil, err
5656
}
5757

58+
var tbsDER []byte
59+
if isPrecert {
60+
parsed, err := x509.ParseCertificate(certDER)
61+
if err != nil {
62+
return nil, err
63+
}
64+
tbsDER = parsed.RawTBSCertificate
65+
}
66+
5867
// Now marshal using cryptobyte into the form expected by mapFn
59-
// version = 0, leafType = 0, timestamp = 12345678, certType = 0 (x509)
68+
// version = 0, leafType = 0, timestamp = 12345678
6069
var b cryptobyte.Builder
6170
b.AddUint8(0) // version
6271
b.AddUint8(0) // leafType
6372
b.AddUint64(12345678) // timestamp
64-
b.AddUint16(0) // certType (x509)
65-
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
66-
b.AddBytes(certDER)
67-
})
73+
if isPrecert {
74+
b.AddUint16(1) // certType (precert)
75+
dummyIKH := make([]byte, 32)
76+
b.AddBytes(dummyIKH)
77+
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
78+
b.AddBytes(tbsDER)
79+
})
80+
} else {
81+
b.AddUint16(0) // certType (x509)
82+
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
83+
b.AddBytes(certDER)
84+
})
85+
}
6886
return b.Bytes()
6987
}
7088

7189
func TestMapFn(t *testing.T) {
7290
testCases := []struct {
73-
desc string
74-
dnsNames []string
75-
wantKeys []string
91+
desc string
92+
dnsNames []string
93+
wantKeys []string
94+
isPrecert bool
95+
rawBytes []byte
7696
}{
7797
{
78-
desc: "standard domains and wildcards",
79-
dnsNames: []string{"*.google.com", "google.com", "maps.google.com"},
80-
wantKeys: []string{"google.com", "maps.google.com"},
98+
desc: "standard domains and wildcards",
99+
dnsNames: []string{"*.google.com", "google.com", "maps.google.com"},
100+
wantKeys: []string{"google.com", "maps.google.com"},
101+
isPrecert: false,
102+
},
103+
{
104+
desc: "deeper subdomain",
105+
dnsNames: []string{"deep.maps.google.co.uk"},
106+
wantKeys: []string{"deep.maps.google.co.uk", "maps.google.co.uk", "google.co.uk"},
107+
isPrecert: false,
108+
},
109+
{
110+
desc: "mixed case",
111+
dnsNames: []string{"MAPS.GOOGLE.COM"},
112+
wantKeys: []string{"maps.google.com", "google.com"},
113+
isPrecert: false,
114+
},
115+
{
116+
desc: "invalid or TLD",
117+
dnsNames: []string{"localhost", "*.co.uk"},
118+
wantKeys: []string{"localhost", "co.uk"},
119+
isPrecert: false,
120+
},
121+
{
122+
desc: "precert: standard domains and wildcards",
123+
dnsNames: []string{"*.google.com", "google.com", "maps.google.com"},
124+
wantKeys: []string{"google.com", "maps.google.com"},
125+
isPrecert: true,
81126
},
82127
{
83-
desc: "deeper subdomain",
84-
dnsNames: []string{"deep.maps.google.co.uk"},
85-
wantKeys: []string{"deep.maps.google.co.uk", "maps.google.co.uk", "google.co.uk"},
128+
desc: "precert: deeper subdomain",
129+
dnsNames: []string{"deep.maps.google.co.uk"},
130+
wantKeys: []string{"deep.maps.google.co.uk", "maps.google.co.uk", "google.co.uk"},
131+
isPrecert: true,
86132
},
87133
{
88-
desc: "mixed case",
89-
dnsNames: []string{"MAPS.GOOGLE.COM"},
90-
wantKeys: []string{"maps.google.com", "google.com"},
134+
desc: "truncated header",
135+
rawBytes: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 11 bytes, header is 12 bytes
136+
wantKeys: []string{},
91137
},
92138
{
93-
desc: "invalid or TLD",
94-
dnsNames: []string{"localhost", "*.co.uk"},
95-
wantKeys: []string{"localhost", "co.uk"},
139+
desc: "unknown cert type",
140+
rawBytes: []byte{
141+
0, // version
142+
0, // leafType
143+
0, 0, 0, 0, 0, 0, 0, 0, // timestamp
144+
0, 99, // certType = 99
145+
},
146+
wantKeys: []string{},
147+
},
148+
{
149+
desc: "precert truncated TBS",
150+
rawBytes: func() []byte {
151+
var b cryptobyte.Builder
152+
b.AddUint8(0) // version
153+
b.AddUint8(0) // leafType
154+
b.AddUint64(12345678) // timestamp
155+
b.AddUint16(1) // certType (precert)
156+
dummyIKH := make([]byte, 32)
157+
b.AddBytes(dummyIKH)
158+
b.AddUint24(100) // length prefix 100
159+
b.AddBytes([]byte{1, 2, 3, 4, 5}) // only 5 bytes of data
160+
bytes, _ := b.Bytes()
161+
return bytes
162+
}(),
163+
wantKeys: []string{},
96164
},
97165
}
98166

99167
for _, tc := range testCases {
100168
t.Run(tc.desc, func(t *testing.T) {
101-
data, err := createTestCertBytes(tc.dnsNames)
102-
if err != nil {
103-
t.Fatalf("failed to create test cert: %v", err)
169+
var data []byte
170+
var err error
171+
if tc.rawBytes != nil {
172+
data = tc.rawBytes
173+
} else {
174+
data, err = createTestCertBytes(tc.dnsNames, tc.isPrecert)
175+
if err != nil {
176+
t.Fatalf("failed to create test cert: %v", err)
177+
}
104178
}
105179
hashes := mapFn(data)
106180

@@ -146,4 +220,3 @@ func TestMetricsEndpoint(t *testing.T) {
146220
t.Errorf("GET /metrics: got status %d, want %d", w.Code, http.StatusOK)
147221
}
148222
}
149-

0 commit comments

Comments
 (0)