-
Notifications
You must be signed in to change notification settings - Fork 247
/
Copy pathsth.go
170 lines (145 loc) · 5.83 KB
/
sth.go
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")
}