...

Source file src/github.com/google/certificate-transparency-go/trillian/ctfe/sth.go

Documentation: github.com/google/certificate-transparency-go/trillian/ctfe

     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 ctfe
    16  
    17  import (
    18  	"context"
    19  	"crypto/sha256"
    20  	"errors"
    21  	"fmt"
    22  
    23  	ct "github.com/google/certificate-transparency-go"
    24  	"github.com/google/trillian"
    25  	"github.com/google/trillian/types"
    26  	"google.golang.org/protobuf/encoding/prototext"
    27  	"k8s.io/klog/v2"
    28  )
    29  
    30  type contextKey string
    31  
    32  // remoteQuotaCtxKey is the key used to attach a Trillian quota user to
    33  // context.Context passed in to STH getters.
    34  var remoteQuotaCtxKey = contextKey("quotaUser")
    35  
    36  // MirrorSTHStorage provides STHs of a source log to be served from a mirror.
    37  type MirrorSTHStorage interface {
    38  	// GetMirrorSTH returns an STH of TreeSize <= maxTreeSize. It does best
    39  	// effort to maximize the returned STH's TreeSize and/or Timestamp.
    40  	GetMirrorSTH(ctx context.Context, maxTreeSize int64) (*ct.SignedTreeHead, error)
    41  }
    42  
    43  // STHGetter provides latest STHs for a log.
    44  type STHGetter interface {
    45  	// GetSTH returns the latest STH for the log, as required by the RFC-6962
    46  	// get-sth endpoint: https://tools.ietf.org/html/rfc6962#section-4.3.
    47  	GetSTH(ctx context.Context) (*ct.SignedTreeHead, error)
    48  }
    49  
    50  // FrozenSTHGetter is an STHGetter implementation returning a constant STH.
    51  type FrozenSTHGetter struct {
    52  	sth *ct.SignedTreeHead
    53  }
    54  
    55  // GetSTH returns the frozen STH.
    56  func (sg *FrozenSTHGetter) GetSTH(ctx context.Context) (*ct.SignedTreeHead, error) {
    57  	return sg.sth, nil
    58  }
    59  
    60  // LogSTHGetter is an STHGetter implementation for regular (non-mirror) logs,
    61  // i.e. logs that have their own key and actively sign STHs.
    62  type LogSTHGetter struct {
    63  	li    *logInfo
    64  	cache SignatureCache
    65  }
    66  
    67  // GetSTH retrieves and builds a tree head structure for the given log.
    68  // nolint:staticcheck
    69  func (sg *LogSTHGetter) GetSTH(ctx context.Context) (*ct.SignedTreeHead, error) {
    70  	currentRoot, err := getSignedLogRoot(ctx, sg.li.rpcClient, sg.li.logID, sg.li.LogPrefix)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	// Build the CT STH object, except the signature.
    76  	sth := &ct.SignedTreeHead{
    77  		Version:   ct.V1,
    78  		TreeSize:  uint64(currentRoot.TreeSize),
    79  		Timestamp: uint64(currentRoot.TimestampNanos / 1000 / 1000),
    80  	}
    81  	// Note: The size was checked in getSignedLogRoot.
    82  	copy(sth.SHA256RootHash[:], currentRoot.RootHash)
    83  
    84  	// Add the signature over the STH contents.
    85  	err = signV1TreeHead(sg.li.signer, sth, &sg.cache)
    86  	if err != nil || len(sth.TreeHeadSignature.Signature) == 0 {
    87  		return nil, fmt.Errorf("failed to sign tree head: %v", err)
    88  	}
    89  
    90  	return sth, nil
    91  }
    92  
    93  // MirrorSTHGetter is an STHGetter implementation for mirror logs. It assumes
    94  // no knowledge of the key, and returns STHs obtained from an external source
    95  // represented by the MirrorSTHStorage interface.
    96  type MirrorSTHGetter struct {
    97  	li *logInfo
    98  	st MirrorSTHStorage
    99  }
   100  
   101  // GetSTH returns a known source log's STH with as large TreeSize and/or
   102  // timestamp as possible, but such that TreeSize <= Trillian log size. This is
   103  // to ensure that the mirror doesn't expose a "future" state of the log before
   104  // it is properly stored in Trillian.
   105  func (sg *MirrorSTHGetter) GetSTH(ctx context.Context) (*ct.SignedTreeHead, error) {
   106  	currentRoot, err := getSignedLogRoot(ctx, sg.li.rpcClient, sg.li.logID, sg.li.LogPrefix)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	sth, err := sg.st.GetMirrorSTH(ctx, int64(currentRoot.TreeSize)) // nolint:staticcheck
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	// TODO(pavelkalinnikov): Check sth signature.
   116  	// TODO(pavelkalinnikov): Check consistency between slr and sth.
   117  	return sth, nil
   118  }
   119  
   120  // getSignedLogRoot obtains the latest LogRootV1 from Trillian log.
   121  // nolint:staticcheck
   122  func getSignedLogRoot(ctx context.Context, client trillian.TrillianLogClient, logID int64, prefix string) (*types.LogRootV1, error) {
   123  	req := trillian.GetLatestSignedLogRootRequest{LogId: logID}
   124  	if q := ctx.Value(remoteQuotaCtxKey); q != nil {
   125  		quotaUser, ok := q.(string)
   126  		if !ok {
   127  			return nil, fmt.Errorf("incorrect quota value: %v, type %T", q, q)
   128  		}
   129  		req.ChargeTo = appendUserCharge(req.ChargeTo, quotaUser)
   130  	}
   131  
   132  	klog.V(2).Infof("%s: GetSTH => grpc.GetLatestSignedLogRoot %+v", prefix, prototext.Format(&req))
   133  	rsp, err := client.GetLatestSignedLogRoot(ctx, &req)
   134  	klog.V(2).Infof("%s: GetSTH <= grpc.GetLatestSignedLogRoot err=%v", prefix, err)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	// Check over the response.
   140  	slr := rsp.SignedLogRoot
   141  	if slr == nil {
   142  		return nil, errors.New("no log root returned")
   143  	}
   144  	klog.V(3).Infof("%s: GetSTH <= slr=%+v", prefix, slr)
   145  	var currentRoot types.LogRootV1
   146  	if err := currentRoot.UnmarshalBinary(slr.GetLogRoot()); err != nil {
   147  		return nil, fmt.Errorf("failed to unmarshal root: %v", slr)
   148  	}
   149  	if hashSize := len(currentRoot.RootHash); hashSize != sha256.Size {
   150  		return nil, fmt.Errorf("bad hash size from backend expecting: %d got %d", sha256.Size, hashSize)
   151  	}
   152  
   153  	return &currentRoot, nil
   154  }
   155  
   156  // DefaultMirrorSTHFactory creates DefaultMirrorSTHStorage instances.
   157  type DefaultMirrorSTHFactory struct{}
   158  
   159  // NewStorage creates a dummy STH storage.
   160  func (f DefaultMirrorSTHFactory) NewStorage(logID [sha256.Size]byte) (MirrorSTHStorage, error) {
   161  	return DefaultMirrorSTHStorage{}, nil
   162  }
   163  
   164  // DefaultMirrorSTHStorage is a dummy STH storage that always returns an error.
   165  type DefaultMirrorSTHStorage struct{}
   166  
   167  // GetMirrorSTH returns an error.
   168  func (st DefaultMirrorSTHStorage) GetMirrorSTH(ctx context.Context, maxTreeSize int64) (*ct.SignedTreeHead, error) {
   169  	return nil, errors.New("not implemented")
   170  }
   171  

View as plain text