Skip to content

Commit

Permalink
upgrade(localMeta): Verify delegated targets
Browse files Browse the repository at this point in the history
Signed-off-by: Baptiste Foy <[email protected]>
  • Loading branch information
BaptisteFoy committed Dec 22, 2022
1 parent 3831342 commit f569829
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 11 deletions.
53 changes: 51 additions & 2 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io"

"github.com/theupdateframework/go-tuf/data"
Expand Down Expand Up @@ -400,8 +401,8 @@ func (c *Client) getLocalMeta() error {
}
}

snapshot := &data.Snapshot{}
if snapshotJSON, ok := meta["snapshot.json"]; ok {
snapshot := &data.Snapshot{}
if err := c.db.UnmarshalTrusted(snapshotJSON, snapshot, "snapshot"); err != nil {
loadFailed = true
retErr = err
Expand All @@ -425,19 +426,67 @@ func (c *Client) getLocalMeta() error {
}
}

if loadFailed {
// If any of the metadata failed to be verified, return the reason for that failure
// and fail fast before delegated targets
return retErr
}

verifiedDelegatedTargets := make(map[string]bool)
for fileName := range meta {
if roles.IsDelegatedTargetsManifest(fileName) {
c.localMeta[fileName] = meta[fileName]
if delegations, err := c.getDelegationPathFromRaw(snapshot, meta[fileName]); err != nil {
loadFailed = true
retErr = err
} else {
for _, key := range delegations {
fileName := fmt.Sprintf("%s.json", key)
if !verifiedDelegatedTargets[fileName] {
verifiedDelegatedTargets[fileName] = true
}
}
}
}
}

for fileName := range verifiedDelegatedTargets {
c.localMeta[fileName] = meta[fileName]
}

if loadFailed {
// If any of the metadata failed to be verified, return the reason for that failure
return retErr
}
return nil
}

// getDelegationPathFromRaw verifies a delegated targets against
// a given snapshot and returns an error if it's invalid
//
// Delegation must have targets to get a path, else an empty list
// will be returned: this is because the delegation iterator is leveraged.
func (c *Client) getDelegationPathFromRaw(snapshot *data.Snapshot, delegatedTargetsJSON json.RawMessage) ([]string, error) {
// unmarshal the delegated targets first without verifying as
// we need at least one targets file name to leverage the
// getTargetFileMetaDelegationPath method
s := &data.Signed{}
if err := json.Unmarshal(delegatedTargetsJSON, s); err != nil {
return nil, err
}
targets := &data.Targets{}
if err := json.Unmarshal(s.Signed, targets); err != nil {
return nil, err
}
for targetPath := range targets.Targets {
_, resp, err := c.getTargetFileMetaDelegationPath(targetPath, snapshot)
// We only need to test one targets file:
// - If it is valid, it means the delegated targets has been validated
// - If it is not, the delegated targets isn't valid
return resp, err
}
return nil, nil
}

// loadAndVerifyLocalRootMeta decodes and verifies root metadata from
// local storage and loads the top-level keys. This method first clears
// the DB for top-level keys and then loads the new keys.
Expand Down
53 changes: 44 additions & 9 deletions client/delegations.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,99 @@ import (

// getTargetFileMeta searches for a verified TargetFileMeta matching a target
// Requires a local snapshot to be loaded and is locked to the snapshot versions.
// Searches through delegated targets following TUF spec 1.0.19 section 5.6.
func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) {
snapshot, err := c.loadLocalSnapshot()
if err != nil {
return data.TargetFileMeta{}, err
}

targetFileMeta, _, err := c.getTargetFileMetaDelegationPath(target, snapshot)
if err != nil {
return data.TargetFileMeta{}, err
}
return targetFileMeta, nil
}

// getTargetFileMetaDelegationPath searches for a verified TargetFileMeta matching a target
// Requires snapshot to be passed and is locked to that specific snapshot versions.
// Searches through delegated targets following TUF spec 1.0.19 section 5.6.
func (c *Client) getTargetFileMetaDelegationPath(target string, snapshot *data.Snapshot) (data.TargetFileMeta, []string, error) {
// delegationsIterator covers 5.6.7
// - pre-order depth-first search starting with the top targets
// - filter delegations with paths or path_hash_prefixes matching searched target
// - 5.6.7.1 cycles protection
// - 5.6.7.2 terminations
delegations, err := targets.NewDelegationsIterator(target, c.db)
if err != nil {
return data.TargetFileMeta{}, err
return data.TargetFileMeta{}, nil, err
}

targetFileMeta := data.TargetFileMeta{}
delegationRole := ""

for i := 0; i < c.MaxDelegations; i++ {
d, ok := delegations.Next()
if !ok {
return data.TargetFileMeta{}, ErrUnknownTarget{target, snapshot.Version}
return data.TargetFileMeta{}, nil, ErrUnknownTarget{target, snapshot.Version}
}

// covers 5.6.{1,2,3,4,5,6}
targets, err := c.loadDelegatedTargets(snapshot, d.Delegatee.Name, d.DB)
if err != nil {
return data.TargetFileMeta{}, err
return data.TargetFileMeta{}, nil, err
}

// stop when the searched TargetFileMeta is found
if m, ok := targets.Targets[target]; ok {
return m, nil
delegationRole = d.Delegatee.Name
targetFileMeta = m
break
}

if targets.Delegations != nil {
delegationsDB, err := verify.NewDBFromDelegations(targets.Delegations)
if err != nil {
return data.TargetFileMeta{}, err
return data.TargetFileMeta{}, nil, err
}
err = delegations.Add(targets.Delegations.Roles, d.Delegatee.Name, delegationsDB)
if err != nil {
return data.TargetFileMeta{}, err
return data.TargetFileMeta{}, nil, err
}
}
}

return data.TargetFileMeta{}, ErrMaxDelegations{
if len(delegationRole) > 0 {
return targetFileMeta, buildPath(delegations.Parent, delegationRole, ""), nil
}

return data.TargetFileMeta{}, nil, ErrMaxDelegations{
Target: target,
MaxDelegations: c.MaxDelegations,
SnapshotVersion: snapshot.Version,
}
}

func buildPath(parent func(string) string, start string, end string) []string {
if start == end {
return nil
}

path := []string{start}
current := start
for {
current = parent(current)
if current == end {
break
}
path = append(path, current)
}
return path
}

func (c *Client) loadLocalSnapshot() (*data.Snapshot, error) {
if err := c.getLocalMeta(); err != nil {
return nil, err
}

rawS, ok := c.localMeta["snapshot.json"]
if !ok {
return nil, ErrNoLocalSnapshot
Expand Down
7 changes: 7 additions & 0 deletions pkg/targets/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type delegationsIterator struct {
stack []Delegation
target string
visitedRoles map[string]struct{}
parents map[string]string
}

var ErrTopLevelTargetsRoleMissing = errors.New("tuf: top level targets role missing from top level keys DB")
Expand All @@ -43,6 +44,7 @@ func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) (*delegati
},
},
visitedRoles: make(map[string]struct{}),
parents: make(map[string]string),
}
return i, nil
}
Expand Down Expand Up @@ -88,8 +90,13 @@ func (d *delegationsIterator) Add(roles []data.DelegatedRole, delegator string,
DB: db,
}
d.stack = append(d.stack, delegation)
d.parents[r.Name] = delegator
}
}

return nil
}

func (d *delegationsIterator) Parent(role string) string {
return d.parents[role]
}

0 comments on commit f569829

Please sign in to comment.