Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Support delegated targets roles in repo writer #171

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
cjson "github.com/tent/canonical-json-go"
tuf "github.com/theupdateframework/go-tuf"
"github.com/theupdateframework/go-tuf/data"
"github.com/theupdateframework/go-tuf/internal/sets"
"github.com/theupdateframework/go-tuf/pkg/keys"
"github.com/theupdateframework/go-tuf/sign"
"github.com/theupdateframework/go-tuf/util"
Expand Down Expand Up @@ -362,7 +363,7 @@ func (s *ClientSuite) TestNewRoot(c *C) {
}
role := client.db.GetRole(name)
c.Assert(role, NotNil)
c.Assert(role.KeyIDs, DeepEquals, util.StringSliceToSet(ids))
c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(ids))
}
}

Expand Down Expand Up @@ -602,7 +603,7 @@ func (s *ClientSuite) TestNewTimestampKey(c *C) {
}
role := client.db.GetRole("timestamp")
c.Assert(role, NotNil)
c.Assert(role.KeyIDs, DeepEquals, util.StringSliceToSet(newIDs))
c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
}

func (s *ClientSuite) TestNewSnapshotKey(c *C) {
Expand Down Expand Up @@ -642,7 +643,7 @@ func (s *ClientSuite) TestNewSnapshotKey(c *C) {
}
role := client.db.GetRole("snapshot")
c.Assert(role, NotNil)
c.Assert(role.KeyIDs, DeepEquals, util.StringSliceToSet(newIDs))
c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
}

func (s *ClientSuite) TestNewTargetsKey(c *C) {
Expand Down Expand Up @@ -685,7 +686,7 @@ func (s *ClientSuite) TestNewTargetsKey(c *C) {
}
role := client.db.GetRole("targets")
c.Assert(role, NotNil)
c.Assert(role.KeyIDs, DeepEquals, util.StringSliceToSet(newIDs))
c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
}

