| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package ct |
| |
| import ( |
| "crypto" |
| "crypto/sha256" |
| "encoding/json" |
| "fmt" |
| "strings" |
| "time" |
| |
| "github.com/google/certificate-transparency-go/tls" |
| "github.com/google/certificate-transparency-go/x509" |
| ) |
| |
| // SerializeSCTSignatureInput serializes the passed in sct and log entry into |
| // the correct format for signing. |
| func SerializeSCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) { |
| switch sct.SCTVersion { |
| case V1: |
| input := CertificateTimestamp{ |
| SCTVersion: sct.SCTVersion, |
| SignatureType: CertificateTimestampSignatureType, |
| Timestamp: sct.Timestamp, |
| EntryType: entry.Leaf.TimestampedEntry.EntryType, |
| Extensions: sct.Extensions, |
| } |
| switch entry.Leaf.TimestampedEntry.EntryType { |
| case X509LogEntryType: |
| input.X509Entry = entry.Leaf.TimestampedEntry.X509Entry |
| case PrecertLogEntryType: |
| input.PrecertEntry = &PreCert{ |
| IssuerKeyHash: entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash, |
| TBSCertificate: entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate, |
| } |
| case XJSONLogEntryType: |
| input.JSONEntry = entry.Leaf.TimestampedEntry.JSONEntry |
| default: |
| return nil, fmt.Errorf("unsupported entry type %s", entry.Leaf.TimestampedEntry.EntryType) |
| } |
| return tls.Marshal(input) |
| default: |
| return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion) |
| } |
| } |
| |
| // SerializeSTHSignatureInput serializes the passed in STH into the correct |
| // format for signing. |
| func SerializeSTHSignatureInput(sth SignedTreeHead) ([]byte, error) { |
| switch sth.Version { |
| case V1: |
| if len(sth.SHA256RootHash) != crypto.SHA256.Size() { |
| return nil, fmt.Errorf("invalid TreeHash length, got %d expected %d", len(sth.SHA256RootHash), crypto.SHA256.Size()) |
| } |
| |
| input := TreeHeadSignature{ |
| Version: sth.Version, |
| SignatureType: TreeHashSignatureType, |
| Timestamp: sth.Timestamp, |
| TreeSize: sth.TreeSize, |
| SHA256RootHash: sth.SHA256RootHash, |
| } |
| return tls.Marshal(input) |
| default: |
| return nil, fmt.Errorf("unsupported STH version %d", sth.Version) |
| } |
| } |
| |
| // CreateX509MerkleTreeLeaf generates a MerkleTreeLeaf for an X509 cert |
| func CreateX509MerkleTreeLeaf(cert ASN1Cert, timestamp uint64) *MerkleTreeLeaf { |
| return &MerkleTreeLeaf{ |
| Version: V1, |
| LeafType: TimestampedEntryLeafType, |
| TimestampedEntry: &TimestampedEntry{ |
| Timestamp: timestamp, |
| EntryType: X509LogEntryType, |
| X509Entry: &cert, |
| }, |
| } |
| } |
| |
| // CreateJSONMerkleTreeLeaf creates the merkle tree leaf for json data. |
| func CreateJSONMerkleTreeLeaf(data interface{}, timestamp uint64) *MerkleTreeLeaf { |
| jsonData, err := json.Marshal(AddJSONRequest{Data: data}) |
| if err != nil { |
| return nil |
| } |
| // Match the JSON serialization implemented by json-c |
| jsonStr := strings.Replace(string(jsonData), ":", ": ", -1) |
| jsonStr = strings.Replace(jsonStr, ",", ", ", -1) |
| jsonStr = strings.Replace(jsonStr, "{", "{ ", -1) |
| jsonStr = strings.Replace(jsonStr, "}", " }", -1) |
| jsonStr = strings.Replace(jsonStr, "/", `\/`, -1) |
| // TODO: Pending google/certificate-transparency#1243, replace with |
| // ObjectHash once supported by CT server. |
| |
| return &MerkleTreeLeaf{ |
| Version: V1, |
| LeafType: TimestampedEntryLeafType, |
| TimestampedEntry: &TimestampedEntry{ |
| Timestamp: timestamp, |
| EntryType: XJSONLogEntryType, |
| JSONEntry: &JSONDataEntry{Data: []byte(jsonStr)}, |
| }, |
| } |
| } |
| |
| // MerkleTreeLeafFromRawChain generates a MerkleTreeLeaf from a chain (in DER-encoded form) and timestamp. |
| func MerkleTreeLeafFromRawChain(rawChain []ASN1Cert, etype LogEntryType, timestamp uint64) (*MerkleTreeLeaf, error) { |
| // Need at most 3 of the chain |
| count := 3 |
| if count > len(rawChain) { |
| count = len(rawChain) |
| } |
| chain := make([]*x509.Certificate, count) |
| for i := range chain { |
| cert, err := x509.ParseCertificate(rawChain[i].Data) |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse chain[%d] cert: %v", i, err) |
| } |
| chain[i] = cert |
| } |
| return MerkleTreeLeafFromChain(chain, etype, timestamp) |
| } |
| |
| // MerkleTreeLeafFromChain generates a MerkleTreeLeaf from a chain and timestamp. |
| func MerkleTreeLeafFromChain(chain []*x509.Certificate, etype LogEntryType, timestamp uint64) (*MerkleTreeLeaf, error) { |
| leaf := MerkleTreeLeaf{ |
| Version: V1, |
| LeafType: TimestampedEntryLeafType, |
| TimestampedEntry: &TimestampedEntry{ |
| EntryType: etype, |
| Timestamp: timestamp, |
| }, |
| } |
| if etype == X509LogEntryType { |
| leaf.TimestampedEntry.X509Entry = &ASN1Cert{Data: chain[0].Raw} |
| return &leaf, nil |
| } |
| if etype != PrecertLogEntryType { |
| return nil, fmt.Errorf("unknown LogEntryType %d", etype) |
| } |
| |
| // Pre-certs are more complicated. First, parse the leaf pre-cert and its |
| // putative issuer. |
| if len(chain) < 2 { |
| return nil, fmt.Errorf("no issuer cert available for precert leaf building") |
| } |
| issuer := chain[1] |
| cert := chain[0] |
| |
| var preIssuer *x509.Certificate |
| if IsPreIssuer(issuer) { |
| // Replace the cert's issuance information with details from the pre-issuer. |
| preIssuer = issuer |
| |
| // The issuer of the pre-cert is not going to be the issuer of the final |
| // cert. Change to use the final issuer's key hash. |
| if len(chain) < 3 { |
| return nil, fmt.Errorf("no issuer cert available for pre-issuer") |
| } |
| issuer = chain[2] |
| } |
| |
| // Next, post-process the DER-encoded TBSCertificate, to remove the CT poison |
| // extension and possibly update the issuer field. |
| defangedTBS, err := x509.BuildPrecertTBS(cert.RawTBSCertificate, preIssuer) |
| if err != nil { |
| return nil, fmt.Errorf("failed to remove poison extension: %v", err) |
| } |
| |
| leaf.TimestampedEntry.EntryType = PrecertLogEntryType |
| leaf.TimestampedEntry.PrecertEntry = &PreCert{ |
| IssuerKeyHash: sha256.Sum256(issuer.RawSubjectPublicKeyInfo), |
| TBSCertificate: defangedTBS, |
| } |
| return &leaf, nil |
| } |
| |
| // MerkleTreeLeafForEmbeddedSCT generates a MerkleTreeLeaf from a chain and an |
| // SCT timestamp, where the leaf certificate at chain[0] is a certificate that |
| // contains embedded SCTs. It is assumed that the timestamp provided is from |
| // one of the SCTs embedded within the leaf certificate. |
| func MerkleTreeLeafForEmbeddedSCT(chain []*x509.Certificate, timestamp uint64) (*MerkleTreeLeaf, error) { |
| // For building the leaf for a certificate and SCT where the SCT is embedded |
| // in the certificate, we need to build the original precertificate TBS |
| // data. First, parse the leaf cert and its issuer. |
| if len(chain) < 2 { |
| return nil, fmt.Errorf("no issuer cert available for precert leaf building") |
| } |
| issuer := chain[1] |
| cert := chain[0] |
| |
| // Next, post-process the DER-encoded TBSCertificate, to remove the SCTList |
| // extension. |
| tbs, err := x509.RemoveSCTList(cert.RawTBSCertificate) |
| if err != nil { |
| return nil, fmt.Errorf("failed to remove SCT List extension: %v", err) |
| } |
| |
| return &MerkleTreeLeaf{ |
| Version: V1, |
| LeafType: TimestampedEntryLeafType, |
| TimestampedEntry: &TimestampedEntry{ |
| EntryType: PrecertLogEntryType, |
| Timestamp: timestamp, |
| PrecertEntry: &PreCert{ |
| IssuerKeyHash: sha256.Sum256(issuer.RawSubjectPublicKeyInfo), |
| TBSCertificate: tbs, |
| }, |
| }, |
| }, nil |
| } |
| |
| // LeafHashForLeaf returns the leaf hash for a Merkle tree leaf. |
| func LeafHashForLeaf(leaf *MerkleTreeLeaf) ([sha256.Size]byte, error) { |
| leafData, err := tls.Marshal(*leaf) |
| if err != nil { |
| return [sha256.Size]byte{}, fmt.Errorf("failed to tls-encode MerkleTreeLeaf: %s", err) |
| } |
| |
| data := append([]byte{TreeLeafPrefix}, leafData...) |
| leafHash := sha256.Sum256(data) |
| return leafHash, nil |
| } |
| |
| // IsPreIssuer indicates whether a certificate is a pre-cert issuer with the specific |
| // certificate transparency extended key usage. |
| func IsPreIssuer(issuer *x509.Certificate) bool { |
| for _, eku := range issuer.ExtKeyUsage { |
| if eku == x509.ExtKeyUsageCertificateTransparency { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // LogEntryFromLeaf converts a LeafEntry object (which has the raw leaf data after JSON parsing) |
| // into a LogEntry object (which includes x509.Certificate objects, after TLS and ASN.1 parsing). |
| // Note that this function may return a valid LogEntry object and a non-nil error value, when |
| // the error indicates a non-fatal parsing error (of type x509.NonFatalErrors). |
| func LogEntryFromLeaf(index int64, leafEntry *LeafEntry) (*LogEntry, error) { |
| var leaf MerkleTreeLeaf |
| if rest, err := tls.Unmarshal(leafEntry.LeafInput, &leaf); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal MerkleTreeLeaf for index %d: %v", index, err) |
| } else if len(rest) > 0 { |
| return nil, fmt.Errorf("trailing data (%d bytes) after MerkleTreeLeaf for index %d", len(rest), index) |
| } |
| |
| var err error |
| entry := LogEntry{Index: index, Leaf: leaf} |
| switch leaf.TimestampedEntry.EntryType { |
| case X509LogEntryType: |
| var certChain CertificateChain |
| if rest, err := tls.Unmarshal(leafEntry.ExtraData, &certChain); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal ExtraData for index %d: %v", index, err) |
| } else if len(rest) > 0 { |
| return nil, fmt.Errorf("trailing data (%d bytes) after CertificateChain for index %d", len(rest), index) |
| } |
| entry.Chain = certChain.Entries |
| entry.X509Cert, err = leaf.X509Certificate() |
| if _, ok := err.(x509.NonFatalErrors); !ok && err != nil { |
| return nil, fmt.Errorf("failed to parse certificate in MerkleTreeLeaf for index %d: %v", index, err) |
| } |
| |
| case PrecertLogEntryType: |
| var precertChain PrecertChainEntry |
| if rest, err := tls.Unmarshal(leafEntry.ExtraData, &precertChain); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal PrecertChainEntry for index %d: %v", index, err) |
| } else if len(rest) > 0 { |
| return nil, fmt.Errorf("trailing data (%d bytes) after PrecertChainEntry for index %d", len(rest), index) |
| } |
| entry.Chain = precertChain.CertificateChain |
| var tbsCert *x509.Certificate |
| tbsCert, err = leaf.Precertificate() |
| if _, ok := err.(x509.NonFatalErrors); !ok && err != nil { |
| return nil, fmt.Errorf("failed to parse precertificate in MerkleTreeLeaf for index %d: %v", index, err) |
| } |
| entry.Precert = &Precertificate{ |
| Submitted: precertChain.PreCertificate, |
| IssuerKeyHash: leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash, |
| TBSCertificate: tbsCert, |
| } |
| |
| default: |
| return nil, fmt.Errorf("saw unknown entry type at index %d: %v", index, leaf.TimestampedEntry.EntryType) |
| } |
| // err may hold a x509.NonFatalErrors object. |
| return &entry, err |
| } |
| |
| // TimestampToTime converts a timestamp in the style of RFC 6962 (milliseconds |
| // since UNIX epoch) to a Go Time. |
| func TimestampToTime(ts uint64) time.Time { |
| secs := int64(ts / 1000) |
| msecs := int64(ts % 1000) |
| return time.Unix(secs, msecs*1000000) |
| } |