Skip to content

Commit

Permalink
[Delegations prereq] Use a verify.DB for delegation in client
Browse files Browse the repository at this point in the history
Splitting up #175
  • Loading branch information
ethan-lowman-dd committed Dec 10, 2021
1 parent 8699d1f commit 092aa7c
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 80 deletions.
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
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
108 changes: 66 additions & 42 deletions internal/targets/delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var (
)

func TestDelegationsIterator(t *testing.T) {
defaultKeyIDs := []string{"26b878ad73362774b8b69dd4fdeb2cc6a2688e4133ed5ace9e18a06e9d998a6d"}
var iteratorTests = []struct {
testName string
roles map[string][]data.DelegatedRole
Expand All @@ -25,23 +26,23 @@ func TestDelegationsIterator(t *testing.T) {
testName: "no termination",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "b", Paths: defaultPathPatterns},
{Name: "e", Paths: defaultPathPatterns},
{Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "e", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"b": {
{Name: "c", Paths: defaultPathPatterns},
{Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"c": {
{Name: "d", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"e": {
{Name: "f", Paths: defaultPathPatterns},
{Name: "g", Paths: defaultPathPatterns},
{Name: "f", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "g", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"g": {
{Name: "h", Paths: defaultPathPatterns},
{Name: "i", Paths: defaultPathPatterns},
{Name: "j", Paths: defaultPathPatterns},
{Name: "h", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "i", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "j", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
},
file: "",
Expand All @@ -51,12 +52,12 @@ func TestDelegationsIterator(t *testing.T) {
testName: "terminated in b",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "b", Paths: defaultPathPatterns, Terminating: true},
{Name: "e", Paths: defaultPathPatterns},
{Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs, Terminating: true},
{Name: "e", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"b": {
{Name: "c", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns},
{Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
},
file: "",
Expand All @@ -66,12 +67,12 @@ func TestDelegationsIterator(t *testing.T) {
testName: "path does not match b",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "b", Paths: noMatchPathPatterns},
{Name: "e", Paths: defaultPathPatterns},
{Name: "b", Paths: noMatchPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "e", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"b": {
{Name: "c", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns},
{Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
},
file: "",
Expand All @@ -81,12 +82,13 @@ func TestDelegationsIterator(t *testing.T) {
testName: "path does not match b - path prefixes",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "b", PathHashPrefixes: []string{"33472a4909"}},
{Name: "c", PathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633"}},
{Name: "b", PathHashPrefixes: []string{"33472a4909"}, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "c", PathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633"}, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"c": {
{Name: "d", PathHashPrefixes: []string{"8baf"}},
{Name: "e", PathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633472a49096ed87f8f764bd597831eac371f40ac39"}},

{Name: "d", PathHashPrefixes: []string{"8baf"}, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "e", PathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633472a49096ed87f8f764bd597831eac371f40ac39"}, Threshold: 1, KeyIDs: defaultKeyIDs},
},
},
file: "/e/f/g.txt",
Expand All @@ -96,7 +98,7 @@ func TestDelegationsIterator(t *testing.T) {
testName: "err paths and pathHashPrefixes are set",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "b", Paths: defaultPathPatterns, PathHashPrefixes: defaultPathPatterns},
{Name: "b", Paths: defaultPathPatterns, PathHashPrefixes: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"b": {},
},
Expand All @@ -108,48 +110,54 @@ func TestDelegationsIterator(t *testing.T) {
testName: "cycle avoided 1",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "b", Paths: defaultPathPatterns},
{Name: "e", Paths: defaultPathPatterns},
{Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"a": {
{Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "e", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"b": {
{Name: "targets", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns},
{Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
},
file: "",
resultOrder: []string{"targets", "b", "d", "e"},
resultOrder: []string{"targets", "a", "b", "d", "e"},
},
{
testName: "cycle avoided 2",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "targets", Paths: defaultPathPatterns},
{Name: "b", Paths: defaultPathPatterns},
{Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"a": {
{Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"b": {
{Name: "targets", Paths: defaultPathPatterns},
{Name: "b", Paths: defaultPathPatterns},
{Name: "c", Paths: defaultPathPatterns},
{Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"c": {
{Name: "c", Paths: defaultPathPatterns},
{Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
},
file: "",
resultOrder: []string{"targets", "b", "c"},
resultOrder: []string{"targets", "a", "b", "c"},
},
{
testName: "diamond delegation",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "b", Paths: defaultPathPatterns},
{Name: "c", Paths: defaultPathPatterns},
{Name: "b", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
{Name: "c", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"b": {
{Name: "d", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"c": {
{Name: "d", Paths: defaultPathPatterns},
{Name: "d", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
},
file: "",
Expand All @@ -159,10 +167,10 @@ func TestDelegationsIterator(t *testing.T) {
testName: "simple cycle",
roles: map[string][]data.DelegatedRole{
"targets": {
{Name: "a", Paths: defaultPathPatterns},
{Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
"a": {
{Name: "a", Paths: defaultPathPatterns},
{Name: "a", Paths: defaultPathPatterns, Threshold: 1, KeyIDs: defaultKeyIDs},
},
},
file: "",
Expand All @@ -172,7 +180,17 @@ func TestDelegationsIterator(t *testing.T) {

for _, tt := range iteratorTests {
t.Run(tt.testName, func(t *testing.T) {
d := NewDelegationsIterator(tt.file)
flattened := []data.DelegatedRole{}
for _, roles := range tt.roles {
flattened = append(flattened, roles...)
}
db, err := verify.NewDBFromDelegations(&data.Delegations{
Roles: flattened,
})

assert.NoError(t, err)
d := NewDelegationsIterator(tt.file, db)

var iterationOrder []string
for {
r, ok := d.Next()
Expand All @@ -184,7 +202,13 @@ func TestDelegationsIterator(t *testing.T) {
if !ok {
continue
}
err := d.Add(delegations, r.Delegatee.Name, verify.DelegationsVerifier{})

db, err := verify.NewDBFromDelegations(&data.Delegations{
Roles: delegations,
})
assert.NoError(t, err)

err = d.Add(delegations, r.Delegatee.Name, db)
assert.Equal(t, tt.err, err)
}
assert.Equal(t, tt.resultOrder, iterationOrder)
Expand Down
23 changes: 7 additions & 16 deletions verify/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,28 @@ func NewDB() *DB {
}
}

type DelegationsVerifier struct {
DB *DB
}

func (d *DelegationsVerifier) Unmarshal(b []byte, v interface{}, role string, minVersion int) error {
return d.DB.Unmarshal(b, v, role, minVersion)
}

// NewDelegationsVerifier returns a DelegationsVerifier that verifies delegations
// of a given Targets. It reuses the DB struct to leverage verified keys, roles
// unmarshals.
func NewDelegationsVerifier(d *data.Delegations) (DelegationsVerifier, error) {
// NewDBFromDelegations returns a DB that verifies delegations
// of a given Targets.
func NewDBFromDelegations(d *data.Delegations) (*DB, error) {
db := &DB{
roles: make(map[string]*Role, len(d.Roles)),
verifiers: make(map[string]keys.Verifier, len(d.Keys)),
}
for _, r := range d.Roles {
if _, ok := roles.TopLevelRoles[r.Name]; ok {
return DelegationsVerifier{}, ErrInvalidDelegatedRole
return nil, ErrInvalidDelegatedRole
}
role := &data.Role{Threshold: r.Threshold, KeyIDs: r.KeyIDs}
if err := db.addRole(r.Name, role); err != nil {
return DelegationsVerifier{}, err
return nil, err
}
}
for id, k := range d.Keys {
if err := db.AddKey(id, k); err != nil {
return DelegationsVerifier{}, err
return nil, err
}
}
return DelegationsVerifier{db}, nil
return db, nil
}

func (db *DB) AddKey(id string, k *data.PublicKey) error {
Expand Down
Loading

0 comments on commit 092aa7c

Please sign in to comment.