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