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