1 package client
2
3 import (
4 "bytes"
5 "crypto/ed25519"
6 "crypto/sha256"
7 "encoding/hex"
8 "encoding/json"
9 "errors"
10 "fmt"
11 "io"
12 "net"
13 "net/http"
14 "os"
15 "path/filepath"
16 "testing"
17 "time"
18
19 "github.com/secure-systems-lab/go-securesystemslib/cjson"
20 "github.com/stretchr/testify/assert"
21 tuf "github.com/theupdateframework/go-tuf"
22 "github.com/theupdateframework/go-tuf/data"
23 "github.com/theupdateframework/go-tuf/internal/sets"
24 "github.com/theupdateframework/go-tuf/pkg/keys"
25 "github.com/theupdateframework/go-tuf/sign"
26 "github.com/theupdateframework/go-tuf/util"
27 "github.com/theupdateframework/go-tuf/verify"
28 . "gopkg.in/check.v1"
29 )
30
31
32 func Test(t *testing.T) { TestingT(t) }
33
34 type ClientSuite struct {
35 store tuf.LocalStore
36 repo *tuf.Repo
37 local LocalStore
38 remote RemoteStore
39 expiredTime time.Time
40 keyIDs map[string][]string
41 useFileStore bool
42
43 tmpDir string
44 }
45
46 var _ = Suite(&ClientSuite{useFileStore: false})
47 var _ = Suite(&ClientSuite{useFileStore: true})
48
49 func newFakeRemoteStore() *fakeRemoteStore {
50 return &fakeRemoteStore{
51 meta: make(map[string]*fakeFile),
52 targets: make(map[string]*fakeFile),
53 }
54 }
55
56 type fakeRemoteStore struct {
57 meta map[string]*fakeFile
58 targets map[string]*fakeFile
59 }
60
61 func (f *fakeRemoteStore) GetMeta(name string) (io.ReadCloser, int64, error) {
62 return f.get(name, f.meta)
63 }
64
65 func (f *fakeRemoteStore) GetTarget(path string) (io.ReadCloser, int64, error) {
66 return f.get(path, f.targets)
67 }
68
69 func (f *fakeRemoteStore) get(name string, store map[string]*fakeFile) (io.ReadCloser, int64, error) {
70 file, ok := store[name]
71 if !ok {
72 return nil, 0, ErrNotFound{name}
73 }
74 return file, file.size, nil
75 }
76
77
78
79
80
81 func (s *ClientSuite) setRemoteMeta(path string, data []byte) error {
82 switch impl := s.remote.(type) {
83 case *fakeRemoteStore:
84 impl.meta[path] = newFakeFile(data)
85 return nil
86 case *FileRemoteStore:
87 return impl.addMeta(path, data)
88 default:
89 return fmt.Errorf("non-supoprted RemoteStore, got %+v", impl)
90 }
91 }
92
93 func (s *ClientSuite) setRemoteTarget(path string, data []byte) error {
94 switch impl := s.remote.(type) {
95 case *fakeRemoteStore:
96 impl.targets[path] = newFakeFile(data)
97 return nil
98 case *FileRemoteStore:
99 return impl.addTarget(path, data)
100 default:
101 return fmt.Errorf("non-supoprted RemoteStore, got %+v", impl)
102 }
103 }
104
105 func (s *ClientSuite) deleteMeta(path string) error {
106 switch impl := s.remote.(type) {
107 case *fakeRemoteStore:
108 delete(impl.meta, path)
109 return nil
110 case *FileRemoteStore:
111 return impl.deleteMeta(path)
112 default:
113 return fmt.Errorf("non-supported RemoteStore, got %+v", impl)
114 }
115 }
116
117 func (s *ClientSuite) deleteTarget(path string) error {
118 switch impl := s.remote.(type) {
119 case *fakeRemoteStore:
120 delete(impl.targets, path)
121 return nil
122 case *FileRemoteStore:
123 return impl.deleteTarget(path)
124 default:
125 return fmt.Errorf("non-supported RemoteStore, got %+v", impl)
126 }
127 }
128
129 func (s *ClientSuite) readMeta(name string) ([]byte, error) {
130 stream, _, err := s.remote.GetMeta(name)
131 if err != nil {
132 return nil, err
133 }
134 return io.ReadAll(stream)
135 }
136
137 func newFakeFile(b []byte) *fakeFile {
138 return &fakeFile{buf: bytes.NewReader(b), size: int64(len(b))}
139 }
140
141 type fakeFile struct {
142 buf *bytes.Reader
143 bytesRead int
144 size int64
145 }
146
147 func (f *fakeFile) Read(p []byte) (int, error) {
148 n, err := f.buf.Read(p)
149 f.bytesRead += n
150 return n, err
151 }
152
153 func (f *fakeFile) Close() error {
154 f.buf.Seek(0, io.SeekStart)
155 return nil
156 }
157
158 var targetFiles = map[string][]byte{
159 "foo.txt": []byte("foo"),
160 "bar.txt": []byte("bar"),
161 "baz.txt": []byte("baz"),
162 }
163
164 func (s *ClientSuite) SetUpTest(c *C) {
165 s.store = tuf.MemoryStore(nil, targetFiles)
166
167
168 var err error
169 s.repo, err = tuf.NewRepo(s.store)
170 c.Assert(err, IsNil)
171
172
173 c.Assert(s.repo.Init(false), IsNil)
174 s.keyIDs = map[string][]string{
175 "root": s.genKey(c, "root"),
176 "targets": s.genKey(c, "targets"),
177 "snapshot": s.genKey(c, "snapshot"),
178 "timestamp": s.genKey(c, "timestamp"),
179 }
180 c.Assert(s.repo.AddTarget("foo.txt", nil), IsNil)
181 c.Assert(s.repo.Snapshot(), IsNil)
182 c.Assert(s.repo.Timestamp(), IsNil)
183 c.Assert(s.repo.Commit(), IsNil)
184
185
186 if s.useFileStore {
187 s.remote, s.tmpDir, err = newTestFileStoreFS()
188 if err != nil {
189 c.Fatalf("failed to create new FileStore: %v", err)
190 }
191 } else {
192 s.remote = newFakeRemoteStore()
193 }
194 s.syncRemote(c)
195 for path, data := range targetFiles {
196 s.setRemoteTarget(path, data)
197 }
198
199 s.expiredTime = time.Now().Add(time.Hour)
200 }
201
202 func (s *ClientSuite) TearDownTest(c *C) {
203 if s.tmpDir != "" {
204 rmrf(s.tmpDir, c.Logf)
205 }
206 }
207
208 func (s *ClientSuite) genKey(c *C, role string) []string {
209 ids, err := s.repo.GenKey(role)
210 c.Assert(err, IsNil)
211 return ids
212 }
213
214 func (s *ClientSuite) genKeyExpired(c *C, role string) []string {
215 ids, err := s.repo.GenKeyWithExpires(role, s.expiredTime)
216 c.Assert(err, IsNil)
217 return ids
218 }
219
220
221
222
223 func (s *ClientSuite) withMetaExpired(f func()) {
224 e := verify.IsExpired
225 defer func() { verify.IsExpired = e }()
226 verify.IsExpired = func(t time.Time) bool {
227 return t.Unix() == s.expiredTime.Round(time.Second).Unix()
228 }
229 f()
230 }
231
232 func (s *ClientSuite) syncLocal(c *C) {
233 meta, err := s.store.GetMeta()
234 c.Assert(err, IsNil)
235 for k, v := range meta {
236 c.Assert(s.local.SetMeta(k, v), IsNil)
237 }
238 }
239
240 func (s *ClientSuite) syncRemote(c *C) {
241 meta, err := s.store.GetMeta()
242 c.Assert(err, IsNil)
243 for name, data := range meta {
244 if err := s.setRemoteMeta(name, data); err != nil {
245 panic(fmt.Sprintf("setMetadata failed: %v", err))
246 }
247 }
248 }
249
250 func (s *ClientSuite) addRemoteTarget(c *C, name string) {
251 c.Assert(s.repo.AddTarget(name, nil), IsNil)
252 c.Assert(s.repo.Snapshot(), IsNil)
253 c.Assert(s.repo.Timestamp(), IsNil)
254 c.Assert(s.repo.Commit(), IsNil)
255 s.syncRemote(c)
256 }
257
258 func (s *ClientSuite) rootMeta(c *C) []byte {
259 meta, err := s.repo.GetMeta()
260 c.Assert(err, IsNil)
261 rootMeta, ok := meta["root.json"]
262 c.Assert(ok, Equals, true)
263 return rootMeta
264 }
265
266 func (s *ClientSuite) newClient(c *C) *Client {
267 s.local = MemoryLocalStore()
268 client := NewClient(s.local, s.remote)
269 c.Assert(client.Init(s.rootMeta(c)), IsNil)
270 return client
271 }
272
273 func (s *ClientSuite) updatedClient(c *C) *Client {
274 client := s.newClient(c)
275 _, err := client.Update()
276 c.Assert(err, IsNil)
277 return client
278 }
279
280 func assertFile(c *C, file data.TargetFileMeta, name string) {
281 target, ok := targetFiles[name]
282 if !ok {
283 c.Fatalf("unknown target %s", name)
284 }
285
286 meta, err := util.GenerateTargetFileMeta(bytes.NewReader(target), file.HashAlgorithms()...)
287 c.Assert(err, IsNil)
288 c.Assert(util.TargetFileMetaEqual(file, meta), IsNil)
289 }
290
291 func assertFiles(c *C, files data.TargetFiles, names []string) {
292 c.Assert(files, HasLen, len(names))
293 for _, name := range names {
294 file, ok := files[name]
295 if !ok {
296 c.Fatalf("expected files to contain %s", name)
297 }
298
299 assertFile(c, file, name)
300 }
301 }
302
303 func assertWrongHash(c *C, err error) {
304
305
306 e, ok := err.(ErrDownloadFailed)
307 if !ok {
308 c.Fatalf("expected err to have type ErrDownloadFailed, got %T", err)
309 }
310 if _, ok := e.Err.(util.ErrWrongHash); !ok {
311 c.Fatalf("expected err.Err to have type util.ErrWrongHash, got %T", err)
312 }
313 }
314
315 func (s *ClientSuite) assertErrExpired(c *C, err error, file string) {
316 decodeErr, ok := err.(ErrDecodeFailed)
317 if !ok {
318 c.Fatalf("expected err to have type ErrDecodeFailed, got %T", err)
319 }
320 c.Assert(decodeErr.File, Equals, file)
321 expiredErr, ok := decodeErr.Err.(verify.ErrExpired)
322 if !ok {
323 c.Fatalf("expected err.Err to have type signed.ErrExpired, got %T", err)
324 }
325 c.Assert(expiredErr.Expired.Unix(), Equals, s.expiredTime.Round(time.Second).Unix())
326 }
327
328 func (s *ClientSuite) TestInitAllowsExpired(c *C) {
329 s.genKeyExpired(c, "targets")
330 c.Assert(s.repo.Snapshot(), IsNil)
331 c.Assert(s.repo.Timestamp(), IsNil)
332 c.Assert(s.repo.Commit(), IsNil)
333 s.syncRemote(c)
334 client := NewClient(MemoryLocalStore(), s.remote)
335 bytes, err := s.readMeta("root.json")
336 c.Assert(err, IsNil)
337 s.withMetaExpired(func() {
338 c.Assert(client.Init(bytes), IsNil)
339 })
340 }
341
342 func (s *ClientSuite) TestInit(c *C) {
343 client := NewClient(MemoryLocalStore(), s.remote)
344 bytes, err := s.readMeta("root.json")
345 c.Assert(err, IsNil)
346 dataSigned := &data.Signed{}
347 c.Assert(json.Unmarshal(bytes, dataSigned), IsNil)
348 root := &data.Root{}
349 c.Assert(json.Unmarshal(dataSigned.Signed, root), IsNil)
350
351
352 _, err = client.Update()
353 c.Assert(err, Equals, ErrNoRootKeys)
354
355
356
357
358 root.Version = root.Version + 1
359 rootBytes, err := json.Marshal(root)
360 c.Assert(err, IsNil)
361 dataSigned.Signed = rootBytes
362 dataBytes, err := json.Marshal(dataSigned)
363 c.Assert(err, IsNil)
364 c.Assert(client.Init(dataBytes), Equals, verify.ErrRoleThreshold{
365 Expected: 1, Actual: 0})
366
367
368 c.Assert(client.Init(bytes), IsNil)
369 _, err = client.Update()
370 c.Assert(err, IsNil)
371 }
372
373
374
375
376
377 func (s *ClientSuite) TestExtraRootSignaturesOnInit(c *C) {
378 client := NewClient(MemoryLocalStore(), s.remote)
379 bytes, err := s.readMeta("root.json")
380 c.Assert(err, IsNil)
381 dataSigned := &data.Signed{}
382 c.Assert(json.Unmarshal(bytes, dataSigned), IsNil)
383
384
385
386 dataSigned.Signatures = append(dataSigned.Signatures,
387 data.Signature{
388 KeyID: dataSigned.Signatures[0].KeyID,
389 Signature: make([]byte, ed25519.SignatureSize),
390 })
391 dataBytes, err := json.Marshal(dataSigned)
392 c.Assert(err, IsNil)
393 c.Assert(client.Init(dataBytes), IsNil)
394 }
395
396 func (s *ClientSuite) TestFirstUpdate(c *C) {
397 files, err := s.newClient(c).Update()
398 c.Assert(err, IsNil)
399 c.Assert(files, HasLen, 1)
400 assertFiles(c, files, []string{"foo.txt"})
401 }
402
403 func (s *ClientSuite) TestMissingRemoteMetadata(c *C) {
404 client := s.newClient(c)
405
406 s.deleteMeta("targets.json")
407 _, err := client.Update()
408 c.Assert(err, Equals, ErrMissingRemoteMetadata{"targets.json"})
409
410 s.deleteMeta("timestamp.json")
411 _, err = client.Update()
412 c.Assert(err, Equals, ErrMissingRemoteMetadata{"timestamp.json"})
413 }
414
415 func (s *ClientSuite) TestNoChangeUpdate(c *C) {
416 client := s.newClient(c)
417 _, err := client.Update()
418 c.Assert(err, IsNil)
419 _, err = client.Update()
420 c.Assert(err, IsNil)
421 }
422
423 func (s *ClientSuite) TestNewTimestamp(c *C) {
424 client := s.updatedClient(c)
425 version := client.timestampVer
426 c.Assert(version > 0, Equals, true)
427 c.Assert(s.repo.Timestamp(), IsNil)
428 s.syncRemote(c)
429 _, err := client.Update()
430 c.Assert(err, IsNil)
431 c.Assert(client.timestampVer > version, Equals, true)
432 }
433
434 func (s *ClientSuite) TestNewRoot(c *C) {
435 client := s.newClient(c)
436
437
438 newKeyIDs := make(map[string][]string)
439 for role, ids := range s.keyIDs {
440 c.Assert(len(ids) > 0, Equals, true)
441 c.Assert(s.repo.RevokeKey(role, ids[0]), IsNil)
442 newKeyIDs[role] = s.genKey(c, role)
443 }
444
445
446 c.Assert(s.repo.Sign("targets.json"), IsNil)
447 c.Assert(s.repo.Snapshot(), IsNil)
448 c.Assert(s.repo.Timestamp(), IsNil)
449 c.Assert(s.repo.Commit(), IsNil)
450 s.syncRemote(c)
451
452
453 c.Assert(client.getLocalMeta(), IsNil)
454 version := client.rootVer
455 c.Assert(version > 0, Equals, true)
456 _, err := client.Update()
457 c.Assert(err, IsNil)
458 c.Assert(client.rootVer > version, Equals, true)
459
460
461 for _, ids := range s.keyIDs {
462 c.Assert(len(ids) > 0, Equals, true)
463 for _, id := range ids {
464 _, err := client.db.GetVerifier(id)
465 c.Assert(err, NotNil)
466 }
467 }
468
469
470 for name, ids := range newKeyIDs {
471 c.Assert(len(ids) > 0, Equals, true)
472 for _, id := range ids {
473 verifier, err := client.db.GetVerifier(id)
474 c.Assert(err, IsNil)
475 c.Assert(verifier.MarshalPublicKey().IDs(), DeepEquals, ids)
476 }
477 role := client.db.GetRole(name)
478 c.Assert(role, NotNil)
479 c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(ids))
480 }
481 }
482
483
484
485
486
487 func (s *ClientSuite) TestExtraSignaturesOnRootUpdate(c *C) {
488 client := s.newClient(c)
489
490
491 s.genKey(c, "root")
492
493 c.Assert(s.repo.Sign("targets.json"), IsNil)
494 c.Assert(s.repo.Snapshot(), IsNil)
495 c.Assert(s.repo.Timestamp(), IsNil)
496 c.Assert(s.repo.Commit(), IsNil)
497 s.syncRemote(c)
498
499
500 bytes, err := s.readMeta("root.json")
501 c.Assert(err, IsNil)
502 dataSigned := &data.Signed{}
503 c.Assert(json.Unmarshal(bytes, dataSigned), IsNil)
504 dataSigned.Signatures = append(dataSigned.Signatures,
505 data.Signature{
506 KeyID: dataSigned.Signatures[0].KeyID,
507 Signature: make([]byte, ed25519.SignatureSize),
508 })
509 dataBytes, err := json.Marshal(dataSigned)
510 c.Assert(err, IsNil)
511 s.setRemoteMeta("root.json", dataBytes)
512 s.setRemoteMeta("2.root.json", dataBytes)
513
514
515
516 _, err = client.Update()
517 c.Assert(err, IsNil)
518 c.Assert(client.rootVer, Equals, int64(2))
519 }
520
521
522 func startTUFRepoServer(baseDir string, relPath string) (net.Listener, error) {
523 serverDir := filepath.Join(baseDir, relPath)
524 l, err := net.Listen("tcp", "127.0.0.1:0")
525 go http.Serve(l, http.FileServer(http.Dir(serverDir)))
526 return l, err
527 }
528
529
530 func newClientWithMeta(baseDir string, relPath string, serverAddr string) (*Client, error) {
531 initialStateDir := filepath.Join(baseDir, relPath)
532 opts := &HTTPRemoteOptions{
533 MetadataPath: "metadata",
534 TargetsPath: "targets",
535 }
536
537 remote, err := HTTPRemoteStore(fmt.Sprintf("http://%s/", serverAddr), opts, nil)
538 if err != nil {
539 return nil, err
540 }
541 c := NewClient(MemoryLocalStore(), remote)
542 for _, m := range []string{"root.json", "snapshot.json", "timestamp.json", "targets.json"} {
543 if _, err := os.Stat(initialStateDir + "/" + m); err == nil {
544 metadataJSON, err := os.ReadFile(initialStateDir + "/" + m)
545 if err != nil {
546 return nil, err
547 }
548 c.local.SetMeta(m, metadataJSON)
549 }
550 }
551 return c, nil
552 }
553
554 func initRootTest(c *C, baseDir string) (*Client, func() error) {
555 l, err := startTUFRepoServer(baseDir, "server")
556 c.Assert(err, IsNil)
557 tufClient, err := newClientWithMeta(baseDir, "client/metadata/current", l.Addr().String())
558 c.Assert(err, IsNil)
559 return tufClient, l.Close
560 }
561
562 func (s *ClientSuite) TestUpdateRoots(c *C) {
563 var tests = []struct {
564 fixturePath string
565 expectedError error
566 expectedVersions map[string]int64
567 }{
568
569 {"testdata/Published1Time", nil, map[string]int64{"root": 1, "timestamp": 1, "snapshot": 1, "targets": 1}},
570
571 {"testdata/Published1Time_client_root_only", nil, map[string]int64{"root": 1, "timestamp": 1, "snapshot": 1, "targets": 1}},
572
573 {"testdata/Published2Times_keyrotated", nil, map[string]int64{"root": 2, "timestamp": 1, "snapshot": 1, "targets": 1}},
574
575 {"testdata/Published2Times_keyrotated_initialrootexpired", nil, map[string]int64{"root": 2, "timestamp": 1, "snapshot": 1, "targets": 1}},
576
577 {"testdata/Published3Times_keyrotated_initialrootsexpired", nil, map[string]int64{"root": 3, "timestamp": 1, "snapshot": 1, "targets": 1}},
578
579 {"testdata/Published3Times_keyrotated_initialrootsexpired_clientversionis2", nil, map[string]int64{"root": 3, "timestamp": 1, "snapshot": 1, "targets": 1}},
580
581 {"testdata/Published3Times_keyrotated_latestrootexpired", ErrDecodeFailed{File: "root.json", Err: verify.ErrExpired{}}, map[string]int64{"root": 2, "timestamp": 1, "snapshot": 1, "targets": 1}},
582
583
584
585 {"testdata/Published2Times_keyrotated_invalidOldRootSignature", nil, map[string]int64{}},
586
587 {"testdata/Published2Times_keyrotated_invalidNewRootSignature", verify.ErrRoleThreshold{Expected: 1, Actual: 0}, map[string]int64{}},
588
589 {"testdata/Published1Time_backwardRootVersion", verify.ErrWrongVersion(verify.ErrWrongVersion{Given: 1, Expected: 2}), map[string]int64{}},
590
591 {"testdata/Published3Times_keyrotated_forwardRootVersion", verify.ErrWrongVersion(verify.ErrWrongVersion{Given: 3, Expected: 2}), map[string]int64{}},
592
593 {"testdata/Published1Time_client_no_root", errors.New("tuf: no root keys found in local meta store"), map[string]int64{}},
594
595
596 {"testdata/Published2Times_snapshot_keyrotated", nil, map[string]int64{"root": 2, "timestamp": 2, "snapshot": 2, "targets": 1}},
597
598 {"testdata/Published2Times_targets_keyrotated", nil, map[string]int64{"root": 2, "timestamp": 2, "snapshot": 2, "targets": 2}},
599
600 {"testdata/Published2Times_timestamp_keyrotated", nil, map[string]int64{"root": 2, "timestamp": 2, "snapshot": 1, "targets": 1}},
601
602 {"testdata/Published2Times_roottoolarge", ErrMetaTooLarge{Name: "2.root.json", Size: defaultRootDownloadLimit + 1, MaxSize: defaultRootDownloadLimit}, map[string]int64{}},
603 }
604
605 for _, test := range tests {
606 tufClient, closer := initRootTest(c, test.fixturePath)
607 _, err := tufClient.Update()
608 if test.expectedError == nil {
609 c.Assert(err, IsNil)
610
611 tufClient.getLocalMeta()
612 versionMethods := map[string]int64{"root": tufClient.rootVer,
613 "timestamp": tufClient.timestampVer,
614 "snapshot": tufClient.snapshotVer,
615 "targets": tufClient.targetsVer}
616 for m, v := range test.expectedVersions {
617 assert.Equal(c, v, versionMethods[m])
618 }
619 } else {
620
621
622 if _, ok := test.expectedError.(ErrDecodeFailed); ok {
623 decodeErr, ok := err.(ErrDecodeFailed)
624 c.Assert(ok, Equals, true)
625 c.Assert(decodeErr.File, Equals, "root.json")
626 _, ok = decodeErr.Err.(verify.ErrExpired)
627 c.Assert(ok, Equals, true)
628 } else {
629 assert.Equal(c, test.expectedError, err)
630 }
631 }
632 closer()
633 }
634 }
635
636 func (s *ClientSuite) TestFastForwardAttackRecovery(c *C) {
637 var tests = []struct {
638 fixturePath string
639 expectMetaDeleted map[string]bool
640 }{
641
642
643
644
645
646
647
648
649
650
651 {"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_root",
652 map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
653 {"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_snapshot",
654 map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
655 {"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_targets",
656 map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
657 {"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_timestamp",
658 map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
659
660
661 {"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_root",
662 map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
663
664
665 {"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_snapshot",
666 map[string]bool{"root.json": false, "timestamp.json": true, "snapshot.json": true, "targets.json": false}},
667
668 {"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_targets",
669 map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": true, "targets.json": true}},
670
671 {"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_timestamp",
672 map[string]bool{"root.json": false, "timestamp.json": true, "snapshot.json": false, "targets.json": false}},
673 }
674 for _, test := range tests {
675 tufClient, closer := initRootTest(c, test.fixturePath)
676 c.Assert(tufClient.UpdateRoots(), IsNil)
677 m, err := tufClient.local.GetMeta()
678 c.Assert(err, IsNil)
679 for md, deleted := range test.expectMetaDeleted {
680 if deleted {
681 if _, ok := m[md]; ok {
682 c.Fatalf("Metadata %s is not deleted!", md)
683 }
684 } else {
685 if _, ok := m[md]; !ok {
686 c.Fatalf("Metadata %s deleted!", md)
687 }
688 }
689 }
690 closer()
691 }
692
693 }
694
695 func (s *ClientSuite) TestUpdateRace(c *C) {
696
697
698 for i := 0; i < 2; i++ {
699 go func() {
700 c := NewClient(MemoryLocalStore(), newFakeRemoteStore())
701 c.Update()
702 }()
703 }
704 }
705
706 func (s *ClientSuite) TestNewTargets(c *C) {
707 client := s.newClient(c)
708 files, err := client.Update()
709 c.Assert(err, IsNil)
710 assertFiles(c, files, []string{"foo.txt"})
711
712 s.addRemoteTarget(c, "bar.txt")
713 s.addRemoteTarget(c, "baz.txt")
714
715 files, err = client.Update()
716 c.Assert(err, IsNil)
717 assertFiles(c, files, []string{"bar.txt", "baz.txt"})
718
719
720 s.addRemoteTarget(c, "bar.txt")
721 files, err = client.Update()
722 c.Assert(err, IsNil)
723 c.Assert(files, HasLen, 0)
724 }
725
726 func (s *ClientSuite) TestNewTimestampKey(c *C) {
727 client := s.newClient(c)
728
729
730 oldIDs := s.keyIDs["timestamp"]
731 c.Assert(s.repo.RevokeKey("timestamp", oldIDs[0]), IsNil)
732 newIDs := s.genKey(c, "timestamp")
733
734
735 c.Assert(s.repo.Snapshot(), IsNil)
736 c.Assert(s.repo.Timestamp(), IsNil)
737 c.Assert(s.repo.Commit(), IsNil)
738 s.syncRemote(c)
739
740
741 c.Assert(client.getLocalMeta(), IsNil)
742 rootVer := client.rootVer
743 timestampVer := client.timestampVer
744 _, err := client.Update()
745 c.Assert(err, IsNil)
746 c.Assert(client.rootVer > rootVer, Equals, true)
747 c.Assert(client.timestampVer > timestampVer, Equals, true)
748
749
750 for _, oldID := range oldIDs {
751 _, err := client.db.GetVerifier(oldID)
752 c.Assert(err, NotNil)
753 }
754 for _, newID := range newIDs {
755 verifier, err := client.db.GetVerifier(newID)
756 c.Assert(err, IsNil)
757 c.Assert(verifier.MarshalPublicKey().IDs(), DeepEquals, newIDs)
758 }
759 role := client.db.GetRole("timestamp")
760 c.Assert(role, NotNil)
761 c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
762 }
763
764 func (s *ClientSuite) TestNewSnapshotKey(c *C) {
765 client := s.newClient(c)
766
767
768 oldIDs := s.keyIDs["snapshot"]
769 c.Assert(s.repo.RevokeKey("snapshot", oldIDs[0]), IsNil)
770 newIDs := s.genKey(c, "snapshot")
771
772
773 c.Assert(s.repo.Snapshot(), IsNil)
774 c.Assert(s.repo.Timestamp(), IsNil)
775 c.Assert(s.repo.Commit(), IsNil)
776 s.syncRemote(c)
777
778
779 c.Assert(client.getLocalMeta(), IsNil)
780 rootVer := client.rootVer
781 snapshotVer := client.snapshotVer
782 timestampVer := client.timestampVer
783 _, err := client.Update()
784 c.Assert(err, IsNil)
785 c.Assert(client.rootVer > rootVer, Equals, true)
786 c.Assert(client.snapshotVer > snapshotVer, Equals, true)
787 c.Assert(client.timestampVer > timestampVer, Equals, true)
788
789
790 for _, oldID := range oldIDs {
791 _, err := client.db.GetVerifier(oldID)
792 c.Assert(err, NotNil)
793 }
794 for _, newID := range newIDs {
795 verifier, err := client.db.GetVerifier(newID)
796 c.Assert(err, IsNil)
797 c.Assert(verifier.MarshalPublicKey().IDs(), DeepEquals, newIDs)
798 }
799 role := client.db.GetRole("snapshot")
800 c.Assert(role, NotNil)
801 c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
802 }
803
804 func (s *ClientSuite) TestNewTargetsKey(c *C) {
805 client := s.newClient(c)
806
807
808 oldIDs := s.keyIDs["targets"]
809 c.Assert(s.repo.RevokeKey("targets", oldIDs[0]), IsNil)
810 newIDs := s.genKey(c, "targets")
811
812
813 c.Assert(s.repo.Sign("targets.json"), IsNil)
814 c.Assert(s.repo.Snapshot(), IsNil)
815 c.Assert(s.repo.Timestamp(), IsNil)
816 c.Assert(s.repo.Commit(), IsNil)
817 s.syncRemote(c)
818
819
820 c.Assert(client.getLocalMeta(), IsNil)
821 rootVer := client.rootVer
822 targetsVer := client.targetsVer
823 snapshotVer := client.snapshotVer
824 timestampVer := client.timestampVer
825 _, err := client.Update()
826 c.Assert(err, IsNil)
827 c.Assert(client.rootVer > rootVer, Equals, true)
828 c.Assert(client.targetsVer > targetsVer, Equals, true)
829 c.Assert(client.snapshotVer > snapshotVer, Equals, true)
830 c.Assert(client.timestampVer > timestampVer, Equals, true)
831
832
833 for _, oldID := range oldIDs {
834 _, err := client.db.GetVerifier(oldID)
835 c.Assert(err, NotNil)
836 }
837 for _, newID := range newIDs {
838 verifier, err := client.db.GetVerifier(newID)
839 c.Assert(err, IsNil)
840 c.Assert(verifier.MarshalPublicKey().IDs(), DeepEquals, newIDs)
841 }
842 role := client.db.GetRole("targets")
843 c.Assert(role, NotNil)
844 c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
845 }
846
847 func (s *ClientSuite) TestOfflineSignatureFlow(c *C) {
848 client := s.newClient(c)
849
850
851 oldIDs := s.keyIDs["targets"]
852 c.Assert(s.repo.RevokeKey("targets", oldIDs[0]), IsNil)
853 _ = s.genKey(c, "targets")
854
855
856 payload, err := s.repo.Payload("targets.json")
857 c.Assert(err, IsNil)
858 signed := data.Signed{Signed: payload}
859 _, err = s.repo.SignPayload("targets", &signed)
860 c.Assert(err, IsNil)
861 for _, sig := range signed.Signatures {
862
863 err = s.repo.AddOrUpdateSignature("targets.json", sig)
864 c.Assert(err, IsNil)
865 }
866 c.Assert(s.repo.Snapshot(), IsNil)
867 c.Assert(s.repo.Timestamp(), IsNil)
868 c.Assert(s.repo.Commit(), IsNil)
869 s.syncRemote(c)
870
871
872 c.Assert(client.getLocalMeta(), IsNil)
873 _, err = client.Update()
874 c.Assert(err, IsNil)
875 }
876
877 func (s *ClientSuite) TestLocalExpired(c *C) {
878 client := s.newClient(c)
879
880
881 version := client.timestampVer
882 c.Assert(s.repo.TimestampWithExpires(s.expiredTime), IsNil)
883 s.syncLocal(c)
884 s.withMetaExpired(func() {
885 c.Assert(client.getLocalMeta(), IsNil)
886 c.Assert(client.timestampVer > version, Equals, true)
887 })
888
889
890 version = client.snapshotVer
891 c.Assert(s.repo.SnapshotWithExpires(s.expiredTime), IsNil)
892 s.syncLocal(c)
893 s.withMetaExpired(func() {
894 c.Assert(client.getLocalMeta(), IsNil)
895 c.Assert(client.snapshotVer > version, Equals, true)
896 })
897
898
899 version = client.targetsVer
900 c.Assert(s.repo.AddTargetWithExpires("foo.txt", nil, s.expiredTime), IsNil)
901 s.syncLocal(c)
902 s.withMetaExpired(func() {
903 c.Assert(client.getLocalMeta(), IsNil)
904 c.Assert(client.targetsVer > version, Equals, true)
905 })
906
907
908 version = client.rootVer
909 s.genKeyExpired(c, "targets")
910 s.syncLocal(c)
911 s.withMetaExpired(func() {
912 err := client.getLocalMeta()
913 if _, ok := err.(verify.ErrExpired); !ok {
914 c.Fatalf("expected err to have type signed.ErrExpired, got %T", err)
915 }
916 c.Assert(client.rootVer, Equals, version)
917 })
918 }
919
920 func (s *ClientSuite) TestTimestampTooLarge(c *C) {
921 s.setRemoteMeta("timestamp.json", make([]byte, defaultTimestampDownloadLimit+1))
922 _, err := s.newClient(c).Update()
923 c.Assert(err, Equals, ErrMetaTooLarge{"timestamp.json", defaultTimestampDownloadLimit + 1, defaultTimestampDownloadLimit})
924 }
925
926 func (s *ClientSuite) TestUpdateLocalRootExpired(c *C) {
927 client := s.newClient(c)
928
929
930 s.genKeyExpired(c, "timestamp")
931 c.Assert(s.repo.Snapshot(), IsNil)
932 c.Assert(s.repo.Timestamp(), IsNil)
933 c.Assert(s.repo.Commit(), IsNil)
934 s.syncLocal(c)
935
936
937 s.genKey(c, "timestamp")
938 s.addRemoteTarget(c, "bar.txt")
939 c.Assert(s.repo.Snapshot(), IsNil)
940 c.Assert(s.repo.Timestamp(), IsNil)
941 c.Assert(s.repo.Commit(), IsNil)
942 s.syncRemote(c)
943
944 const expectedRootVersion = int64(3)
945
946
947
948 s.withMetaExpired(func() {
949 err := client.getLocalMeta()
950 if _, ok := err.(verify.ErrExpired); !ok {
951 c.Fatalf("expected err to have type signed.ErrExpired, got %T", err)
952 }
953 _, err = client.Update()
954 c.Assert(err, IsNil)
955 c.Assert(client.rootVer, Equals, expectedRootVersion)
956 })
957 }
958
959 func (s *ClientSuite) TestUpdateRemoteExpired(c *C) {
960 client := s.updatedClient(c)
961
962
963 c.Assert(s.repo.TimestampWithExpires(s.expiredTime), IsNil)
964 s.syncRemote(c)
965 s.withMetaExpired(func() {
966 _, err := client.Update()
967 s.assertErrExpired(c, err, "timestamp.json")
968 })
969
970 c.Assert(s.repo.SnapshotWithExpires(s.expiredTime), IsNil)
971 c.Assert(s.repo.Timestamp(), IsNil)
972 c.Assert(s.repo.Commit(), IsNil)
973 s.syncRemote(c)
974 s.withMetaExpired(func() {
975 _, err := client.Update()
976 s.assertErrExpired(c, err, "snapshot.json")
977 })
978
979 c.Assert(s.repo.AddTargetWithExpires("bar.txt", nil, s.expiredTime), IsNil)
980 c.Assert(s.repo.Snapshot(), IsNil)
981 c.Assert(s.repo.Timestamp(), IsNil)
982 s.syncRemote(c)
983 s.withMetaExpired(func() {
984 _, err := client.Update()
985 s.assertErrExpired(c, err, "targets.json")
986 })
987
988 s.genKeyExpired(c, "timestamp")
989 c.Assert(s.repo.RemoveTarget("bar.txt"), IsNil)
990 c.Assert(s.repo.Snapshot(), IsNil)
991 c.Assert(s.repo.Timestamp(), IsNil)
992 c.Assert(s.repo.Commit(), IsNil)
993 s.syncRemote(c)
994 s.withMetaExpired(func() {
995 _, err := client.Update()
996 s.assertErrExpired(c, err, "root.json")
997 })
998 }
999
1000 func (s *ClientSuite) TestUpdateLocalRootExpiredKeyChange(c *C) {
1001 client := s.newClient(c)
1002
1003
1004 s.genKeyExpired(c, "timestamp")
1005 c.Assert(s.repo.Snapshot(), IsNil)
1006 c.Assert(s.repo.Timestamp(), IsNil)
1007 c.Assert(s.repo.Commit(), IsNil)
1008 s.syncLocal(c)
1009
1010
1011 newKeyIDs := make(map[string][]string)
1012 for role, ids := range s.keyIDs {
1013 if role != "snapshot" && role != "timestamp" && role != "targets" {
1014 c.Assert(s.repo.RevokeKey(role, ids[0]), IsNil)
1015 newKeyIDs[role] = s.genKey(c, role)
1016 }
1017 }
1018
1019
1020 c.Assert(s.repo.Sign("targets.json"), IsNil)
1021 c.Assert(s.repo.Snapshot(), IsNil)
1022 c.Assert(s.repo.Timestamp(), IsNil)
1023 c.Assert(s.repo.Commit(), IsNil)
1024 s.syncRemote(c)
1025
1026
1027
1028 s.withMetaExpired(func() {
1029 err := client.getLocalMeta()
1030 c.Assert(err, FitsTypeOf, verify.ErrExpired{})
1031
1032 _, err = client.Update()
1033 c.Assert(err, IsNil)
1034 })
1035 }
1036
1037 func (s *ClientSuite) TestUpdateMixAndMatchAttack(c *C) {
1038
1039 expires := time.Now().Add(time.Hour)
1040 c.Assert(s.repo.AddTargetWithExpires("foo.txt", nil, expires), IsNil)
1041 c.Assert(s.repo.Snapshot(), IsNil)
1042 c.Assert(s.repo.Timestamp(), IsNil)
1043 c.Assert(s.repo.Commit(), IsNil)
1044 s.syncRemote(c)
1045 client := s.updatedClient(c)
1046
1047
1048 oldTargets, err := s.readMeta("targets.json")
1049 if err != nil {
1050 c.Fatal("missing remote targets.json")
1051 }
1052
1053
1054 c.Assert(s.repo.AddTargetWithExpires("bar.txt", nil, expires), IsNil)
1055 c.Assert(s.repo.Snapshot(), IsNil)
1056 c.Assert(s.repo.Timestamp(), IsNil)
1057 c.Assert(s.repo.Commit(), IsNil)
1058 s.syncRemote(c)
1059 newTargets, err := s.readMeta("targets.json")
1060 if err != nil {
1061 c.Fatal("missing remote targets.json")
1062 }
1063 s.setRemoteMeta("targets.json", oldTargets)
1064
1065
1066 _, err = client.Update()
1067 c.Assert(err, DeepEquals, ErrWrongSize{"targets.json", int64(len(oldTargets)), int64(len(newTargets))})
1068
1069
1070 c.Assert(s.repo.RemoveTargetWithExpires("foo.txt", expires), IsNil)
1071 c.Assert(s.repo.Snapshot(), IsNil)
1072 c.Assert(s.repo.Timestamp(), IsNil)
1073 s.syncRemote(c)
1074 s.setRemoteMeta("targets.json", oldTargets)
1075
1076
1077 _, err = client.Update()
1078 assertWrongHash(c, err)
1079 }
1080
1081 func (s *ClientSuite) TestUpdateReplayAttack(c *C) {
1082 client := s.updatedClient(c)
1083
1084
1085 oldTimestamp, err := s.readMeta("timestamp.json")
1086 if err != nil {
1087 c.Fatal("missing remote timestamp.json")
1088 }
1089
1090
1091 version := client.timestampVer
1092 c.Assert(version > 0, Equals, true)
1093 c.Assert(s.repo.Timestamp(), IsNil)
1094 s.syncRemote(c)
1095 _, err = client.Update()
1096 c.Assert(err, IsNil)
1097 c.Assert(client.timestampVer > version, Equals, true)
1098
1099
1100 s.setRemoteMeta("timestamp.json", oldTimestamp)
1101
1102
1103 _, err = client.Update()
1104 c.Assert(err, DeepEquals, ErrDecodeFailed{
1105 File: "timestamp.json",
1106 Err: verify.ErrLowVersion{
1107 Actual: version,
1108 Current: client.timestampVer,
1109 },
1110 })
1111 }
1112
1113 func (s *ClientSuite) TestUpdateForkTimestamp(c *C) {
1114 client := s.updatedClient(c)
1115
1116
1117 oldTimestamp, err := s.readMeta("timestamp.json")
1118 if err != nil {
1119 c.Fatal("missing remote timestamp.json")
1120 }
1121
1122
1123 version := client.timestampVer
1124 c.Assert(version > 0, Equals, true)
1125 c.Assert(s.repo.Timestamp(), IsNil)
1126 s.syncRemote(c)
1127 _, err = client.Update()
1128 c.Assert(err, IsNil)
1129 newVersion := client.timestampVer
1130 c.Assert(newVersion > version, Equals, true)
1131
1132
1133 s.setRemoteMeta("timestamp.json", oldTimestamp)
1134 c.Assert(s.repo.Timestamp(), IsNil)
1135 c.Assert(client.timestampVer, Equals, newVersion)
1136 s.syncRemote(c)
1137
1138 oldMeta, err := client.local.GetMeta()
1139 c.Assert(err, IsNil)
1140 _, err = client.Update()
1141 c.Assert(err, IsNil)
1142
1143 newMeta, err := client.local.GetMeta()
1144 c.Assert(err, IsNil)
1145 c.Assert(oldMeta, DeepEquals, newMeta)
1146 }
1147
1148 func (s *ClientSuite) TestUpdateTamperedTargets(c *C) {
1149 client := s.newClient(c)
1150
1151
1152 meta, err := s.store.GetMeta()
1153 c.Assert(err, IsNil)
1154 targetsJSON, ok := meta["targets.json"]
1155 if !ok {
1156 c.Fatal("missing targets.json")
1157 }
1158
1159 type signedTargets struct {
1160 Signed data.Targets `json:"signed"`
1161 Signatures []data.Signature `json:"signatures"`
1162 }
1163 targets := &signedTargets{}
1164 c.Assert(json.Unmarshal(targetsJSON, targets), IsNil)
1165
1166
1167 targets.Signed.Type = "xxxxxxx"
1168 tamperedJSON, err := json.Marshal(targets)
1169 c.Assert(err, IsNil)
1170 s.store.SetMeta("targets.json", tamperedJSON)
1171 s.store.Commit(false, nil, nil)
1172 s.syncRemote(c)
1173 _, err = client.Update()
1174 assertWrongHash(c, err)
1175
1176
1177 targets.Signed.Type = "xxx"
1178 tamperedJSON, err = json.Marshal(targets)
1179 c.Assert(err, IsNil)
1180 s.store.SetMeta("targets.json", tamperedJSON)
1181 s.store.Commit(false, nil, nil)
1182 c.Assert(s.repo.Timestamp(), IsNil)
1183 s.syncRemote(c)
1184 _, err = client.Update()
1185 c.Assert(err, DeepEquals, ErrWrongSize{"targets.json", int64(len(tamperedJSON)), int64(len(targetsJSON))})
1186 }
1187
1188 func (s *ClientSuite) TestUpdateHTTP(c *C) {
1189 tmp := c.MkDir()
1190
1191
1192 addr, cleanup := startFileServer(c, tmp)
1193 defer cleanup()
1194
1195 for _, consistentSnapshot := range []bool{false, true} {
1196 dir := fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot)
1197
1198
1199 repo := generateRepoFS(c, filepath.Join(tmp, dir), targetFiles, consistentSnapshot)
1200
1201
1202 remote, err := HTTPRemoteStore(fmt.Sprintf("http://%s/%s/repository", addr, dir), nil, nil)
1203 c.Assert(err, IsNil)
1204 client := NewClient(MemoryLocalStore(), remote)
1205 rootMeta, err := repo.SignedMeta("root.json")
1206 c.Assert(err, IsNil)
1207 rootJsonBytes, err := json.Marshal(rootMeta)
1208 c.Assert(err, IsNil)
1209 c.Assert(client.Init(rootJsonBytes), IsNil)
1210
1211
1212 targets, err := client.Update()
1213 c.Assert(err, IsNil)
1214 assertFiles(c, targets, []string{"foo.txt", "bar.txt", "baz.txt"})
1215
1216
1217 for name, data := range targetFiles {
1218 var dest testDestination
1219 c.Assert(client.Download(name, &dest), IsNil)
1220 c.Assert(dest.deleted, Equals, false)
1221 c.Assert(dest.String(), Equals, string(data))
1222 }
1223 }
1224 }
1225
1226
1227 func (s *ClientSuite) TestRollbackSnapshot(c *C) {
1228 client := s.updatedClient(c)
1229
1230
1231 version := client.snapshotVer
1232 c.Assert(version > 0, Equals, true)
1233 c.Assert(s.repo.Snapshot(), IsNil)
1234 c.Assert(s.repo.Timestamp(), IsNil)
1235 c.Assert(s.repo.Commit(), IsNil)
1236 s.syncRemote(c)
1237 _, err := client.Update()
1238 c.Assert(err, IsNil)
1239 c.Assert(client.snapshotVer > version, Equals, true)
1240
1241
1242 s.repo.SetSnapshotVersion(version)
1243 c.Assert(s.repo.Snapshot(), IsNil)
1244 c.Assert(s.repo.Timestamp(), IsNil)
1245 c.Assert(s.repo.Commit(), IsNil)
1246 s.syncRemote(c)
1247
1248
1249 _, err = client.Update()
1250
1251 c.Assert(err, DeepEquals, verify.ErrLowVersion{
1252 Actual: version,
1253 Current: client.snapshotVer,
1254 })
1255 }
1256
1257 func (s *ClientSuite) TestRollbackTopLevelTargets(c *C) {
1258 client := s.updatedClient(c)
1259
1260
1261 version := client.targetsVer
1262 c.Assert(version > 0, Equals, true)
1263 s.addRemoteTarget(c, "bar.txt")
1264 _, err := client.Update()
1265 c.Assert(err, IsNil)
1266 c.Assert(client.targetsVer > version, Equals, true)
1267
1268
1269 s.repo.SetTargetsVersion(version)
1270 c.Assert(s.repo.Snapshot(), IsNil)
1271 c.Assert(s.repo.Timestamp(), IsNil)
1272 c.Assert(s.repo.Commit(), IsNil)
1273 s.syncRemote(c)
1274
1275
1276 _, err = client.Update()
1277 c.Assert(err, DeepEquals, verify.ErrLowVersion{
1278 Actual: version,
1279 Current: client.targetsVer,
1280 })
1281 }
1282
1283 func (s *ClientSuite) TestRollbackDelegatedTargets(c *C) {
1284 client := s.updatedClient(c)
1285
1286 signer, err := keys.GenerateEd25519Key()
1287 c.Assert(err, IsNil)
1288 role := data.DelegatedRole{
1289 Name: "role",
1290 KeyIDs: signer.PublicData().IDs(),
1291 Paths: []string{"bar.txt", "baz.txt"},
1292 Threshold: 1,
1293 }
1294 s.store.SaveSigner("role", signer)
1295 s.repo.AddDelegatedRole("targets", role, []*data.PublicKey{signer.PublicData()})
1296 s.repo.AddTargetToPreferredRole("bar.txt", nil, "role")
1297 c.Assert(s.repo.Snapshot(), IsNil)
1298 c.Assert(s.repo.Timestamp(), IsNil)
1299 c.Assert(s.repo.Commit(), IsNil)
1300 s.syncRemote(c)
1301
1302
1303 meta, err := s.store.GetMeta()
1304 c.Assert(err, IsNil)
1305 oldRole, ok := meta["role.json"]
1306 if !ok {
1307 c.Fatal("missing role.json")
1308 }
1309
1310 _, err = client.Update()
1311 c.Assert(err, IsNil)
1312 var dest testDestination
1313 c.Assert(client.Download("bar.txt", &dest), IsNil)
1314
1315
1316 s.repo.AddTargetToPreferredRole("baz.txt", nil, "role")
1317 c.Assert(s.repo.Snapshot(), IsNil)
1318 c.Assert(s.repo.Timestamp(), IsNil)
1319 c.Assert(s.repo.Commit(), IsNil)
1320 s.syncRemote(c)
1321
1322
1323 _, err = client.Update()
1324 c.Assert(err, IsNil)
1325 c.Assert(dest.Delete(), IsNil)
1326 c.Assert(client.Download("baz.txt", &dest), IsNil)
1327
1328
1329 c.Assert(s.store.SetMeta("role.json", oldRole), IsNil)
1330 repo, err := tuf.NewRepo(s.store)
1331 c.Assert(err, IsNil)
1332 c.Assert(repo.Snapshot(), IsNil)
1333 c.Assert(repo.Timestamp(), IsNil)
1334 c.Assert(repo.Commit(), IsNil)
1335 s.syncRemote(c)
1336
1337
1338 _, err = client.Update()
1339 c.Assert(err, DeepEquals, verify.ErrLowVersion{
1340 Actual: 1,
1341 Current: 2,
1342 })
1343 }
1344
1345 type testDestination struct {
1346 bytes.Buffer
1347 deleted bool
1348 }
1349
1350 func (t *testDestination) Delete() error {
1351 t.deleted = true
1352 return nil
1353 }
1354
1355 func (s *ClientSuite) TestDownloadUnknownTarget(c *C) {
1356 client := s.updatedClient(c)
1357 var dest testDestination
1358 c.Assert(client.Download("nonexistent", &dest), Equals, ErrUnknownTarget{Name: "nonexistent", SnapshotVersion: 1})
1359 c.Assert(dest.deleted, Equals, true)
1360 }
1361
1362 func (s *ClientSuite) TestDownloadNoExist(c *C) {
1363 client := s.updatedClient(c)
1364 s.deleteTarget("foo.txt")
1365 var dest testDestination
1366 c.Assert(client.Download("foo.txt", &dest), Equals, ErrNotFound{"foo.txt"})
1367 c.Assert(dest.deleted, Equals, true)
1368 }
1369
1370 func (s *ClientSuite) TestDownloadOK(c *C) {
1371 client := s.updatedClient(c)
1372
1373 for _, name := range []string{"/foo.txt", "foo.txt"} {
1374 var dest testDestination
1375 c.Assert(client.Download(name, &dest), IsNil)
1376 c.Assert(dest.deleted, Equals, false)
1377 c.Assert(dest.String(), Equals, "foo")
1378 }
1379 }
1380
1381 func (s *ClientSuite) TestDownloadWrongSize(c *C) {
1382 client := s.updatedClient(c)
1383
1384 s.setRemoteTarget("foo.txt", []byte("wrong-size"))
1385 var dest testDestination
1386 c.Assert(client.Download("foo.txt", &dest), DeepEquals, ErrWrongSize{"foo.txt", 10, 3})
1387 c.Assert(dest.deleted, Equals, true)
1388 }
1389
1390 func (s *ClientSuite) TestDownloadTargetTooLong(c *C) {
1391 client := s.updatedClient(c)
1392 s.setRemoteTarget("foo.txt", []byte("foo-ooo"))
1393 var dest testDestination
1394 c.Assert(client.Download("foo.txt", &dest), DeepEquals, ErrWrongSize{"foo.txt", 7, 3})
1395 c.Assert(dest.deleted, Equals, true)
1396 }
1397
1398 func (s *ClientSuite) TestDownloadTargetTooShort(c *C) {
1399 client := s.updatedClient(c)
1400 s.setRemoteTarget("foo.txt", []byte("fo"))
1401 var dest testDestination
1402 c.Assert(client.Download("foo.txt", &dest), DeepEquals, ErrWrongSize{"foo.txt", 2, 3})
1403 c.Assert(dest.deleted, Equals, true)
1404 }
1405
1406 func (s *ClientSuite) TestDownloadTargetCorruptData(c *C) {
1407 client := s.updatedClient(c)
1408 s.setRemoteTarget("foo.txt", []byte("ooo"))
1409 var dest testDestination
1410 assertWrongHash(c, client.Download("foo.txt", &dest))
1411 c.Assert(dest.deleted, Equals, true)
1412 }
1413
1414 func (s *ClientSuite) TestAvailableTargets(c *C) {
1415 client := s.updatedClient(c)
1416 files, err := client.Targets()
1417 c.Assert(err, IsNil)
1418 assertFiles(c, files, []string{"foo.txt"})
1419
1420 s.addRemoteTarget(c, "bar.txt")
1421 s.addRemoteTarget(c, "baz.txt")
1422 _, err = client.Update()
1423 c.Assert(err, IsNil)
1424 files, err = client.Targets()
1425 c.Assert(err, IsNil)
1426 assertFiles(c, files, []string{"foo.txt", "bar.txt", "baz.txt"})
1427 }
1428
1429 func (s *ClientSuite) TestAvailableTarget(c *C) {
1430 client := s.updatedClient(c)
1431
1432 target, err := client.Target("foo.txt")
1433 c.Assert(err, IsNil)
1434 assertFile(c, target, "foo.txt")
1435
1436 target, err = client.Target("/foo.txt")
1437 c.Assert(err, IsNil)
1438 assertFile(c, target, "foo.txt")
1439
1440 _, err = client.Target("bar.txt")
1441 c.Assert(err, Equals, ErrNotFound{"bar.txt"})
1442
1443 _, err = client.Target("/bar.txt")
1444 c.Assert(err, Equals, ErrNotFound{"/bar.txt"})
1445 }
1446
1447 func (s *ClientSuite) TestUnknownKeyIDs(c *C) {
1448
1449 meta, err := s.store.GetMeta()
1450 c.Assert(err, IsNil)
1451
1452 rootJSON, ok := meta["root.json"]
1453 c.Assert(ok, Equals, true)
1454
1455 var root struct {
1456 Signed data.Root `json:"signed"`
1457 Signatures []data.Signature `json:"signatures"`
1458 }
1459 c.Assert(json.Unmarshal(rootJSON, &root), IsNil)
1460
1461
1462 signer, err := keys.GenerateEd25519Key()
1463 c.Assert(err, IsNil)
1464
1465 root.Signed.Keys["unknown-key-id"] = signer.PublicData()
1466
1467
1468 signingKeys, err := s.store.GetSigners("root")
1469 c.Assert(err, IsNil)
1470
1471 signedRoot, err := sign.Marshal(root.Signed, signingKeys...)
1472 c.Assert(err, IsNil)
1473
1474 rootJSON, err = cjson.EncodeCanonical(signedRoot)
1475 c.Assert(err, IsNil)
1476
1477 s.store.SetMeta("root.json", rootJSON)
1478 s.store.Commit(false, nil, nil)
1479 s.syncRemote(c)
1480
1481
1482
1483
1484 repo, err := tuf.NewRepo(s.store)
1485 c.Assert(err, IsNil)
1486 c.Assert(repo.Snapshot(), IsNil)
1487 c.Assert(repo.Timestamp(), IsNil)
1488 c.Assert(repo.Commit(), IsNil)
1489 s.syncRemote(c)
1490
1491
1492 client := s.newClient(c)
1493 _, err = client.Update()
1494 c.Assert(err, IsNil)
1495 }
1496
1497 func generateRepoFS(c *C, dir string, files map[string][]byte, consistentSnapshot bool) *tuf.Repo {
1498 repo, err := tuf.NewRepo(tuf.FileSystemStore(dir, nil))
1499 c.Assert(err, IsNil)
1500 if !consistentSnapshot {
1501 c.Assert(repo.Init(false), IsNil)
1502 }
1503 for _, role := range []string{"root", "snapshot", "targets", "timestamp"} {
1504 _, err := repo.GenKey(role)
1505 c.Assert(err, IsNil)
1506 }
1507 for file, data := range files {
1508 path := filepath.Join(dir, "staged", "targets", file)
1509 c.Assert(os.MkdirAll(filepath.Dir(path), 0755), IsNil)
1510 c.Assert(os.WriteFile(path, data, 0644), IsNil)
1511 c.Assert(repo.AddTarget(file, nil), IsNil)
1512 }
1513 c.Assert(repo.Snapshot(), IsNil)
1514 c.Assert(repo.Timestamp(), IsNil)
1515 c.Assert(repo.Commit(), IsNil)
1516 return repo
1517 }
1518
1519 func (s *ClientSuite) TestVerifyDigest(c *C) {
1520 digest := "sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
1521 hash := "bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
1522 size := int64(42)
1523
1524 c.Assert(s.repo.AddTargetsWithDigest(hash, "sha256", size, digest, nil), IsNil)
1525 c.Assert(s.repo.Snapshot(), IsNil)
1526 c.Assert(s.repo.Timestamp(), IsNil)
1527 c.Assert(s.repo.Commit(), IsNil)
1528 s.syncRemote(c)
1529
1530 client := s.newClient(c)
1531 _, err := client.Update()
1532 c.Assert(err, IsNil)
1533
1534 c.Assert(client.VerifyDigest(hash, "sha256", size, digest), IsNil)
1535 }
1536
1537 type StateLessSuite struct{}
1538
1539 var _ = Suite(&StateLessSuite{})
1540
1541 func (s *StateLessSuite) TestRejectsMultiSignaturesSameKeyDifferentIDs(c *C) {
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564 alice, err := keys.GenerateEd25519Key()
1565 if err != nil {
1566 panic(err)
1567 }
1568
1569 root := data.NewRoot()
1570 root.Version = 1
1571 root.Roles["root"] = &data.Role{
1572 KeyIDs: []string{},
1573 Threshold: 2,
1574 }
1575
1576
1577
1578 oldTUFIDs := func(k *data.PublicKey) []string {
1579 bytes, _ := cjson.EncodeCanonical(k)
1580 digest := sha256.Sum256(bytes)
1581 ids := []string{hex.EncodeToString(digest[:])}
1582
1583 if k.Scheme != "" || len(k.Algorithms) != 0 {
1584 bytes, _ = cjson.EncodeCanonical(&data.PublicKey{
1585 Type: k.Type,
1586 Value: k.Value,
1587 })
1588 digest = sha256.Sum256(bytes)
1589 ids = append(ids, hex.EncodeToString(digest[:]))
1590 }
1591
1592 return ids
1593 }
1594
1595
1596
1597 for _, keyID := range oldTUFIDs(alice.PublicData()) {
1598 root.Keys[keyID] = alice.PublicData()
1599 root.Roles["root"].KeyIDs = append(root.Roles["root"].KeyIDs, keyID)
1600 }
1601
1602 bob, err := keys.GenerateEd25519Key()
1603 if err != nil {
1604 panic(err)
1605 }
1606
1607 root.AddKey(bob.PublicData())
1608 root.Roles["root"].KeyIDs = append(
1609 root.Roles["root"].KeyIDs,
1610 bob.PublicData().IDs()...,
1611 )
1612
1613
1614 delegatedSigner, _ := keys.GenerateEd25519Key()
1615 root.AddKey(delegatedSigner.PublicData())
1616 for _, role := range []string{"targets", "snapshot", "timestamp"} {
1617 root.Roles[role] = &data.Role{
1618 KeyIDs: delegatedSigner.PublicData().IDs(),
1619 Threshold: 1,
1620 }
1621 }
1622
1623 signedRoot, err := sign.Marshal(root, alice, bob)
1624 c.Assert(err, IsNil)
1625 rootJSON, err := json.Marshal(signedRoot)
1626 c.Assert(err, IsNil)
1627
1628
1629
1630 evilRoot := root
1631 evilRoot.Version = 2
1632
1633 canonical, err := cjson.EncodeCanonical(evilRoot)
1634 c.Assert(err, IsNil)
1635 sig, err := alice.SignMessage(canonical)
1636 c.Assert(err, IsNil)
1637 signedEvilRoot := &data.Signed{
1638 Signed: canonical,
1639 Signatures: make([]data.Signature, 0),
1640 }
1641 for _, keyID := range oldTUFIDs(alice.PublicData()) {
1642 signedEvilRoot.Signatures = append(signedEvilRoot.Signatures, data.Signature{
1643 Signature: sig,
1644 KeyID: keyID,
1645 })
1646 }
1647 evilRootJSON, err := json.Marshal(signedEvilRoot)
1648 c.Assert(err, IsNil)
1649
1650
1651
1652
1653 localStore := MemoryLocalStore()
1654 err = localStore.SetMeta("root.json", rootJSON)
1655 c.Assert(err, IsNil)
1656
1657 remoteStore := newFakeRemoteStore()
1658 remoteStore.meta["2.root.json"] = newFakeFile(evilRootJSON)
1659
1660 client := NewClient(localStore, remoteStore)
1661
1662 err = client.UpdateRoots()
1663 if err != nil {
1664 c.Assert(err, DeepEquals, verify.ErrRoleThreshold{Expected: 2, Actual: 1})
1665 } else {
1666 c.Fatalf("client returned no error when updating with evil root")
1667 }
1668 }
1669
View as plain text