1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package ct
17
18 import (
19 "crypto/sha256"
20 "encoding/base64"
21 "encoding/json"
22 "fmt"
23
24 "github.com/google/certificate-transparency-go/tls"
25 "github.com/google/certificate-transparency-go/x509"
26 )
27
28
29
30
31
32
33
34
35
36 type LogEntryType tls.Enum
37
38
39 const (
40 X509LogEntryType LogEntryType = 0
41 PrecertLogEntryType LogEntryType = 1
42 )
43
44 func (e LogEntryType) String() string {
45 switch e {
46 case X509LogEntryType:
47 return "X509LogEntryType"
48 case PrecertLogEntryType:
49 return "PrecertLogEntryType"
50 default:
51 return fmt.Sprintf("UnknownEntryType(%d)", e)
52 }
53 }
54
55
56 const (
57 TreeLeafPrefix = byte(0x00)
58 TreeNodePrefix = byte(0x01)
59 )
60
61
62
63
64 type MerkleLeafType tls.Enum
65
66
67 const TimestampedEntryLeafType MerkleLeafType = 0
68
69 func (m MerkleLeafType) String() string {
70 switch m {
71 case TimestampedEntryLeafType:
72 return "TimestampedEntryLeafType"
73 default:
74 return fmt.Sprintf("UnknownLeafType(%d)", m)
75 }
76 }
77
78
79
80
81 type Version tls.Enum
82
83
84 const (
85 V1 Version = 0
86 )
87
88 func (v Version) String() string {
89 switch v {
90 case V1:
91 return "V1"
92 default:
93 return fmt.Sprintf("UnknownVersion(%d)", v)
94 }
95 }
96
97
98
99
100 type SignatureType tls.Enum
101
102
103 const (
104 CertificateTimestampSignatureType SignatureType = 0
105 TreeHashSignatureType SignatureType = 1
106 )
107
108 func (st SignatureType) String() string {
109 switch st {
110 case CertificateTimestampSignatureType:
111 return "CertificateTimestamp"
112 case TreeHashSignatureType:
113 return "TreeHash"
114 default:
115 return fmt.Sprintf("UnknownSignatureType(%d)", st)
116 }
117 }
118
119
120
121 type ASN1Cert struct {
122 Data []byte `tls:"minlen:1,maxlen:16777215"`
123 }
124
125
126
127 type LogID struct {
128 KeyID [sha256.Size]byte
129 }
130
131
132 type PreCert struct {
133 IssuerKeyHash [sha256.Size]byte
134 TBSCertificate []byte `tls:"minlen:1,maxlen:16777215"`
135 }
136
137
138
139
140 type CTExtensions []byte
141
142
143 type MerkleTreeNode []byte
144
145
146
147 type ConsistencyProof []MerkleTreeNode
148
149
150 type AuditPath []MerkleTreeNode
151
152
153 type LeafInput []byte
154
155
156
157 type DigitallySigned tls.DigitallySigned
158
159
160
161 func (d *DigitallySigned) FromBase64String(b64 string) error {
162 raw, err := base64.StdEncoding.DecodeString(b64)
163 if err != nil {
164 return fmt.Errorf("failed to unbase64 DigitallySigned: %v", err)
165 }
166 var ds tls.DigitallySigned
167 if rest, err := tls.Unmarshal(raw, &ds); err != nil {
168 return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
169 } else if len(rest) > 0 {
170 return fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest))
171 }
172 *d = DigitallySigned(ds)
173 return nil
174 }
175
176
177 func (d DigitallySigned) Base64String() (string, error) {
178 b, err := tls.Marshal(d)
179 if err != nil {
180 return "", err
181 }
182 return base64.StdEncoding.EncodeToString(b), nil
183 }
184
185
186 func (d DigitallySigned) MarshalJSON() ([]byte, error) {
187 b64, err := d.Base64String()
188 if err != nil {
189 return []byte{}, err
190 }
191 return []byte(`"` + b64 + `"`), nil
192 }
193
194
195 func (d *DigitallySigned) UnmarshalJSON(b []byte) error {
196 var content string
197 if err := json.Unmarshal(b, &content); err != nil {
198 return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
199 }
200 return d.FromBase64String(content)
201 }
202
203
204 type RawLogEntry struct {
205
206 Index int64
207
208 Leaf MerkleTreeLeaf
209
210
211
212
213
214
215
216 Cert ASN1Cert
217
218
219 Chain []ASN1Cert
220 }
221
222
223
224
225 type LogEntry struct {
226 Index int64
227 Leaf MerkleTreeLeaf
228
229 X509Cert *x509.Certificate
230 Precert *Precertificate
231 JSONData []byte
232
233
234
235 Chain []ASN1Cert
236 }
237
238
239
240 type PrecertChainEntry struct {
241 PreCertificate ASN1Cert `tls:"minlen:1,maxlen:16777215"`
242 CertificateChain []ASN1Cert `tls:"minlen:0,maxlen:16777215"`
243 }
244
245
246
247 type CertificateChain struct {
248 Entries []ASN1Cert `tls:"minlen:0,maxlen:16777215"`
249 }
250
251
252 type JSONDataEntry struct {
253 Data []byte `tls:"minlen:0,maxlen:1677215"`
254 }
255
256
257 type SHA256Hash [sha256.Size]byte
258
259
260 func (s *SHA256Hash) FromBase64String(b64 string) error {
261 bs, err := base64.StdEncoding.DecodeString(b64)
262 if err != nil {
263 return fmt.Errorf("failed to unbase64 LogID: %v", err)
264 }
265 if len(bs) != sha256.Size {
266 return fmt.Errorf("invalid SHA256 length, expected 32 but got %d", len(bs))
267 }
268 copy(s[:], bs)
269 return nil
270 }
271
272
273 func (s SHA256Hash) Base64String() string {
274 return base64.StdEncoding.EncodeToString(s[:])
275 }
276
277
278 func (s SHA256Hash) MarshalJSON() ([]byte, error) {
279 return []byte(`"` + s.Base64String() + `"`), nil
280 }
281
282
283 func (s *SHA256Hash) UnmarshalJSON(b []byte) error {
284 var content string
285 if err := json.Unmarshal(b, &content); err != nil {
286 return fmt.Errorf("failed to unmarshal SHA256Hash: %v", err)
287 }
288 return s.FromBase64String(content)
289 }
290
291
292
293 type SignedTreeHead struct {
294 Version Version `json:"sth_version"`
295 TreeSize uint64 `json:"tree_size"`
296 Timestamp uint64 `json:"timestamp"`
297 SHA256RootHash SHA256Hash `json:"sha256_root_hash"`
298 TreeHeadSignature DigitallySigned `json:"tree_head_signature"`
299 LogID SHA256Hash `json:"log_id"`
300 }
301
302 func (s SignedTreeHead) String() string {
303 sigStr, err := s.TreeHeadSignature.Base64String()
304 if err != nil {
305 sigStr = tls.DigitallySigned(s.TreeHeadSignature).String()
306 }
307
308
309
310 var logIDStr string
311 if id, empty := s.LogID, (SHA256Hash{}); id != empty {
312 logIDStr = fmt.Sprintf("LogID:%s, ", id.Base64String())
313 }
314
315 return fmt.Sprintf("{%sTreeSize:%d, Timestamp:%d, SHA256RootHash:%q, TreeHeadSignature:%q}",
316 logIDStr, s.TreeSize, s.Timestamp, s.SHA256RootHash.Base64String(), sigStr)
317 }
318
319
320
321 type TreeHeadSignature struct {
322 Version Version `tls:"maxval:255"`
323 SignatureType SignatureType `tls:"maxval:255"`
324 Timestamp uint64
325 TreeSize uint64
326 SHA256RootHash SHA256Hash
327 }
328
329
330
331
332 type SignedCertificateTimestamp struct {
333 SCTVersion Version `tls:"maxval:255"`
334 LogID LogID
335 Timestamp uint64
336 Extensions CTExtensions `tls:"minlen:0,maxlen:65535"`
337 Signature DigitallySigned
338 }
339
340
341
342 type CertificateTimestamp struct {
343 SCTVersion Version `tls:"maxval:255"`
344 SignatureType SignatureType `tls:"maxval:255"`
345 Timestamp uint64
346 EntryType LogEntryType `tls:"maxval:65535"`
347 X509Entry *ASN1Cert `tls:"selector:EntryType,val:0"`
348 PrecertEntry *PreCert `tls:"selector:EntryType,val:1"`
349 JSONEntry *JSONDataEntry `tls:"selector:EntryType,val:32768"`
350 Extensions CTExtensions `tls:"minlen:0,maxlen:65535"`
351 }
352
353 func (s SignedCertificateTimestamp) String() string {
354 return fmt.Sprintf("{Version:%d LogId:%s Timestamp:%d Extensions:'%s' Signature:%v}", s.SCTVersion,
355 base64.StdEncoding.EncodeToString(s.LogID.KeyID[:]),
356 s.Timestamp,
357 s.Extensions,
358 s.Signature)
359 }
360
361
362 type TimestampedEntry struct {
363 Timestamp uint64
364 EntryType LogEntryType `tls:"maxval:65535"`
365 X509Entry *ASN1Cert `tls:"selector:EntryType,val:0"`
366 PrecertEntry *PreCert `tls:"selector:EntryType,val:1"`
367 JSONEntry *JSONDataEntry `tls:"selector:EntryType,val:32768"`
368 Extensions CTExtensions `tls:"minlen:0,maxlen:65535"`
369 }
370
371
372
373 type MerkleTreeLeaf struct {
374 Version Version `tls:"maxval:255"`
375 LeafType MerkleLeafType `tls:"maxval:255"`
376 TimestampedEntry *TimestampedEntry `tls:"selector:LeafType,val:0"`
377 }
378
379
380 type Precertificate struct {
381
382
383
384
385 Submitted ASN1Cert
386
387 IssuerKeyHash [sha256.Size]byte
388
389 TBSCertificate *x509.Certificate
390 }
391
392
393
394 func (m *MerkleTreeLeaf) X509Certificate() (*x509.Certificate, error) {
395 if m.TimestampedEntry.EntryType != X509LogEntryType {
396 return nil, fmt.Errorf("cannot call X509Certificate on a MerkleTreeLeaf that is not an X509 entry")
397 }
398 return x509.ParseCertificate(m.TimestampedEntry.X509Entry.Data)
399 }
400
401
402
403
404
405
406
407 func (m *MerkleTreeLeaf) Precertificate() (*x509.Certificate, error) {
408 if m.TimestampedEntry.EntryType != PrecertLogEntryType {
409 return nil, fmt.Errorf("cannot call Precertificate on a MerkleTreeLeaf that is not a precert entry")
410 }
411 return x509.ParseTBSCertificate(m.TimestampedEntry.PrecertEntry.TBSCertificate)
412 }
413
414
415
416 type APIEndpoint string
417
418
419
420
421 const (
422 AddChainStr APIEndpoint = "add-chain"
423 AddPreChainStr APIEndpoint = "add-pre-chain"
424 GetSTHStr APIEndpoint = "get-sth"
425 GetEntriesStr APIEndpoint = "get-entries"
426 GetProofByHashStr APIEndpoint = "get-proof-by-hash"
427 GetSTHConsistencyStr APIEndpoint = "get-sth-consistency"
428 GetRootsStr APIEndpoint = "get-roots"
429 GetEntryAndProofStr APIEndpoint = "get-entry-and-proof"
430 )
431
432
433
434
435 const (
436 AddChainPath = "/ct/v1/add-chain"
437 AddPreChainPath = "/ct/v1/add-pre-chain"
438 GetSTHPath = "/ct/v1/get-sth"
439 GetEntriesPath = "/ct/v1/get-entries"
440 GetProofByHashPath = "/ct/v1/get-proof-by-hash"
441 GetSTHConsistencyPath = "/ct/v1/get-sth-consistency"
442 GetRootsPath = "/ct/v1/get-roots"
443 GetEntryAndProofPath = "/ct/v1/get-entry-and-proof"
444
445 AddJSONPath = "/ct/v1/add-json"
446 )
447
448
449
450 type AddChainRequest struct {
451 Chain [][]byte `json:"chain"`
452 }
453
454
455
456
457
458 type AddChainResponse struct {
459 SCTVersion Version `json:"sct_version"`
460 ID []byte `json:"id"`
461 Timestamp uint64 `json:"timestamp"`
462 Extensions string `json:"extensions"`
463 Signature []byte `json:"signature"`
464 }
465
466
467
468 func (r *AddChainResponse) ToSignedCertificateTimestamp() (*SignedCertificateTimestamp, error) {
469 sct := SignedCertificateTimestamp{
470 SCTVersion: r.SCTVersion,
471 Timestamp: r.Timestamp,
472 }
473
474 if len(r.ID) != sha256.Size {
475 return nil, fmt.Errorf("id is invalid length, expected %d got %d", sha256.Size, len(r.ID))
476 }
477 copy(sct.LogID.KeyID[:], r.ID)
478
479 exts, err := base64.StdEncoding.DecodeString(r.Extensions)
480 if err != nil {
481 return nil, fmt.Errorf("invalid base64 data in Extensions (%q): %v", r.Extensions, err)
482 }
483 sct.Extensions = CTExtensions(exts)
484
485 var ds DigitallySigned
486 if rest, err := tls.Unmarshal(r.Signature, &ds); err != nil {
487 return nil, fmt.Errorf("tls.Unmarshal(): %s", err)
488 } else if len(rest) > 0 {
489 return nil, fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest))
490 }
491 sct.Signature = ds
492
493 return &sct, nil
494 }
495
496
497
498
499 type AddJSONRequest struct {
500 Data interface{} `json:"data"`
501 }
502
503
504 type GetSTHResponse struct {
505 TreeSize uint64 `json:"tree_size"`
506 Timestamp uint64 `json:"timestamp"`
507 SHA256RootHash []byte `json:"sha256_root_hash"`
508 TreeHeadSignature []byte `json:"tree_head_signature"`
509 }
510
511
512 func (r *GetSTHResponse) ToSignedTreeHead() (*SignedTreeHead, error) {
513 sth := SignedTreeHead{
514 TreeSize: r.TreeSize,
515 Timestamp: r.Timestamp,
516 }
517
518 if len(r.SHA256RootHash) != sha256.Size {
519 return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(r.SHA256RootHash))
520 }
521 copy(sth.SHA256RootHash[:], r.SHA256RootHash)
522
523 var ds DigitallySigned
524 if rest, err := tls.Unmarshal(r.TreeHeadSignature, &ds); err != nil {
525 return nil, fmt.Errorf("tls.Unmarshal(): %s", err)
526 } else if len(rest) > 0 {
527 return nil, fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest))
528 }
529 sth.TreeHeadSignature = ds
530
531 return &sth, nil
532 }
533
534
535
536
537 type GetSTHConsistencyResponse struct {
538 Consistency [][]byte `json:"consistency"`
539 }
540
541
542
543
544 type GetProofByHashResponse struct {
545 LeafIndex int64 `json:"leaf_index"`
546 AuditPath [][]byte `json:"audit_path"`
547 }
548
549
550
551 type LeafEntry struct {
552
553 LeafInput []byte `json:"leaf_input"`
554
555 ExtraData []byte `json:"extra_data"`
556 }
557
558
559
560 type GetEntriesResponse struct {
561 Entries []LeafEntry `json:"entries"`
562 }
563
564
565 type GetRootsResponse struct {
566 Certificates []string `json:"certificates"`
567 }
568
569
570
571
572 type GetEntryAndProofResponse struct {
573 LeafInput []byte `json:"leaf_input"`
574 ExtraData []byte `json:"extra_data"`
575 AuditPath [][]byte `json:"audit_path"`
576 }
577
View as plain text