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