1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ctfe
16
17 import (
18 "crypto/x509"
19 "encoding/base64"
20 "encoding/pem"
21 "fmt"
22 "os"
23 "strings"
24 "testing"
25
26 "github.com/google/certificate-transparency-go/trillian/ctfe/configpb"
27 "github.com/google/trillian/crypto/keyspb"
28 "google.golang.org/protobuf/proto"
29 "google.golang.org/protobuf/types/known/anypb"
30 "google.golang.org/protobuf/types/known/timestamppb"
31 )
32
33 var (
34 invalidTimestamp = ×tamppb.Timestamp{Nanos: int32(1e9)}
35
36
37 validSTH = &configpb.SignedTreeHead{
38 TreeSize: 766,
39 Timestamp: 1538659276115,
40 Sha256RootHash: mustDecodeBase64("rMWSvrYQ+n6kAmU6sMJuVV5LjoKBGK2OL719X5a+T9Y="),
41 TreeHeadSignature: mustDecodeBase64("BAMASDBGAiEApo+OIdPXIVEwdnS1v5Iu1gQHaiWmCY73h28zfKMmHrYCIQD1U6qj1mKBXYIQVP52YCdaxdHJH4jDsL1JlaA+J/MqCw=="),
42 }
43 )
44
45 func mustMarshalAny(pb proto.Message) *anypb.Any {
46 ret, err := anypb.New(pb)
47 if err != nil {
48 panic(fmt.Sprintf("MarshalAny failed: %v", err))
49 }
50 return ret
51 }
52
53 func mustReadPublicKey(path string) *keyspb.PublicKey {
54 keyPEM, err := os.ReadFile(path)
55 if err != nil {
56 panic(fmt.Sprintf("os.ReadFile(%q): %v", path, err))
57 }
58 block, _ := pem.Decode(keyPEM)
59 pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
60 if err != nil {
61 panic(fmt.Sprintf("ReadPublicKeyFile(): %v", err))
62 }
63 keyDER, err := x509.MarshalPKIXPublicKey(pubKey)
64 if err != nil {
65 panic(fmt.Sprintf("x509.MarshalPKIXPublicKey(): %v", err))
66 }
67 return &keyspb.PublicKey{Der: keyDER}
68 }
69
70 func mustDecodeBase64(str string) []byte {
71 data, err := base64.StdEncoding.DecodeString(str)
72 if err != nil {
73 panic(fmt.Sprintf("base64: DecodeString failed: %v", err))
74 }
75 return data
76 }
77
78 func TestValidateLogConfig(t *testing.T) {
79 pubKey := mustReadPublicKey("../testdata/ct-http-server.pubkey.pem")
80 privKey := mustMarshalAny(&keyspb.PEMKeyFile{Path: "../testdata/ct-http-server.privkey.pem", Password: "dirk"})
81
82 corruptedSTH := proto.Clone(validSTH).(*configpb.SignedTreeHead)
83 corruptedSTH.TreeSize = 1234
84
85 for _, tc := range []struct {
86 desc string
87 cfg *configpb.LogConfig
88 wantErr string
89 }{
90 {
91 desc: "empty-log-ID",
92 wantErr: "empty log ID",
93 cfg: &configpb.LogConfig{},
94 },
95 {
96 desc: "empty-public-key-mirror",
97 wantErr: "empty public key for mirror",
98 cfg: &configpb.LogConfig{LogId: 123, IsMirror: true},
99 },
100 {
101 desc: "empty-public-key-frozen",
102 wantErr: "empty public key for frozen STH",
103 cfg: &configpb.LogConfig{LogId: 123, FrozenSth: &configpb.SignedTreeHead{}},
104 },
105 {
106 desc: "invalid-public-key-empty",
107 wantErr: "x509.ParsePKIXPublicKey",
108 cfg: &configpb.LogConfig{
109 LogId: 123,
110 PublicKey: &keyspb.PublicKey{},
111 IsMirror: true,
112 },
113 },
114 {
115 desc: "invalid-public-key-abacaba",
116 wantErr: "x509.ParsePKIXPublicKey",
117 cfg: &configpb.LogConfig{
118 LogId: 123,
119 PublicKey: &keyspb.PublicKey{Der: []byte("abacaba")},
120 IsMirror: true,
121 },
122 },
123 {
124 desc: "empty-private-key",
125 wantErr: "empty private key",
126 cfg: &configpb.LogConfig{LogId: 123},
127 },
128 {
129 desc: "invalid-private-key",
130 wantErr: "invalid private key",
131 cfg: &configpb.LogConfig{
132 LogId: 123,
133 PrivateKey: &anypb.Any{},
134 },
135 },
136 {
137 desc: "unnecessary-private-key",
138 wantErr: "unnecessary private key",
139 cfg: &configpb.LogConfig{
140 LogId: 123,
141 PublicKey: pubKey,
142 PrivateKey: privKey,
143 IsMirror: true,
144 },
145 },
146 {
147 desc: "rejecting-all",
148 wantErr: "rejecting all certificates",
149 cfg: &configpb.LogConfig{
150 LogId: 123,
151 RejectExpired: true,
152 RejectUnexpired: true,
153 PrivateKey: privKey,
154 },
155 },
156 {
157 desc: "unknown-ext-key-usage-1",
158 wantErr: "unknown extended key usage",
159 cfg: &configpb.LogConfig{
160 LogId: 123,
161 PrivateKey: privKey,
162 ExtKeyUsages: []string{"wrong_usage"},
163 },
164 },
165 {
166 desc: "unknown-ext-key-usage-2",
167 wantErr: "unknown extended key usage",
168 cfg: &configpb.LogConfig{
169 LogId: 123,
170 PrivateKey: privKey,
171 ExtKeyUsages: []string{"ClientAuth", "ServerAuth", "TimeStomping"},
172 },
173 },
174 {
175 desc: "unknown-ext-key-usage-3",
176 wantErr: "unknown extended key usage",
177 cfg: &configpb.LogConfig{
178 LogId: 123,
179 PrivateKey: privKey,
180 ExtKeyUsages: []string{"Any "},
181 },
182 },
183 {
184 desc: "invalid-start-timestamp",
185 wantErr: "invalid start timestamp",
186 cfg: &configpb.LogConfig{
187 LogId: 123,
188 PrivateKey: privKey,
189 NotAfterStart: invalidTimestamp,
190 },
191 },
192 {
193 desc: "invalid-limit-timestamp",
194 wantErr: "invalid limit timestamp",
195 cfg: &configpb.LogConfig{
196 LogId: 123,
197 PrivateKey: privKey,
198 NotAfterLimit: invalidTimestamp,
199 },
200 },
201 {
202 desc: "limit-before-start",
203 wantErr: "limit before start",
204 cfg: &configpb.LogConfig{
205 LogId: 123,
206 PrivateKey: privKey,
207 NotAfterStart: ×tamppb.Timestamp{Seconds: 200},
208 NotAfterLimit: ×tamppb.Timestamp{Seconds: 100},
209 },
210 },
211 {
212 desc: "negative-maximum-merge",
213 wantErr: "negative maximum merge",
214 cfg: &configpb.LogConfig{
215 LogId: 123,
216 PrivateKey: privKey,
217 MaxMergeDelaySec: -100,
218 },
219 },
220 {
221 desc: "negative-expected-merge",
222 wantErr: "negative expected merge",
223 cfg: &configpb.LogConfig{
224 LogId: 123,
225 PrivateKey: privKey,
226 ExpectedMergeDelaySec: -100,
227 },
228 },
229 {
230 desc: "expected-exceeds-max",
231 wantErr: "expected merge delay exceeds MMD",
232 cfg: &configpb.LogConfig{
233 LogId: 123,
234 PrivateKey: privKey,
235 MaxMergeDelaySec: 50,
236 ExpectedMergeDelaySec: 100,
237 },
238 },
239 {
240 desc: "invalid-frozen-STH",
241 wantErr: "invalid frozen STH",
242 cfg: &configpb.LogConfig{
243 LogId: 123,
244 PublicKey: pubKey,
245 IsMirror: true,
246 FrozenSth: &configpb.SignedTreeHead{
247 TreeSize: 10,
248 Timestamp: 100500,
249 },
250 },
251 },
252 {
253 desc: "signature-verification-failed",
254 wantErr: "signature verification failed",
255 cfg: &configpb.LogConfig{
256 LogId: 123,
257 PublicKey: pubKey,
258 PrivateKey: privKey,
259 FrozenSth: corruptedSTH,
260 },
261 },
262 {
263 desc: "ok",
264 cfg: &configpb.LogConfig{
265 LogId: 123,
266 PrivateKey: privKey,
267 },
268 },
269 {
270
271
272
273
274 desc: "ok-not-a-key",
275 cfg: &configpb.LogConfig{
276 LogId: 123,
277 PrivateKey: mustMarshalAny(&configpb.LogConfig{}),
278 },
279 },
280 {
281 desc: "ok-mirror",
282 cfg: &configpb.LogConfig{
283 LogId: 123,
284 PublicKey: pubKey,
285 IsMirror: true,
286 },
287 },
288 {
289 desc: "ok-ext-key-usages",
290 cfg: &configpb.LogConfig{
291 LogId: 123,
292 PrivateKey: privKey,
293 ExtKeyUsages: []string{"ServerAuth", "ClientAuth", "OCSPSigning"},
294 },
295 },
296 {
297 desc: "ok-start-timestamp",
298 cfg: &configpb.LogConfig{
299 LogId: 123,
300 PrivateKey: privKey,
301 NotAfterStart: ×tamppb.Timestamp{Seconds: 100},
302 },
303 },
304 {
305 desc: "ok-limit-timestamp",
306 cfg: &configpb.LogConfig{
307 LogId: 123,
308 PrivateKey: privKey,
309 NotAfterLimit: ×tamppb.Timestamp{Seconds: 200},
310 },
311 },
312 {
313 desc: "ok-range-timestamp",
314 cfg: &configpb.LogConfig{
315 LogId: 123,
316 PrivateKey: privKey,
317 NotAfterStart: ×tamppb.Timestamp{Seconds: 300},
318 NotAfterLimit: ×tamppb.Timestamp{Seconds: 400},
319 },
320 },
321 {
322 desc: "ok-merge-delay",
323 cfg: &configpb.LogConfig{
324 LogId: 123,
325 PrivateKey: privKey,
326 MaxMergeDelaySec: 86400,
327 ExpectedMergeDelaySec: 7200,
328 },
329 },
330 {
331 desc: "ok-frozen-STH",
332 cfg: &configpb.LogConfig{
333 LogId: 123,
334 PublicKey: pubKey,
335 PrivateKey: privKey,
336 FrozenSth: validSTH,
337 },
338 },
339 } {
340 t.Run(tc.desc, func(t *testing.T) {
341 vc, err := ValidateLogConfig(tc.cfg)
342 if len(tc.wantErr) == 0 && err != nil {
343 t.Errorf("ValidateLogConfig()=%v, want nil", err)
344 }
345 if len(tc.wantErr) > 0 && (err == nil || !strings.Contains(err.Error(), tc.wantErr)) {
346 t.Errorf("ValidateLogConfig()=%v, want err containing %q", err, tc.wantErr)
347 }
348 if err == nil && vc == nil {
349 t.Error("err and ValidatedLogConfig are both nil")
350 }
351
352 })
353 }
354 }
355
356 func TestValidateLogMultiConfig(t *testing.T) {
357 privKey := mustMarshalAny(&keyspb.PEMKeyFile{Path: "../testdata/ct-http-server.privkey.pem", Password: "dirk"})
358 for _, tc := range []struct {
359 desc string
360 cfg *configpb.LogMultiConfig
361 wantErr string
362 }{
363 {
364 desc: "empty-backend-name",
365 wantErr: "empty backend name",
366 cfg: &configpb.LogMultiConfig{
367 Backends: &configpb.LogBackendSet{
368 Backend: []*configpb.LogBackend{
369 {BackendSpec: "testspec"},
370 },
371 },
372 },
373 },
374 {
375 desc: "empty-backend-spec",
376 wantErr: "empty backend spec",
377 cfg: &configpb.LogMultiConfig{
378 Backends: &configpb.LogBackendSet{
379 Backend: []*configpb.LogBackend{
380 {Name: "log1"},
381 },
382 },
383 },
384 },
385 {
386 desc: "duplicate-backend-name",
387 wantErr: "duplicate backend name",
388 cfg: &configpb.LogMultiConfig{
389 Backends: &configpb.LogBackendSet{
390 Backend: []*configpb.LogBackend{
391 {Name: "dup", BackendSpec: "testspec"},
392 {Name: "dup", BackendSpec: "testspec"},
393 },
394 },
395 },
396 },
397 {
398 desc: "duplicate-backend-spec",
399 wantErr: "duplicate backend spec",
400 cfg: &configpb.LogMultiConfig{
401 Backends: &configpb.LogBackendSet{
402 Backend: []*configpb.LogBackend{
403 {Name: "log1", BackendSpec: "testspec"},
404 {Name: "log2", BackendSpec: "testspec"},
405 },
406 },
407 },
408 },
409 {
410 desc: "invalid-log-config",
411 wantErr: "log config: empty log ID",
412 cfg: &configpb.LogMultiConfig{
413 Backends: &configpb.LogBackendSet{
414 Backend: []*configpb.LogBackend{
415 {Name: "log1", BackendSpec: "testspec"},
416 },
417 },
418 LogConfigs: &configpb.LogConfigSet{
419 Config: []*configpb.LogConfig{
420 {Prefix: "pref"},
421 },
422 },
423 },
424 },
425 {
426 desc: "empty-prefix",
427 wantErr: "empty prefix",
428 cfg: &configpb.LogMultiConfig{
429 Backends: &configpb.LogBackendSet{
430 Backend: []*configpb.LogBackend{
431 {Name: "log1", BackendSpec: "testspec"},
432 },
433 },
434 LogConfigs: &configpb.LogConfigSet{
435 Config: []*configpb.LogConfig{
436 {LogId: 1, PrivateKey: privKey, LogBackendName: "log1"},
437 },
438 },
439 },
440 },
441 {
442 desc: "duplicate-prefix",
443 wantErr: "duplicate prefix",
444 cfg: &configpb.LogMultiConfig{
445 Backends: &configpb.LogBackendSet{
446 Backend: []*configpb.LogBackend{
447 {Name: "log1", BackendSpec: "testspec1"},
448 },
449 },
450 LogConfigs: &configpb.LogConfigSet{
451 Config: []*configpb.LogConfig{
452 {LogId: 1, Prefix: "pref1", PrivateKey: privKey, LogBackendName: "log1"},
453 {LogId: 2, Prefix: "pref2", PrivateKey: privKey, LogBackendName: "log1"},
454 {LogId: 3, Prefix: "pref1", PrivateKey: privKey, LogBackendName: "log1"},
455 },
456 },
457 },
458 },
459 {
460 desc: "references-undefined-backend",
461 wantErr: "references undefined backend",
462 cfg: &configpb.LogMultiConfig{
463 Backends: &configpb.LogBackendSet{
464 Backend: []*configpb.LogBackend{
465 {Name: "log1", BackendSpec: "testspec"},
466 },
467 },
468 LogConfigs: &configpb.LogConfigSet{
469 Config: []*configpb.LogConfig{
470 {LogId: 2, Prefix: "pref2", PrivateKey: privKey, LogBackendName: "log2"},
471 },
472 },
473 },
474 },
475 {
476 desc: "dup-tree-id-on-same-backend",
477 wantErr: "dup tree id",
478 cfg: &configpb.LogMultiConfig{
479 Backends: &configpb.LogBackendSet{
480 Backend: []*configpb.LogBackend{
481 {Name: "log1", BackendSpec: "testspec1"},
482 },
483 },
484 LogConfigs: &configpb.LogConfigSet{
485 Config: []*configpb.LogConfig{
486 {LogId: 1, Prefix: "pref1", PrivateKey: privKey, LogBackendName: "log1"},
487 {LogId: 2, Prefix: "pref2", PrivateKey: privKey, LogBackendName: "log1"},
488 {LogId: 1, Prefix: "pref3", PrivateKey: privKey, LogBackendName: "log1"},
489 },
490 },
491 },
492 },
493 {
494 desc: "ok-all-distinct",
495 cfg: &configpb.LogMultiConfig{
496 Backends: &configpb.LogBackendSet{
497 Backend: []*configpb.LogBackend{
498 {Name: "log1", BackendSpec: "testspec1"},
499 {Name: "log2", BackendSpec: "testspec2"},
500 {Name: "log3", BackendSpec: "testspec3"},
501 },
502 },
503 LogConfigs: &configpb.LogConfigSet{
504 Config: []*configpb.LogConfig{
505 {LogId: 1, Prefix: "pref1", PrivateKey: privKey, LogBackendName: "log1"},
506 {LogId: 2, Prefix: "pref2", PrivateKey: privKey, LogBackendName: "log2"},
507 {LogId: 3, Prefix: "pref3", PrivateKey: privKey, LogBackendName: "log3"},
508 },
509 },
510 },
511 },
512 {
513 desc: "ok-dup-tree-ids-on-different-backends",
514 cfg: &configpb.LogMultiConfig{
515 Backends: &configpb.LogBackendSet{
516 Backend: []*configpb.LogBackend{
517 {Name: "log1", BackendSpec: "testspec1"},
518 {Name: "log2", BackendSpec: "testspec2"},
519 {Name: "log3", BackendSpec: "testspec3"},
520 },
521 },
522 LogConfigs: &configpb.LogConfigSet{
523 Config: []*configpb.LogConfig{
524 {LogId: 1, Prefix: "pref1", PrivateKey: privKey, LogBackendName: "log1"},
525 {LogId: 1, Prefix: "pref2", PrivateKey: privKey, LogBackendName: "log2"},
526 {LogId: 1, Prefix: "pref3", PrivateKey: privKey, LogBackendName: "log3"},
527 },
528 },
529 },
530 },
531 } {
532 t.Run(tc.desc, func(t *testing.T) {
533 _, err := ValidateLogMultiConfig(tc.cfg)
534 if len(tc.wantErr) == 0 && err != nil {
535 t.Fatalf("ValidateLogMultiConfig()=%v, want nil", err)
536 }
537 if len(tc.wantErr) > 0 && (err == nil || !strings.Contains(err.Error(), tc.wantErr)) {
538 t.Errorf("ValidateLogMultiConfig()=%v, want err containing %q", err, tc.wantErr)
539 }
540 })
541 }
542 }
543
544 func TestToMultiLogConfig(t *testing.T) {
545
546
547
548
549 for _, tc := range []struct {
550 desc string
551 cfg []*configpb.LogConfig
552 want *configpb.LogMultiConfig
553 }{
554 {
555 desc: "ok-one-config",
556 cfg: []*configpb.LogConfig{
557 {LogId: 1, Prefix: "test"},
558 },
559 want: &configpb.LogMultiConfig{
560 Backends: &configpb.LogBackendSet{
561 Backend: []*configpb.LogBackend{{Name: "default", BackendSpec: "spec"}},
562 },
563 LogConfigs: &configpb.LogConfigSet{
564 Config: []*configpb.LogConfig{
565 {LogId: 1, Prefix: "test", LogBackendName: "default"},
566 },
567 },
568 },
569 },
570 {
571 desc: "ok-three-configs",
572 cfg: []*configpb.LogConfig{
573 {LogId: 1, Prefix: "test1"},
574 {LogId: 2, Prefix: "test2"},
575 {LogId: 3, Prefix: "test3"},
576 },
577 want: &configpb.LogMultiConfig{
578 Backends: &configpb.LogBackendSet{
579 Backend: []*configpb.LogBackend{{Name: "default", BackendSpec: "spec"}},
580 },
581 LogConfigs: &configpb.LogConfigSet{
582 Config: []*configpb.LogConfig{
583 {LogId: 1, Prefix: "test1", LogBackendName: "default"},
584 {LogId: 2, Prefix: "test2", LogBackendName: "default"},
585 {LogId: 3, Prefix: "test3", LogBackendName: "default"},
586 },
587 },
588 },
589 },
590 } {
591 t.Run(tc.desc, func(t *testing.T) {
592 got := ToMultiLogConfig(tc.cfg, "spec")
593 if !proto.Equal(got, tc.want) {
594 t.Errorf("TestToMultiLogConfig()=%v, want %v", got, tc.want)
595 }
596 })
597 }
598 }
599
View as plain text