File: delegations.go

package info (click to toggle)
golang-github-theupdateframework-go-tuf 0.5.2-5~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 7,596 kB
  • sloc: python: 163; sh: 37; makefile: 12
file content (152 lines) | stat: -rw-r--r-- 4,445 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package client

import (
	"github.com/theupdateframework/go-tuf/data"
	"github.com/theupdateframework/go-tuf/pkg/targets"
	"github.com/theupdateframework/go-tuf/verify"
)

// getTargetFileMeta searches for a verified TargetFileMeta matching a target
// Requires a local snapshot to be loaded and is locked to the snapshot versions.
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{}, nil, err
	}

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

	for i := 0; i < c.MaxDelegations; i++ {
		d, ok := delegations.Next()
		if !ok {
			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{}, nil, err
		}

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

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

	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
	}

	snapshot := &data.Snapshot{}
	if err := c.db.Unmarshal(rawS, snapshot, "snapshot", c.snapshotVer); err != nil {
		return nil, ErrDecodeFailed{"snapshot.json", err}
	}
	return snapshot, nil
}

// loadDelegatedTargets downloads, decodes, verifies and stores targets
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]
	if !ok {
		return nil, ErrRoleNotInSnapshot{role, snapshot.Version}
	}

	// 5.6.1 download target if not in the local store
	// 5.6.2 check against snapshot hash
	// 5.6.4 check against snapshot version
	raw, alreadyStored := c.localMetaFromSnapshot(fileName, fileMeta)
	if !alreadyStored {
		raw, err = c.downloadMetaFromSnapshot(fileName, fileMeta)
		if err != nil {
			return nil, err
		}
	}

	targets := &data.Targets{}
	// 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
	err = db.Unmarshal(raw, targets, role, fileMeta.Version)
	if err != nil {
		return nil, ErrDecodeFailed{fileName, err}
	}

	// 5.6.6 persist
	if !alreadyStored {
		if err := c.local.SetMeta(fileName, raw); err != nil {
			return nil, err
		}
	}
	return targets, nil
}