Skip to content

Commit 78b1631

Browse files
committed
test for fast forward attack recovery
1 parent 05537c5 commit 78b1631

File tree

2 files changed

+93
-10
lines changed

2 files changed

+93
-10
lines changed

client/client.go

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"io"
88
"io/ioutil"
99
"log"
10-
"reflect"
1110
"sort"
1211
"time"
1312

@@ -245,13 +244,13 @@ func (c *Client) updateRoots() error {
245244

246245
// Prepare for 5.3.11: If the timestamp and / or snapshot keys have been rotated,
247246
// then delete the trusted timestamp and snapshot metadata files.
248-
getKeyIDs := func(role string) []string {
247+
getKeyIDs := func(role string) ([]string, int) {
249248
keyIDs := make([]string, 0, len(c.db.GetRole(role).KeyIDs))
250249
for k := range c.db.GetRole(role).KeyIDs {
251250
keyIDs = append(keyIDs, k)
252251
}
253252
sort.Strings(keyIDs)
254-
return keyIDs
253+
return keyIDs, c.db.GetRole(role).Threshold
255254
}
256255

257256
// The manifest looks like this:
@@ -261,8 +260,9 @@ func (c *Client) updateRoots() error {
261260
// "targets": ["KEYID4", "KEYID5", "KEYID6"]
262261
// }
263262
nonRootManifests := map[string][]string{"timestamp": {}, "snapshot": {}, "targets": {}}
263+
nonRootThreshold := map[string]int{"timestamp": 1, "snapshot": 1, "targets": 1}
264264
for k := range nonRootManifests {
265-
nonRootManifests[k] = getKeyIDs(k)
265+
nonRootManifests[k], nonRootThreshold[k] = getKeyIDs(k)
266266
}
267267

268268
// 5.3.1 Temorarily turn on the consistent snapshots in order to download
@@ -348,13 +348,41 @@ func (c *Client) updateRoots() error {
348348
return err
349349
}
350350

351-
// 5.3.11 If the timestamp and / or snapshot keys have been rotated,
352-
// then delete the trusted timestamp and snapshot metadata files.
351+
countDeleted := func(s1 []string, s2 []string) int {
352+
c := len(s1)
353+
p2 := 0
354+
for _, v := range s1 {
355+
for p2 < len(s2) && v >= s2[p2] {
356+
if v == s2[p2] {
357+
c--
358+
p2++
359+
break
360+
}
361+
p2++
362+
}
363+
}
364+
return c
365+
}
366+
367+
// 5.3.11 To recover from fast-forward attack, certain metadata files need
368+
// to be deleted if a threshold of keys are revoked.
369+
// List of metadata that should be deleted per role if a threshold of keys
370+
// are revoked:
371+
// timestamp -> delete timestamp.json
372+
// snapshot -> delete timestamp.json and snapshot.json
373+
// targets -> delete snapshot.json and targets.json
353374
for topLevelRolename := range nonRootManifests {
354-
if !reflect.DeepEqual(
355-
nonRootManifests[topLevelRolename],
356-
getKeyIDs(topLevelRolename)) {
357-
c.local.DeleteMeta(topLevelRolename)
375+
keyIDs, _ := getKeyIDs(topLevelRolename)
376+
if countDeleted(nonRootManifests[topLevelRolename], keyIDs) >= nonRootThreshold[topLevelRolename] {
377+
deleteMeta := map[string][]string{
378+
"timestamp": {"timestamp.json"},
379+
"snapshot": {"timestamp.json", "snapshot.json"},
380+
"targets": {"snapshot.json", "targets.json"},
381+
}
382+
383+
for _, r := range deleteMeta[topLevelRolename] {
384+
c.local.DeleteMeta(r)
385+
}
358386
}
359387
}
360388

client/client_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,61 @@ func (s *ClientSuite) TestUpdateRoots(c *C) {
471471
}
472472
}
473473

474+
func (s *ClientSuite) TestFastForwardAttackRecovery(c *C) {
475+
var tests = []struct {
476+
fixturePath string
477+
expectMetaDeleted map[string]bool
478+
}{
479+
// No non-root-metadata recovery if root keys are revoked only.
480+
{"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_root",
481+
map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
482+
// No non-root-metadata recovery if root keys are revoked only even threshold number of root keys are revoked.
483+
{"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_root",
484+
map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
485+
// No snapshot metadata recovery less than threashold keys changed.
486+
{"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_snapshot",
487+
map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
488+
// Delete snapshot and timestamp metadata if threashold number of keys changed.
489+
{"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_snapshot",
490+
map[string]bool{"root.json": false, "timestamp.json": true, "snapshot.json": true, "targets.json": false}},
491+
// No targets metadata recovery less than threashold keys changed.
492+
{"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_targets",
493+
map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
494+
// Delete targets and snapshot metadata if threashold number of keys changed.
495+
{"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_targets",
496+
map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": true, "targets.json": true}},
497+
// No timestamp metadata recovery less than threashold keys changed.
498+
{"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_timestamp",
499+
map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
500+
// Delete timestamp metadata if threashold number of keys changed.
501+
{"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_timestamp",
502+
map[string]bool{"root.json": false, "timestamp.json": true, "snapshot.json": false, "targets.json": false}},
503+
}
504+
for _, test := range tests {
505+
e := verify.IsExpired
506+
verify.IsExpired = func(t time.Time) bool { return false }
507+
tufClient, closer := initRootTest(c, test.fixturePath, true)
508+
c.Assert(tufClient.updateRoots(), IsNil)
509+
m, err := tufClient.local.GetMeta()
510+
c.Assert(err, IsNil)
511+
for md, deleted := range test.expectMetaDeleted {
512+
if deleted {
513+
if _, ok := m[md]; ok {
514+
c.Fatalf("Metadata %s is not deleted!", md)
515+
}
516+
} else {
517+
if _, ok := m[md]; !ok {
518+
c.Fatalf("Metadata %s deleted!", md)
519+
}
520+
}
521+
}
522+
closer()
523+
verify.IsExpired = e
524+
525+
}
526+
527+
}
528+
474529
func (s *ClientSuite) TestNewTargets(c *C) {
475530
client := s.newClient(c)
476531
files, err := client.Update()

0 commit comments

Comments
 (0)