func (s *ClientSuite) TestLocalExpired(c *C) {
Expand Down
16 changes: 6 additions & 10 deletions client/delegations.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) {
// - filter delegations with paths or path_hash_prefixes matching searched target
// - 5.6.7.1 cycles protection
// - 5.6.7.2 terminations
delegations := targets.NewDelegationsIterator(target)
delegations := targets.NewDelegationsIterator(target, c.db)
for i := 0; i < c.MaxDelegations; i++ {
d, ok := delegations.Next()
if !ok {
return data.TargetFileMeta{}, ErrUnknownTarget{target, snapshot.Version}
}

// covers 5.6.{1,2,3,4,5,6}
targets, err := c.loadDelegatedTargets(snapshot, d.Delegatee.Name, d.Verifier)
targets, err := c.loadDelegatedTargets(snapshot, d.Delegatee.Name, d.DB)
if err != nil {
return data.TargetFileMeta{}, err
}
Expand All @@ -39,11 +39,11 @@ func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) {
}

if targets.Delegations != nil {
delegationsVerifier, err := verify.NewDelegationsVerifier(targets.Delegations)
delegationsDB, err := verify.NewDBFromDelegations(targets.Delegations)
if err != nil {
return data.TargetFileMeta{}, err
}
err = delegations.Add(targets.Delegations.Roles, d.Delegatee.Name, delegationsVerifier)
err = delegations.Add(targets.Delegations.Roles, d.Delegatee.Name, delegationsDB)
if err != nil {
return data.TargetFileMeta{}, err
}
Expand Down Expand Up @@ -75,7 +75,7 @@ func (c *Client) loadLocalSnapshot() (*data.Snapshot, error) {
}

// loadDelegatedTargets downloads, decodes, verifies and stores targets
func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, verifier verify.DelegationsVerifier) (*data.Targets, error) {
func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, db *verify.DB) (*data.Targets, error) {
var err error
fileName := role + ".json"
fileMeta, ok := snapshot.Meta[fileName]
Expand All @@ -98,11 +98,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri
// 5.6.3 verify signature with parent public keys
// 5.6.5 verify that the targets is not expired
// role "targets" is a top role verified by root keys loaded in the client db
if role == "targets" {
err = c.db.Unmarshal(raw, targets, role, fileMeta.Version)
} else {
err = verifier.Unmarshal(raw, targets, role, fileMeta.Version)
}
err = db.Unmarshal(raw, targets, role, fileMeta.Version)
if err != nil {
return nil, ErrDecodeFailed{fileName, err}
}
Expand Down
5 changes: 3 additions & 2 deletions data/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,13 @@ func DefaultExpires(role string) time.Time {
switch role {
case "root":
t = time.Now().AddDate(1, 0, 0)
case "targets":
t = time.Now().AddDate(0, 3, 0)
case "snapshot":
t = time.Now().AddDate(0, 0, 7)
case "timestamp":
t = time.Now().AddDate(0, 0, 1)
default:
// Target
t = time.Now().AddDate(0, 3, 0)
}
return t.UTC().Round(time.Second)
}
Expand Down
8 changes: 8 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,11 @@ type ErrPassphraseRequired struct {
func (e ErrPassphraseRequired) Error() string {
return fmt.Sprintf("tuf: a passphrase is required to access the encrypted %s keys file", e.Role)
}

type ErrNoDelegatedTarget struct {
Path string
}

func (e ErrNoDelegatedTarget) Error() string {
return fmt.Sprintf("tuf: no delegated target for path %s", e.Path)
}
41 changes: 41 additions & 0 deletions internal/roles/roles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package roles

import (
"strconv"
"strings"
)

var TopLevelRoles = map[string]struct{}{
"root": {},
"targets": {},
"snapshot": {},
"timestamp": {},
}

func IsTopLevelRole(name string) bool {
_, ok := TopLevelRoles[name]
return ok
}

func IsDelegatedTargetsRole(name string) bool {
return !IsTopLevelRole(name)
}

func IsTopLevelManifest(name string) bool {
return IsTopLevelRole(strings.TrimSuffix(name, ".json"))
}

func IsDelegatedTargetsManifest(name string) bool {
return !IsTopLevelManifest(name)
}

func IsVersionedManifest(name string) bool {
parts := strings.Split(name, ".")
// Versioned manifests have the form "x.role.json"
if len(parts) < 3 {
return false
}

_, err := strconv.Atoi(parts[0])
return err == nil
}
24 changes: 24 additions & 0 deletions internal/sets/strings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package sets

func StringSliceToSet(items []string) map[string]struct{} {
s := make(map[string]struct{})
for _, item := range items {
s[item] = struct{}{}
}
return s
}

func StringSetToSlice(items map[string]struct{}) []string {
ret := []string{}

for k := range items {
ret = append(ret, k)
}

return ret
}

func DeduplicateStrings(items []string) []string {
s := StringSliceToSet(items)
return StringSetToSlice(s)
}
23 changes: 17 additions & 6 deletions internal/targets/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package targets

import (
"github.com/theupdateframework/go-tuf/data"
"github.com/theupdateframework/go-tuf/internal/sets"
"github.com/theupdateframework/go-tuf/verify"
)

type Delegation struct {
Delegator string
Verifier verify.DelegationsVerifier
Delegatee data.DelegatedRole
DB *verify.DB
}

type delegationsIterator struct {
Expand All @@ -18,13 +19,23 @@ type delegationsIterator struct {
}

// NewDelegationsIterator initialises an iterator with a first step
// on top level targets
func NewDelegationsIterator(target string) *delegationsIterator {
// on top level targets.
func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) *delegationsIterator {
role := topLevelKeysDB.GetRole("targets")
keyIDs := []string{}
if role != nil {
keyIDs = sets.StringSetToSlice(role.KeyIDs)
}

i := &delegationsIterator{
target: target,
stack: []Delegation{
{
Delegatee: data.DelegatedRole{Name: "targets"},
Delegatee: data.DelegatedRole{
Name: "targets",
KeyIDs: keyIDs,
},
DB: topLevelKeysDB,
},
},
visitedRoles: make(map[string]struct{}),
Expand Down Expand Up @@ -57,7 +68,7 @@ func (d *delegationsIterator) Next() (value Delegation, ok bool) {
return delegation, true
}

func (d *delegationsIterator) Add(roles []data.DelegatedRole, delegator string, verifier verify.DelegationsVerifier) error {
func (d *delegationsIterator) Add(roles []data.DelegatedRole, delegator string, db *verify.DB) error {
for i := len(roles) - 1; i >= 0; i-- {
// Push the roles onto the stack in reverse so we get an preorder traversal
// of the delegations graph.
Expand All @@ -70,7 +81,7 @@ func (d *delegationsIterator) Add(roles []data.DelegatedRole, delegator string,
delegation := Delegation{
Delegator: delegator,
Delegatee: r,
Verifier: verifier,
DB: db,
}
d.stack = append(d.stack, delegation)
}
Expand Down
Loading