// Copyright 2018 Google LLC. 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 ctutil contains utilities for Certificate Transparency. package ctutil import ( "bytes" "crypto" "crypto/sha256" "encoding/base64" "errors" "fmt" ct "github.com/google/certificate-transparency-go" "github.com/google/certificate-transparency-go/tls" "github.com/google/certificate-transparency-go/x509" ) var emptyHash = [sha256.Size]byte{} // LeafHashB64 does as LeafHash does, but returns the leaf hash base64-encoded. // The base64-encoded leaf hash returned by B64LeafHash can be used with the // get-proof-by-hash API endpoint of Certificate Transparency Logs. func LeafHashB64(chain []*x509.Certificate, sct *ct.SignedCertificateTimestamp, embedded bool) (string, error) { hash, err := LeafHash(chain, sct, embedded) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(hash[:]), nil } // LeafHash calculates the leaf hash of the certificate or precertificate at // chain[0] that sct was issued for. // // sct is required because the SCT timestamp is used to calculate the leaf hash. // Leaf hashes are unique to (pre)certificate-SCT pairs. // // This function can be used with three different types of leaf certificate: // - X.509 Certificate: // If using this function to calculate the leaf hash for a normal X.509 // certificate then it is enough to just provide the end entity // certificate in chain. This case assumes that the SCT being provided is // not embedded within the leaf certificate provided, i.e. the certificate // is what was submitted to the Certificate Transparency Log in order to // obtain the SCT. For this case, set embedded to false. // - Precertificate: // If using this function to calculate the leaf hash for a precertificate // then the issuing certificate must also be provided in chain. The // precertificate should be at chain[0], and its issuer at chain[1]. For // this case, set embedded to false. // - X.509 Certificate containing the SCT embedded within it: // If using this function to calculate the leaf hash for a certificate // where the SCT provided is embedded within the certificate you // are providing at chain[0], set embedded to true. LeafHash will // calculate the leaf hash by building the corresponding precertificate. // LeafHash will return an error if the provided SCT cannot be found // embedded within chain[0]. As with the precertificate case, the issuing // certificate must also be provided in chain. The certificate containing // the embedded SCT should be at chain[0], and its issuer at chain[1]. // // Note: LeafHash doesn't check that the provided SCT verifies for the given // chain. It simply calculates what the leaf hash would be for the given // (pre)certificate-SCT pair. func LeafHash(chain []*x509.Certificate, sct *ct.SignedCertificateTimestamp, embedded bool) ([sha256.Size]byte, error) { leaf, err := createLeaf(chain, sct, embedded) if err != nil { return emptyHash, err } return ct.LeafHashForLeaf(leaf) } // VerifySCT takes the public key of a Certificate Transparency Log, a // certificate chain, and an SCT and verifies whether the SCT is a valid SCT for // the certificate at chain[0], signed by the Log that the public key belongs // to. If the SCT does not verify, an error will be returned. // // This function can be used with three different types of leaf certificate: // - X.509 Certificate: // If using this function to verify an SCT for a normal X.509 certificate // then it is enough to just provide the end entity certificate in chain. // This case assumes that the SCT being provided is not embedded within // the leaf certificate provided, i.e. the certificate is what was // submitted to the Certificate Transparency Log in order to obtain the // SCT. For this case, set embedded to false. // - Precertificate: // If using this function to verify an SCT for a precertificate then the // issuing certificate must also be provided in chain. The precertificate // should be at chain[0], and its issuer at chain[1]. For this case, set // embedded to false. // - X.509 Certificate containing the SCT embedded within it: // If the SCT you wish to verify is embedded within the certificate you // are providing at chain[0], set embedded to true. VerifySCT will // verify the provided SCT by building the corresponding precertificate. // VerifySCT will return an error if the provided SCT cannot be found // embedded within chain[0]. As with the precertificate case, the issuing // certificate must also be provided in chain. The certificate containing // the embedded SCT should be at chain[0], and its issuer at chain[1]. func VerifySCT(pubKey crypto.PublicKey, chain []*x509.Certificate, sct *ct.SignedCertificateTimestamp, embedded bool) error { s, err := ct.NewSignatureVerifier(pubKey) if err != nil { return fmt.Errorf("error creating signature verifier: %s", err) } return VerifySCTWithVerifier(s, chain, sct, embedded) } // VerifySCTWithVerifier takes a ct.SignatureVerifier, a certificate chain, and // an SCT and verifies whether the SCT is a valid SCT for the certificate at // chain[0], signed by the Log whose public key was used to set up the // ct.SignatureVerifier. If the SCT does not verify, an error will be returned. // // This function can be used with three different types of leaf certificate: // - X.509 Certificate: // If using this function to verify an SCT for a normal X.509 certificate // then it is enough to just provide the end entity certificate in chain. // This case assumes that the SCT being provided is not embedded within // the leaf certificate provided, i.e. the certificate is what was // submitted to the Certificate Transparency Log in order to obtain the // SCT. For this case, set embedded to false. // - Precertificate: // If using this function to verify an SCT for a precertificate then the // issuing certificate must also be provided in chain. The precertificate // should be at chain[0], and its issuer at chain[1]. For this case, set // embedded to false. // - X.509 Certificate containing the SCT embedded within it: // If the SCT you wish to verify is embedded within the certificate you // are providing at chain[0], set embedded to true. VerifySCT will // verify the provided SCT by building the corresponding precertificate. // VerifySCT will return an error if the provided SCT cannot be found // embedded within chain[0]. As with the precertificate case, the issuing // certificate must also be provided in chain. The certificate containing // the embedded SCT should be at chain[0], and its issuer at chain[1]. func VerifySCTWithVerifier(sv *ct.SignatureVerifier, chain []*x509.Certificate, sct *ct.SignedCertificateTimestamp, embedded bool) error { if sv == nil { return errors.New("ct.SignatureVerifier is nil") } leaf, err := createLeaf(chain, sct, embedded) if err != nil { return err } return sv.VerifySCTSignature(*sct, ct.LogEntry{Leaf: *leaf}) } func createLeaf(chain []*x509.Certificate, sct *ct.SignedCertificateTimestamp, embedded bool) (*ct.MerkleTreeLeaf, error) { if len(chain) == 0 { return nil, errors.New("chain is empty") } if sct == nil { return nil, errors.New("sct is nil") } if embedded { sctPresent, err := ContainsSCT(chain[0], sct) if err != nil { return nil, fmt.Errorf("error checking for SCT in leaf certificate: %s", err) } if !sctPresent { return nil, errors.New("SCT provided is not embedded within leaf certificate") } } certType := ct.X509LogEntryType if chain[0].IsPrecertificate() || embedded { certType = ct.PrecertLogEntryType } var leaf *ct.MerkleTreeLeaf var err error if embedded { leaf, err = ct.MerkleTreeLeafForEmbeddedSCT(chain, sct.Timestamp) } else { leaf, err = ct.MerkleTreeLeafFromChain(chain, certType, sct.Timestamp) } if err != nil { return nil, fmt.Errorf("error creating MerkleTreeLeaf: %s", err) } return leaf, nil } // ContainsSCT checks to see whether the given SCT is embedded within the given // certificate. func ContainsSCT(cert *x509.Certificate, sct *ct.SignedCertificateTimestamp) (bool, error) { if cert == nil || sct == nil { return false, nil } sctBytes, err := tls.Marshal(*sct) if err != nil { return false, fmt.Errorf("error tls.Marshalling SCT: %s", err) } for _, s := range cert.SCTList.SCTList { if bytes.Equal(sctBytes, s.Val) { return true, nil } } return false, nil }