1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ctfe
16
17 import (
18 "context"
19 "crypto"
20 "errors"
21 "io"
22 "reflect"
23 "strings"
24 "testing"
25
26 "github.com/golang/mock/gomock"
27 ct "github.com/google/certificate-transparency-go"
28 "github.com/google/certificate-transparency-go/tls"
29 "github.com/google/certificate-transparency-go/trillian/mockclient"
30 "github.com/google/trillian"
31 "github.com/google/trillian/types"
32 )
33
34 type testCase struct {
35 desc string
36 ctxSetup func(ctx context.Context) context.Context
37 ms MirrorSTHStorage
38 slr *trillian.GetLatestSignedLogRootResponse
39 slrErr error
40 sig []byte
41 sigErr error
42 wantSTH *ct.SignedTreeHead
43 errStr string
44 }
45
46
47
48 func commonTests(t *testing.T) []testCase {
49 t.Helper()
50 return []testCase{
51 {
52 desc: "bad quota value",
53 ctxSetup: func(ctx context.Context) context.Context {
54 return context.WithValue(ctx, remoteQuotaCtxKey, []byte("not a string value"))
55 },
56 errStr: "incorrect quota",
57 },
58 {
59 desc: "latest root RPC fails",
60 slrErr: errors.New("slr failed"),
61 errStr: "slr failed",
62 },
63 {
64 desc: "nil slr",
65 slr: &trillian.GetLatestSignedLogRootResponse{},
66 errStr: "no log root",
67 },
68 {
69 desc: "bad slr",
70 slr: &trillian.GetLatestSignedLogRootResponse{SignedLogRoot: &trillian.SignedLogRoot{LogRoot: []byte("not tls encoded")}},
71 errStr: "unmarshal root: log_root:\"not tls",
72 },
73 {
74 desc: "bad hash",
75 slr: &trillian.GetLatestSignedLogRootResponse{
76 SignedLogRoot: mustMarshalRoot(t,
77 &types.LogRootV1{RootHash: []byte("not a 32 byte hash")}),
78 },
79 errStr: "bad hash size",
80 },
81 }
82 }
83
84
85 func logTests(t *testing.T) []testCase {
86 t.Helper()
87 return []testCase{
88 {
89 desc: "signer error",
90 slr: &trillian.GetLatestSignedLogRootResponse{
91 SignedLogRoot: mustMarshalRoot(t,
92 &types.LogRootV1{RootHash: []byte("12345678123456781234567812345678")}),
93 },
94 sigErr: errors.New("not signing that"),
95 errStr: "sign tree head: not signing",
96 },
97 {
98 desc: "empty sig",
99 slr: &trillian.GetLatestSignedLogRootResponse{
100 SignedLogRoot: mustMarshalRoot(t,
101 &types.LogRootV1{RootHash: []byte("12345678123456781234567812345678")}),
102 },
103 sig: []byte{},
104 errStr: "sign tree head: <nil>",
105 },
106 {
107 desc: "ok",
108 slr: &trillian.GetLatestSignedLogRootResponse{
109 SignedLogRoot: mustMarshalRoot(t,
110 &types.LogRootV1{
111
112 TreeSize: 12345,
113 TimestampNanos: 987654321,
114 RootHash: []byte("12345678123456781234567812345678")}),
115 },
116 sig: []byte("signedit"),
117 wantSTH: &ct.SignedTreeHead{
118 Timestamp: 987,
119 SHA256RootHash: hashFromString("12345678123456781234567812345678"),
120 TreeHeadSignature: ct.DigitallySigned{
121 Algorithm: tls.SignatureAndHashAlgorithm{
122 Hash: tls.SHA256,
123 Signature: tls.SignatureAlgorithmFromPubKey(tls.Anonymous),
124 },
125 Signature: []byte("signedit"),
126 },
127 TreeSize: 12345,
128 },
129 },
130 }
131 }
132
133
134 func mirrorTests(t *testing.T) []testCase {
135 t.Helper()
136 return []testCase{
137 {
138 desc: "bad mirror storage",
139 ms: &fakeMirrorSTHStorage{
140 err: errors.New("mirror store failed"),
141 },
142 slr: &trillian.GetLatestSignedLogRootResponse{
143 SignedLogRoot: mustMarshalRoot(t,
144 &types.LogRootV1{
145
146 TreeSize: 12345,
147 TimestampNanos: 987654321,
148 RootHash: []byte("12345678123456781234567812345678")}),
149 },
150 errStr: "mirror store failed",
151 },
152 {
153 desc: "ok",
154 ms: &fakeMirrorSTHStorage{
155 sth: &ct.SignedTreeHead{
156 Timestamp: 987,
157 SHA256RootHash: hashFromString("12345678123456781234567812345678"),
158 TreeHeadSignature: ct.DigitallySigned{
159 Algorithm: tls.SignatureAndHashAlgorithm{
160 Hash: tls.SHA256,
161 Signature: tls.SignatureAlgorithmFromPubKey(tls.Anonymous),
162 },
163 Signature: []byte("signedit"),
164 },
165 TreeSize: 12345,
166 },
167 },
168 slr: &trillian.GetLatestSignedLogRootResponse{
169 SignedLogRoot: mustMarshalRoot(t,
170 &types.LogRootV1{
171
172 TreeSize: 12345,
173 TimestampNanos: 987654321,
174 RootHash: []byte("12345678123456781234567812345678")}),
175 },
176 wantSTH: &ct.SignedTreeHead{
177 Timestamp: 987,
178 SHA256RootHash: hashFromString("12345678123456781234567812345678"),
179 TreeHeadSignature: ct.DigitallySigned{
180 Algorithm: tls.SignatureAndHashAlgorithm{
181 Hash: tls.SHA256,
182 Signature: tls.SignatureAlgorithmFromPubKey(tls.Anonymous),
183 },
184 Signature: []byte("signedit"),
185 },
186 TreeSize: 12345,
187 },
188 },
189 }
190 }
191
192 func TestLogSTHGetter(t *testing.T) {
193
194
195 tests := make([]testCase, 0, 30)
196 tests = append(tests, commonTests(t)...)
197 tests = append(tests, logTests(t)...)
198
199 for _, tc := range tests {
200 t.Run(tc.desc, func(t *testing.T) {
201 ctrl := gomock.NewController(t)
202 rpcCl := mockclient.NewMockTrillianLogClient(ctrl)
203 if tc.slr != nil || tc.slrErr != nil {
204 rpcCl.EXPECT().GetLatestSignedLogRoot(gomock.Any(), cmpMatcher{&trillian.GetLatestSignedLogRootRequest{LogId: 99}}).Return(tc.slr, tc.slrErr)
205 }
206
207 sthg := LogSTHGetter{li: &logInfo{rpcClient: rpcCl, logID: 99, signer: &fakeSigner{sig: tc.sig, err: tc.sigErr}}}
208 ctx := context.Background()
209 if tc.ctxSetup != nil {
210 ctx = tc.ctxSetup(ctx)
211 }
212
213 sth, err := sthg.GetSTH(ctx)
214 if len(tc.errStr) > 0 {
215 if err == nil || !strings.Contains(err.Error(), tc.errStr) {
216 t.Errorf("GetSTH()=%v, %v want: nil, err containing %s", sth, err, tc.errStr)
217 }
218 } else {
219 if err != nil || !reflect.DeepEqual(sth, tc.wantSTH) {
220 t.Errorf("GetSTH()=%v, %v, want: %v, nil", sth, err, tc.wantSTH)
221 }
222 }
223 ctrl.Finish()
224 })
225 }
226 }
227
228 func TestMirrorSTHGetter(t *testing.T) {
229
230
231 tests := make([]testCase, 0, 30)
232 tests = append(tests, commonTests(t)...)
233 tests = append(tests, mirrorTests(t)...)
234
235 for _, tc := range tests {
236 t.Run(tc.desc, func(t *testing.T) {
237 ctrl := gomock.NewController(t)
238 rpcCl := mockclient.NewMockTrillianLogClient(ctrl)
239 if tc.slr != nil || tc.slrErr != nil {
240 rpcCl.EXPECT().GetLatestSignedLogRoot(gomock.Any(), cmpMatcher{&trillian.GetLatestSignedLogRootRequest{LogId: 99}}).Return(tc.slr, tc.slrErr)
241 }
242
243 sthg := MirrorSTHGetter{li: &logInfo{rpcClient: rpcCl, logID: 99}, st: tc.ms}
244 ctx := context.Background()
245 if tc.ctxSetup != nil {
246 ctx = tc.ctxSetup(ctx)
247 }
248
249 sth, err := sthg.GetSTH(ctx)
250 if len(tc.errStr) > 0 {
251 if err == nil || !strings.Contains(err.Error(), tc.errStr) {
252 t.Errorf("GetSTH()=%v, %v want: nil, err containing %s", sth, err, tc.errStr)
253 }
254 } else {
255 if err != nil || !reflect.DeepEqual(sth, tc.wantSTH) {
256 t.Errorf("GetSTH()=%v, %v, want: %v, nil", sth, err, tc.wantSTH)
257 }
258 }
259 ctrl.Finish()
260 })
261 }
262 }
263
264 func TestFrozenSTHGetter(t *testing.T) {
265 sth := &ct.SignedTreeHead{TreeSize: 123, Version: 1}
266 f := FrozenSTHGetter{sth: sth}
267
268 if sth2, err := f.GetSTH(context.Background()); sth2 != sth || err != nil {
269 t.Fatalf("FrozenSTHGetter.GetSTH()=%v, %v, want: %v, nil", sth2, err, sth)
270 }
271 }
272
273 func TestDefaultMirrorSTHStorage(t *testing.T) {
274 s, err := DefaultMirrorSTHFactory{}.NewStorage([32]byte{})
275 if err != nil {
276 t.Fatalf("NewStorage()=%v, %v, want: no err", s, err)
277 }
278
279 sth, err := s.GetMirrorSTH(context.Background(), 9999)
280 if sth != nil || err == nil || !strings.Contains(err.Error(), "not impl") {
281 t.Fatalf("MirrorSTHStorage.GetMirrorSTH(): got: %v, %v, want: nil, err containing 'not impl'", sth, err)
282 }
283 }
284
285 type fakeSigner struct {
286 sig []byte
287 err error
288 }
289
290 func (f *fakeSigner) Public() crypto.PublicKey {
291 return []byte("this key is public")
292 }
293
294 func (f *fakeSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
295 return f.sig, f.err
296 }
297
298 type fakeMirrorSTHStorage struct {
299 sth *ct.SignedTreeHead
300 err error
301 }
302
303 func (f *fakeMirrorSTHStorage) GetMirrorSTH(ctx context.Context, maxTreeSize int64) (*ct.SignedTreeHead, error) {
304 return f.sth, f.err
305 }
306
307 func hashFromString(str string) [32]byte {
308 var hash = [32]byte{}
309 copy(hash[:], []byte(str))
310 return hash
311 }
312
View as plain text