...

Source file src/github.com/google/certificate-transparency-go/ctutil/loginfo.go

Documentation: github.com/google/certificate-transparency-go/ctutil

     1  // Copyright 2018 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 ctutil
    16  
    17  import (
    18  	"context"
    19  	"crypto/sha256"
    20  	"fmt"
    21  	"net/http"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  
    26  	ct "github.com/google/certificate-transparency-go"
    27  	"github.com/google/certificate-transparency-go/client"
    28  	"github.com/google/certificate-transparency-go/jsonclient"
    29  	"github.com/google/certificate-transparency-go/loglist3"
    30  	"github.com/google/certificate-transparency-go/x509"
    31  	"github.com/transparency-dev/merkle/proof"
    32  	"github.com/transparency-dev/merkle/rfc6962"
    33  )
    34  
    35  // LogInfo holds the objects needed to perform per-log verification and
    36  // validation of SCTs.
    37  type LogInfo struct {
    38  	Description string
    39  	Client      client.CheckLogClient
    40  	MMD         time.Duration
    41  	Verifier    *ct.SignatureVerifier
    42  	PublicKey   []byte
    43  
    44  	mu      sync.RWMutex
    45  	lastSTH *ct.SignedTreeHead
    46  }
    47  
    48  // NewLogInfo builds a LogInfo object based on a log list entry.
    49  func NewLogInfo(log *loglist3.Log, hc *http.Client) (*LogInfo, error) {
    50  	url := log.URL
    51  	if !strings.HasPrefix(url, "https://") {
    52  		url = "https://" + url
    53  	}
    54  	lc, err := client.New(url, hc, jsonclient.Options{PublicKeyDER: log.Key, UserAgent: "ct-go-logclient"})
    55  	if err != nil {
    56  		return nil, fmt.Errorf("failed to create client for log %q: %v", log.Description, err)
    57  	}
    58  	return newLogInfo(log, lc)
    59  }
    60  
    61  func newLogInfo(log *loglist3.Log, lc client.CheckLogClient) (*LogInfo, error) {
    62  	logKey, err := x509.ParsePKIXPublicKey(log.Key)
    63  	if err != nil {
    64  		return nil, fmt.Errorf("failed to parse public key data for log %q: %v", log.Description, err)
    65  	}
    66  	verifier, err := ct.NewSignatureVerifier(logKey)
    67  	if err != nil {
    68  		return nil, fmt.Errorf("failed to build verifier log %q: %v", log.Description, err)
    69  	}
    70  	mmd := time.Duration(log.MMD) * time.Second
    71  	return &LogInfo{
    72  		Description: log.Description,
    73  		Client:      lc,
    74  		MMD:         mmd,
    75  		Verifier:    verifier,
    76  		PublicKey:   log.Key,
    77  	}, nil
    78  }
    79  
    80  // LogInfoByHash holds LogInfo objects index by the SHA-256 hash of the log's public key.
    81  type LogInfoByHash map[[sha256.Size]byte]*LogInfo
    82  
    83  // LogInfoByKeyHash builds a map of LogInfo objects indexed by their key hashes.
    84  func LogInfoByKeyHash(ll *loglist3.LogList, hc *http.Client) (LogInfoByHash, error) {
    85  	return logInfoByKeyHash(ll, hc, NewLogInfo)
    86  }
    87  
    88  func logInfoByKeyHash(ll *loglist3.LogList, hc *http.Client, infoFactory func(*loglist3.Log, *http.Client) (*LogInfo, error)) (map[[sha256.Size]byte]*LogInfo, error) {
    89  	result := make(map[[sha256.Size]byte]*LogInfo)
    90  	for _, operator := range ll.Operators {
    91  		for _, log := range operator.Logs {
    92  			h := sha256.Sum256(log.Key)
    93  			li, err := infoFactory(log, hc)
    94  			if err != nil {
    95  				return nil, err
    96  			}
    97  			result[h] = li
    98  		}
    99  	}
   100  	return result, nil
   101  }
   102  
   103  // LastSTH returns the last STH known for the log.
   104  func (li *LogInfo) LastSTH() *ct.SignedTreeHead {
   105  	li.mu.RLock()
   106  	defer li.mu.RUnlock()
   107  	return li.lastSTH
   108  }
   109  
   110  // SetSTH sets the last STH known for the log.
   111  func (li *LogInfo) SetSTH(sth *ct.SignedTreeHead) {
   112  	li.mu.Lock()
   113  	defer li.mu.Unlock()
   114  	li.lastSTH = sth
   115  }
   116  
   117  // VerifySCTSignature checks the signature in the SCT matches the given leaf (adjusted for the
   118  // timestamp in the SCT) and log.
   119  func (li *LogInfo) VerifySCTSignature(sct ct.SignedCertificateTimestamp, leaf ct.MerkleTreeLeaf) error {
   120  	leaf.TimestampedEntry.Timestamp = sct.Timestamp
   121  	if err := li.Verifier.VerifySCTSignature(sct, ct.LogEntry{Leaf: leaf}); err != nil {
   122  		return fmt.Errorf("failed to verify SCT signature from log %q: %v", li.Description, err)
   123  	}
   124  	return nil
   125  }
   126  
   127  // VerifyInclusionLatest checks that the given Merkle tree leaf, adjusted for the provided timestamp,
   128  // is present in the latest known tree size of the log.  If no tree size for the log is known, it will
   129  // be queried.  On success, returns the index of the leaf in the log.
   130  func (li *LogInfo) VerifyInclusionLatest(ctx context.Context, leaf ct.MerkleTreeLeaf, timestamp uint64) (int64, error) {
   131  	sth := li.LastSTH()
   132  	if sth == nil {
   133  		var err error
   134  		sth, err = li.Client.GetSTH(ctx)
   135  		if err != nil {
   136  			return -1, fmt.Errorf("failed to get current STH for %q log: %v", li.Description, err)
   137  		}
   138  		li.SetSTH(sth)
   139  	}
   140  	return li.VerifyInclusionAt(ctx, leaf, timestamp, sth.TreeSize, sth.SHA256RootHash[:])
   141  }
   142  
   143  // VerifyInclusion checks that the given Merkle tree leaf, adjusted for the provided timestamp,
   144  // is present in the current tree size of the log.  On success, returns the index of the leaf
   145  // in the log.
   146  func (li *LogInfo) VerifyInclusion(ctx context.Context, leaf ct.MerkleTreeLeaf, timestamp uint64) (int64, error) {
   147  	sth, err := li.Client.GetSTH(ctx)
   148  	if err != nil {
   149  		return -1, fmt.Errorf("failed to get current STH for %q log: %v", li.Description, err)
   150  	}
   151  	li.SetSTH(sth)
   152  	return li.VerifyInclusionAt(ctx, leaf, timestamp, sth.TreeSize, sth.SHA256RootHash[:])
   153  }
   154  
   155  // VerifyInclusionAt checks that the given Merkle tree leaf, adjusted for the provided timestamp,
   156  // is present in the given tree size & root hash of the log. On success, returns the index of the
   157  // leaf in the log.
   158  func (li *LogInfo) VerifyInclusionAt(ctx context.Context, leaf ct.MerkleTreeLeaf, timestamp, treeSize uint64, rootHash []byte) (int64, error) {
   159  	leaf.TimestampedEntry.Timestamp = timestamp
   160  	leafHash, err := ct.LeafHashForLeaf(&leaf)
   161  	if err != nil {
   162  		return -1, fmt.Errorf("failed to create leaf hash: %v", err)
   163  	}
   164  
   165  	rsp, err := li.Client.GetProofByHash(ctx, leafHash[:], treeSize)
   166  	if err != nil {
   167  		return -1, fmt.Errorf("failed to GetProofByHash(sct,size=%d): %v", treeSize, err)
   168  	}
   169  
   170  	if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(rsp.LeafIndex), treeSize, leafHash[:], rsp.AuditPath, rootHash); err != nil {
   171  		return -1, fmt.Errorf("failed to verify inclusion proof at size %d: %v", treeSize, err)
   172  	}
   173  	return rsp.LeafIndex, nil
   174  }
   175  

View as plain text