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