1// Copyright 2015 Google LLC. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package ct holds core types and utilities for Certificate Transparency.
16package ct
17
18import (
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// The following structures represent those outlined in RFC6962; any section
30// numbers mentioned refer to that RFC.
31///////////////////////////////////////////////////////////////////////////////
32
33// LogEntryType represents the LogEntryType enum from section 3.1:
34//   enum { x509_entry(0), precert_entry(1), (65535) } LogEntryType;
35type LogEntryType tls.Enum // tls:"maxval:65535"
36
37// LogEntryType constants from section 3.1.
38const (
39	X509LogEntryType    LogEntryType = 0
40	PrecertLogEntryType LogEntryType = 1
41)
42
43func (e LogEntryType) String() string {
44	switch e {
45	case X509LogEntryType:
46		return "X509LogEntryType"
47	case PrecertLogEntryType:
48		return "PrecertLogEntryType"
49	default:
50		return fmt.Sprintf("UnknownEntryType(%d)", e)
51	}
52}
53
54// RFC6962 section 2.1 requires a prefix byte on hash inputs for second preimage resistance.
55const (
56	TreeLeafPrefix = byte(0x00)
57	TreeNodePrefix = byte(0x01)
58)
59
60// MerkleLeafType represents the MerkleLeafType enum from section 3.4:
61//   enum { timestamped_entry(0), (255) } MerkleLeafType;
62type MerkleLeafType tls.Enum // tls:"maxval:255"
63
64// TimestampedEntryLeafType is the only defined MerkleLeafType constant from section 3.4.
65const TimestampedEntryLeafType MerkleLeafType = 0 // Entry type for an SCT
66
67func (m MerkleLeafType) String() string {
68	switch m {
69	case TimestampedEntryLeafType:
70		return "TimestampedEntryLeafType"
71	default:
72		return fmt.Sprintf("UnknownLeafType(%d)", m)
73	}
74}
75
76// Version represents the Version enum from section 3.2:
77//   enum { v1(0), (255) } Version;
78type Version tls.Enum // tls:"maxval:255"
79
80// CT Version constants from section 3.2.
81const (
82	V1 Version = 0
83)
84
85func (v Version) String() string {
86	switch v {
87	case V1:
88		return "V1"
89	default:
90		return fmt.Sprintf("UnknownVersion(%d)", v)
91	}
92}
93
94// SignatureType differentiates STH signatures from SCT signatures, see section 3.2.
95//   enum { certificate_timestamp(0), tree_hash(1), (255) } SignatureType;
96type SignatureType tls.Enum // tls:"maxval:255"
97
98// SignatureType constants from section 3.2.
99const (
100	CertificateTimestampSignatureType SignatureType = 0
101	TreeHashSignatureType             SignatureType = 1
102)
103
104func (st SignatureType) String() string {
105	switch st {
106	case CertificateTimestampSignatureType:
107		return "CertificateTimestamp"
108	case TreeHashSignatureType:
109		return "TreeHash"
110	default:
111		return fmt.Sprintf("UnknownSignatureType(%d)", st)
112	}
113}
114
115// ASN1Cert type for holding the raw DER bytes of an ASN.1 Certificate
116// (section 3.1).
117type ASN1Cert struct {
118	Data []byte `tls:"minlen:1,maxlen:16777215"`
119}
120
121// LogID holds the hash of the Log's public key (section 3.2).
122// TODO(pphaneuf): Users should be migrated to the one in the logid package.
123type LogID struct {
124	KeyID [sha256.Size]byte
125}
126
127// PreCert represents a Precertificate (section 3.2).
128type PreCert struct {
129	IssuerKeyHash  [sha256.Size]byte
130	TBSCertificate []byte `tls:"minlen:1,maxlen:16777215"` // DER-encoded TBSCertificate
131}
132
133// CTExtensions is a representation of the raw bytes of any CtExtension
134// structure (see section 3.2).
135// nolint: golint
136type CTExtensions []byte // tls:"minlen:0,maxlen:65535"`
137
138// MerkleTreeNode represents an internal node in the CT tree.
139type MerkleTreeNode []byte
140
141// ConsistencyProof represents a CT consistency proof (see sections 2.1.2 and
142// 4.4).
143type ConsistencyProof []MerkleTreeNode
144
145// AuditPath represents a CT inclusion proof (see sections 2.1.1 and 4.5).
146type AuditPath []MerkleTreeNode
147
148// LeafInput represents a serialized MerkleTreeLeaf structure.
149type LeafInput []byte
150
151// DigitallySigned is a local alias for tls.DigitallySigned so that we can
152// attach a MarshalJSON method.
153type DigitallySigned tls.DigitallySigned
154
155// FromBase64String populates the DigitallySigned structure from the base64 data passed in.
156// Returns an error if the base64 data is invalid.
157func (d *DigitallySigned) FromBase64String(b64 string) error {
158	raw, err := base64.StdEncoding.DecodeString(b64)
159	if err != nil {
160		return fmt.Errorf("failed to unbase64 DigitallySigned: %v", err)
161	}
162	var ds tls.DigitallySigned
163	if rest, err := tls.Unmarshal(raw, &ds); err != nil {
164		return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
165	} else if len(rest) > 0 {
166		return fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest))
167	}
168	*d = DigitallySigned(ds)
169	return nil
170}
171
172// Base64String returns the base64 representation of the DigitallySigned struct.
173func (d DigitallySigned) Base64String() (string, error) {
174	b, err := tls.Marshal(d)
175	if err != nil {
176		return "", err
177	}
178	return base64.StdEncoding.EncodeToString(b), nil
179}
180
181// MarshalJSON implements the json.Marshaller interface.
182func (d DigitallySigned) MarshalJSON() ([]byte, error) {
183	b64, err := d.Base64String()
184	if err != nil {
185		return []byte{}, err
186	}
187	return []byte(`"` + b64 + `"`), nil
188}
189
190// UnmarshalJSON implements the json.Unmarshaler interface.
191func (d *DigitallySigned) UnmarshalJSON(b []byte) error {
192	var content string
193	if err := json.Unmarshal(b, &content); err != nil {
194		return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
195	}
196	return d.FromBase64String(content)
197}
198
199// RawLogEntry represents the (TLS-parsed) contents of an entry in a CT log.
200type RawLogEntry struct {
201	// Index is a position of the entry in the log.
202	Index int64
203	// Leaf is a parsed Merkle leaf hash input.
204	Leaf MerkleTreeLeaf
205	// Cert is:
206	// - A certificate if Leaf.TimestampedEntry.EntryType is X509LogEntryType.
207	// - A precertificate if Leaf.TimestampedEntry.EntryType is
208	//   PrecertLogEntryType, in the form of a DER-encoded Certificate as
209	//   originally added (which includes the poison extension and a signature
210	//   generated over the pre-cert by the pre-cert issuer).
211	// - Empty otherwise.
212	Cert ASN1Cert
213	// Chain is the issuing certificate chain starting with the issuer of Cert,
214	// or an empty slice if Cert is empty.
215	Chain []ASN1Cert
216}
217
218// LogEntry represents the (parsed) contents of an entry in a CT log.  This is described
219// in section 3.1, but note that this structure does *not* match the TLS structure
220// defined there (the TLS structure is never used directly in RFC6962).
221type LogEntry struct {
222	Index int64
223	Leaf  MerkleTreeLeaf
224	// Exactly one of the following three fields should be non-empty.
225	X509Cert *x509.Certificate // Parsed X.509 certificate
226	Precert  *Precertificate   // Extracted precertificate
227	JSONData []byte
228
229	// Chain holds the issuing certificate chain, starting with the
230	// issuer of the leaf certificate / pre-certificate.
231	Chain []ASN1Cert
232}
233
234// PrecertChainEntry holds an precertificate together with a validation chain
235// for it; see section 3.1.
236type PrecertChainEntry struct {
237	PreCertificate   ASN1Cert   `tls:"minlen:1,maxlen:16777215"`
238	CertificateChain []ASN1Cert `tls:"minlen:0,maxlen:16777215"`
239}
240
241// CertificateChain holds a chain of certificates, as returned as extra data
242// for get-entries (section 4.6).
243type CertificateChain struct {
244	Entries []ASN1Cert `tls:"minlen:0,maxlen:16777215"`
245}
246
247// JSONDataEntry holds arbitrary data.
248type JSONDataEntry struct {
249	Data []byte `tls:"minlen:0,maxlen:1677215"`
250}
251
252// SHA256Hash represents the output from the SHA256 hash function.
253type SHA256Hash [sha256.Size]byte
254
255// FromBase64String populates the SHA256 struct with the contents of the base64 data passed in.
256func (s *SHA256Hash) FromBase64String(b64 string) error {
257	bs, err := base64.StdEncoding.DecodeString(b64)
258	if err != nil {
259		return fmt.Errorf("failed to unbase64 LogID: %v", err)
260	}
261	if len(bs) != sha256.Size {
262		return fmt.Errorf("invalid SHA256 length, expected 32 but got %d", len(bs))
263	}
264	copy(s[:], bs)
265	return nil
266}
267
268// Base64String returns the base64 representation of this SHA256Hash.
269func (s SHA256Hash) Base64String() string {
270	return base64.StdEncoding.EncodeToString(s[:])
271}
272
273// MarshalJSON implements the json.Marshaller interface for SHA256Hash.
274func (s SHA256Hash) MarshalJSON() ([]byte, error) {
275	return []byte(`"` + s.Base64String() + `"`), nil
276}
277
278// UnmarshalJSON implements the json.Unmarshaller interface.
279func (s *SHA256Hash) UnmarshalJSON(b []byte) error {
280	var content string
281	if err := json.Unmarshal(b, &content); err != nil {
282		return fmt.Errorf("failed to unmarshal SHA256Hash: %v", err)
283	}
284	return s.FromBase64String(content)
285}
286
287// SignedTreeHead represents the structure returned by the get-sth CT method
288// after base64 decoding; see sections 3.5 and 4.3.
289type SignedTreeHead struct {
290	Version           Version         `json:"sth_version"`         // The version of the protocol to which the STH conforms
291	TreeSize          uint64          `json:"tree_size"`           // The number of entries in the new tree
292	Timestamp         uint64          `json:"timestamp"`           // The time at which the STH was created
293	SHA256RootHash    SHA256Hash      `json:"sha256_root_hash"`    // The root hash of the log's Merkle tree
294	TreeHeadSignature DigitallySigned `json:"tree_head_signature"` // Log's signature over a TLS-encoded TreeHeadSignature
295	LogID             SHA256Hash      `json:"log_id"`              // The SHA256 hash of the log's public key
296}
297
298func (s SignedTreeHead) String() string {
299	sigStr, err := s.TreeHeadSignature.Base64String()
300	if err != nil {
301		sigStr = tls.DigitallySigned(s.TreeHeadSignature).String()
302	}
303
304	// If the LogID field in the SignedTreeHead is empty, don't include it in
305	// the string.
306	var logIDStr string
307	if id, empty := s.LogID, (SHA256Hash{}); id != empty {
308		logIDStr = fmt.Sprintf("LogID:%s, ", id.Base64String())
309	}
310
311	return fmt.Sprintf("{%sTreeSize:%d, Timestamp:%d, SHA256RootHash:%q, TreeHeadSignature:%q}",
312		logIDStr, s.TreeSize, s.Timestamp, s.SHA256RootHash.Base64String(), sigStr)
313}
314
315// TreeHeadSignature holds the data over which the signature in an STH is
316// generated; see section 3.5
317type TreeHeadSignature struct {
318	Version        Version       `tls:"maxval:255"`
319	SignatureType  SignatureType `tls:"maxval:255"` // == TreeHashSignatureType
320	Timestamp      uint64
321	TreeSize       uint64
322	SHA256RootHash SHA256Hash
323}
324
325// SignedCertificateTimestamp represents the structure returned by the
326// add-chain and add-pre-chain methods after base64 decoding; see sections
327// 3.2, 4.1 and 4.2.
328type SignedCertificateTimestamp struct {
329	SCTVersion Version `tls:"maxval:255"`
330	LogID      LogID
331	Timestamp  uint64
332	Extensions CTExtensions    `tls:"minlen:0,maxlen:65535"`
333	Signature  DigitallySigned // Signature over TLS-encoded CertificateTimestamp
334}
335
336// CertificateTimestamp is the collection of data that the signature in an
337// SCT is over; see section 3.2.
338type CertificateTimestamp struct {
339	SCTVersion    Version       `tls:"maxval:255"`
340	SignatureType SignatureType `tls:"maxval:255"`
341	Timestamp     uint64
342	EntryType     LogEntryType   `tls:"maxval:65535"`
343	X509Entry     *ASN1Cert      `tls:"selector:EntryType,val:0"`
344	PrecertEntry  *PreCert       `tls:"selector:EntryType,val:1"`
345	JSONEntry     *JSONDataEntry `tls:"selector:EntryType,val:32768"`
346	Extensions    CTExtensions   `tls:"minlen:0,maxlen:65535"`
347}
348
349func (s SignedCertificateTimestamp) String() string {
350	return fmt.Sprintf("{Version:%d LogId:%s Timestamp:%d Extensions:'%s' Signature:%v}", s.SCTVersion,
351		base64.StdEncoding.EncodeToString(s.LogID.KeyID[:]),
352		s.Timestamp,
353		s.Extensions,
354		s.Signature)
355}
356
357// TimestampedEntry is part of the MerkleTreeLeaf structure; see section 3.4.
358type TimestampedEntry struct {
359	Timestamp    uint64
360	EntryType    LogEntryType   `tls:"maxval:65535"`
361	X509Entry    *ASN1Cert      `tls:"selector:EntryType,val:0"`
362	PrecertEntry *PreCert       `tls:"selector:EntryType,val:1"`
363	JSONEntry    *JSONDataEntry `tls:"selector:EntryType,val:32768"`
364	Extensions   CTExtensions   `tls:"minlen:0,maxlen:65535"`
365}
366
367// MerkleTreeLeaf represents the deserialized structure of the hash input for the
368// leaves of a log's Merkle tree; see section 3.4.
369type MerkleTreeLeaf struct {
370	Version          Version           `tls:"maxval:255"`
371	LeafType         MerkleLeafType    `tls:"maxval:255"`
372	TimestampedEntry *TimestampedEntry `tls:"selector:LeafType,val:0"`
373}
374
375// Precertificate represents the parsed CT Precertificate structure.
376type Precertificate struct {
377	// DER-encoded pre-certificate as originally added, which includes a
378	// poison extension and a signature generated over the pre-cert by
379	// the pre-cert issuer (which might differ from the issuer of the final
380	// cert, see RFC6962 s3.1).
381	Submitted ASN1Cert
382	// SHA256 hash of the issuing key
383	IssuerKeyHash [sha256.Size]byte
384	// Parsed TBSCertificate structure, held in an x509.Certificate for convenience.
385	TBSCertificate *x509.Certificate
386}
387
388// X509Certificate returns the X.509 Certificate contained within the
389// MerkleTreeLeaf.
390func (m *MerkleTreeLeaf) X509Certificate() (*x509.Certificate, error) {
391	if m.TimestampedEntry.EntryType != X509LogEntryType {
392		return nil, fmt.Errorf("cannot call X509Certificate on a MerkleTreeLeaf that is not an X509 entry")
393	}
394	return x509.ParseCertificate(m.TimestampedEntry.X509Entry.Data)
395}
396
397// Precertificate returns the X.509 Precertificate contained within the MerkleTreeLeaf.
398//
399// The returned precertificate is embedded in an x509.Certificate, but is in the
400// form stored internally in the log rather than the original submitted form
401// (i.e. it does not include the poison extension and any changes to reflect the
402// final certificate's issuer have been made; see x509.BuildPrecertTBS).
403func (m *MerkleTreeLeaf) Precertificate() (*x509.Certificate, error) {
404	if m.TimestampedEntry.EntryType != PrecertLogEntryType {
405		return nil, fmt.Errorf("cannot call Precertificate on a MerkleTreeLeaf that is not a precert entry")
406	}
407	return x509.ParseTBSCertificate(m.TimestampedEntry.PrecertEntry.TBSCertificate)
408}
409
410// APIEndpoint is a string that represents one of the Certificate Transparency
411// Log API endpoints.
412type APIEndpoint string
413
414// Certificate Transparency Log API endpoints; see section 4.
415// WARNING: Should match the URI paths without the "/ct/v1/" prefix.  If
416// changing these constants, may need to change those too.
417const (
418	AddChainStr          APIEndpoint = "add-chain"
419	AddPreChainStr       APIEndpoint = "add-pre-chain"
420	GetSTHStr            APIEndpoint = "get-sth"
421	GetEntriesStr        APIEndpoint = "get-entries"
422	GetProofByHashStr    APIEndpoint = "get-proof-by-hash"
423	GetSTHConsistencyStr APIEndpoint = "get-sth-consistency"
424	GetRootsStr          APIEndpoint = "get-roots"
425	GetEntryAndProofStr  APIEndpoint = "get-entry-and-proof"
426)
427
428// URI paths for Log requests; see section 4.
429// WARNING: Should match the API endpoints, with the "/ct/v1/" prefix.  If
430// changing these constants, may need to change those too.
431const (
432	AddChainPath          = "/ct/v1/add-chain"
433	AddPreChainPath       = "/ct/v1/add-pre-chain"
434	GetSTHPath            = "/ct/v1/get-sth"
435	GetEntriesPath        = "/ct/v1/get-entries"
436	GetProofByHashPath    = "/ct/v1/get-proof-by-hash"
437	GetSTHConsistencyPath = "/ct/v1/get-sth-consistency"
438	GetRootsPath          = "/ct/v1/get-roots"
439	GetEntryAndProofPath  = "/ct/v1/get-entry-and-proof"
440
441	AddJSONPath = "/ct/v1/add-json" // Experimental addition
442)
443
444// AddChainRequest represents the JSON request body sent to the add-chain and
445// add-pre-chain POST methods from sections 4.1 and 4.2.
446type AddChainRequest struct {
447	Chain [][]byte `json:"chain"`
448}
449
450// AddChainResponse represents the JSON response to the add-chain and
451// add-pre-chain POST methods.
452// An SCT represents a Log's promise to integrate a [pre-]certificate into the
453// log within a defined period of time.
454type AddChainResponse struct {
455	SCTVersion Version `json:"sct_version"` // SCT structure version
456	ID         []byte  `json:"id"`          // Log ID
457	Timestamp  uint64  `json:"timestamp"`   // Timestamp of issuance
458	Extensions string  `json:"extensions"`  // Holder for any CT extensions
459	Signature  []byte  `json:"signature"`   // Log signature for this SCT
460}
461
462// ToSignedCertificateTimestamp creates a SignedCertificateTimestamp from the
463// AddChainResponse.
464func (r *AddChainResponse) ToSignedCertificateTimestamp() (*SignedCertificateTimestamp, error) {
465	sct := SignedCertificateTimestamp{
466		SCTVersion: r.SCTVersion,
467		Timestamp:  r.Timestamp,
468	}
469
470	if len(r.ID) != sha256.Size {
471		return nil, fmt.Errorf("id is invalid length, expected %d got %d", sha256.Size, len(r.ID))
472	}
473	copy(sct.LogID.KeyID[:], r.ID)
474
475	exts, err := base64.StdEncoding.DecodeString(r.Extensions)
476	if err != nil {
477		return nil, fmt.Errorf("invalid base64 data in Extensions (%q): %v", r.Extensions, err)
478	}
479	sct.Extensions = CTExtensions(exts)
480
481	var ds DigitallySigned
482	if rest, err := tls.Unmarshal(r.Signature, &ds); err != nil {
483		return nil, fmt.Errorf("tls.Unmarshal(): %s", err)
484	} else if len(rest) > 0 {
485		return nil, fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest))
486	}
487	sct.Signature = ds
488
489	return &sct, nil
490}
491
492// AddJSONRequest represents the JSON request body sent to the add-json POST method.
493// The corresponding response re-uses AddChainResponse.
494// This is an experimental addition not covered by RFC6962.
495type AddJSONRequest struct {
496	Data interface{} `json:"data"`
497}
498
499// GetSTHResponse represents the JSON response to the get-sth GET method from section 4.3.
500type GetSTHResponse struct {
501	TreeSize          uint64 `json:"tree_size"`           // Number of certs in the current tree
502	Timestamp         uint64 `json:"timestamp"`           // Time that the tree was created
503	SHA256RootHash    []byte `json:"sha256_root_hash"`    // Root hash of the tree
504	TreeHeadSignature []byte `json:"tree_head_signature"` // Log signature for this STH
505}
506
507// ToSignedTreeHead creates a SignedTreeHead from the GetSTHResponse.
508func (r *GetSTHResponse) ToSignedTreeHead() (*SignedTreeHead, error) {
509	sth := SignedTreeHead{
510		TreeSize:  r.TreeSize,
511		Timestamp: r.Timestamp,
512	}
513
514	if len(r.SHA256RootHash) != sha256.Size {
515		return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(r.SHA256RootHash))
516	}
517	copy(sth.SHA256RootHash[:], r.SHA256RootHash)
518
519	var ds DigitallySigned
520	if rest, err := tls.Unmarshal(r.TreeHeadSignature, &ds); err != nil {
521		return nil, fmt.Errorf("tls.Unmarshal(): %s", err)
522	} else if len(rest) > 0 {
523		return nil, fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest))
524	}
525	sth.TreeHeadSignature = ds
526
527	return &sth, nil
528}
529
530// GetSTHConsistencyResponse represents the JSON response to the get-sth-consistency
531// GET method from section 4.4.  (The corresponding GET request has parameters 'first' and
532// 'second'.)
533type GetSTHConsistencyResponse struct {
534	Consistency [][]byte `json:"consistency"`
535}
536
537// GetProofByHashResponse represents the JSON response to the get-proof-by-hash GET
538// method from section 4.5.  (The corresponding GET request has parameters 'hash'
539// and 'tree_size'.)
540type GetProofByHashResponse struct {
541	LeafIndex int64    `json:"leaf_index"` // The 0-based index of the end entity corresponding to the "hash" parameter.
542	AuditPath [][]byte `json:"audit_path"` // An array of base64-encoded Merkle Tree nodes proving the inclusion of the chosen certificate.
543}
544
545// LeafEntry represents a leaf in the Log's Merkle tree, as returned by the get-entries
546// GET method from section 4.6.
547type LeafEntry struct {
548	// LeafInput is a TLS-encoded MerkleTreeLeaf
549	LeafInput []byte `json:"leaf_input"`
550	// ExtraData holds (unsigned) extra data, normally the cert validation chain.
551	ExtraData []byte `json:"extra_data"`
552}
553
554// GetEntriesResponse respresents the JSON response to the get-entries GET method
555// from section 4.6.
556type GetEntriesResponse struct {
557	Entries []LeafEntry `json:"entries"` // the list of returned entries
558}
559
560// GetRootsResponse represents the JSON response to the get-roots GET method from section 4.7.
561type GetRootsResponse struct {
562	Certificates []string `json:"certificates"`
563}
564
565// GetEntryAndProofResponse represents the JSON response to the get-entry-and-proof
566// GET method from section 4.8. (The corresponding GET request has parameters 'leaf_index'
567// and 'tree_size'.)
568type GetEntryAndProofResponse struct {
569	LeafInput []byte   `json:"leaf_input"` // the entry itself
570	ExtraData []byte   `json:"extra_data"` // any chain provided when the entry was added to the log
571	AuditPath [][]byte `json:"audit_path"` // the corresponding proof
572}
573