From 145392cc8c9d8dbad574d1a103dcd6cc355abba8 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Thu, 24 Jun 2021 10:06:36 -0400 Subject: [PATCH 1/4] Support modifying role thresholds Signed-off-by: Asra Ali --- README.md | 9 ++++--- cmd/tuf/main.go | 1 + cmd/tuf/set_threshold.go | 33 ++++++++++++++++++++++++ repo.go | 34 +++++++++++++++++++++++++ repo_test.go | 54 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 cmd/tuf/set_threshold.go diff --git a/README.md b/README.md index 51bf8282..aefbe316 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,11 @@ Prompts the user for an encryption passphrase (unless the writes it to the relevant key file in the `keys` directory. It also stages the addition of the new key to the `root` manifest. +#### `tuf set-threshold ` + +Sets the `role` threshold, the required number of keys for signing, to +`threshold`. + #### `tuf add [...]` Hashes files in the `staged/targets` directory at the given path(s), then @@ -501,10 +506,6 @@ $ tree . └── staged ``` -#### Modify key thresholds - -TODO - ## Client For the client package, see https://godoc.org/github.com/theupdateframework/go-tuf/client. diff --git a/cmd/tuf/main.go b/cmd/tuf/main.go index b79a3fd8..7acb0eae 100644 --- a/cmd/tuf/main.go +++ b/cmd/tuf/main.go @@ -40,6 +40,7 @@ Commands: regenerate Recreate the targets manifest clean Remove all staged manifests root-keys Output a JSON serialized array of root keys to STDOUT + set-threshold Sets the threshold for a role See "tuf help " for more information on a specific command ` diff --git a/cmd/tuf/set_threshold.go b/cmd/tuf/set_threshold.go new file mode 100644 index 00000000..bfc40fa0 --- /dev/null +++ b/cmd/tuf/set_threshold.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "strconv" + + "github.com/flynn/go-docopt" + "github.com/theupdateframework/go-tuf" +) + +func init() { + register("set-threshold", cmdSetThreshold, ` +usage: tuf set-threshold + +Set the threshold for a role. +`) +} + +func cmdSetThreshold(args *docopt.Args, repo *tuf.Repo) error { + role := args.String[""] + thresholdStr := args.String[""] + threshold, err := strconv.Atoi(thresholdStr) + if err != nil { + return err + } + + if err := repo.SetThreshold(role, threshold); err != nil { + return err + } + + fmt.Println("Set ", role, "threshold to", threshold) + return nil +} diff --git a/repo.go b/repo.go index cc1a3167..d25ea03e 100644 --- a/repo.go +++ b/repo.go @@ -167,6 +167,40 @@ func (r *Repo) RootVersion() (int, error) { return root.Version, nil } +func (r *Repo) GetThreshold(keyRole string) (int, error) { + root, err := r.root() + if err != nil { + return -1, err + } + role, ok := root.Roles[keyRole] + if !ok { + return -1, ErrInvalidRole{keyRole} + } + + return role.Threshold, nil +} + +func (r *Repo) SetThreshold(keyRole string, t int) error { + root, err := r.root() + if err != nil { + return err + } + role, ok := root.Roles[keyRole] + if !ok { + return ErrInvalidRole{keyRole} + } + if role.Threshold == t { + // Change was a no-op. + return nil + } + role.Threshold = t + if _, ok := r.versionUpdated["root.json"]; !ok { + root.Version++ + r.versionUpdated["root.json"] = struct{}{} + } + return r.setMeta("root.json", root) +} + func (r *Repo) Targets() (data.TargetFiles, error) { targets, err := r.targets() if err != nil { diff --git a/repo_test.go b/repo_test.go index 8e284629..43694f2d 100644 --- a/repo_test.go +++ b/repo_test.go @@ -705,7 +705,7 @@ func (rs *RepoSuite) TestCommitVersions(c *C) { c.Assert(err, IsNil) c.Assert(snapshotVersion, Equals, 3) - timestampVersion, err = r.SnapshotVersion() + timestampVersion, err = r.TimestampVersion() c.Assert(err, IsNil) c.Assert(timestampVersion, Equals, 3) } @@ -1378,3 +1378,55 @@ func (rs *RepoSuite) TestUnknownKeyIDs(c *C) { c.Assert(ok, Equals, true) c.Assert(unknownKey, DeepEquals, key.PublicData()) } + +func (rs *RepoSuite) TestThreshold(c *C) { + local := MemoryStore(make(map[string]json.RawMessage), nil) + r, err := NewRepo(local) + c.Assert(err, IsNil) + + // Add one key to each role + genKey(c, r, "root") + genKey(c, r, "targets") + genKey(c, r, "snapshot") + genKey(c, r, "timestamp") + t, err := r.GetThreshold("root") + c.Assert(err, IsNil) + c.Assert(t, Equals, 1) + + // commit the metadata to the store. + c.Assert(r.AddTargets([]string{}, nil), IsNil) + c.Assert(r.Snapshot(CompressionTypeNone), IsNil) + c.Assert(r.Timestamp(), IsNil) + c.Assert(r.Commit(), IsNil) + + // Set a new threshold. Commit without threshold keys + c.Assert(r.SetThreshold("root", 2), IsNil) + t, err = r.GetThreshold("root") + c.Assert(err, IsNil) + c.Assert(t, Equals, 2) + c.Assert(r.Commit(), DeepEquals, ErrNotEnoughKeys{"root", 1, 2}) + + // Add a second root key and try again + genKey(c, r, "root") + c.Assert(r.Sign("root.json"), IsNil) + c.Assert(r.Snapshot(CompressionTypeNone), IsNil) + c.Assert(r.Timestamp(), IsNil) + c.Assert(r.Commit(), IsNil) + + // Check versions updated + rootVersion, err := r.RootVersion() + c.Assert(err, IsNil) + c.Assert(rootVersion, Equals, 2) + + targetsVersion, err := r.TargetsVersion() + c.Assert(err, IsNil) + c.Assert(targetsVersion, Equals, 1) + + snapshotVersion, err := r.SnapshotVersion() + c.Assert(err, IsNil) + c.Assert(snapshotVersion, Equals, 2) + + timestampVersion, err := r.TimestampVersion() + c.Assert(err, IsNil) + c.Assert(timestampVersion, Equals, 2) +} From 7681f9686cad34a11fd2865efb4f477724b61408 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Thu, 24 Jun 2021 12:28:12 -0400 Subject: [PATCH 2/4] add getter Signed-off-by: Asra Ali --- cmd/tuf/get_threshold.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 cmd/tuf/get_threshold.go diff --git a/cmd/tuf/get_threshold.go b/cmd/tuf/get_threshold.go new file mode 100644 index 00000000..8eba7999 --- /dev/null +++ b/cmd/tuf/get_threshold.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + + "github.com/flynn/go-docopt" + "github.com/theupdateframework/go-tuf" +) + +func init() { + register("get-threshold", cmdGetThreshold, ` +usage: tuf get-threshold + +Gets the threshold for a role. +`) +} + +func cmdGetThreshold(args *docopt.Args, repo *tuf.Repo) error { + role := args.String[""] + + threshold, err := repo.GetThreshold(role) + if err != nil { + return err + } + + fmt.Println("Got", role, "threshold", threshold) + return nil +} From 6b6273e4e6e85a2d49ea88e44e0b5e42be2dbbf4 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Fri, 25 Jun 2021 10:52:37 -0400 Subject: [PATCH 3/4] error condition when delegation Signed-off-by: Asra Ali --- repo.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/repo.go b/repo.go index d25ea03e..fb8b3087 100644 --- a/repo.go +++ b/repo.go @@ -181,6 +181,11 @@ func (r *Repo) GetThreshold(keyRole string) (int, error) { } func (r *Repo) SetThreshold(keyRole string, t int) error { + if !validManifest(keyRole) { + // Delegations are not currently supported, so return an error if this is not a + // top-level manifest. + return ErrInvalidRole{keyRole} + } root, err := r.root() if err != nil { return err From f29eec511dc6c09cd441921a7c476332d2f64bd1 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Mon, 28 Jun 2021 07:43:24 -0400 Subject: [PATCH 4/4] fix valid manifest Signed-off-by: Asra Ali --- repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo.go b/repo.go index fb8b3087..15ada8b7 100644 --- a/repo.go +++ b/repo.go @@ -181,7 +181,7 @@ func (r *Repo) GetThreshold(keyRole string) (int, error) { } func (r *Repo) SetThreshold(keyRole string, t int) error { - if !validManifest(keyRole) { + if !validManifest(keyRole + ".json") { // Delegations are not currently supported, so return an error if this is not a // top-level manifest. return ErrInvalidRole{keyRole}