Files
buildx/vendor/github.com/google/certificate-transparency-go/ctutil/ctutil.go
T
2026-01-14 09:03:42 -08:00

212 lines
9.0 KiB
Go

// 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
}