From 17ca5d957ccf486204d28ed7f71e77d848d271e7 Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Fri, 9 Jul 2021 14:01:32 +0200 Subject: [PATCH 01/22] Add delegation client --- client/client.go | 46 ++++-- client/client_test.go | 2 +- client/delegations.go | 185 ++++++++++++++++++++++ client/delegations_test.go | 315 +++++++++++++++++++++++++++++++++++++ client/errors.go | 36 ++++- data/hex_bytes.go | 9 ++ data/types.go | 45 +++++- data/types_test.go | 61 +++++++ go.mod | 1 + go.sum | 9 ++ verify/db.go | 30 ++++ verify/verify.go | 2 +- 12 files changed, 718 insertions(+), 23 deletions(-) create mode 100644 client/delegations.go create mode 100644 client/delegations_test.go diff --git a/client/client.go b/client/client.go index 129ec31b..7148abf0 100644 --- a/client/client.go +++ b/client/client.go @@ -18,6 +18,7 @@ const ( // big it is. defaultRootDownloadLimit = 512000 defaultTimestampDownloadLimit = 16384 + defaultMaxDelegations = 32 ) // LocalStore is local storage for downloaded top-level metadata. @@ -81,12 +82,17 @@ type Client struct { // consistentSnapshot indicates whether the remote storage is using // consistent snapshots (as specified in root.json) consistentSnapshot bool + + // MaxDelegations limits by default the number of delegations visited for any + // target + MaxDelegations int } func NewClient(local LocalStore, remote RemoteStore) *Client { return &Client{ - local: local, - remote: remote, + local: local, + remote: remote, + MaxDelegations: defaultMaxDelegations, } } @@ -198,7 +204,7 @@ func (c *Client) update(latestRoot bool) (data.TargetFiles, error) { if err != nil { return nil, err } - rootMeta, targetsMeta, err := c.decodeSnapshot(snapshotJSON) + rootMeta, rootInSnapshot, targetsMeta, err := c.decodeSnapshot(snapshotJSON) if err != nil { // ErrRoleThreshold could indicate snapshot keys have been // revoked, so retry with the latest root.json @@ -210,7 +216,8 @@ func (c *Client) update(latestRoot bool) (data.TargetFiles, error) { // If we don't have the root.json, download it, save it in local // storage and restart the update - if !c.hasMetaFromSnapshot("root.json", rootMeta) { + // Root should no longer be pinned in snapshot meta https://github.com/theupdateframework/tuf/pull/988 + if rootInSnapshot && !c.hasMetaFromSnapshot("root.json", rootMeta) { return c.updateWithLatestRoot(&rootMeta) } @@ -539,13 +546,14 @@ func (c *Client) decodeRoot(b json.RawMessage) error { // decodeSnapshot decodes and verifies snapshot metadata, and returns the new // root and targets file meta. -func (c *Client) decodeSnapshot(b json.RawMessage) (data.SnapshotFileMeta, data.SnapshotFileMeta, error) { +func (c *Client) decodeSnapshot(b json.RawMessage) (data.SnapshotFileMeta, bool, data.SnapshotFileMeta, error) { snapshot := &data.Snapshot{} if err := c.db.Unmarshal(b, snapshot, "snapshot", c.snapshotVer); err != nil { - return data.SnapshotFileMeta{}, data.SnapshotFileMeta{}, ErrDecodeFailed{"snapshot.json", err} + return data.SnapshotFileMeta{}, false, data.SnapshotFileMeta{}, ErrDecodeFailed{"snapshot.json", err} } c.snapshotVer = snapshot.Version - return snapshot.Meta["root.json"], snapshot.Meta["targets.json"], nil + rootMeta, rootInSnapshot := snapshot.Meta["root.json"] + return rootMeta, rootInSnapshot, snapshot.Meta["targets.json"], nil } // decodeTargets decodes and verifies targets metadata, sets c.targets and @@ -582,18 +590,24 @@ func (c *Client) decodeTimestamp(b json.RawMessage) (data.TimestampFileMeta, err return timestamp.Meta["snapshot.json"], nil } -// hasSnapshotMeta checks whether local metadata has the given meta +// hasMetaFromSnapshot checks whether local metadata has the given meta func (c *Client) hasMetaFromSnapshot(name string, m data.SnapshotFileMeta) bool { + _, ok := c.localMetaFromSnapshot(name, m) + return ok +} + +// localMetaFromSnapshot returns localmetadata if it matches the snapshot +func (c *Client) localMetaFromSnapshot(name string, m data.SnapshotFileMeta) (json.RawMessage, bool) { b, ok := c.localMeta[name] if !ok { - return false + return nil, false } meta, err := util.GenerateSnapshotFileMeta(bytes.NewReader(b), m.HashAlgorithms()...) if err != nil { - return false + return nil, false } err = util.SnapshotFileMetaEqual(meta, m) - return err == nil + return b, err == nil } // hasTargetsMeta checks whether local metadata has the given snapshot meta @@ -634,7 +648,8 @@ type Destination interface { // dest will be deleted and an error returned in the following situations: // // * The target does not exist in the local targets.json -// * The target does not exist in remote storage +// * Failed to fetch the chain of delegations accessible from local snapshot.json +// * The target does not exist in any targets // * Metadata cannot be generated for the downloaded data // * Generated metadata does not match local metadata for the given file func (c *Client) Download(name string, dest Destination) (err error) { @@ -652,11 +667,14 @@ func (c *Client) Download(name string, dest Destination) (err error) { } } - // return ErrUnknownTarget if the file is not in the local targets.json normalizedName := util.NormalizeTarget(name) localMeta, ok := c.targets[normalizedName] if !ok { - return ErrUnknownTarget{name} + // search in delegations + localMeta, err = c.getTargetFileMeta(normalizedName) + if err != nil { + return err + } } // get the data from remote storage diff --git a/client/client_test.go b/client/client_test.go index 39bb019c..a8ca536f 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -804,7 +804,7 @@ func (t *testDestination) Delete() error { func (s *ClientSuite) TestDownloadUnknownTarget(c *C) { client := s.updatedClient(c) var dest testDestination - c.Assert(client.Download("nonexistent", &dest), Equals, ErrUnknownTarget{"nonexistent"}) + c.Assert(client.Download("nonexistent", &dest), Equals, ErrUnknownTarget{Name: "nonexistent", SnapshotVersion: 1}) c.Assert(dest.deleted, Equals, true) } diff --git a/client/delegations.go b/client/delegations.go new file mode 100644 index 00000000..f55cb3f5 --- /dev/null +++ b/client/delegations.go @@ -0,0 +1,185 @@ +package client + +import ( + "github.com/theupdateframework/go-tuf/data" + "github.com/theupdateframework/go-tuf/verify" +) + +// getTargetFileMeta searches for a verified TargetFileMeta matching a file name +// 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(file string) (data.TargetFileMeta, error) { + snapshot, err := c.loadLocalSnapshot() + if err != nil { + return data.TargetFileMeta{}, err + } + verifiers := map[string]verify.DelegationsVerifier{"root": c.db} + + // 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 file + // - 5.6.7.1 cycles protection + // - 5.6.7.2 terminations + delegations := newDelegationsIterator(c.rootTargetDelegation(), "root", file) + for i := 0; i < c.MaxDelegations; i++ { + d, ok := delegations.next() + if !ok { + return data.TargetFileMeta{}, ErrUnknownTarget{file, snapshot.Version} + } + verifier := verifiers[d.parent] + // covers 5.6.{1,2,3,4,5,6} + target, err := c.loadDelegatedTargets(snapshot, d.child.Name, verifier) + if err != nil { + return data.TargetFileMeta{}, err + } + // stop when the searched TargetFileMeta is found + if m, ok := target.Targets[file]; ok { + return m, nil + } + if target.Delegations != nil { + delegations.add(target.Delegations.Roles, d.child.Name) + targetVerifier, err := verify.NewDelegationsVerifier(target.Delegations) + if err != nil { + return data.TargetFileMeta{}, err + } + verifiers[d.child.Name] = targetVerifier + } + } + return data.TargetFileMeta{}, ErrMaxDelegations{ + File: file, + MaxDelegations: c.MaxDelegations, + SnapshotVersion: snapshot.Version, + } +} + +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 delegated targets +func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, verifier verify.DelegationsVerifier) (*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 + raw, alreadyStored := c.localMetaFromSnapshot(fileName, fileMeta) + if !alreadyStored { + raw, err = c.downloadMetaFromSnapshot(fileName, fileMeta) + if err != nil { + return nil, err + } + } + target := &data.Targets{} + // 5.6.3 verify signature with parent public keys + // 5.6.5 verify that the targets is not expired + if err := verifier.Unmarshal(raw, target, role, fileMeta.Version); err != nil { + return nil, ErrDecodeFailed{fileName, err} + } + // 5.6.4 check against snapshot version + if target.Version != fileMeta.Version { + return nil, ErrTargetsSnapshotVersionMismatch{ + Role: fileName, + DownloadedTargetsVersion: fileMeta.Version, + TargetsSnapshotVersion: target.Version, + SnapshotVersion: snapshot.Version, + } + } + // 5.6.6 persist + if !alreadyStored { + if err := c.local.SetMeta(fileName, raw); err != nil { + return nil, err + } + } + return target, nil +} + +func (c *Client) rootTargetDelegation() data.DelegatedRole { + role := "targets" + r := c.db.GetRole(role) + if r == nil { + return data.DelegatedRole{} + } + keyIDs := make([]string, 0, len(r.KeyIDs)) + for id, _ := range r.KeyIDs { + keyIDs = append(keyIDs, id) + } + return data.DelegatedRole{ + Name: role, + KeyIDs: keyIDs, + Threshold: r.Threshold, + Paths: []string{"*"}, + } +} + +type delegation struct { + parent string + child data.DelegatedRole +} + +type delegationID struct { + parent string + child string +} + +type delegationsIterator struct { + stack []delegation + file string + visited map[delegationID]struct{} +} + +func newDelegationsIterator(role data.DelegatedRole, parent string, file string) *delegationsIterator { + i := &delegationsIterator{ + file: file, + stack: make([]delegation, 0, 1), + visited: make(map[delegationID]struct{}), + } + i.add([]data.DelegatedRole{role}, parent) + return i +} + +func (d *delegationsIterator) next() (delegation, bool) { + if len(d.stack) == 0 { + return delegation{}, false + } + delegation := d.stack[len(d.stack)-1] + d.stack = d.stack[:len(d.stack)-1] + + // 5.6.7.1 cycles protection + id := delegationID{delegation.parent, delegation.child.Name} + if _, ok := d.visited[id]; ok { + return d.next() + } + d.visited[id] = struct{}{} + + // 5.6.7.2 trim delegations to visit, only the current role and its delegations + // will be considered + // https://github.com/theupdateframework/specification/issues/168 + if delegation.child.Terminating { + d.stack = d.stack[0:0] + } + return delegation, true +} + +func (d *delegationsIterator) add(roles []data.DelegatedRole, parent string) { + for i := len(roles) - 1; i >= 0; i-- { + r := roles[i] + if r.MatchesPath(d.file) { + d.stack = append(d.stack, delegation{parent, r}) + } + } +} diff --git a/client/delegations_test.go b/client/delegations_test.go new file mode 100644 index 00000000..6308d575 --- /dev/null +++ b/client/delegations_test.go @@ -0,0 +1,315 @@ +package client + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "os" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/theupdateframework/go-tuf/data" + "github.com/theupdateframework/go-tuf/verify" +) + +var ( + defaultPaths = []string{"tmp", "*"} + notMatchingPaths = []string{"vars", "null"} +) + +func TestDelegationsIterator(t *testing.T) { + var iteratorTests = []struct { + testName string + roles map[string][]data.DelegatedRole + rootDelegation data.DelegatedRole + file string + resultOrder []string + }{ + { + "no termination", + map[string][]data.DelegatedRole{ + "a": []data.DelegatedRole{{Name: "b", Paths: defaultPaths}, {Name: "e", Paths: defaultPaths}}, + "b": []data.DelegatedRole{{Name: "c", Paths: defaultPaths}, {Name: "d", Paths: defaultPaths}}, + }, + data.DelegatedRole{Name: "a", Paths: defaultPaths}, + "", + []string{"a", "b", "c", "d", "e"}, + }, + { + "terminated in b", + map[string][]data.DelegatedRole{ + "a": []data.DelegatedRole{{Name: "b", Paths: defaultPaths, Terminating: true}, {Name: "e", Paths: defaultPaths}}, + "b": []data.DelegatedRole{{Name: "c", Paths: defaultPaths}, {Name: "d", Paths: defaultPaths}}, + }, + data.DelegatedRole{Name: "a", Paths: defaultPaths}, + "", + []string{"a", "b", "c", "d"}, + }, + { + "path does not match b", + map[string][]data.DelegatedRole{ + "a": []data.DelegatedRole{{Name: "b", Paths: notMatchingPaths}, {Name: "e", Paths: defaultPaths}}, + "b": []data.DelegatedRole{{Name: "c", Paths: defaultPaths}, {Name: "d", Paths: defaultPaths}}, + }, + data.DelegatedRole{Name: "a", Paths: defaultPaths}, + "", + []string{"a", "e"}, + }, + { + "cycle avoided", + map[string][]data.DelegatedRole{ + "a": []data.DelegatedRole{{Name: "b", Paths: defaultPaths}, {Name: "e", Paths: defaultPaths}}, + "b": []data.DelegatedRole{{Name: "a", Paths: defaultPaths}, {Name: "d", Paths: defaultPaths}}, + }, + data.DelegatedRole{Name: "a", Paths: defaultPaths}, + "", + []string{"a", "b", "a", "e", "d"}, + }, + } + + for _, tt := range iteratorTests { + t.Run(tt.testName, func(t *testing.T) { + d := newDelegationsIterator(tt.rootDelegation, "root", tt.file) + var iterationOrder []string + for { + r, ok := d.next() + if !ok { + break + } + iterationOrder = append(iterationOrder, r.child.Name) + delegations, ok := tt.roles[r.child.Name] + if !ok { + continue + } + d.add(delegations, r.child.Name) + } + assert.Equal(t, len(iterationOrder), len(tt.resultOrder)) + for i, role := range iterationOrder { + assert.Equal(t, role, tt.resultOrder[i]) + } + }) + } +} + +func TestGetTargetMeta(t *testing.T) { + verify.IsExpired = func(t time.Time) bool { return false } + c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") + defer closer() + _, err := c.Update() + assert.Nil(t, err) + + f, err := c.getTargetFileMeta("f.txt") + assert.Nil(t, err) + assert.Equal(t, f.Length, int64(15)) +} + +func TestMaxDelegations(t *testing.T) { + verify.IsExpired = func(t time.Time) bool { return false } + c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") + defer closer() + _, err := c.Update() + assert.Nil(t, err) + c.MaxDelegations = 2 + _, err = c.getTargetFileMeta("c.txt") + assert.Equal(t, err, ErrMaxDelegations{File: "c.txt", MaxDelegations: 2, SnapshotVersion: 2}) +} + +func TestMetaNotFound(t *testing.T) { + verify.IsExpired = func(t time.Time) bool { return false } + c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") + defer closer() + _, err := c.Update() + assert.Nil(t, err) + _, err = c.getTargetFileMeta("unknown.txt") + assert.Equal(t, err, ErrUnknownTarget{Name: "unknown.txt", SnapshotVersion: 2}) +} + +func TestPersistedMeta(t *testing.T) { + verify.IsExpired = func(t time.Time) bool { return false } + c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") + defer closer() + _, err := c.Update() + assert.Nil(t, err) + + _, err = c.local.GetMeta() + assert.Nil(t, err) + + type expectedTargets struct { + name string + version int + } + var persistedTests = []struct { + file string + targets []expectedTargets + downloadError error + fileContent string + }{ + { + "unknown", + []expectedTargets{ + { + "targets.json", + 2, + }, + }, + ErrUnknownTarget{Name: "unknown", SnapshotVersion: 2}, + "", + }, + { + "b.txt", + []expectedTargets{ + { + "targets.json", + 2, + }, + { + "a.json", + 1, + }, + { + "b.json", + 1, + }, + }, + nil, + "Contents: b.txt", + }, + { + "f.txt", + []expectedTargets{ + { + "targets.json", + 2, + }, + { + "a.json", + 1, + }, + { + "b.json", + 1, + }, + { + "c.json", + 1, + }, + { + "d.json", + 1, + }, + { + "e.json", + 1, + }, + { + "f.json", + 1, + }, + }, + nil, + "Contents: f.txt", + }, + } + + for _, tt := range persistedTests { + t.Run("search "+tt.file, func(t *testing.T) { + var dest testDestination + err = c.Download(tt.file, &dest) + assert.Equal(t, tt.downloadError, err) + assert.Equal(t, tt.fileContent, dest.String()) + + p, err := c.local.GetMeta() + assert.Nil(t, err) + persisted := copyStore(p) + // trim non targets metas + for _, notTargets := range []string{"root.json", "snapshot.json", "timestamp.json"} { + delete(persisted, notTargets) + } + for _, targets := range tt.targets { + storedVersion, err := versionOfStoredTargets(targets.name, persisted) + assert.Equal(t, storedVersion, targets.version) + assert.Nil(t, err) + delete(persisted, targets.name) + } + assert.Empty(t, persisted) + }) + } +} + +func versionOfStoredTargets(name string, store map[string]json.RawMessage) (int, error) { + rawTargets, ok := store[name] + if !ok { + return 0, nil + } + s := &data.Signed{} + if err := json.Unmarshal(rawTargets, s); err != nil { + return 0, err + } + targets := &data.Targets{} + if err := json.Unmarshal(s.Signed, targets); err != nil { + return 0, err + } + return targets.Version, nil +} + +func initTestDelegationClient(t *testing.T, dirPrefix string) (*Client, func() error) { + serverDir := dirPrefix + "/server" + initialStateDir := dirPrefix + "/client/metadata/current" + l, err := net.Listen("tcp", "127.0.0.1:0") + assert.Nil(t, err) + addr := l.Addr().String() + go http.Serve(l, http.FileServer(http.Dir(serverDir))) + + opts := &HTTPRemoteOptions{ + MetadataPath: "metadata", + TargetsPath: "targets", + } + remote, err := HTTPRemoteStore(fmt.Sprintf("http://%s/", addr), opts, nil) + + c := NewClient(MemoryLocalStore(), remote) + rawFile, err := os.ReadFile(initialStateDir + "/" + "root.json") + assert.Nil(t, err) + s := &data.Signed{} + root := &data.Root{} + assert.Nil(t, json.Unmarshal(rawFile, s)) + assert.Nil(t, json.Unmarshal(s.Signed, root)) + var keys []*data.Key + for _, sig := range s.Signatures { + k, ok := root.Keys[sig.KeyID] + if ok { + keys = append(keys, k) + } + } + + assert.Nil(t, c.Init(keys, 1)) + files, err := os.ReadDir(initialStateDir) + assert.Nil(t, err) + + // load local files + for _, f := range files { + if f.IsDir() { + continue + } + name := f.Name() + // ignoring consistent snapshot when loading initial state + if len(strings.Split(name, ".")) == 1 && strings.HasSuffix(name, ".json") { + rawFile, err := os.ReadFile(initialStateDir + "/" + name) + assert.Nil(t, err) + assert.Nil(t, c.local.SetMeta(name, rawFile)) + } + } + return c, l.Close +} + +func copyStore(store map[string]json.RawMessage) map[string]json.RawMessage { + new := make(map[string]json.RawMessage, len(store)) + for k, raw := range store { + newRaw := make([]byte, len(raw)) + copy(newRaw, []byte(raw)) + new[k] = json.RawMessage(newRaw) + } + return new +} diff --git a/client/errors.go b/client/errors.go index e9997f57..dc2db6ac 100644 --- a/client/errors.go +++ b/client/errors.go @@ -10,6 +10,7 @@ import ( var ( ErrNoRootKeys = errors.New("tuf: no root keys found in local meta store") ErrInsufficientKeys = errors.New("tuf: insufficient keys to meet threshold") + ErrNoLocalSnapshot = errors.New("tuf: no snapshot stored locally") ) type ErrMissingRemoteMetadata struct { @@ -38,6 +39,16 @@ func (e ErrDecodeFailed) Error() string { return fmt.Sprintf("tuf: failed to decode %s: %s", e.File, e.Err) } +type ErrMaxDelegations struct { + File string + MaxDelegations int + SnapshotVersion int +} + +func (e ErrMaxDelegations) Error() string { + return fmt.Sprintf("tuf: max delegation of %d reached searching for %s with snapshot version %d", e.MaxDelegations, e.File, e.SnapshotVersion) +} + func isDecodeFailedWithErrRoleThreshold(err error) bool { e, ok := err.(ErrDecodeFailed) if !ok { @@ -88,11 +99,12 @@ func IsLatestSnapshot(err error) bool { } type ErrUnknownTarget struct { - Name string + Name string + SnapshotVersion int } func (e ErrUnknownTarget) Error() string { - return fmt.Sprintf("tuf: unknown target file: %s", e.Name) + return fmt.Sprintf("tuf: unknown target file: %s with snapshot version %d", e.Name, e.SnapshotVersion) } type ErrMetaTooLarge struct { @@ -112,3 +124,23 @@ type ErrInvalidURL struct { func (e ErrInvalidURL) Error() string { return fmt.Sprintf("tuf: invalid repository URL %s", e.URL) } + +type ErrRoleNotInSnapshot struct { + Role string + SnapshotVersion int +} + +func (e ErrRoleNotInSnapshot) Error() string { + return fmt.Sprintf("tuf: role %s not in snapshot version %d", e.Role, e.SnapshotVersion) +} + +type ErrTargetsSnapshotVersionMismatch struct { + Role string + DownloadedTargetsVersion int + TargetsSnapshotVersion int + SnapshotVersion int +} + +func (e ErrTargetsSnapshotVersionMismatch) Error() string { + return fmt.Sprintf("tuf: downloaded %s version %d expected %d in snapshot v%d ", e.Role, e.DownloadedTargetsVersion, e.TargetsSnapshotVersion, e.SnapshotVersion) +} diff --git a/data/hex_bytes.go b/data/hex_bytes.go index 5dee51cc..ec200412 100644 --- a/data/hex_bytes.go +++ b/data/hex_bytes.go @@ -1,6 +1,7 @@ package data import ( + "crypto/sha256" "encoding/hex" "errors" ) @@ -31,3 +32,11 @@ func (b HexBytes) MarshalJSON() ([]byte, error) { func (b HexBytes) String() string { return hex.EncodeToString(b) } + +// 4.5. File formats: targets.json and delegated target roles: +// ...each target path, when hashed with the SHA-256 hash function to produce +// a 64-byte hexadecimal digest (HEX_DIGEST)... +func PathHexDigest(s string) string { + b := sha256.Sum256([]byte(s)) + return hex.EncodeToString(b[:]) +} diff --git a/data/types.go b/data/types.go index 27519f54..a1673226 100644 --- a/data/types.go +++ b/data/types.go @@ -4,6 +4,8 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" + "path/filepath" + "strings" "sync" "time" @@ -208,11 +210,44 @@ func (f TargetFileMeta) HashAlgorithms() []string { } type Targets struct { - Type string `json:"_type"` - SpecVersion string `json:"spec_version"` - Version int `json:"version"` - Expires time.Time `json:"expires"` - Targets TargetFiles `json:"targets"` + Type string `json:"_type"` + SpecVersion string `json:"spec_version"` + Version int `json:"version"` + Expires time.Time `json:"expires"` + Targets TargetFiles `json:"targets"` + Delegations *Delegations `json:"delegations,omitempty"` +} + +type Delegations struct { + Keys map[string]*Key `json:"keys"` + Roles []DelegatedRole `json:"roles"` +} + +type DelegatedRole struct { + Name string `json:"name"` + KeyIDs []string `json:"keyids"` + Threshold int `json:"threshold"` + PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"` + Paths []string `json:"paths"` + Terminating bool `json:"terminating"` +} + +func (d DelegatedRole) MatchesPath(file string) bool { + for _, path := range d.Paths { + if matched, _ := filepath.Match(path, file); matched { + return true + } + } + if len(d.PathHashPrefixes) == 0 { + return false + } + pathHash := PathHexDigest(file) + for _, prefix := range d.PathHashPrefixes { + if strings.HasPrefix(pathHash, prefix) { + return true + } + } + return false } func NewTargets() *Targets { diff --git a/data/types_test.go b/data/types_test.go index d5b00f2e..ac966b64 100644 --- a/data/types_test.go +++ b/data/types_test.go @@ -2,7 +2,9 @@ package data import ( "encoding/json" + "testing" + "github.com/stretchr/testify/assert" . "gopkg.in/check.v1" ) @@ -91,3 +93,62 @@ func (TypesSuite) TestRoleAddKeyIDs(c *C) { c.Assert(role.AddKeyIDs(key.IDs()), Equals, true) c.Assert(role.KeyIDs, DeepEquals, []string{keyid10, keyid10algos}) } + +func TestDelegatedRolePathMatch(t *testing.T) { + var tts = []struct { + testName string + file string + pathHashPrefixes []string + paths []string + matches bool + }{ + { + testName: "no path", + file: "licence.txt", + }, + { + testName: "match path *", + paths: []string{"null", "targets/*.tgz"}, + file: "targets/foo.tgz", + matches: true, + }, + { + testName: "does not match path *", + paths: []string{"null", "targets/*.tgz"}, + file: "targets/foo.txt", + }, + { + testName: "match path ?", + paths: []string{"foo-version-?.tgz"}, + file: "foo-version-a.tgz", + matches: true, + }, + { + testName: "does not match ?", + paths: []string{"foo-version-?.tgz"}, + file: "foo-version-alpha.tgz", + }, + // picked from https://github.com/theupdateframework/tuf/blob/30ba6e9f9ab25e0370e29ce574dada2d8809afa0/tests/test_updater.py#L1726-L1734 + { + testName: "match hash prefix", + pathHashPrefixes: []string{"badd", "8baf"}, + file: "/file3.txt", + matches: true, + }, + { + testName: "does not match hash prefix", + pathHashPrefixes: []string{"badd"}, + file: "/file3.txt", + }, + } + for _, tt := range tts { + t.Run(tt.testName, func(t *testing.T) { + d := DelegatedRole{ + PathHashPrefixes: tt.pathHashPrefixes, + Paths: tt.paths, + } + assert.Equal(t, tt.matches, d.MatchesPath(tt.file)) + }) + + } +} diff --git a/go.mod b/go.mod index a3539921..47b72185 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/dustin/go-humanize v1.0.0 github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/stretchr/testify v1.7.0 // indirect github.com/syndtr/goleveldb v1.0.0 github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d diff --git a/go.sum b/go.sum index 9a22576e..7dd8a821 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e h1:Ss/B3/5wWRh8+emnK0++g5zQzwDTi30W10pKxKc4JXI= @@ -15,6 +17,11 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 h1:iGnD/q9160NWqKZZ5vY4p0dMiYMRknzctfSkqA4nBDw= @@ -36,3 +43,5 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/verify/db.go b/verify/db.go index 8bc80a3c..b1c75834 100644 --- a/verify/db.go +++ b/verify/db.go @@ -26,6 +26,32 @@ func NewDB() *DB { } } +type DelegationsVerifier interface { + Unmarshal([]byte, interface{}, string, int) error +} + +// 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) { + db := &DB{ + roles: make(map[string]*Role, len(d.Roles)), + keys: make(map[string]*data.Key), + } + for _, r := range d.Roles { + role := &data.Role{Threshold: r.Threshold, KeyIDs: r.KeyIDs} + if err := db.addRole(r.Name, role); err != nil { + return nil, err + } + } + for id, k := range d.Keys { + if err := db.AddKey(id, k); err != nil { + return nil, err + } + } + return db, nil +} + func (db *DB) AddKey(id string, k *data.Key) error { v, ok := Verifiers[k.Type] if !ok { @@ -59,6 +85,10 @@ func (db *DB) AddRole(name string, r *data.Role) error { if !ValidRole(name) { return ErrInvalidRole } + return db.addRole(name, r) +} + +func (db *DB) addRole(name string, r *data.Role) error { if r.Threshold < 1 { return ErrInvalidThreshold } diff --git a/verify/verify.go b/verify/verify.go index eb618ae0..bdf04694 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -24,7 +24,7 @@ func (db *DB) Verify(s *data.Signed, role string, minVersion int) error { if err := json.Unmarshal(s.Signed, sm); err != nil { return err } - if strings.ToLower(sm.Type) != strings.ToLower(role) { + if typ := strings.ToLower(sm.Type); typ != strings.ToLower(role) && typ != "targets" { return ErrWrongMetaType } if IsExpired(sm.Expires) { From b01e941b12edefa86816c1ed2d2d5eac65c2ff2e Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Fri, 9 Jul 2021 20:37:16 +0200 Subject: [PATCH 02/22] Add TUF3 php test --- .../__init__.py | 35 +++++++ .../client/metadata/current/1.root.json | 87 ++++++++++++++++++ .../client/metadata/current/1.snapshot.json | 19 ++++ .../client/metadata/current/1.targets.json | 19 ++++ .../client/metadata/current/1.timestamp.json | 24 +++++ .../client/metadata/current/root.json | 87 ++++++++++++++++++ .../client/metadata/current/snapshot.json | 19 ++++ .../client/metadata/current/targets.json | 19 ++++ .../client/metadata/current/timestamp.json | 24 +++++ .../client/metadata/previous/1.root.json | 87 ++++++++++++++++++ .../client/metadata/previous/1.snapshot.json | 19 ++++ .../client/metadata/previous/1.targets.json | 19 ++++ .../client/metadata/previous/1.timestamp.json | 24 +++++ .../client/metadata/previous/root.json | 87 ++++++++++++++++++ .../client/metadata/previous/snapshot.json | 19 ++++ .../client/metadata/previous/targets.json | 19 ++++ .../client/metadata/previous/timestamp.json | 24 +++++ .../server/metadata/.1.f.json.swp | Bin 0 -> 12288 bytes .../server/metadata/1.a.json | 74 +++++++++++++++ .../server/metadata/1.b.json | 74 +++++++++++++++ .../server/metadata/1.c.json | 28 ++++++ .../server/metadata/1.d.json | 28 ++++++ .../server/metadata/1.e.json | 28 ++++++ .../server/metadata/1.f.json | 28 ++++++ .../server/metadata/1.root.json | 87 ++++++++++++++++++ .../server/metadata/1.snapshot.json | 19 ++++ .../server/metadata/1.targets.json | 19 ++++ .../server/metadata/1.timestamp.json | 24 +++++ .../server/metadata/2.root.json | 87 ++++++++++++++++++ .../server/metadata/2.snapshot.json | 37 ++++++++ .../server/metadata/2.targets.json | 73 +++++++++++++++ .../server/metadata/2.timestamp.json | 24 +++++ .../server/metadata/a.json | 74 +++++++++++++++ .../server/metadata/b.json | 74 +++++++++++++++ .../server/metadata/c.json | 28 ++++++ .../server/metadata/d.json | 28 ++++++ .../server/metadata/e.json | 28 ++++++ .../server/metadata/f.json | 28 ++++++ .../server/metadata/root.json | 87 ++++++++++++++++++ .../server/metadata/snapshot.json | 37 ++++++++ .../server/metadata/targets.json | 73 +++++++++++++++ .../server/metadata/timestamp.json | 24 +++++ ...aa0d59d82cc18411037b0ef0318eb754ce86.e.txt | 1 + ...4d3a52221c0cf81ef7b68ce312476a.targets.txt | 1 + ...18daf93b4c0083306cef17df547b42e4e985.a.txt | 1 + ...bf4ebfaaaa3ea2011b57b4af63f7e6.targets.txt | 1 + ...ae784c3d3488e994d29f003decc81638d589.d.txt | 1 + ...fb207966bc32bd6324ebdad2c50c82075ffd.c.txt | 1 + ...669ba031919ccf9bce0f5b4d0b61d1c0aa2e.b.txt | 1 + .../server/targets/a.txt | 1 + .../server/targets/b.txt | 1 + ...4f60baaad5d6e0a7d544e65e1791f2df5e55.d.txt | 1 + ...7ec0b65bb253cf5f5b961515658864301207.f.txt | 1 + .../server/targets/c.txt | 1 + .../server/targets/d.txt | 1 + ...f15dbe0d63fbc5ca7a9619fb4c39544d78a9.c.txt | 1 + .../server/targets/e.txt | 1 + .../server/targets/f.txt | 1 + ...d04623b0171de458d94dbec4abb7646bc60e.f.txt | 1 + ...0131ce49ba86066ab5f7b7480dde719e0bfd.a.txt | 1 + ...acbf1af8393fdbfed899cecb995c87a11e3c.b.txt | 1 + ...611bc9ed2f2841da2c0dd70bb870be7275f6.e.txt | 1 + .../server/targets/targets.txt | 1 + 63 files changed, 1763 insertions(+) create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/__init__.py create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.root.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.snapshot.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.targets.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.timestamp.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/root.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/snapshot.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/targets.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/timestamp.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.root.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.snapshot.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.targets.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.timestamp.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/root.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/snapshot.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/targets.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/timestamp.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/.1.f.json.swp create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.a.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.b.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.c.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.d.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.e.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.f.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.root.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.snapshot.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.targets.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.timestamp.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.root.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.snapshot.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.targets.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.timestamp.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/a.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/b.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/c.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/d.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/e.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/f.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/root.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/snapshot.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/targets.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/timestamp.json create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/027b3d1814dae8547aed3d7e7b99aa0d59d82cc18411037b0ef0318eb754ce86.e.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/0951c719b5adf26a55700bd1ac6b657fb513b04e4adb335a935a0da7f12f2ac99834b47442d0b832e5f4ffdd365ef538f14d3a52221c0cf81ef7b68ce312476a.targets.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/3f90cedf303207851bbdc5f857e018daf93b4c0083306cef17df547b42e4e985.a.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/6da6f624811b0d7792c57dc9ed58f1af8cbf4ebfaaaa3ea2011b57b4af63f7e6.targets.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/79b44a183eaa0025f3997b856537502ac1759de74263985ad1dd9ab0bb5cc8ce46b6c45c1bf37adb87c2839cca54ae784c3d3488e994d29f003decc81638d589.d.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/946e2ea9180de673891ae09ce0edfb207966bc32bd6324ebdad2c50c82075ffd.c.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/949c6a4318dabe8bbd140cef99ea669ba031919ccf9bce0f5b4d0b61d1c0aa2e.b.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/a.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b2ce56612fa7e840d56d263f75804f60baaad5d6e0a7d544e65e1791f2df5e55.d.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b8061c4c83d478aaadc6758a74861f749420e83dd6bf07e931738b0619404a75d12758429b12f8d047643d6d13047ec0b65bb253cf5f5b961515658864301207.f.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/c.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/d.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/ddb5fb256a368d778b5fdd03d497ad79944c766245f3cccfc8b098b14c488ec424a68b86a6a2add36db4ef0f0214f15dbe0d63fbc5ca7a9619fb4c39544d78a9.c.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/e.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f0b07df15d4a3e98ffc6ee6a4c00d04623b0171de458d94dbec4abb7646bc60e.f.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f4631ef7ea7b015d7b88e411842fafeb78a72f0181bec72ea9754604ede74ea0e491bf8411659aabc96304fc764d0131ce49ba86066ab5f7b7480dde719e0bfd.a.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f4cc9ce5c73b37e2a6707af7a0ea614ea5fa428bd2509e3af4528a5d330ce98a09c4dd98c859ad9b27b8aba24e1eacbf1af8393fdbfed899cecb995c87a11e3c.b.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/ffca8c347335e5e019deba8a15212297fe25afc734ac06fcd01b4d9dae12b16d750d7a44bdafcada254148990aa6611bc9ed2f2841da2c0dd70bb870be7275f6.e.txt create mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/targets.txt diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/__init__.py b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/__init__.py new file mode 100644 index 00000000..36285a68 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/__init__.py @@ -0,0 +1,35 @@ +# Delegation tree +# +# Targets +# / \ +# a f +# / \ +# b e +# / \ +# c d +# +# No terminating delegations. +# +# Roles should be evaluated in the order: +# Targets > a > b > c > d > e > f + +from fixtures.builder import FixtureBuilder + + +def build(): + FixtureBuilder('TUFTestFixture3LevelDelegation')\ + .publish(with_client=True)\ + .create_target('targets.txt')\ + .delegate('a', ['*.txt'])\ + .create_target('a.txt', signing_role='a')\ + .delegate('b', ['*.txt'], parent='a') \ + .create_target('b.txt', signing_role='b') \ + .delegate('c', ['*.txt'], parent='b') \ + .create_target('c.txt', signing_role='c') \ + .delegate('d', ['*.txt'], parent='b') \ + .create_target('d.txt', signing_role='d') \ + .delegate('e', ['*.txt'], parent='a') \ + .create_target('e.txt', signing_role='e') \ + .delegate('f', ['*.txt']) \ + .create_target('f.txt', signing_role='f') \ + .publish() diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.root.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.root.json new file mode 100644 index 00000000..08986107 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129", + "sig": "d0bf76a5cfc0aee1b8a1b1bf0ed8ca646a1a6d5f205945c515e8546bfd3c1e6b5e07cc0b93836bd030dd05ba68f177aecb05f6bf90c6702fd178e53310022506" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2020-12-31T05:48:20Z", + "keys": { + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6bac59b8d9e1aae02fae6fba6e7fe3fc9fe5b4a9fe98c3fca255d8c8ec3e5b35" + }, + "scheme": "ed25519" + }, + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6400d770c7c1bce4b3d59ce0079ed686e843b6500bbea77d869a1ae7df4565a1" + }, + "scheme": "ed25519" + }, + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "28bf74baa87ed923f8fa27e3292684f8ec4730ce0bdc65150ed58199206ce089" + }, + "scheme": "ed25519" + }, + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e6ae9d3b67d7b3ce274130291dd90287f32b8fd72bfb4ac5430859ebd1c28a46" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.snapshot.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.snapshot.json new file mode 100644 index 00000000..dcbd2f47 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.snapshot.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93", + "sig": "61db8765350398f7f750853337d9a55c5d6e790812d29146b5b45d5fd43d2a42c474a7a9fab263c3a50a28114a82f79dbf24ff1f99ae737a8d06f332f9f7d103" + } + ], + "signed": { + "_type": "snapshot", + "expires": "2020-01-08T00:00:00Z", + "meta": { + "targets.json": { + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.targets.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.targets.json new file mode 100644 index 00000000..820691ec --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.targets.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4", + "sig": "c150e8ed5d352f366a979f4c4b9d556350c414c2da7ef1279045aaed3438c60872142d0dfe5ddbb627fec2d8fb7c5d8e692e04a87230b78d74714c5db035620a" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": {}, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.timestamp.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.timestamp.json new file mode 100644 index 00000000..aae05fba --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/1.timestamp.json @@ -0,0 +1,24 @@ +{ + "signatures": [ + { + "keyid": "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae", + "sig": "1d668531c7a0960cf90825faa684106a8aef0799c1b47e72301bac45d87f2dd42c14f1a3ac7db862323ca5177dd4fd686573ea92aea99638f17414dde561c00b" + } + ], + "signed": { + "_type": "timestamp", + "expires": "2020-01-02T00:00:00Z", + "meta": { + "snapshot.json": { + "hashes": { + "sha256": "f4ca389c2c9fbc592d91d4e693c31113b8803a11bcb5ecd973581fa0e3d34ce0", + "sha512": "92a0989e44c0e9f16d3e56268a3b8dd4e4416ee2ac91a4c871a405f1e426062651ec4effa0078fc4409c8b0422ccad9b1aa197db58f178406f398562b2e98195" + }, + "length": 431, + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/root.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/root.json new file mode 100644 index 00000000..08986107 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129", + "sig": "d0bf76a5cfc0aee1b8a1b1bf0ed8ca646a1a6d5f205945c515e8546bfd3c1e6b5e07cc0b93836bd030dd05ba68f177aecb05f6bf90c6702fd178e53310022506" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2020-12-31T05:48:20Z", + "keys": { + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6bac59b8d9e1aae02fae6fba6e7fe3fc9fe5b4a9fe98c3fca255d8c8ec3e5b35" + }, + "scheme": "ed25519" + }, + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6400d770c7c1bce4b3d59ce0079ed686e843b6500bbea77d869a1ae7df4565a1" + }, + "scheme": "ed25519" + }, + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "28bf74baa87ed923f8fa27e3292684f8ec4730ce0bdc65150ed58199206ce089" + }, + "scheme": "ed25519" + }, + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e6ae9d3b67d7b3ce274130291dd90287f32b8fd72bfb4ac5430859ebd1c28a46" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/snapshot.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/snapshot.json new file mode 100644 index 00000000..dcbd2f47 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/snapshot.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93", + "sig": "61db8765350398f7f750853337d9a55c5d6e790812d29146b5b45d5fd43d2a42c474a7a9fab263c3a50a28114a82f79dbf24ff1f99ae737a8d06f332f9f7d103" + } + ], + "signed": { + "_type": "snapshot", + "expires": "2020-01-08T00:00:00Z", + "meta": { + "targets.json": { + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/targets.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/targets.json new file mode 100644 index 00000000..820691ec --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/targets.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4", + "sig": "c150e8ed5d352f366a979f4c4b9d556350c414c2da7ef1279045aaed3438c60872142d0dfe5ddbb627fec2d8fb7c5d8e692e04a87230b78d74714c5db035620a" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": {}, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/timestamp.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/timestamp.json new file mode 100644 index 00000000..aae05fba --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/current/timestamp.json @@ -0,0 +1,24 @@ +{ + "signatures": [ + { + "keyid": "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae", + "sig": "1d668531c7a0960cf90825faa684106a8aef0799c1b47e72301bac45d87f2dd42c14f1a3ac7db862323ca5177dd4fd686573ea92aea99638f17414dde561c00b" + } + ], + "signed": { + "_type": "timestamp", + "expires": "2020-01-02T00:00:00Z", + "meta": { + "snapshot.json": { + "hashes": { + "sha256": "f4ca389c2c9fbc592d91d4e693c31113b8803a11bcb5ecd973581fa0e3d34ce0", + "sha512": "92a0989e44c0e9f16d3e56268a3b8dd4e4416ee2ac91a4c871a405f1e426062651ec4effa0078fc4409c8b0422ccad9b1aa197db58f178406f398562b2e98195" + }, + "length": 431, + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.root.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.root.json new file mode 100644 index 00000000..08986107 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129", + "sig": "d0bf76a5cfc0aee1b8a1b1bf0ed8ca646a1a6d5f205945c515e8546bfd3c1e6b5e07cc0b93836bd030dd05ba68f177aecb05f6bf90c6702fd178e53310022506" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2020-12-31T05:48:20Z", + "keys": { + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6bac59b8d9e1aae02fae6fba6e7fe3fc9fe5b4a9fe98c3fca255d8c8ec3e5b35" + }, + "scheme": "ed25519" + }, + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6400d770c7c1bce4b3d59ce0079ed686e843b6500bbea77d869a1ae7df4565a1" + }, + "scheme": "ed25519" + }, + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "28bf74baa87ed923f8fa27e3292684f8ec4730ce0bdc65150ed58199206ce089" + }, + "scheme": "ed25519" + }, + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e6ae9d3b67d7b3ce274130291dd90287f32b8fd72bfb4ac5430859ebd1c28a46" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.snapshot.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.snapshot.json new file mode 100644 index 00000000..dcbd2f47 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.snapshot.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93", + "sig": "61db8765350398f7f750853337d9a55c5d6e790812d29146b5b45d5fd43d2a42c474a7a9fab263c3a50a28114a82f79dbf24ff1f99ae737a8d06f332f9f7d103" + } + ], + "signed": { + "_type": "snapshot", + "expires": "2020-01-08T00:00:00Z", + "meta": { + "targets.json": { + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.targets.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.targets.json new file mode 100644 index 00000000..820691ec --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.targets.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4", + "sig": "c150e8ed5d352f366a979f4c4b9d556350c414c2da7ef1279045aaed3438c60872142d0dfe5ddbb627fec2d8fb7c5d8e692e04a87230b78d74714c5db035620a" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": {}, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.timestamp.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.timestamp.json new file mode 100644 index 00000000..aae05fba --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/1.timestamp.json @@ -0,0 +1,24 @@ +{ + "signatures": [ + { + "keyid": "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae", + "sig": "1d668531c7a0960cf90825faa684106a8aef0799c1b47e72301bac45d87f2dd42c14f1a3ac7db862323ca5177dd4fd686573ea92aea99638f17414dde561c00b" + } + ], + "signed": { + "_type": "timestamp", + "expires": "2020-01-02T00:00:00Z", + "meta": { + "snapshot.json": { + "hashes": { + "sha256": "f4ca389c2c9fbc592d91d4e693c31113b8803a11bcb5ecd973581fa0e3d34ce0", + "sha512": "92a0989e44c0e9f16d3e56268a3b8dd4e4416ee2ac91a4c871a405f1e426062651ec4effa0078fc4409c8b0422ccad9b1aa197db58f178406f398562b2e98195" + }, + "length": 431, + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/root.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/root.json new file mode 100644 index 00000000..08986107 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129", + "sig": "d0bf76a5cfc0aee1b8a1b1bf0ed8ca646a1a6d5f205945c515e8546bfd3c1e6b5e07cc0b93836bd030dd05ba68f177aecb05f6bf90c6702fd178e53310022506" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2020-12-31T05:48:20Z", + "keys": { + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6bac59b8d9e1aae02fae6fba6e7fe3fc9fe5b4a9fe98c3fca255d8c8ec3e5b35" + }, + "scheme": "ed25519" + }, + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6400d770c7c1bce4b3d59ce0079ed686e843b6500bbea77d869a1ae7df4565a1" + }, + "scheme": "ed25519" + }, + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "28bf74baa87ed923f8fa27e3292684f8ec4730ce0bdc65150ed58199206ce089" + }, + "scheme": "ed25519" + }, + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e6ae9d3b67d7b3ce274130291dd90287f32b8fd72bfb4ac5430859ebd1c28a46" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/snapshot.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/snapshot.json new file mode 100644 index 00000000..dcbd2f47 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/snapshot.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93", + "sig": "61db8765350398f7f750853337d9a55c5d6e790812d29146b5b45d5fd43d2a42c474a7a9fab263c3a50a28114a82f79dbf24ff1f99ae737a8d06f332f9f7d103" + } + ], + "signed": { + "_type": "snapshot", + "expires": "2020-01-08T00:00:00Z", + "meta": { + "targets.json": { + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/targets.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/targets.json new file mode 100644 index 00000000..820691ec --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/targets.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4", + "sig": "c150e8ed5d352f366a979f4c4b9d556350c414c2da7ef1279045aaed3438c60872142d0dfe5ddbb627fec2d8fb7c5d8e692e04a87230b78d74714c5db035620a" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": {}, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/timestamp.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/timestamp.json new file mode 100644 index 00000000..aae05fba --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/client/metadata/previous/timestamp.json @@ -0,0 +1,24 @@ +{ + "signatures": [ + { + "keyid": "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae", + "sig": "1d668531c7a0960cf90825faa684106a8aef0799c1b47e72301bac45d87f2dd42c14f1a3ac7db862323ca5177dd4fd686573ea92aea99638f17414dde561c00b" + } + ], + "signed": { + "_type": "timestamp", + "expires": "2020-01-02T00:00:00Z", + "meta": { + "snapshot.json": { + "hashes": { + "sha256": "f4ca389c2c9fbc592d91d4e693c31113b8803a11bcb5ecd973581fa0e3d34ce0", + "sha512": "92a0989e44c0e9f16d3e56268a3b8dd4e4416ee2ac91a4c871a405f1e426062651ec4effa0078fc4409c8b0422ccad9b1aa197db58f178406f398562b2e98195" + }, + "length": 431, + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/.1.f.json.swp b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/.1.f.json.swp new file mode 100644 index 0000000000000000000000000000000000000000..8fe9767ff0c659e5373b30d690c5229e1597e848 GIT binary patch literal 12288 zcmeI2J&znk5QfLd0f7w$OGE_AAQI%;>FVz}5g>t#kT4LO2{4xFkLumE_jXr1bEmT` zLj?bU93m1TM8-dW5D}8$z%d!3W^T_&mYpJD)o9+G?XK#s>Zc}IZAJ0P``0FZDl57! zRn)dzTt%Xe{!v(PWrtER*4F>aef?8CSore%7&Td$s)DI1u;vq<1#r6h>qWUtq+ zO)kHD{aHLDpNR=D0Vco%m;e)C0!)AjFoCBK=r`x8FUj`Y&i?A|dG4|2ZpaTNzyz28 z6JP>NfC(@GCcp%k025#WOyGY=Ampn0>Oxh$bdi#$|NqZ^|Ns4BRXrp37=uJ<{8xhV&-s4bm&5%cLRpB@$mu zfC(@GCcp%k025#WOn?b6f&Y}i{c1w@q^5@9j(Ugd{Rz%D z*+!7XXQ7?dD%%W-zG4#5ni!=~shDD7Yr%lA#(VM-Acd>P#Kw%Iu|q~hMB#D)lXXzo z`cjfbv_U5!Xn!kJ6yPAEHa@pHM@%}zNO5gUR$x66nAG#o&9S2$?~Nhqbkz0h!(n{N zs4mm~q~F==K5S>$pSjoF2Ggy7$<{CxQ!(ybq4xwF2)8fYK8R08 zr(J~YdO14YxH+Uy-@&_MfYDm#M{VDaqTcH7ZgAN6EE_sX-7RJ-%%^$Y9L_dWeViUV z3vq?4Vo4?|Dj2k_i;yU>By4sGtXC+CL#JITIp$)M17EE0;2~1sp-`)>46Q1~BZxLC z=YmtAP?Z!?wY)Sg3L}Dv7L*h8b^z5u6|0H%m<*k4F{D`2B1mn(QrgzHAZ$Rypp1ex pLxv)(15Iy18)PX$7^&NADH%gIML8`V6>L0p7<=Yv+Z)xr>K|&!9Nho_ literal 0 HcmV?d00001 diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.a.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.a.json new file mode 100644 index 00000000..90237d1e --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.a.json @@ -0,0 +1,74 @@ +{ + "signatures": [ + { + "keyid": "05e17c1501d627b2597322f80d33aacec6f30a507552d3326a88913422b0e30b", + "sig": "35eae38d12b845e5e2fb89c957b1e4b16bccfb5717160e0c1a36924b3ae89972d78e0837639e266cf5957ddf9d1f7eb184148ef2d242d7ff99b844342b48020a" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": { + "15024498e03f033ec92758a1dc7107b34eebe759b09827b02a7fb3c64ca3e586": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e104199cda6e018d7d9044fa6225aa5dc9c2af5ee4e1c0fe6d16ad002220390d" + }, + "scheme": "ed25519" + }, + "718fedad390b4d0d470b890781eb8c94e5a7e975aebe65fc0862246c945fce68": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82f52e4503dbb364fabe8e5567f1cf909d4175d45468a021dfe75653db9ac98c" + }, + "scheme": "ed25519" + } + }, + "roles": [ + { + "keyids": [ + "718fedad390b4d0d470b890781eb8c94e5a7e975aebe65fc0862246c945fce68" + ], + "name": "b", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + }, + { + "keyids": [ + "15024498e03f033ec92758a1dc7107b34eebe759b09827b02a7fb3c64ca3e586" + ], + "name": "e", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + } + ] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "a.txt": { + "custom": {}, + "hashes": { + "sha256": "3f90cedf303207851bbdc5f857e018daf93b4c0083306cef17df547b42e4e985", + "sha512": "f4631ef7ea7b015d7b88e411842fafeb78a72f0181bec72ea9754604ede74ea0e491bf8411659aabc96304fc764d0131ce49ba86066ab5f7b7480dde719e0bfd" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.b.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.b.json new file mode 100644 index 00000000..bee9490e --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.b.json @@ -0,0 +1,74 @@ +{ + "signatures": [ + { + "keyid": "718fedad390b4d0d470b890781eb8c94e5a7e975aebe65fc0862246c945fce68", + "sig": "a99aa2bd1aa6473f48fc9c3a93289de4efea45071906bc4deecd3a85ffe0e6bc2c1b1751a2b0975e8f28d1b8114ac964f6dc55e5e160f602d56eff51d25c7907" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": { + "9ca81f7ff17f6218246474a51b47eb035741bc472557ef5ac493e279f446b85b": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "06e4dee0de7826c8d539a6112940b7459892b4ecaf696e67dc064aea0923f95c" + }, + "scheme": "ed25519" + }, + "e9829d3f2fdff6d6f31002c17cf7f20cf0398e215ca0c0c44d075ccd76a26f62": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "fa386632ae9cc358ad0b56565edef362ad10d7fadb05bc8dc8995627372b990e" + }, + "scheme": "ed25519" + } + }, + "roles": [ + { + "keyids": [ + "9ca81f7ff17f6218246474a51b47eb035741bc472557ef5ac493e279f446b85b" + ], + "name": "c", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + }, + { + "keyids": [ + "e9829d3f2fdff6d6f31002c17cf7f20cf0398e215ca0c0c44d075ccd76a26f62" + ], + "name": "d", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + } + ] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "b.txt": { + "custom": {}, + "hashes": { + "sha256": "949c6a4318dabe8bbd140cef99ea669ba031919ccf9bce0f5b4d0b61d1c0aa2e", + "sha512": "f4cc9ce5c73b37e2a6707af7a0ea614ea5fa428bd2509e3af4528a5d330ce98a09c4dd98c859ad9b27b8aba24e1eacbf1af8393fdbfed899cecb995c87a11e3c" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.c.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.c.json new file mode 100644 index 00000000..9305d64f --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.c.json @@ -0,0 +1,28 @@ +{ + "signatures": [ + { + "keyid": "9ca81f7ff17f6218246474a51b47eb035741bc472557ef5ac493e279f446b85b", + "sig": "9f0c5ef7e0a11012256c4a47ed757f9e90e930238f6a6e5c758a014f1b768519acb2b7d9aa9bd0456d71ba09b032598086e42d832830391e307c926c16f5b303" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "c.txt": { + "custom": {}, + "hashes": { + "sha256": "946e2ea9180de673891ae09ce0edfb207966bc32bd6324ebdad2c50c82075ffd", + "sha512": "ddb5fb256a368d778b5fdd03d497ad79944c766245f3cccfc8b098b14c488ec424a68b86a6a2add36db4ef0f0214f15dbe0d63fbc5ca7a9619fb4c39544d78a9" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.d.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.d.json new file mode 100644 index 00000000..2c9f1117 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.d.json @@ -0,0 +1,28 @@ +{ + "signatures": [ + { + "keyid": "e9829d3f2fdff6d6f31002c17cf7f20cf0398e215ca0c0c44d075ccd76a26f62", + "sig": "04d8264dd6d75fad106d599603801842057de038803935cf9b043fff4d5ee2618675c85291822f18efff770fa89056ed960de35309356a24baef45d9f64c8c0f" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "d.txt": { + "custom": {}, + "hashes": { + "sha256": "b2ce56612fa7e840d56d263f75804f60baaad5d6e0a7d544e65e1791f2df5e55", + "sha512": "79b44a183eaa0025f3997b856537502ac1759de74263985ad1dd9ab0bb5cc8ce46b6c45c1bf37adb87c2839cca54ae784c3d3488e994d29f003decc81638d589" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.e.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.e.json new file mode 100644 index 00000000..a473d7dc --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.e.json @@ -0,0 +1,28 @@ +{ + "signatures": [ + { + "keyid": "15024498e03f033ec92758a1dc7107b34eebe759b09827b02a7fb3c64ca3e586", + "sig": "11ea2f743a5e5ec7f5bea84804c1fdc2e70f42a1f955668b0072fc6bed00e6535574c129270f97e8849e589a57f48a077903244250ab970eac45f7c46c43b003" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "e.txt": { + "custom": {}, + "hashes": { + "sha256": "027b3d1814dae8547aed3d7e7b99aa0d59d82cc18411037b0ef0318eb754ce86", + "sha512": "ffca8c347335e5e019deba8a15212297fe25afc734ac06fcd01b4d9dae12b16d750d7a44bdafcada254148990aa6611bc9ed2f2841da2c0dd70bb870be7275f6" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.f.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.f.json new file mode 100644 index 00000000..941ae66f --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.f.json @@ -0,0 +1,28 @@ +{ + "signatures": [ + { + "keyid": "f581c9aeff9989106aeeea35319d1d1f067149619a2ff005249d6f60560557be", + "sig": "ff5f1d3bfc5d31f71997b1c020c06d7c1683e0f7e747cfdbdf6c718f608181b4c81f3212a93ff8e109b377a73af5883e1568257b050a5b6137058a10463f640e" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "f.txt": { + "custom": {}, + "hashes": { + "sha256": "f0b07df15d4a3e98ffc6ee6a4c00d04623b0171de458d94dbec4abb7646bc60e", + "sha512": "b8061c4c83d478aaadc6758a74861f749420e83dd6bf07e931738b0619404a75d12758429b12f8d047643d6d13047ec0b65bb253cf5f5b961515658864301207" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.root.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.root.json new file mode 100644 index 00000000..08986107 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129", + "sig": "d0bf76a5cfc0aee1b8a1b1bf0ed8ca646a1a6d5f205945c515e8546bfd3c1e6b5e07cc0b93836bd030dd05ba68f177aecb05f6bf90c6702fd178e53310022506" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2020-12-31T05:48:20Z", + "keys": { + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6bac59b8d9e1aae02fae6fba6e7fe3fc9fe5b4a9fe98c3fca255d8c8ec3e5b35" + }, + "scheme": "ed25519" + }, + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6400d770c7c1bce4b3d59ce0079ed686e843b6500bbea77d869a1ae7df4565a1" + }, + "scheme": "ed25519" + }, + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "28bf74baa87ed923f8fa27e3292684f8ec4730ce0bdc65150ed58199206ce089" + }, + "scheme": "ed25519" + }, + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e6ae9d3b67d7b3ce274130291dd90287f32b8fd72bfb4ac5430859ebd1c28a46" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.snapshot.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.snapshot.json new file mode 100644 index 00000000..dcbd2f47 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.snapshot.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93", + "sig": "61db8765350398f7f750853337d9a55c5d6e790812d29146b5b45d5fd43d2a42c474a7a9fab263c3a50a28114a82f79dbf24ff1f99ae737a8d06f332f9f7d103" + } + ], + "signed": { + "_type": "snapshot", + "expires": "2020-01-08T00:00:00Z", + "meta": { + "targets.json": { + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.targets.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.targets.json new file mode 100644 index 00000000..820691ec --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.targets.json @@ -0,0 +1,19 @@ +{ + "signatures": [ + { + "keyid": "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4", + "sig": "c150e8ed5d352f366a979f4c4b9d556350c414c2da7ef1279045aaed3438c60872142d0dfe5ddbb627fec2d8fb7c5d8e692e04a87230b78d74714c5db035620a" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": {}, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.timestamp.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.timestamp.json new file mode 100644 index 00000000..aae05fba --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/1.timestamp.json @@ -0,0 +1,24 @@ +{ + "signatures": [ + { + "keyid": "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae", + "sig": "1d668531c7a0960cf90825faa684106a8aef0799c1b47e72301bac45d87f2dd42c14f1a3ac7db862323ca5177dd4fd686573ea92aea99638f17414dde561c00b" + } + ], + "signed": { + "_type": "timestamp", + "expires": "2020-01-02T00:00:00Z", + "meta": { + "snapshot.json": { + "hashes": { + "sha256": "f4ca389c2c9fbc592d91d4e693c31113b8803a11bcb5ecd973581fa0e3d34ce0", + "sha512": "92a0989e44c0e9f16d3e56268a3b8dd4e4416ee2ac91a4c871a405f1e426062651ec4effa0078fc4409c8b0422ccad9b1aa197db58f178406f398562b2e98195" + }, + "length": 431, + "version": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.root.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.root.json new file mode 100644 index 00000000..2d5222c3 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129", + "sig": "e911db300e8eb378a2cabc48979e4c8f72beca30335db0579a7a1d81ae90c8bc63c27d92ce5a17846e3b1adb63a45a411bb4308e018e0d3cc4c9908311b5f208" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2020-12-31T05:48:20Z", + "keys": { + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6bac59b8d9e1aae02fae6fba6e7fe3fc9fe5b4a9fe98c3fca255d8c8ec3e5b35" + }, + "scheme": "ed25519" + }, + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6400d770c7c1bce4b3d59ce0079ed686e843b6500bbea77d869a1ae7df4565a1" + }, + "scheme": "ed25519" + }, + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "28bf74baa87ed923f8fa27e3292684f8ec4730ce0bdc65150ed58199206ce089" + }, + "scheme": "ed25519" + }, + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e6ae9d3b67d7b3ce274130291dd90287f32b8fd72bfb4ac5430859ebd1c28a46" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 2 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.snapshot.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.snapshot.json new file mode 100644 index 00000000..11e7e071 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.snapshot.json @@ -0,0 +1,37 @@ +{ + "signatures": [ + { + "keyid": "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93", + "sig": "a12c5320e165f6a674b5d030c1c2f6a1716ad413ad6bda8942febab0af19f510958998d36c76bb47274fe20d3531f67f99f6f6dde8bc3bc0bca7098d8042510c" + } + ], + "signed": { + "_type": "snapshot", + "expires": "2020-01-08T00:00:00Z", + "meta": { + "a.json": { + "version": 1 + }, + "b.json": { + "version": 1 + }, + "c.json": { + "version": 1 + }, + "d.json": { + "version": 1 + }, + "e.json": { + "version": 1 + }, + "f.json": { + "version": 1 + }, + "targets.json": { + "version": 2 + } + }, + "spec_version": "1.0.0", + "version": 2 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.targets.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.targets.json new file mode 100644 index 00000000..bece42a9 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.targets.json @@ -0,0 +1,73 @@ +{ + "signatures": [ + { + "keyid": "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4", + "sig": "91abfb44e844a293837d1e081953acc4a7d179ff695353ea0472eb71846f8aee9903b352fa595084243218bec93336defc184ed0b4a1bc50e497878ed7f03c0c" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": { + "05e17c1501d627b2597322f80d33aacec6f30a507552d3326a88913422b0e30b": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "f758af464295e62a1da4d3267be6d13f4aba9c7d52166d01b6bd5b4559496c9d" + }, + "scheme": "ed25519" + }, + "f581c9aeff9989106aeeea35319d1d1f067149619a2ff005249d6f60560557be": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "683d345a948a9baa343be4e44c076ca115da3838e72c28a06340c8ec1b3ef6be" + }, + "scheme": "ed25519" + } + }, + "roles": [ + { + "keyids": [ + "05e17c1501d627b2597322f80d33aacec6f30a507552d3326a88913422b0e30b" + ], + "name": "a", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + }, + { + "keyids": [ + "f581c9aeff9989106aeeea35319d1d1f067149619a2ff005249d6f60560557be" + ], + "name": "f", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + } + ] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "targets.txt": { + "hashes": { + "sha256": "6da6f624811b0d7792c57dc9ed58f1af8cbf4ebfaaaa3ea2011b57b4af63f7e6", + "sha512": "0951c719b5adf26a55700bd1ac6b657fb513b04e4adb335a935a0da7f12f2ac99834b47442d0b832e5f4ffdd365ef538f14d3a52221c0cf81ef7b68ce312476a" + }, + "length": 21 + } + }, + "version": 2 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.timestamp.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.timestamp.json new file mode 100644 index 00000000..218e6de7 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/2.timestamp.json @@ -0,0 +1,24 @@ +{ + "signatures": [ + { + "keyid": "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae", + "sig": "c48177d304312637216cfa3e424e1f7a214dfbd2121597e2b6ef10b40c000d48720078742503d68d19e50dd39da093e5d25d72ff6a634e7e8880bd9992fc110b" + } + ], + "signed": { + "_type": "timestamp", + "expires": "2020-01-02T00:00:00Z", + "meta": { + "snapshot.json": { + "hashes": { + "sha256": "b5a9218242238080aa294948c18f9d48022b1c3ca68ebdde92fed68bbc0c3c09", + "sha512": "ffb8446b2517a5db4a0ca7b59f4800bcd00f807e918b884af53c7c82b941df242d790e64feaba08ae33edd92902c494f85c9f7c25e8b7071ab0f0b8a1e61206d" + }, + "length": 659, + "version": 2 + } + }, + "spec_version": "1.0.0", + "version": 2 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/a.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/a.json new file mode 100644 index 00000000..90237d1e --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/a.json @@ -0,0 +1,74 @@ +{ + "signatures": [ + { + "keyid": "05e17c1501d627b2597322f80d33aacec6f30a507552d3326a88913422b0e30b", + "sig": "35eae38d12b845e5e2fb89c957b1e4b16bccfb5717160e0c1a36924b3ae89972d78e0837639e266cf5957ddf9d1f7eb184148ef2d242d7ff99b844342b48020a" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": { + "15024498e03f033ec92758a1dc7107b34eebe759b09827b02a7fb3c64ca3e586": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e104199cda6e018d7d9044fa6225aa5dc9c2af5ee4e1c0fe6d16ad002220390d" + }, + "scheme": "ed25519" + }, + "718fedad390b4d0d470b890781eb8c94e5a7e975aebe65fc0862246c945fce68": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82f52e4503dbb364fabe8e5567f1cf909d4175d45468a021dfe75653db9ac98c" + }, + "scheme": "ed25519" + } + }, + "roles": [ + { + "keyids": [ + "718fedad390b4d0d470b890781eb8c94e5a7e975aebe65fc0862246c945fce68" + ], + "name": "b", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + }, + { + "keyids": [ + "15024498e03f033ec92758a1dc7107b34eebe759b09827b02a7fb3c64ca3e586" + ], + "name": "e", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + } + ] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "a.txt": { + "custom": {}, + "hashes": { + "sha256": "3f90cedf303207851bbdc5f857e018daf93b4c0083306cef17df547b42e4e985", + "sha512": "f4631ef7ea7b015d7b88e411842fafeb78a72f0181bec72ea9754604ede74ea0e491bf8411659aabc96304fc764d0131ce49ba86066ab5f7b7480dde719e0bfd" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/b.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/b.json new file mode 100644 index 00000000..bee9490e --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/b.json @@ -0,0 +1,74 @@ +{ + "signatures": [ + { + "keyid": "718fedad390b4d0d470b890781eb8c94e5a7e975aebe65fc0862246c945fce68", + "sig": "a99aa2bd1aa6473f48fc9c3a93289de4efea45071906bc4deecd3a85ffe0e6bc2c1b1751a2b0975e8f28d1b8114ac964f6dc55e5e160f602d56eff51d25c7907" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": { + "9ca81f7ff17f6218246474a51b47eb035741bc472557ef5ac493e279f446b85b": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "06e4dee0de7826c8d539a6112940b7459892b4ecaf696e67dc064aea0923f95c" + }, + "scheme": "ed25519" + }, + "e9829d3f2fdff6d6f31002c17cf7f20cf0398e215ca0c0c44d075ccd76a26f62": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "fa386632ae9cc358ad0b56565edef362ad10d7fadb05bc8dc8995627372b990e" + }, + "scheme": "ed25519" + } + }, + "roles": [ + { + "keyids": [ + "9ca81f7ff17f6218246474a51b47eb035741bc472557ef5ac493e279f446b85b" + ], + "name": "c", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + }, + { + "keyids": [ + "e9829d3f2fdff6d6f31002c17cf7f20cf0398e215ca0c0c44d075ccd76a26f62" + ], + "name": "d", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + } + ] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "b.txt": { + "custom": {}, + "hashes": { + "sha256": "949c6a4318dabe8bbd140cef99ea669ba031919ccf9bce0f5b4d0b61d1c0aa2e", + "sha512": "f4cc9ce5c73b37e2a6707af7a0ea614ea5fa428bd2509e3af4528a5d330ce98a09c4dd98c859ad9b27b8aba24e1eacbf1af8393fdbfed899cecb995c87a11e3c" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/c.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/c.json new file mode 100644 index 00000000..9305d64f --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/c.json @@ -0,0 +1,28 @@ +{ + "signatures": [ + { + "keyid": "9ca81f7ff17f6218246474a51b47eb035741bc472557ef5ac493e279f446b85b", + "sig": "9f0c5ef7e0a11012256c4a47ed757f9e90e930238f6a6e5c758a014f1b768519acb2b7d9aa9bd0456d71ba09b032598086e42d832830391e307c926c16f5b303" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "c.txt": { + "custom": {}, + "hashes": { + "sha256": "946e2ea9180de673891ae09ce0edfb207966bc32bd6324ebdad2c50c82075ffd", + "sha512": "ddb5fb256a368d778b5fdd03d497ad79944c766245f3cccfc8b098b14c488ec424a68b86a6a2add36db4ef0f0214f15dbe0d63fbc5ca7a9619fb4c39544d78a9" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/d.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/d.json new file mode 100644 index 00000000..2c9f1117 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/d.json @@ -0,0 +1,28 @@ +{ + "signatures": [ + { + "keyid": "e9829d3f2fdff6d6f31002c17cf7f20cf0398e215ca0c0c44d075ccd76a26f62", + "sig": "04d8264dd6d75fad106d599603801842057de038803935cf9b043fff4d5ee2618675c85291822f18efff770fa89056ed960de35309356a24baef45d9f64c8c0f" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "d.txt": { + "custom": {}, + "hashes": { + "sha256": "b2ce56612fa7e840d56d263f75804f60baaad5d6e0a7d544e65e1791f2df5e55", + "sha512": "79b44a183eaa0025f3997b856537502ac1759de74263985ad1dd9ab0bb5cc8ce46b6c45c1bf37adb87c2839cca54ae784c3d3488e994d29f003decc81638d589" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/e.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/e.json new file mode 100644 index 00000000..a473d7dc --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/e.json @@ -0,0 +1,28 @@ +{ + "signatures": [ + { + "keyid": "15024498e03f033ec92758a1dc7107b34eebe759b09827b02a7fb3c64ca3e586", + "sig": "11ea2f743a5e5ec7f5bea84804c1fdc2e70f42a1f955668b0072fc6bed00e6535574c129270f97e8849e589a57f48a077903244250ab970eac45f7c46c43b003" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "e.txt": { + "custom": {}, + "hashes": { + "sha256": "027b3d1814dae8547aed3d7e7b99aa0d59d82cc18411037b0ef0318eb754ce86", + "sha512": "ffca8c347335e5e019deba8a15212297fe25afc734ac06fcd01b4d9dae12b16d750d7a44bdafcada254148990aa6611bc9ed2f2841da2c0dd70bb870be7275f6" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/f.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/f.json new file mode 100644 index 00000000..941ae66f --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/f.json @@ -0,0 +1,28 @@ +{ + "signatures": [ + { + "keyid": "f581c9aeff9989106aeeea35319d1d1f067149619a2ff005249d6f60560557be", + "sig": "ff5f1d3bfc5d31f71997b1c020c06d7c1683e0f7e747cfdbdf6c718f608181b4c81f3212a93ff8e109b377a73af5883e1568257b050a5b6137058a10463f640e" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": {}, + "roles": [] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "f.txt": { + "custom": {}, + "hashes": { + "sha256": "f0b07df15d4a3e98ffc6ee6a4c00d04623b0171de458d94dbec4abb7646bc60e", + "sha512": "b8061c4c83d478aaadc6758a74861f749420e83dd6bf07e931738b0619404a75d12758429b12f8d047643d6d13047ec0b65bb253cf5f5b961515658864301207" + }, + "length": 15 + } + }, + "version": 1 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/root.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/root.json new file mode 100644 index 00000000..2d5222c3 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129", + "sig": "e911db300e8eb378a2cabc48979e4c8f72beca30335db0579a7a1d81ae90c8bc63c27d92ce5a17846e3b1adb63a45a411bb4308e018e0d3cc4c9908311b5f208" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2020-12-31T05:48:20Z", + "keys": { + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6bac59b8d9e1aae02fae6fba6e7fe3fc9fe5b4a9fe98c3fca255d8c8ec3e5b35" + }, + "scheme": "ed25519" + }, + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "6400d770c7c1bce4b3d59ce0079ed686e843b6500bbea77d869a1ae7df4565a1" + }, + "scheme": "ed25519" + }, + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "28bf74baa87ed923f8fa27e3292684f8ec4730ce0bdc65150ed58199206ce089" + }, + "scheme": "ed25519" + }, + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "e6ae9d3b67d7b3ce274130291dd90287f32b8fd72bfb4ac5430859ebd1c28a46" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "d4dab4b4d68b91665a6d0dac5b4e64677aa6d853fc787669168b4b4ba9822129" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 2 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/snapshot.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/snapshot.json new file mode 100644 index 00000000..11e7e071 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/snapshot.json @@ -0,0 +1,37 @@ +{ + "signatures": [ + { + "keyid": "77dfdca206c0fe1b8e55d67d21dd0e195a0998a9d2b56c6d3ee8f68d04c21e93", + "sig": "a12c5320e165f6a674b5d030c1c2f6a1716ad413ad6bda8942febab0af19f510958998d36c76bb47274fe20d3531f67f99f6f6dde8bc3bc0bca7098d8042510c" + } + ], + "signed": { + "_type": "snapshot", + "expires": "2020-01-08T00:00:00Z", + "meta": { + "a.json": { + "version": 1 + }, + "b.json": { + "version": 1 + }, + "c.json": { + "version": 1 + }, + "d.json": { + "version": 1 + }, + "e.json": { + "version": 1 + }, + "f.json": { + "version": 1 + }, + "targets.json": { + "version": 2 + } + }, + "spec_version": "1.0.0", + "version": 2 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/targets.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/targets.json new file mode 100644 index 00000000..bece42a9 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/targets.json @@ -0,0 +1,73 @@ +{ + "signatures": [ + { + "keyid": "e4dae3872d28d29f7624a702bfd25f68453544d597229ee9e0a8569d1f940cf4", + "sig": "91abfb44e844a293837d1e081953acc4a7d179ff695353ea0472eb71846f8aee9903b352fa595084243218bec93336defc184ed0b4a1bc50e497878ed7f03c0c" + } + ], + "signed": { + "_type": "targets", + "delegations": { + "keys": { + "05e17c1501d627b2597322f80d33aacec6f30a507552d3326a88913422b0e30b": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "f758af464295e62a1da4d3267be6d13f4aba9c7d52166d01b6bd5b4559496c9d" + }, + "scheme": "ed25519" + }, + "f581c9aeff9989106aeeea35319d1d1f067149619a2ff005249d6f60560557be": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "683d345a948a9baa343be4e44c076ca115da3838e72c28a06340c8ec1b3ef6be" + }, + "scheme": "ed25519" + } + }, + "roles": [ + { + "keyids": [ + "05e17c1501d627b2597322f80d33aacec6f30a507552d3326a88913422b0e30b" + ], + "name": "a", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + }, + { + "keyids": [ + "f581c9aeff9989106aeeea35319d1d1f067149619a2ff005249d6f60560557be" + ], + "name": "f", + "paths": [ + "*.txt" + ], + "terminating": false, + "threshold": 1 + } + ] + }, + "expires": "2020-04-01T07:27:10Z", + "spec_version": "1.0.0", + "targets": { + "targets.txt": { + "hashes": { + "sha256": "6da6f624811b0d7792c57dc9ed58f1af8cbf4ebfaaaa3ea2011b57b4af63f7e6", + "sha512": "0951c719b5adf26a55700bd1ac6b657fb513b04e4adb335a935a0da7f12f2ac99834b47442d0b832e5f4ffdd365ef538f14d3a52221c0cf81ef7b68ce312476a" + }, + "length": 21 + } + }, + "version": 2 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/timestamp.json b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/timestamp.json new file mode 100644 index 00000000..218e6de7 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/timestamp.json @@ -0,0 +1,24 @@ +{ + "signatures": [ + { + "keyid": "3a05831328273e4b821c3bbe1fed0c5332749d8e071675879af26a401a5c85ae", + "sig": "c48177d304312637216cfa3e424e1f7a214dfbd2121597e2b6ef10b40c000d48720078742503d68d19e50dd39da093e5d25d72ff6a634e7e8880bd9992fc110b" + } + ], + "signed": { + "_type": "timestamp", + "expires": "2020-01-02T00:00:00Z", + "meta": { + "snapshot.json": { + "hashes": { + "sha256": "b5a9218242238080aa294948c18f9d48022b1c3ca68ebdde92fed68bbc0c3c09", + "sha512": "ffb8446b2517a5db4a0ca7b59f4800bcd00f807e918b884af53c7c82b941df242d790e64feaba08ae33edd92902c494f85c9f7c25e8b7071ab0f0b8a1e61206d" + }, + "length": 659, + "version": 2 + } + }, + "spec_version": "1.0.0", + "version": 2 + } +} \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/027b3d1814dae8547aed3d7e7b99aa0d59d82cc18411037b0ef0318eb754ce86.e.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/027b3d1814dae8547aed3d7e7b99aa0d59d82cc18411037b0ef0318eb754ce86.e.txt new file mode 100644 index 00000000..7bece393 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/027b3d1814dae8547aed3d7e7b99aa0d59d82cc18411037b0ef0318eb754ce86.e.txt @@ -0,0 +1 @@ +Contents: e.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/0951c719b5adf26a55700bd1ac6b657fb513b04e4adb335a935a0da7f12f2ac99834b47442d0b832e5f4ffdd365ef538f14d3a52221c0cf81ef7b68ce312476a.targets.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/0951c719b5adf26a55700bd1ac6b657fb513b04e4adb335a935a0da7f12f2ac99834b47442d0b832e5f4ffdd365ef538f14d3a52221c0cf81ef7b68ce312476a.targets.txt new file mode 100644 index 00000000..5f1e6033 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/0951c719b5adf26a55700bd1ac6b657fb513b04e4adb335a935a0da7f12f2ac99834b47442d0b832e5f4ffdd365ef538f14d3a52221c0cf81ef7b68ce312476a.targets.txt @@ -0,0 +1 @@ +Contents: targets.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/3f90cedf303207851bbdc5f857e018daf93b4c0083306cef17df547b42e4e985.a.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/3f90cedf303207851bbdc5f857e018daf93b4c0083306cef17df547b42e4e985.a.txt new file mode 100644 index 00000000..3c405619 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/3f90cedf303207851bbdc5f857e018daf93b4c0083306cef17df547b42e4e985.a.txt @@ -0,0 +1 @@ +Contents: a.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/6da6f624811b0d7792c57dc9ed58f1af8cbf4ebfaaaa3ea2011b57b4af63f7e6.targets.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/6da6f624811b0d7792c57dc9ed58f1af8cbf4ebfaaaa3ea2011b57b4af63f7e6.targets.txt new file mode 100644 index 00000000..5f1e6033 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/6da6f624811b0d7792c57dc9ed58f1af8cbf4ebfaaaa3ea2011b57b4af63f7e6.targets.txt @@ -0,0 +1 @@ +Contents: targets.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/79b44a183eaa0025f3997b856537502ac1759de74263985ad1dd9ab0bb5cc8ce46b6c45c1bf37adb87c2839cca54ae784c3d3488e994d29f003decc81638d589.d.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/79b44a183eaa0025f3997b856537502ac1759de74263985ad1dd9ab0bb5cc8ce46b6c45c1bf37adb87c2839cca54ae784c3d3488e994d29f003decc81638d589.d.txt new file mode 100644 index 00000000..36ce6ad4 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/79b44a183eaa0025f3997b856537502ac1759de74263985ad1dd9ab0bb5cc8ce46b6c45c1bf37adb87c2839cca54ae784c3d3488e994d29f003decc81638d589.d.txt @@ -0,0 +1 @@ +Contents: d.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/946e2ea9180de673891ae09ce0edfb207966bc32bd6324ebdad2c50c82075ffd.c.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/946e2ea9180de673891ae09ce0edfb207966bc32bd6324ebdad2c50c82075ffd.c.txt new file mode 100644 index 00000000..924df500 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/946e2ea9180de673891ae09ce0edfb207966bc32bd6324ebdad2c50c82075ffd.c.txt @@ -0,0 +1 @@ +Contents: c.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/949c6a4318dabe8bbd140cef99ea669ba031919ccf9bce0f5b4d0b61d1c0aa2e.b.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/949c6a4318dabe8bbd140cef99ea669ba031919ccf9bce0f5b4d0b61d1c0aa2e.b.txt new file mode 100644 index 00000000..976e178d --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/949c6a4318dabe8bbd140cef99ea669ba031919ccf9bce0f5b4d0b61d1c0aa2e.b.txt @@ -0,0 +1 @@ +Contents: b.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/a.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/a.txt new file mode 100644 index 00000000..3c405619 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/a.txt @@ -0,0 +1 @@ +Contents: a.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b.txt new file mode 100644 index 00000000..976e178d --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b.txt @@ -0,0 +1 @@ +Contents: b.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b2ce56612fa7e840d56d263f75804f60baaad5d6e0a7d544e65e1791f2df5e55.d.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b2ce56612fa7e840d56d263f75804f60baaad5d6e0a7d544e65e1791f2df5e55.d.txt new file mode 100644 index 00000000..36ce6ad4 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b2ce56612fa7e840d56d263f75804f60baaad5d6e0a7d544e65e1791f2df5e55.d.txt @@ -0,0 +1 @@ +Contents: d.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b8061c4c83d478aaadc6758a74861f749420e83dd6bf07e931738b0619404a75d12758429b12f8d047643d6d13047ec0b65bb253cf5f5b961515658864301207.f.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b8061c4c83d478aaadc6758a74861f749420e83dd6bf07e931738b0619404a75d12758429b12f8d047643d6d13047ec0b65bb253cf5f5b961515658864301207.f.txt new file mode 100644 index 00000000..f7c5df53 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/b8061c4c83d478aaadc6758a74861f749420e83dd6bf07e931738b0619404a75d12758429b12f8d047643d6d13047ec0b65bb253cf5f5b961515658864301207.f.txt @@ -0,0 +1 @@ +Contents: f.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/c.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/c.txt new file mode 100644 index 00000000..924df500 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/c.txt @@ -0,0 +1 @@ +Contents: c.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/d.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/d.txt new file mode 100644 index 00000000..36ce6ad4 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/d.txt @@ -0,0 +1 @@ +Contents: d.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/ddb5fb256a368d778b5fdd03d497ad79944c766245f3cccfc8b098b14c488ec424a68b86a6a2add36db4ef0f0214f15dbe0d63fbc5ca7a9619fb4c39544d78a9.c.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/ddb5fb256a368d778b5fdd03d497ad79944c766245f3cccfc8b098b14c488ec424a68b86a6a2add36db4ef0f0214f15dbe0d63fbc5ca7a9619fb4c39544d78a9.c.txt new file mode 100644 index 00000000..924df500 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/ddb5fb256a368d778b5fdd03d497ad79944c766245f3cccfc8b098b14c488ec424a68b86a6a2add36db4ef0f0214f15dbe0d63fbc5ca7a9619fb4c39544d78a9.c.txt @@ -0,0 +1 @@ +Contents: c.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/e.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/e.txt new file mode 100644 index 00000000..7bece393 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/e.txt @@ -0,0 +1 @@ +Contents: e.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f.txt new file mode 100644 index 00000000..f7c5df53 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f.txt @@ -0,0 +1 @@ +Contents: f.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f0b07df15d4a3e98ffc6ee6a4c00d04623b0171de458d94dbec4abb7646bc60e.f.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f0b07df15d4a3e98ffc6ee6a4c00d04623b0171de458d94dbec4abb7646bc60e.f.txt new file mode 100644 index 00000000..f7c5df53 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f0b07df15d4a3e98ffc6ee6a4c00d04623b0171de458d94dbec4abb7646bc60e.f.txt @@ -0,0 +1 @@ +Contents: f.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f4631ef7ea7b015d7b88e411842fafeb78a72f0181bec72ea9754604ede74ea0e491bf8411659aabc96304fc764d0131ce49ba86066ab5f7b7480dde719e0bfd.a.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f4631ef7ea7b015d7b88e411842fafeb78a72f0181bec72ea9754604ede74ea0e491bf8411659aabc96304fc764d0131ce49ba86066ab5f7b7480dde719e0bfd.a.txt new file mode 100644 index 00000000..3c405619 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f4631ef7ea7b015d7b88e411842fafeb78a72f0181bec72ea9754604ede74ea0e491bf8411659aabc96304fc764d0131ce49ba86066ab5f7b7480dde719e0bfd.a.txt @@ -0,0 +1 @@ +Contents: a.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f4cc9ce5c73b37e2a6707af7a0ea614ea5fa428bd2509e3af4528a5d330ce98a09c4dd98c859ad9b27b8aba24e1eacbf1af8393fdbfed899cecb995c87a11e3c.b.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f4cc9ce5c73b37e2a6707af7a0ea614ea5fa428bd2509e3af4528a5d330ce98a09c4dd98c859ad9b27b8aba24e1eacbf1af8393fdbfed899cecb995c87a11e3c.b.txt new file mode 100644 index 00000000..976e178d --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/f4cc9ce5c73b37e2a6707af7a0ea614ea5fa428bd2509e3af4528a5d330ce98a09c4dd98c859ad9b27b8aba24e1eacbf1af8393fdbfed899cecb995c87a11e3c.b.txt @@ -0,0 +1 @@ +Contents: b.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/ffca8c347335e5e019deba8a15212297fe25afc734ac06fcd01b4d9dae12b16d750d7a44bdafcada254148990aa6611bc9ed2f2841da2c0dd70bb870be7275f6.e.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/ffca8c347335e5e019deba8a15212297fe25afc734ac06fcd01b4d9dae12b16d750d7a44bdafcada254148990aa6611bc9ed2f2841da2c0dd70bb870be7275f6.e.txt new file mode 100644 index 00000000..7bece393 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/ffca8c347335e5e019deba8a15212297fe25afc734ac06fcd01b4d9dae12b16d750d7a44bdafcada254148990aa6611bc9ed2f2841da2c0dd70bb870be7275f6.e.txt @@ -0,0 +1 @@ +Contents: e.txt \ No newline at end of file diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/targets.txt b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/targets.txt new file mode 100644 index 00000000..5f1e6033 --- /dev/null +++ b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/targets/targets.txt @@ -0,0 +1 @@ +Contents: targets.txt \ No newline at end of file From 205252d3fcf68cb0e96cd610a5f26fbcd8ccecf8 Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Mon, 12 Jul 2021 23:50:11 +0200 Subject: [PATCH 03/22] Use ioutil for go 1.15 --- client/delegations_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/delegations_test.go b/client/delegations_test.go index 6308d575..789da94c 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -3,9 +3,9 @@ package client import ( "encoding/json" "fmt" + "io/ioutil" "net" "net/http" - "os" "strings" "testing" "time" @@ -270,7 +270,7 @@ func initTestDelegationClient(t *testing.T, dirPrefix string) (*Client, func() e remote, err := HTTPRemoteStore(fmt.Sprintf("http://%s/", addr), opts, nil) c := NewClient(MemoryLocalStore(), remote) - rawFile, err := os.ReadFile(initialStateDir + "/" + "root.json") + rawFile, err := ioutil.ReadFile(initialStateDir + "/" + "root.json") assert.Nil(t, err) s := &data.Signed{} root := &data.Root{} @@ -285,7 +285,7 @@ func initTestDelegationClient(t *testing.T, dirPrefix string) (*Client, func() e } assert.Nil(t, c.Init(keys, 1)) - files, err := os.ReadDir(initialStateDir) + files, err := ioutil.ReadDir(initialStateDir) assert.Nil(t, err) // load local files @@ -296,7 +296,7 @@ func initTestDelegationClient(t *testing.T, dirPrefix string) (*Client, func() e name := f.Name() // ignoring consistent snapshot when loading initial state if len(strings.Split(name, ".")) == 1 && strings.HasSuffix(name, ".json") { - rawFile, err := os.ReadFile(initialStateDir + "/" + name) + rawFile, err := ioutil.ReadFile(initialStateDir + "/" + name) assert.Nil(t, err) assert.Nil(t, c.local.SetMeta(name, rawFile)) } From b1d93722f66912dac440b71b75f84f394250c007 Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Tue, 13 Jul 2021 13:51:07 +0200 Subject: [PATCH 04/22] Add more delegations tests --- client/delegations_test.go | 67 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/client/delegations_test.go b/client/delegations_test.go index 789da94c..f233510a 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -3,6 +3,7 @@ package client import ( "encoding/json" "fmt" + "io" "io/ioutil" "net" "net/http" @@ -97,7 +98,7 @@ func TestDelegationsIterator(t *testing.T) { func TestGetTargetMeta(t *testing.T) { verify.IsExpired = func(t time.Time) bool { return false } c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") - defer closer() + defer func() { assert.Nil(t, closer()) }() _, err := c.Update() assert.Nil(t, err) @@ -109,7 +110,7 @@ func TestGetTargetMeta(t *testing.T) { func TestMaxDelegations(t *testing.T) { verify.IsExpired = func(t time.Time) bool { return false } c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") - defer closer() + defer func() { assert.Nil(t, closer()) }() _, err := c.Update() assert.Nil(t, err) c.MaxDelegations = 2 @@ -120,13 +121,73 @@ func TestMaxDelegations(t *testing.T) { func TestMetaNotFound(t *testing.T) { verify.IsExpired = func(t time.Time) bool { return false } c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") - defer closer() + defer func() { assert.Nil(t, closer()) }() _, err := c.Update() assert.Nil(t, err) _, err = c.getTargetFileMeta("unknown.txt") assert.Equal(t, err, ErrUnknownTarget{Name: "unknown.txt", SnapshotVersion: 2}) } +type fakeRemote struct { + getMeta func(name string) (stream io.ReadCloser, size int64, err error) + getTarget func(path string) (stream io.ReadCloser, size int64, err error) +} + +func (f fakeRemote) GetMeta(name string) (stream io.ReadCloser, size int64, err error) { + return f.getMeta(name) +} + +func (f fakeRemote) GetTarget(name string) (stream io.ReadCloser, size int64, err error) { + return f.getTarget(name) +} + +func TestTargetsNotFound(t *testing.T) { + verify.IsExpired = func(t time.Time) bool { return false } + c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") + defer func() { assert.Nil(t, closer()) }() + _, err := c.Update() + assert.Nil(t, err) + + previousRemote := c.remote + newRemote := fakeRemote{ + getMeta: func(path string) (stream io.ReadCloser, size int64, err error) { + if path == "1.c.json" { + return nil, 0, ErrNotFound{} + } + return previousRemote.GetMeta(path) + }, + getTarget: previousRemote.GetTarget, + } + c.remote = newRemote + + _, err = c.getTargetFileMeta("c.txt") + assert.Equal(t, ErrMissingRemoteMetadata{Name: "c.json"}, err) +} + +func TestUnverifiedTargets(t *testing.T) { + verify.IsExpired = func(t time.Time) bool { return false } + c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") + defer closer() + _, err := c.Update() + assert.Nil(t, err) + + previousRemote := c.remote + newRemote := fakeRemote{ + getMeta: func(path string) (stream io.ReadCloser, size int64, err error) { + if path == "1.c.json" { + // returns a snapshot that does not match + return previousRemote.GetMeta("1.d.json") + } + return previousRemote.GetMeta(path) + }, + getTarget: previousRemote.GetTarget, + } + c.remote = newRemote + + _, err = c.getTargetFileMeta("c.txt") + assert.Equal(t, ErrDecodeFailed{File: "c.json", Err: verify.ErrRoleThreshold{Expected: 1, Actual: 0}}, err) +} + func TestPersistedMeta(t *testing.T) { verify.IsExpired = func(t time.Time) bool { return false } c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") From 26d3dcda68ef95365c0066e970fbbe78045d6eff Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Tue, 13 Jul 2021 15:32:58 +0200 Subject: [PATCH 05/22] Cleanups --- client/delegations.go | 2 +- .../server/metadata/.1.f.json.swp | Bin 12288 -> 0 bytes verify/db.go | 14 +++++++++----- 3 files changed, 10 insertions(+), 6 deletions(-) delete mode 100644 client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/.1.f.json.swp diff --git a/client/delegations.go b/client/delegations.go index f55cb3f5..f452ceb6 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -13,7 +13,7 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) { if err != nil { return data.TargetFileMeta{}, err } - verifiers := map[string]verify.DelegationsVerifier{"root": c.db} + verifiers := map[string]verify.DelegationsVerifier{"root": verify.DelegationsVerifier{c.db}} // delegationsIterator covers 5.6.7 // - pre-order depth-first search starting with the top targets diff --git a/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/.1.f.json.swp b/client/testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation/server/metadata/.1.f.json.swp deleted file mode 100644 index 8fe9767ff0c659e5373b30d690c5229e1597e848..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2J&znk5QfLd0f7w$OGE_AAQI%;>FVz}5g>t#kT4LO2{4xFkLumE_jXr1bEmT` zLj?bU93m1TM8-dW5D}8$z%d!3W^T_&mYpJD)o9+G?XK#s>Zc}IZAJ0P``0FZDl57! zRn)dzTt%Xe{!v(PWrtER*4F>aef?8CSore%7&Td$s)DI1u;vq<1#r6h>qWUtq+ zO)kHD{aHLDpNR=D0Vco%m;e)C0!)AjFoCBK=r`x8FUj`Y&i?A|dG4|2ZpaTNzyz28 z6JP>NfC(@GCcp%k025#WOyGY=Ampn0>Oxh$bdi#$|NqZ^|Ns4BRXrp37=uJ<{8xhV&-s4bm&5%cLRpB@$mu zfC(@GCcp%k025#WOn?b6f&Y}i{c1w@q^5@9j(Ugd{Rz%D z*+!7XXQ7?dD%%W-zG4#5ni!=~shDD7Yr%lA#(VM-Acd>P#Kw%Iu|q~hMB#D)lXXzo z`cjfbv_U5!Xn!kJ6yPAEHa@pHM@%}zNO5gUR$x66nAG#o&9S2$?~Nhqbkz0h!(n{N zs4mm~q~F==K5S>$pSjoF2Ggy7$<{CxQ!(ybq4xwF2)8fYK8R08 zr(J~YdO14YxH+Uy-@&_MfYDm#M{VDaqTcH7ZgAN6EE_sX-7RJ-%%^$Y9L_dWeViUV z3vq?4Vo4?|Dj2k_i;yU>By4sGtXC+CL#JITIp$)M17EE0;2~1sp-`)>46Q1~BZxLC z=YmtAP?Z!?wY)Sg3L}Dv7L*h8b^z5u6|0H%m<*k4F{D`2B1mn(QrgzHAZ$Rypp1ex pLxv)(15Iy18)PX$7^&NADH%gIML8`V6>L0p7<=Yv+Z)xr>K|&!9Nho_ diff --git a/verify/db.go b/verify/db.go index b1c75834..f609b606 100644 --- a/verify/db.go +++ b/verify/db.go @@ -26,8 +26,12 @@ func NewDB() *DB { } } -type DelegationsVerifier interface { - Unmarshal([]byte, interface{}, string, int) error +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 @@ -41,15 +45,15 @@ func NewDelegationsVerifier(d *data.Delegations) (DelegationsVerifier, error) { for _, r := range d.Roles { role := &data.Role{Threshold: r.Threshold, KeyIDs: r.KeyIDs} if err := db.addRole(r.Name, role); err != nil { - return nil, err + return DelegationsVerifier{}, err } } for id, k := range d.Keys { if err := db.AddKey(id, k); err != nil { - return nil, err + return DelegationsVerifier{}, err } } - return db, nil + return DelegationsVerifier{db}, nil } func (db *DB) AddKey(id string, k *data.Key) error { From 8eac49ac1dfc789722d85f5c53731a07a89e012d Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Tue, 13 Jul 2021 15:27:41 +0200 Subject: [PATCH 06/22] Check new paths --- client/delegations.go | 8 +-- client/delegations_test.go | 28 ++++---- data/types.go | 82 ++++++++++++++++++----- data/types_test.go | 133 +++++++++++++++++++++++++++++-------- 4 files changed, 187 insertions(+), 64 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index f452ceb6..7935360c 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -119,10 +119,10 @@ func (c *Client) rootTargetDelegation() data.DelegatedRole { keyIDs = append(keyIDs, id) } return data.DelegatedRole{ - Name: role, - KeyIDs: keyIDs, - Threshold: r.Threshold, - Paths: []string{"*"}, + Name: role, + KeyIDs: keyIDs, + Threshold: r.Threshold, + PathMatchers: []string{"*"}, } } diff --git a/client/delegations_test.go b/client/delegations_test.go index f233510a..a702f009 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -17,8 +17,8 @@ import ( ) var ( - defaultPaths = []string{"tmp", "*"} - notMatchingPaths = []string{"vars", "null"} + defaultPathMatchers = []string{"tmp", "*"} + notMatchingPathMatchers = []string{"vars", "null"} ) func TestDelegationsIterator(t *testing.T) { @@ -32,40 +32,40 @@ func TestDelegationsIterator(t *testing.T) { { "no termination", map[string][]data.DelegatedRole{ - "a": []data.DelegatedRole{{Name: "b", Paths: defaultPaths}, {Name: "e", Paths: defaultPaths}}, - "b": []data.DelegatedRole{{Name: "c", Paths: defaultPaths}, {Name: "d", Paths: defaultPaths}}, + "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, + "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, - data.DelegatedRole{Name: "a", Paths: defaultPaths}, + data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, "", []string{"a", "b", "c", "d", "e"}, }, { "terminated in b", map[string][]data.DelegatedRole{ - "a": []data.DelegatedRole{{Name: "b", Paths: defaultPaths, Terminating: true}, {Name: "e", Paths: defaultPaths}}, - "b": []data.DelegatedRole{{Name: "c", Paths: defaultPaths}, {Name: "d", Paths: defaultPaths}}, + "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers, Terminating: true}, {Name: "e", PathMatchers: defaultPathMatchers}}, + "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, - data.DelegatedRole{Name: "a", Paths: defaultPaths}, + data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, "", []string{"a", "b", "c", "d"}, }, { "path does not match b", map[string][]data.DelegatedRole{ - "a": []data.DelegatedRole{{Name: "b", Paths: notMatchingPaths}, {Name: "e", Paths: defaultPaths}}, - "b": []data.DelegatedRole{{Name: "c", Paths: defaultPaths}, {Name: "d", Paths: defaultPaths}}, + "a": []data.DelegatedRole{{Name: "b", PathMatchers: notMatchingPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, + "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, - data.DelegatedRole{Name: "a", Paths: defaultPaths}, + data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, "", []string{"a", "e"}, }, { "cycle avoided", map[string][]data.DelegatedRole{ - "a": []data.DelegatedRole{{Name: "b", Paths: defaultPaths}, {Name: "e", Paths: defaultPaths}}, - "b": []data.DelegatedRole{{Name: "a", Paths: defaultPaths}, {Name: "d", Paths: defaultPaths}}, + "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, + "b": []data.DelegatedRole{{Name: "a", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, - data.DelegatedRole{Name: "a", Paths: defaultPaths}, + data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, "", []string{"a", "b", "a", "e", "d"}, }, diff --git a/data/types.go b/data/types.go index a1673226..cbf2ca82 100644 --- a/data/types.go +++ b/data/types.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" + "errors" "path/filepath" "strings" "sync" @@ -21,7 +22,8 @@ const ( ) var ( - KeyAlgorithms = []string{"sha256", "sha512"} + KeyAlgorithms = []string{"sha256", "sha512"} + ErrPathsAndPathHashesSet = errors.New("tuf: failed decoding targets : paths and path_hash_prefixes are set") ) type Signed struct { @@ -223,33 +225,77 @@ type Delegations struct { Roles []DelegatedRole `json:"roles"` } +// DelegatedRole enforces the spec 1.0.19 section 4.5 'role MUST specify only one of the "path_hash_prefixes" or "paths"' +// by forcing one of path_hash_prefixes, paths with MatchWithHashPrefixes and PathPatterns fields. +// delegatedRoleJSON and delegatedRoleCopy help decoding and encoding jsons. +// DelegatedRole UnmarshalJSON will fail and return ErrPathsAndPathHashesSet if both fields are set and not empty type DelegatedRole struct { - Name string `json:"name"` - KeyIDs []string `json:"keyids"` - Threshold int `json:"threshold"` - PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"` - Paths []string `json:"paths"` - Terminating bool `json:"terminating"` + Name string `json:"name"` + KeyIDs []string `json:"keyids"` + Threshold int `json:"threshold"` + Terminating bool `json:"terminating"` + MatchWithHashPrefixes bool `json:"-"` + PathMatchers []string `json:"-"` } -func (d DelegatedRole) MatchesPath(file string) bool { - for _, path := range d.Paths { - if matched, _ := filepath.Match(path, file); matched { - return true +func (d *DelegatedRole) MatchesPath(file string) bool { + for _, pattern := range d.PathMatchers { + if d.MatchWithHashPrefixes { + pathHash := PathHexDigest(file) + if strings.HasPrefix(pathHash, pattern) { + return true + } } - } - if len(d.PathHashPrefixes) == 0 { - return false - } - pathHash := PathHexDigest(file) - for _, prefix := range d.PathHashPrefixes { - if strings.HasPrefix(pathHash, prefix) { + if matched, _ := filepath.Match(pattern, file); matched { return true } } return false } +type delegatedRoleJSON struct { + delegatedRoleCopy + PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"` + Paths []string `json:"paths"` +} + +// delegatedRoleCopy is used for MarshalJSON and UnmarshalJSON to extract DelegatedRole +// fields from delegatedRoleJSON +type delegatedRoleCopy DelegatedRole + +func (d *DelegatedRole) MarshalJSON() ([]byte, error) { + delegatedJSON := delegatedRoleJSON{delegatedRoleCopy: delegatedRoleCopy(*d)} + if d.MatchWithHashPrefixes { + delegatedJSON.PathHashPrefixes = d.PathMatchers + } else { + delegatedJSON.Paths = d.PathMatchers + } + return json.Marshal(&delegatedJSON) +} + +func (d *DelegatedRole) UnmarshalJSON(b []byte) error { + var djson delegatedRoleJSON + err := json.Unmarshal(b, &djson) + if err != nil { + return err + } + new := DelegatedRole(djson.delegatedRoleCopy) + *d = new + if len(djson.PathHashPrefixes) != 0 && len(djson.Paths) != 0 { + return ErrPathsAndPathHashesSet + } + if len(djson.PathHashPrefixes) > 0 { + d.MatchWithHashPrefixes = true + d.PathMatchers = djson.PathHashPrefixes + return nil + } + if len(djson.Paths) > 0 { + d.PathMatchers = djson.Paths + return nil + } + return nil +} + func NewTargets() *Targets { return &Targets{ Type: "targets", diff --git a/data/types_test.go b/data/types_test.go index ac966b64..f9086971 100644 --- a/data/types_test.go +++ b/data/types_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + cjson "github.com/tent/canonical-json-go" . "gopkg.in/check.v1" ) @@ -96,59 +97,135 @@ func (TypesSuite) TestRoleAddKeyIDs(c *C) { func TestDelegatedRolePathMatch(t *testing.T) { var tts = []struct { - testName string - file string - pathHashPrefixes []string - paths []string - matches bool + testName string + file string + matchWithHashPrefixes bool + pathMatchers []string + matches bool }{ { testName: "no path", file: "licence.txt", }, { - testName: "match path *", - paths: []string{"null", "targets/*.tgz"}, - file: "targets/foo.tgz", - matches: true, + testName: "match path *", + pathMatchers: []string{"null", "targets/*.tgz"}, + file: "targets/foo.tgz", + matches: true, }, { - testName: "does not match path *", - paths: []string{"null", "targets/*.tgz"}, - file: "targets/foo.txt", + testName: "does not match path *", + pathMatchers: []string{"null", "targets/*.tgz"}, + file: "targets/foo.txt", }, { - testName: "match path ?", - paths: []string{"foo-version-?.tgz"}, - file: "foo-version-a.tgz", - matches: true, + testName: "match path ?", + pathMatchers: []string{"foo-version-?.tgz"}, + file: "foo-version-a.tgz", + matches: true, }, { - testName: "does not match ?", - paths: []string{"foo-version-?.tgz"}, - file: "foo-version-alpha.tgz", + testName: "does not match ?", + pathMatchers: []string{"foo-version-?.tgz"}, + file: "foo-version-alpha.tgz", }, // picked from https://github.com/theupdateframework/tuf/blob/30ba6e9f9ab25e0370e29ce574dada2d8809afa0/tests/test_updater.py#L1726-L1734 { - testName: "match hash prefix", - pathHashPrefixes: []string{"badd", "8baf"}, - file: "/file3.txt", - matches: true, + testName: "match hash prefix", + pathMatchers: []string{"badd", "8baf"}, + file: "/file3.txt", + matchWithHashPrefixes: true, + matches: true, }, { - testName: "does not match hash prefix", - pathHashPrefixes: []string{"badd"}, - file: "/file3.txt", + testName: "does not match hash prefix", + pathMatchers: []string{"badd"}, + matchWithHashPrefixes: true, + file: "/file3.txt", + }, + { + testName: "hash prefix first char", + pathMatchers: []string{"2"}, + matchWithHashPrefixes: true, + file: "/a/b/c/file_d.txt", + matches: true, + }, + { + testName: "full hash prefix", + pathMatchers: []string{"34c85d1ee84f61f10d7dc633472a49096ed87f8f764bd597831eac371f40ac39"}, + matchWithHashPrefixes: true, + file: "/e/f/g.txt", + matches: true, }, } for _, tt := range tts { t.Run(tt.testName, func(t *testing.T) { d := DelegatedRole{ - PathHashPrefixes: tt.pathHashPrefixes, - Paths: tt.paths, + PathMatchers: tt.pathMatchers, + MatchWithHashPrefixes: tt.matchWithHashPrefixes, } assert.Equal(t, tt.matches, d.MatchesPath(tt.file)) }) } } + +func TestDelegatedRoleJSON(t *testing.T) { + var tts = []struct { + testName string + d DelegatedRole + rawCJSON string + }{{ + "all fields with hashes", + DelegatedRole{ + Name: "n1", + KeyIDs: []string{"k1"}, + Threshold: 5, + Terminating: true, + MatchWithHashPrefixes: true, + PathMatchers: []string{"8f"}, + }, + `{"keyids":["k1"],"name":"n1","path_hash_prefixes":["8f"],"paths":null,"terminating":true,"threshold":5}`, + }, + { + "paths only", + DelegatedRole{ + Name: "n2", + KeyIDs: []string{"k1", "k3"}, + Threshold: 12, + PathMatchers: []string{"*.txt"}, + }, + `{"keyids":["k1","k3"],"name":"n2","paths":["*.txt"],"terminating":false,"threshold":12}`, + }, + { + "default", + DelegatedRole{}, + `{"keyids":null,"name":"","paths":null,"terminating":false,"threshold":0}`, + }, + } + + for _, tt := range tts { + t.Run(tt.testName, func(t *testing.T) { + d := &DelegatedRole{} + *d = tt.d + + raw, err := cjson.Marshal(d) + assert.Nil(t, err) + assert.Equal(t, tt.rawCJSON, string(raw)) + + var newD DelegatedRole + assert.Nil(t, json.Unmarshal(raw, &newD)) + assert.Equal(t, d, &newD) + }) + } +} + +func TestDelegatedRoleUnmarshalErr(t *testing.T) { + targetsWithBothMatchers := []byte(`{"keyids":null,"name":"","paths":["*.txt"],"path_hash_prefixes":["8f"],"terminating":false,"threshold":0}`) + var d DelegatedRole + assert.Equal(t, json.Unmarshal(targetsWithBothMatchers, &d), ErrPathsAndPathHashesSet) + + // test for type errors + err := json.Unmarshal([]byte(`{"keyids":"a"}`), &d) + assert.Equal(t, err.Error(), "json: cannot unmarshal string into Go struct field delegatedRoleJSON.keyids of type []string") +} From 64a1e27c21b115c4e0f4d5736edd2b06b120364f Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Tue, 13 Jul 2021 15:52:57 +0200 Subject: [PATCH 07/22] Add suggestion --- client/client.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/client/client.go b/client/client.go index 7148abf0..f088b9f9 100644 --- a/client/client.go +++ b/client/client.go @@ -204,7 +204,7 @@ func (c *Client) update(latestRoot bool) (data.TargetFiles, error) { if err != nil { return nil, err } - rootMeta, rootInSnapshot, targetsMeta, err := c.decodeSnapshot(snapshotJSON) + snapshotMetas, err := c.decodeSnapshot(snapshotJSON) if err != nil { // ErrRoleThreshold could indicate snapshot keys have been // revoked, so retry with the latest root.json @@ -217,13 +217,16 @@ func (c *Client) update(latestRoot bool) (data.TargetFiles, error) { // If we don't have the root.json, download it, save it in local // storage and restart the update // Root should no longer be pinned in snapshot meta https://github.com/theupdateframework/tuf/pull/988 - if rootInSnapshot && !c.hasMetaFromSnapshot("root.json", rootMeta) { - return c.updateWithLatestRoot(&rootMeta) + if rootMeta, ok := snapshotMetas["root.json"]; ok { + if !c.hasMetaFromSnapshot("root.json", rootMeta) { + return c.updateWithLatestRoot(&rootMeta) + } } // If we don't have the targets.json, download it, determine updated // targets and save targets.json in local storage var updatedTargets data.TargetFiles + targetsMeta := snapshotMetas["targets.json"] if !c.hasMetaFromSnapshot("targets.json", targetsMeta) { targetsJSON, err := c.downloadMetaFromSnapshot("targets.json", targetsMeta) if err != nil { @@ -546,14 +549,13 @@ func (c *Client) decodeRoot(b json.RawMessage) error { // decodeSnapshot decodes and verifies snapshot metadata, and returns the new // root and targets file meta. -func (c *Client) decodeSnapshot(b json.RawMessage) (data.SnapshotFileMeta, bool, data.SnapshotFileMeta, error) { +func (c *Client) decodeSnapshot(b json.RawMessage) (data.SnapshotFiles, error) { snapshot := &data.Snapshot{} if err := c.db.Unmarshal(b, snapshot, "snapshot", c.snapshotVer); err != nil { - return data.SnapshotFileMeta{}, false, data.SnapshotFileMeta{}, ErrDecodeFailed{"snapshot.json", err} + return data.SnapshotFiles{}, ErrDecodeFailed{"snapshot.json", err} } c.snapshotVer = snapshot.Version - rootMeta, rootInSnapshot := snapshot.Meta["root.json"] - return rootMeta, rootInSnapshot, snapshot.Meta["targets.json"], nil + return snapshot.Meta, nil } // decodeTargets decodes and verifies targets metadata, sets c.targets and From e15f5925bd41a24aad568a933830908f7aafba33 Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Tue, 13 Jul 2021 14:57:33 -0400 Subject: [PATCH 08/22] Add struct tags --- client/delegations_test.go | 64 +++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/client/delegations_test.go b/client/delegations_test.go index a702f009..c91d1d89 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -30,44 +30,44 @@ func TestDelegationsIterator(t *testing.T) { resultOrder []string }{ { - "no termination", - map[string][]data.DelegatedRole{ + testName: "no termination", + roles: map[string][]data.DelegatedRole{ "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, - data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, - "", - []string{"a", "b", "c", "d", "e"}, + rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, + file: "", + resultOrder: []string{"a", "b", "c", "d", "e"}, }, { - "terminated in b", - map[string][]data.DelegatedRole{ + testName: "terminated in b", + roles: map[string][]data.DelegatedRole{ "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers, Terminating: true}, {Name: "e", PathMatchers: defaultPathMatchers}}, "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, - data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, - "", - []string{"a", "b", "c", "d"}, + rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, + file: "", + resultOrder: []string{"a", "b", "c", "d"}, }, { - "path does not match b", - map[string][]data.DelegatedRole{ + testName: "path does not match b", + roles: map[string][]data.DelegatedRole{ "a": []data.DelegatedRole{{Name: "b", PathMatchers: notMatchingPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, - data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, - "", - []string{"a", "e"}, + rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, + file: "", + resultOrder: []string{"a", "e"}, }, { - "cycle avoided", - map[string][]data.DelegatedRole{ + testName: "cycle avoided", + roles: map[string][]data.DelegatedRole{ "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, "b": []data.DelegatedRole{{Name: "a", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, - data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, - "", - []string{"a", "b", "a", "e", "d"}, + rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, + file: "", + resultOrder: []string{"a", "b", "a", "e", "d"}, }, } @@ -209,19 +209,19 @@ func TestPersistedMeta(t *testing.T) { fileContent string }{ { - "unknown", - []expectedTargets{ + file: "unknown", + targets: []expectedTargets{ { "targets.json", 2, }, }, - ErrUnknownTarget{Name: "unknown", SnapshotVersion: 2}, - "", + downloadError: ErrUnknownTarget{Name: "unknown", SnapshotVersion: 2}, + fileContent: "", }, { - "b.txt", - []expectedTargets{ + file: "b.txt", + targets: []expectedTargets{ { "targets.json", 2, @@ -235,12 +235,12 @@ func TestPersistedMeta(t *testing.T) { 1, }, }, - nil, - "Contents: b.txt", + downloadError: nil, + fileContent: "Contents: b.txt", }, { - "f.txt", - []expectedTargets{ + file: "f.txt", + targets: []expectedTargets{ { "targets.json", 2, @@ -270,8 +270,8 @@ func TestPersistedMeta(t *testing.T) { 1, }, }, - nil, - "Contents: f.txt", + downloadError: nil, + fileContent: "Contents: f.txt", }, } From c0b597cb9991003f9fecf51e7549949f7793e503 Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Tue, 13 Jul 2021 15:00:13 -0400 Subject: [PATCH 09/22] Add tags and remove duplicate types --- client/delegations_test.go | 60 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/client/delegations_test.go b/client/delegations_test.go index c91d1d89..bf4836a8 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -32,8 +32,8 @@ func TestDelegationsIterator(t *testing.T) { { testName: "no termination", roles: map[string][]data.DelegatedRole{ - "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, - "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, + "a": {{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, + "b": {{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, file: "", @@ -42,8 +42,8 @@ func TestDelegationsIterator(t *testing.T) { { testName: "terminated in b", roles: map[string][]data.DelegatedRole{ - "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers, Terminating: true}, {Name: "e", PathMatchers: defaultPathMatchers}}, - "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, + "a": {{Name: "b", PathMatchers: defaultPathMatchers, Terminating: true}, {Name: "e", PathMatchers: defaultPathMatchers}}, + "b": {{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, file: "", @@ -52,8 +52,8 @@ func TestDelegationsIterator(t *testing.T) { { testName: "path does not match b", roles: map[string][]data.DelegatedRole{ - "a": []data.DelegatedRole{{Name: "b", PathMatchers: notMatchingPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, - "b": []data.DelegatedRole{{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, + "a": {{Name: "b", PathMatchers: notMatchingPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, + "b": {{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, file: "", @@ -62,8 +62,8 @@ func TestDelegationsIterator(t *testing.T) { { testName: "cycle avoided", roles: map[string][]data.DelegatedRole{ - "a": []data.DelegatedRole{{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, - "b": []data.DelegatedRole{{Name: "a", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, + "a": {{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, + "b": {{Name: "a", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, }, rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, file: "", @@ -212,8 +212,8 @@ func TestPersistedMeta(t *testing.T) { file: "unknown", targets: []expectedTargets{ { - "targets.json", - 2, + name: "targets.json", + version: 2, }, }, downloadError: ErrUnknownTarget{Name: "unknown", SnapshotVersion: 2}, @@ -223,16 +223,16 @@ func TestPersistedMeta(t *testing.T) { file: "b.txt", targets: []expectedTargets{ { - "targets.json", - 2, + name: "targets.json", + version: 2, }, { - "a.json", - 1, + name: "a.json", + version: 1, }, { - "b.json", - 1, + name: "b.json", + version: 1, }, }, downloadError: nil, @@ -242,32 +242,32 @@ func TestPersistedMeta(t *testing.T) { file: "f.txt", targets: []expectedTargets{ { - "targets.json", - 2, + name: "targets.json", + version: 2, }, { - "a.json", - 1, + name: "a.json", + version: 1, }, { - "b.json", - 1, + name: "b.json", + version: 1, }, { - "c.json", - 1, + name: "c.json", + version: 1, }, { - "d.json", - 1, + name: "d.json", + version: 1, }, { - "e.json", - 1, + name: "e.json", + version: 1, }, { - "f.json", - 1, + name: "f.json", + version: 1, }, }, downloadError: nil, From 6b762525a1bc9836164c1c56ac97b407c9225dba Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Tue, 13 Jul 2021 15:09:32 -0400 Subject: [PATCH 10/22] Formatting --- client/delegations.go | 19 ++++++++++++++++-- client/delegations_test.go | 40 ++++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index 7935360c..b17414f6 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -13,7 +13,10 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) { if err != nil { return data.TargetFileMeta{}, err } - verifiers := map[string]verify.DelegationsVerifier{"root": verify.DelegationsVerifier{c.db}} + + verifiers := map[string]verify.DelegationsVerifier{ + "root": {DB: c.db}, + } // delegationsIterator covers 5.6.7 // - pre-order depth-first search starting with the top targets @@ -26,16 +29,19 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) { if !ok { return data.TargetFileMeta{}, ErrUnknownTarget{file, snapshot.Version} } - verifier := verifiers[d.parent] + // covers 5.6.{1,2,3,4,5,6} + verifier := verifiers[d.parent] target, err := c.loadDelegatedTargets(snapshot, d.child.Name, verifier) if err != nil { return data.TargetFileMeta{}, err } + // stop when the searched TargetFileMeta is found if m, ok := target.Targets[file]; ok { return m, nil } + if target.Delegations != nil { delegations.add(target.Delegations.Roles, d.child.Name) targetVerifier, err := verify.NewDelegationsVerifier(target.Delegations) @@ -45,6 +51,7 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) { verifiers[d.child.Name] = targetVerifier } } + return data.TargetFileMeta{}, ErrMaxDelegations{ File: file, MaxDelegations: c.MaxDelegations, @@ -56,10 +63,12 @@ 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} @@ -75,6 +84,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri 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 raw, alreadyStored := c.localMetaFromSnapshot(fileName, fileMeta) @@ -84,6 +94,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri return nil, err } } + target := &data.Targets{} // 5.6.3 verify signature with parent public keys // 5.6.5 verify that the targets is not expired @@ -114,10 +125,12 @@ func (c *Client) rootTargetDelegation() data.DelegatedRole { if r == nil { return data.DelegatedRole{} } + keyIDs := make([]string, 0, len(r.KeyIDs)) for id, _ := range r.KeyIDs { keyIDs = append(keyIDs, id) } + return data.DelegatedRole{ Name: role, KeyIDs: keyIDs, @@ -148,7 +161,9 @@ func newDelegationsIterator(role data.DelegatedRole, parent string, file string) stack: make([]delegation, 0, 1), visited: make(map[delegationID]struct{}), } + i.add([]data.DelegatedRole{role}, parent) + return i } diff --git a/client/delegations_test.go b/client/delegations_test.go index bf4836a8..79ce9907 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -32,8 +32,14 @@ func TestDelegationsIterator(t *testing.T) { { testName: "no termination", roles: map[string][]data.DelegatedRole{ - "a": {{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, - "b": {{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, + "a": { + {Name: "b", PathMatchers: defaultPathMatchers}, + {Name: "e", PathMatchers: defaultPathMatchers}, + }, + "b": { + {Name: "c", PathMatchers: defaultPathMatchers}, + {Name: "d", PathMatchers: defaultPathMatchers}, + }, }, rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, file: "", @@ -42,8 +48,14 @@ func TestDelegationsIterator(t *testing.T) { { testName: "terminated in b", roles: map[string][]data.DelegatedRole{ - "a": {{Name: "b", PathMatchers: defaultPathMatchers, Terminating: true}, {Name: "e", PathMatchers: defaultPathMatchers}}, - "b": {{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, + "a": { + {Name: "b", PathMatchers: defaultPathMatchers, Terminating: true}, + {Name: "e", PathMatchers: defaultPathMatchers}, + }, + "b": { + {Name: "c", PathMatchers: defaultPathMatchers}, + {Name: "d", PathMatchers: defaultPathMatchers}, + }, }, rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, file: "", @@ -52,8 +64,14 @@ func TestDelegationsIterator(t *testing.T) { { testName: "path does not match b", roles: map[string][]data.DelegatedRole{ - "a": {{Name: "b", PathMatchers: notMatchingPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, - "b": {{Name: "c", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, + "a": { + {Name: "b", PathMatchers: notMatchingPathMatchers}, + {Name: "e", PathMatchers: defaultPathMatchers}, + }, + "b": { + {Name: "c", PathMatchers: defaultPathMatchers}, + {Name: "d", PathMatchers: defaultPathMatchers}, + }, }, rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, file: "", @@ -62,8 +80,14 @@ func TestDelegationsIterator(t *testing.T) { { testName: "cycle avoided", roles: map[string][]data.DelegatedRole{ - "a": {{Name: "b", PathMatchers: defaultPathMatchers}, {Name: "e", PathMatchers: defaultPathMatchers}}, - "b": {{Name: "a", PathMatchers: defaultPathMatchers}, {Name: "d", PathMatchers: defaultPathMatchers}}, + "a": { + {Name: "b", PathMatchers: defaultPathMatchers}, + {Name: "e", PathMatchers: defaultPathMatchers}, + }, + "b": { + {Name: "a", PathMatchers: defaultPathMatchers}, + {Name: "d", PathMatchers: defaultPathMatchers}, + }, }, rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, file: "", From 14376fb0b7e37152fb1eb0ff4f7b9b2484e9f0a0 Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Tue, 13 Jul 2021 15:25:06 -0400 Subject: [PATCH 11/22] Fix order of asserts (should be want, got) --- client/delegations_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/delegations_test.go b/client/delegations_test.go index 79ce9907..49ecc265 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -111,9 +111,9 @@ func TestDelegationsIterator(t *testing.T) { } d.add(delegations, r.child.Name) } - assert.Equal(t, len(iterationOrder), len(tt.resultOrder)) + assert.Equal(t, len(tt.resultOrder), len(iterationOrder)) for i, role := range iterationOrder { - assert.Equal(t, role, tt.resultOrder[i]) + assert.Equal(t, tt.resultOrder[i], role) } }) } @@ -128,7 +128,7 @@ func TestGetTargetMeta(t *testing.T) { f, err := c.getTargetFileMeta("f.txt") assert.Nil(t, err) - assert.Equal(t, f.Length, int64(15)) + assert.Equal(t, int64(15), f.Length) } func TestMaxDelegations(t *testing.T) { @@ -139,7 +139,7 @@ func TestMaxDelegations(t *testing.T) { assert.Nil(t, err) c.MaxDelegations = 2 _, err = c.getTargetFileMeta("c.txt") - assert.Equal(t, err, ErrMaxDelegations{File: "c.txt", MaxDelegations: 2, SnapshotVersion: 2}) + assert.Equal(t, ErrMaxDelegations{File: "c.txt", MaxDelegations: 2, SnapshotVersion: 2}, err) } func TestMetaNotFound(t *testing.T) { @@ -149,7 +149,7 @@ func TestMetaNotFound(t *testing.T) { _, err := c.Update() assert.Nil(t, err) _, err = c.getTargetFileMeta("unknown.txt") - assert.Equal(t, err, ErrUnknownTarget{Name: "unknown.txt", SnapshotVersion: 2}) + assert.Equal(t, ErrUnknownTarget{Name: "unknown.txt", SnapshotVersion: 2}, err) } type fakeRemote struct { @@ -315,7 +315,7 @@ func TestPersistedMeta(t *testing.T) { } for _, targets := range tt.targets { storedVersion, err := versionOfStoredTargets(targets.name, persisted) - assert.Equal(t, storedVersion, targets.version) + assert.Equal(t, targets.version, storedVersion) assert.Nil(t, err) delete(persisted, targets.name) } From 0c822ec948a614ddc3b801c9832a68a1a07e958c Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Tue, 13 Jul 2021 16:19:38 -0400 Subject: [PATCH 12/22] Clean up DelegatedRole validation --- client/delegations.go | 8 +-- client/delegations_test.go | 44 ++++++------- data/types.go | 81 +++++++++++------------ data/types_test.go | 127 ++++++++++++++++++------------------- 4 files changed, 124 insertions(+), 136 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index b17414f6..1beca24c 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -132,10 +132,10 @@ func (c *Client) rootTargetDelegation() data.DelegatedRole { } return data.DelegatedRole{ - Name: role, - KeyIDs: keyIDs, - Threshold: r.Threshold, - PathMatchers: []string{"*"}, + Name: role, + KeyIDs: keyIDs, + Threshold: r.Threshold, + Paths: []string{"*"}, } } diff --git a/client/delegations_test.go b/client/delegations_test.go index 49ecc265..7f528942 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -17,8 +17,8 @@ import ( ) var ( - defaultPathMatchers = []string{"tmp", "*"} - notMatchingPathMatchers = []string{"vars", "null"} + defaultPathPatterns = []string{"tmp", "*"} + noMatchPathPatterns = []string{"vars", "null"} ) func TestDelegationsIterator(t *testing.T) { @@ -33,15 +33,15 @@ func TestDelegationsIterator(t *testing.T) { testName: "no termination", roles: map[string][]data.DelegatedRole{ "a": { - {Name: "b", PathMatchers: defaultPathMatchers}, - {Name: "e", PathMatchers: defaultPathMatchers}, + {Name: "b", Paths: defaultPathPatterns}, + {Name: "e", Paths: defaultPathPatterns}, }, "b": { - {Name: "c", PathMatchers: defaultPathMatchers}, - {Name: "d", PathMatchers: defaultPathMatchers}, + {Name: "c", Paths: defaultPathPatterns}, + {Name: "d", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, + rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, file: "", resultOrder: []string{"a", "b", "c", "d", "e"}, }, @@ -49,15 +49,15 @@ func TestDelegationsIterator(t *testing.T) { testName: "terminated in b", roles: map[string][]data.DelegatedRole{ "a": { - {Name: "b", PathMatchers: defaultPathMatchers, Terminating: true}, - {Name: "e", PathMatchers: defaultPathMatchers}, + {Name: "b", Paths: defaultPathPatterns, Terminating: true}, + {Name: "e", Paths: defaultPathPatterns}, }, "b": { - {Name: "c", PathMatchers: defaultPathMatchers}, - {Name: "d", PathMatchers: defaultPathMatchers}, + {Name: "c", Paths: defaultPathPatterns}, + {Name: "d", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, + rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, file: "", resultOrder: []string{"a", "b", "c", "d"}, }, @@ -65,15 +65,15 @@ func TestDelegationsIterator(t *testing.T) { testName: "path does not match b", roles: map[string][]data.DelegatedRole{ "a": { - {Name: "b", PathMatchers: notMatchingPathMatchers}, - {Name: "e", PathMatchers: defaultPathMatchers}, + {Name: "b", Paths: noMatchPathPatterns}, + {Name: "e", Paths: defaultPathPatterns}, }, "b": { - {Name: "c", PathMatchers: defaultPathMatchers}, - {Name: "d", PathMatchers: defaultPathMatchers}, + {Name: "c", Paths: defaultPathPatterns}, + {Name: "d", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, + rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, file: "", resultOrder: []string{"a", "e"}, }, @@ -81,15 +81,15 @@ func TestDelegationsIterator(t *testing.T) { testName: "cycle avoided", roles: map[string][]data.DelegatedRole{ "a": { - {Name: "b", PathMatchers: defaultPathMatchers}, - {Name: "e", PathMatchers: defaultPathMatchers}, + {Name: "b", Paths: defaultPathPatterns}, + {Name: "e", Paths: defaultPathPatterns}, }, "b": { - {Name: "a", PathMatchers: defaultPathMatchers}, - {Name: "d", PathMatchers: defaultPathMatchers}, + {Name: "a", Paths: defaultPathPatterns}, + {Name: "d", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", PathMatchers: defaultPathMatchers}, + rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, file: "", resultOrder: []string{"a", "b", "a", "e", "d"}, }, diff --git a/data/types.go b/data/types.go index cbf2ca82..da62e67c 100644 --- a/data/types.go +++ b/data/types.go @@ -230,70 +230,61 @@ type Delegations struct { // delegatedRoleJSON and delegatedRoleCopy help decoding and encoding jsons. // DelegatedRole UnmarshalJSON will fail and return ErrPathsAndPathHashesSet if both fields are set and not empty type DelegatedRole struct { - Name string `json:"name"` - KeyIDs []string `json:"keyids"` - Threshold int `json:"threshold"` - Terminating bool `json:"terminating"` - MatchWithHashPrefixes bool `json:"-"` - PathMatchers []string `json:"-"` + Name string `json:"name"` + KeyIDs []string `json:"keyids"` + Threshold int `json:"threshold"` + Terminating bool `json:"terminating"` + PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"` + Paths []string `json:"paths"` } func (d *DelegatedRole) MatchesPath(file string) bool { - for _, pattern := range d.PathMatchers { - if d.MatchWithHashPrefixes { - pathHash := PathHexDigest(file) - if strings.HasPrefix(pathHash, pattern) { - return true - } - } + for _, pattern := range d.Paths { if matched, _ := filepath.Match(pattern, file); matched { return true } } + + pathHash := PathHexDigest(file) + for _, hashPrefix := range d.PathHashPrefixes { + if strings.HasPrefix(pathHash, hashPrefix) { + return true + } + } + return false } -type delegatedRoleJSON struct { - delegatedRoleCopy - PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"` - Paths []string `json:"paths"` -} +// validateFields enforces the spec 1.0.19 section 4.5: +// 'role MUST specify only one of the "path_hash_prefixes" or "paths"' +// Marshalling and unmarshalling JSON will fail and return +// ErrPathsAndPathHashesSet if both fields are set and not empty. +func (d *DelegatedRole) validateFields() error { + if len(d.PathHashPrefixes) > 0 && len(d.Paths) > 0 { + return ErrPathsAndPathHashesSet + } -// delegatedRoleCopy is used for MarshalJSON and UnmarshalJSON to extract DelegatedRole -// fields from delegatedRoleJSON -type delegatedRoleCopy DelegatedRole + return nil +} func (d *DelegatedRole) MarshalJSON() ([]byte, error) { - delegatedJSON := delegatedRoleJSON{delegatedRoleCopy: delegatedRoleCopy(*d)} - if d.MatchWithHashPrefixes { - delegatedJSON.PathHashPrefixes = d.PathMatchers - } else { - delegatedJSON.Paths = d.PathMatchers + type delegatedRoleAlias DelegatedRole + + if err := d.validateFields(); err != nil { + return nil, err } - return json.Marshal(&delegatedJSON) + + return json.Marshal((*delegatedRoleAlias)(d)) } func (d *DelegatedRole) UnmarshalJSON(b []byte) error { - var djson delegatedRoleJSON - err := json.Unmarshal(b, &djson) - if err != nil { + type delegatedRoleAlias DelegatedRole + + if err := json.Unmarshal(b, (*delegatedRoleAlias)(d)); err != nil { return err } - new := DelegatedRole(djson.delegatedRoleCopy) - *d = new - if len(djson.PathHashPrefixes) != 0 && len(djson.Paths) != 0 { - return ErrPathsAndPathHashesSet - } - if len(djson.PathHashPrefixes) > 0 { - d.MatchWithHashPrefixes = true - d.PathMatchers = djson.PathHashPrefixes - return nil - } - if len(djson.Paths) > 0 { - d.PathMatchers = djson.Paths - return nil - } - return nil + + return d.validateFields() } func NewTargets() *Targets { diff --git a/data/types_test.go b/data/types_test.go index f9086971..4f28ea67 100644 --- a/data/types_test.go +++ b/data/types_test.go @@ -97,11 +97,11 @@ func (TypesSuite) TestRoleAddKeyIDs(c *C) { func TestDelegatedRolePathMatch(t *testing.T) { var tts = []struct { - testName string - file string - matchWithHashPrefixes bool - pathMatchers []string - matches bool + testName string + pathPatterns []string + pathHashPrefixes []string + file string + shouldMatch bool }{ { testName: "no path", @@ -109,62 +109,62 @@ func TestDelegatedRolePathMatch(t *testing.T) { }, { testName: "match path *", - pathMatchers: []string{"null", "targets/*.tgz"}, + pathPatterns: []string{"null", "targets/*.tgz"}, file: "targets/foo.tgz", - matches: true, + shouldMatch: true, }, { testName: "does not match path *", - pathMatchers: []string{"null", "targets/*.tgz"}, + pathPatterns: []string{"null", "targets/*.tgz"}, file: "targets/foo.txt", + shouldMatch: false, }, { testName: "match path ?", - pathMatchers: []string{"foo-version-?.tgz"}, + pathPatterns: []string{"foo-version-?.tgz"}, file: "foo-version-a.tgz", - matches: true, + shouldMatch: true, }, { testName: "does not match ?", - pathMatchers: []string{"foo-version-?.tgz"}, + pathPatterns: []string{"foo-version-?.tgz"}, file: "foo-version-alpha.tgz", + shouldMatch: false, }, // picked from https://github.com/theupdateframework/tuf/blob/30ba6e9f9ab25e0370e29ce574dada2d8809afa0/tests/test_updater.py#L1726-L1734 { - testName: "match hash prefix", - pathMatchers: []string{"badd", "8baf"}, - file: "/file3.txt", - matchWithHashPrefixes: true, - matches: true, + testName: "match hash prefix", + pathHashPrefixes: []string{"badd", "8baf"}, + file: "/file3.txt", + shouldMatch: true, }, { - testName: "does not match hash prefix", - pathMatchers: []string{"badd"}, - matchWithHashPrefixes: true, - file: "/file3.txt", + testName: "does not match hash prefix", + pathHashPrefixes: []string{"badd"}, + file: "/file3.txt", + shouldMatch: false, }, { - testName: "hash prefix first char", - pathMatchers: []string{"2"}, - matchWithHashPrefixes: true, - file: "/a/b/c/file_d.txt", - matches: true, + testName: "hash prefix first char", + pathHashPrefixes: []string{"2"}, + file: "/a/b/c/file_d.txt", + shouldMatch: true, }, { - testName: "full hash prefix", - pathMatchers: []string{"34c85d1ee84f61f10d7dc633472a49096ed87f8f764bd597831eac371f40ac39"}, - matchWithHashPrefixes: true, - file: "/e/f/g.txt", - matches: true, + testName: "full hash prefix", + pathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633472a49096ed87f8f764bd597831eac371f40ac39"}, + file: "/e/f/g.txt", + shouldMatch: true, }, } for _, tt := range tts { t.Run(tt.testName, func(t *testing.T) { d := DelegatedRole{ - PathMatchers: tt.pathMatchers, - MatchWithHashPrefixes: tt.matchWithHashPrefixes, + Paths: tt.pathPatterns, + PathHashPrefixes: tt.pathHashPrefixes, } - assert.Equal(t, tt.matches, d.MatchesPath(tt.file)) + assert.NoError(t, d.validateFields()) + assert.Equal(t, tt.shouldMatch, d.MatchesPath(tt.file)) }) } @@ -173,49 +173,46 @@ func TestDelegatedRolePathMatch(t *testing.T) { func TestDelegatedRoleJSON(t *testing.T) { var tts = []struct { testName string - d DelegatedRole + d *DelegatedRole rawCJSON string }{{ - "all fields with hashes", - DelegatedRole{ - Name: "n1", - KeyIDs: []string{"k1"}, - Threshold: 5, - Terminating: true, - MatchWithHashPrefixes: true, - PathMatchers: []string{"8f"}, + testName: "all fields with hashes", + d: &DelegatedRole{ + Name: "n1", + KeyIDs: []string{"k1"}, + Threshold: 5, + Terminating: true, + PathHashPrefixes: []string{"8f"}, }, - `{"keyids":["k1"],"name":"n1","path_hash_prefixes":["8f"],"paths":null,"terminating":true,"threshold":5}`, + rawCJSON: `{"keyids":["k1"],"name":"n1","path_hash_prefixes":["8f"],"paths":null,"terminating":true,"threshold":5}`, }, { - "paths only", - DelegatedRole{ - Name: "n2", - KeyIDs: []string{"k1", "k3"}, - Threshold: 12, - PathMatchers: []string{"*.txt"}, + testName: "paths only", + d: &DelegatedRole{ + Name: "n2", + KeyIDs: []string{"k1", "k3"}, + Threshold: 12, + Paths: []string{"*.txt"}, }, - `{"keyids":["k1","k3"],"name":"n2","paths":["*.txt"],"terminating":false,"threshold":12}`, + rawCJSON: `{"keyids":["k1","k3"],"name":"n2","paths":["*.txt"],"terminating":false,"threshold":12}`, }, { - "default", - DelegatedRole{}, - `{"keyids":null,"name":"","paths":null,"terminating":false,"threshold":0}`, + testName: "default", + d: &DelegatedRole{}, + rawCJSON: `{"keyids":null,"name":"","paths":null,"terminating":false,"threshold":0}`, }, } for _, tt := range tts { t.Run(tt.testName, func(t *testing.T) { - d := &DelegatedRole{} - *d = tt.d - - raw, err := cjson.Marshal(d) - assert.Nil(t, err) - assert.Equal(t, tt.rawCJSON, string(raw)) - - var newD DelegatedRole - assert.Nil(t, json.Unmarshal(raw, &newD)) - assert.Equal(t, d, &newD) + b, err := cjson.Marshal(tt.d) + assert.NoError(t, err) + assert.Equal(t, tt.rawCJSON, string(b)) + + newD := &DelegatedRole{} + err = json.Unmarshal(b, newD) + assert.NoError(t, err) + assert.Equal(t, tt.d, newD) }) } } @@ -223,9 +220,9 @@ func TestDelegatedRoleJSON(t *testing.T) { func TestDelegatedRoleUnmarshalErr(t *testing.T) { targetsWithBothMatchers := []byte(`{"keyids":null,"name":"","paths":["*.txt"],"path_hash_prefixes":["8f"],"terminating":false,"threshold":0}`) var d DelegatedRole - assert.Equal(t, json.Unmarshal(targetsWithBothMatchers, &d), ErrPathsAndPathHashesSet) + assert.Equal(t, ErrPathsAndPathHashesSet, json.Unmarshal(targetsWithBothMatchers, &d)) // test for type errors err := json.Unmarshal([]byte(`{"keyids":"a"}`), &d) - assert.Equal(t, err.Error(), "json: cannot unmarshal string into Go struct field delegatedRoleJSON.keyids of type []string") + assert.Equal(t, "keyids", err.(*json.UnmarshalTypeError).Field) } From a8f8cb0c336d1901ad9c7dfc72dedd67bfacf956 Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Wed, 14 Jul 2021 15:41:41 +0200 Subject: [PATCH 13/22] Fix root to top targets delegation --- client/delegations.go | 9 +++++---- client/delegations_test.go | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index 1beca24c..593b328c 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -131,11 +131,12 @@ func (c *Client) rootTargetDelegation() data.DelegatedRole { keyIDs = append(keyIDs, id) } + // root delegates the signing of all files to the top level targets return data.DelegatedRole{ - Name: role, - KeyIDs: keyIDs, - Threshold: r.Threshold, - Paths: []string{"*"}, + Name: role, + KeyIDs: keyIDs, + Threshold: r.Threshold, + PathHashPrefixes: []string{""}, } } diff --git a/client/delegations_test.go b/client/delegations_test.go index 7f528942..01ff9453 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -188,6 +188,14 @@ func TestTargetsNotFound(t *testing.T) { assert.Equal(t, ErrMissingRemoteMetadata{Name: "c.json"}, err) } +func TestRootDelegationMatchesAll(t *testing.T) { + c := &Client{db: verify.NewDB()} + c.db.AddRole("targets", &data.Role{Threshold: 1}) + d := c.rootTargetDelegation() + assert.True(t, d.MatchesPath("a.txt")) + assert.True(t, d.MatchesPath("var/b//g")) +} + func TestUnverifiedTargets(t *testing.T) { verify.IsExpired = func(t time.Time) bool { return false } c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") From ea6bcf81e53e2f85f1124734e758f4a1701cc85b Mon Sep 17 00:00:00 2001 From: Ethan Lowman <53835328+ethan-lowman-dd@users.noreply.github.com> Date: Thu, 15 Jul 2021 07:33:56 -0400 Subject: [PATCH 14/22] July 14 changes (#4) * Clarify validation of signing role & metadata type * Add/update comments * Validation happens on both decode & encode * Bubble up an error if MatchesPath is called on an invalid DelegatedRole * Match spec for delegation traversal * Comment in the iterator * Add diamond test case * Revert "Match spec for delegation traversal" This reverts commit 15fee6bbabeb2f7db58b10323df7b21e5868db8d. * Rename IsTopLevelRole back to ValidRole to avoid breaking change --- client/delegations.go | 18 ++++++++-- client/delegations_test.go | 68 +++++++++++++++++++++++++++++++++----- data/types.go | 31 ++++++++++++----- data/types_test.go | 5 ++- verify/db.go | 11 ++++-- verify/verify.go | 17 ++++++++-- 6 files changed, 123 insertions(+), 27 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index 593b328c..38a01123 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -43,7 +43,11 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) { } if target.Delegations != nil { - delegations.add(target.Delegations.Roles, d.child.Name) + err := delegations.add(target.Delegations.Roles, d.child.Name) + if err != nil { + return data.TargetFileMeta{}, err + } + targetVerifier, err := verify.NewDelegationsVerifier(target.Delegations) if err != nil { return data.TargetFileMeta{}, err @@ -191,11 +195,19 @@ func (d *delegationsIterator) next() (delegation, bool) { return delegation, true } -func (d *delegationsIterator) add(roles []data.DelegatedRole, parent string) { +func (d *delegationsIterator) add(roles []data.DelegatedRole, parent string) error { for i := len(roles) - 1; i >= 0; i-- { + // Push the roles onto the stack in reverse so we get an in-order traversal + // of the delegations graph. r := roles[i] - if r.MatchesPath(d.file) { + matchesPath, err := r.MatchesPath(d.file) + if err != nil { + return err + } + if matchesPath { d.stack = append(d.stack, delegation{parent, r}) } } + + return nil } diff --git a/client/delegations_test.go b/client/delegations_test.go index 01ff9453..33c4c32a 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -38,12 +38,23 @@ func TestDelegationsIterator(t *testing.T) { }, "b": { {Name: "c", Paths: defaultPathPatterns}, + }, + "c": { {Name: "d", Paths: defaultPathPatterns}, }, + "e": { + {Name: "f", Paths: defaultPathPatterns}, + {Name: "g", Paths: defaultPathPatterns}, + }, + "g": { + {Name: "h", Paths: defaultPathPatterns}, + {Name: "i", Paths: defaultPathPatterns}, + {Name: "j", Paths: defaultPathPatterns}, + }, }, rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, file: "", - resultOrder: []string{"a", "b", "c", "d", "e"}, + resultOrder: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}, }, { testName: "terminated in b", @@ -78,7 +89,7 @@ func TestDelegationsIterator(t *testing.T) { resultOrder: []string{"a", "e"}, }, { - testName: "cycle avoided", + testName: "cycle avoided 1", roles: map[string][]data.DelegatedRole{ "a": { {Name: "b", Paths: defaultPathPatterns}, @@ -93,6 +104,44 @@ func TestDelegationsIterator(t *testing.T) { file: "", resultOrder: []string{"a", "b", "a", "e", "d"}, }, + { + testName: "cycle avoided 2", + roles: map[string][]data.DelegatedRole{ + "a": { + {Name: "a", Paths: defaultPathPatterns}, + {Name: "b", Paths: defaultPathPatterns}, + }, + "b": { + {Name: "a", Paths: defaultPathPatterns}, + {Name: "b", Paths: defaultPathPatterns}, + {Name: "c", Paths: defaultPathPatterns}, + }, + "c": { + {Name: "c", Paths: defaultPathPatterns}, + }, + }, + rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, + file: "", + resultOrder: []string{"a", "a", "b", "a", "b", "c", "c"}, + }, + { + testName: "diamond delegation", + roles: map[string][]data.DelegatedRole{ + "a": { + {Name: "b", Paths: defaultPathPatterns}, + {Name: "c", Paths: defaultPathPatterns}, + }, + "b": { + {Name: "d", Paths: defaultPathPatterns}, + }, + "c": { + {Name: "d", Paths: defaultPathPatterns}, + }, + }, + rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, + file: "", + resultOrder: []string{"a", "b", "d", "c", "d"}, + }, } for _, tt := range iteratorTests { @@ -111,10 +160,7 @@ func TestDelegationsIterator(t *testing.T) { } d.add(delegations, r.child.Name) } - assert.Equal(t, len(tt.resultOrder), len(iterationOrder)) - for i, role := range iterationOrder { - assert.Equal(t, tt.resultOrder[i], role) - } + assert.Equal(t, tt.resultOrder, iterationOrder) }) } } @@ -192,8 +238,14 @@ func TestRootDelegationMatchesAll(t *testing.T) { c := &Client{db: verify.NewDB()} c.db.AddRole("targets", &data.Role{Threshold: 1}) d := c.rootTargetDelegation() - assert.True(t, d.MatchesPath("a.txt")) - assert.True(t, d.MatchesPath("var/b//g")) + + matchesPath, err := d.MatchesPath("a.txt") + assert.NoError(t, err) + assert.True(t, matchesPath) + + matchesPath, err = d.MatchesPath("var/b//g") + assert.NoError(t, err) + assert.True(t, matchesPath) } func TestUnverifiedTargets(t *testing.T) { diff --git a/data/types.go b/data/types.go index da62e67c..4ccc1702 100644 --- a/data/types.go +++ b/data/types.go @@ -23,7 +23,7 @@ const ( var ( KeyAlgorithms = []string{"sha256", "sha512"} - ErrPathsAndPathHashesSet = errors.New("tuf: failed decoding targets : paths and path_hash_prefixes are set") + ErrPathsAndPathHashesSet = errors.New("tuf: failed validation of delegated target: paths and path_hash_prefixes are both set") ) type Signed struct { @@ -220,15 +220,15 @@ type Targets struct { Delegations *Delegations `json:"delegations,omitempty"` } +// Delegations represents the edges from a parent Targets role to one or more +// delegated target roles. See spec v1.0.19 section 4.5. type Delegations struct { Keys map[string]*Key `json:"keys"` Roles []DelegatedRole `json:"roles"` } -// DelegatedRole enforces the spec 1.0.19 section 4.5 'role MUST specify only one of the "path_hash_prefixes" or "paths"' -// by forcing one of path_hash_prefixes, paths with MatchWithHashPrefixes and PathPatterns fields. -// delegatedRoleJSON and delegatedRoleCopy help decoding and encoding jsons. -// DelegatedRole UnmarshalJSON will fail and return ErrPathsAndPathHashesSet if both fields are set and not empty +// DelegatedRole describes a delegated role, including what paths it is +// reponsible for. See spec v1.0.19 section 4.5. type DelegatedRole struct { Name string `json:"name"` KeyIDs []string `json:"keyids"` @@ -238,21 +238,28 @@ type DelegatedRole struct { Paths []string `json:"paths"` } -func (d *DelegatedRole) MatchesPath(file string) bool { +// MatchesPath evaluates whether the path patterns or path hash prefixes match +// a given file. This determines whether a delegated role is responsible for +// signing and verifying the file. +func (d *DelegatedRole) MatchesPath(file string) (bool, error) { + if err := d.validateFields(); err != nil { + return false, err + } + for _, pattern := range d.Paths { if matched, _ := filepath.Match(pattern, file); matched { - return true + return true, nil } } pathHash := PathHexDigest(file) for _, hashPrefix := range d.PathHashPrefixes { if strings.HasPrefix(pathHash, hashPrefix) { - return true + return true, nil } } - return false + return false, nil } // validateFields enforces the spec 1.0.19 section 4.5: @@ -267,6 +274,9 @@ func (d *DelegatedRole) validateFields() error { return nil } +// MarshalJSON is called when writing the struct to JSON. We validate prior to +// marshalling to ensure that an invalid delegated role can not be serialized +// to JSON. func (d *DelegatedRole) MarshalJSON() ([]byte, error) { type delegatedRoleAlias DelegatedRole @@ -277,6 +287,9 @@ func (d *DelegatedRole) MarshalJSON() ([]byte, error) { return json.Marshal((*delegatedRoleAlias)(d)) } +// UnmarshalJSON is called when reading the struct from JSON. We validate once +// unmarshalled to ensure that an error is thrown if an invalid delegated role +// is read. func (d *DelegatedRole) UnmarshalJSON(b []byte) error { type delegatedRoleAlias DelegatedRole diff --git a/data/types_test.go b/data/types_test.go index 4f28ea67..a8c5038b 100644 --- a/data/types_test.go +++ b/data/types_test.go @@ -164,7 +164,10 @@ func TestDelegatedRolePathMatch(t *testing.T) { PathHashPrefixes: tt.pathHashPrefixes, } assert.NoError(t, d.validateFields()) - assert.Equal(t, tt.shouldMatch, d.MatchesPath(tt.file)) + + matchesPath, err := d.MatchesPath(tt.file) + assert.NoError(t, err) + assert.Equal(t, tt.shouldMatch, matchesPath) }) } diff --git a/verify/db.go b/verify/db.go index f609b606..f8562c95 100644 --- a/verify/db.go +++ b/verify/db.go @@ -73,20 +73,25 @@ func (db *DB) AddKey(id string, k *data.Key) error { return nil } -var validRoles = map[string]struct{}{ +var topLevelRoles = map[string]struct{}{ "root": {}, "targets": {}, "snapshot": {}, "timestamp": {}, } +// ValidRole checks if a role is a top level role. func ValidRole(name string) bool { - _, ok := validRoles[name] + return isTopLevelRole(name) +} + +func isTopLevelRole(name string) bool { + _, ok := topLevelRoles[name] return ok } func (db *DB) AddRole(name string, r *data.Role) error { - if !ValidRole(name) { + if !isTopLevelRole(name) { return ErrInvalidRole } return db.addRole(name, r) diff --git a/verify/verify.go b/verify/verify.go index bdf04694..3e9c10fd 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -2,7 +2,6 @@ package verify import ( "encoding/json" - "strings" "time" cjson "github.com/tent/canonical-json-go" @@ -24,12 +23,24 @@ func (db *DB) Verify(s *data.Signed, role string, minVersion int) error { if err := json.Unmarshal(s.Signed, sm); err != nil { return err } - if typ := strings.ToLower(sm.Type); typ != strings.ToLower(role) && typ != "targets" { - return ErrWrongMetaType + + if isTopLevelRole(role) { + // Top-level roles can only sign metadata of the same type (e.g. snapshot + // metadata must be signed by the snapshot role). + if sm.Type != role { + return ErrWrongMetaType + } + } else { + // Delegated (non-top-level) roles may only sign targets metadata. + if sm.Type != "targets" { + return ErrWrongMetaType + } } + if IsExpired(sm.Expires) { return ErrExpired{sm.Expires} } + if sm.Version < minVersion { return ErrLowVersion{sm.Version, minVersion} } From 9631aebe2de0c383a399a93f3dcbb0f162d721a9 Mon Sep 17 00:00:00 2001 From: Raphael Gavache Date: Fri, 16 Jul 2021 14:57:23 +0200 Subject: [PATCH 15/22] Update after reviews * Add back lower case check * Initialize with size and comment * Simplify iterator initialization --- client/delegations.go | 55 ++++++++++++---------------- client/delegations_test.go | 73 ++++++++++++++------------------------ data/types.go | 3 +- verify/db.go | 2 +- verify/verify.go | 5 +-- 5 files changed, 54 insertions(+), 84 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index 38a01123..464f83c2 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -14,16 +14,16 @@ func (c *Client) getTargetFileMeta(file string) (data.TargetFileMeta, error) { return data.TargetFileMeta{}, err } - verifiers := map[string]verify.DelegationsVerifier{ - "root": {DB: c.db}, - } + // verifiers is map of parent targets name to an associated DelegationsVerifier + // that can verify all child targets pointed by delegatedRoles in the parent targets + verifiers := make(map[string]verify.DelegationsVerifier) // 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 file // - 5.6.7.1 cycles protection // - 5.6.7.2 terminations - delegations := newDelegationsIterator(c.rootTargetDelegation(), "root", file) + delegations := newDelegationsIterator(file) for i := 0; i < c.MaxDelegations; i++ { d, ok := delegations.next() if !ok { @@ -80,7 +80,7 @@ func (c *Client) loadLocalSnapshot() (*data.Snapshot, error) { return snapshot, nil } -// loadDelegatedTargets downloads, decodes, verifies and stores delegated targets +// loadDelegatedTargets downloads, decodes, verifies and stores targets func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, verifier verify.DelegationsVerifier) (*data.Targets, error) { var err error fileName := role + ".json" @@ -102,9 +102,16 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri target := &data.Targets{} // 5.6.3 verify signature with parent public keys // 5.6.5 verify that the targets is not expired - if err := verifier.Unmarshal(raw, target, role, fileMeta.Version); err != nil { + // role "targets" is the topTargets verified by root roles loaded in the client db + if role == "targets" { + err = c.db.Unmarshal(raw, target, role, fileMeta.Version) + } else { + err = verifier.Unmarshal(raw, target, role, fileMeta.Version) + } + if err != nil { return nil, ErrDecodeFailed{fileName, err} } + // 5.6.4 check against snapshot version if target.Version != fileMeta.Version { return nil, ErrTargetsSnapshotVersionMismatch{ @@ -123,27 +130,6 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri return target, nil } -func (c *Client) rootTargetDelegation() data.DelegatedRole { - role := "targets" - r := c.db.GetRole(role) - if r == nil { - return data.DelegatedRole{} - } - - keyIDs := make([]string, 0, len(r.KeyIDs)) - for id, _ := range r.KeyIDs { - keyIDs = append(keyIDs, id) - } - - // root delegates the signing of all files to the top level targets - return data.DelegatedRole{ - Name: role, - KeyIDs: keyIDs, - Threshold: r.Threshold, - PathHashPrefixes: []string{""}, - } -} - type delegation struct { parent string child data.DelegatedRole @@ -160,15 +146,18 @@ type delegationsIterator struct { visited map[delegationID]struct{} } -func newDelegationsIterator(role data.DelegatedRole, parent string, file string) *delegationsIterator { +// newDelegationsIterator initialises an iterator with a first step +// on top level targets +func newDelegationsIterator(file string) *delegationsIterator { i := &delegationsIterator{ - file: file, - stack: make([]delegation, 0, 1), + file: file, + stack: []delegation{ + { + child: data.DelegatedRole{Name: "targets"}, + }, + }, visited: make(map[delegationID]struct{}), } - - i.add([]data.DelegatedRole{role}, parent) - return i } diff --git a/client/delegations_test.go b/client/delegations_test.go index 33c4c32a..2bbeb8c5 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -23,16 +23,15 @@ var ( func TestDelegationsIterator(t *testing.T) { var iteratorTests = []struct { - testName string - roles map[string][]data.DelegatedRole - rootDelegation data.DelegatedRole - file string - resultOrder []string + testName string + roles map[string][]data.DelegatedRole + file string + resultOrder []string }{ { testName: "no termination", roles: map[string][]data.DelegatedRole{ - "a": { + "targets": { {Name: "b", Paths: defaultPathPatterns}, {Name: "e", Paths: defaultPathPatterns}, }, @@ -52,14 +51,13 @@ func TestDelegationsIterator(t *testing.T) { {Name: "j", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, - file: "", - resultOrder: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}, + file: "", + resultOrder: []string{"targets", "b", "c", "d", "e", "f", "g", "h", "i", "j"}, }, { testName: "terminated in b", roles: map[string][]data.DelegatedRole{ - "a": { + "targets": { {Name: "b", Paths: defaultPathPatterns, Terminating: true}, {Name: "e", Paths: defaultPathPatterns}, }, @@ -68,14 +66,13 @@ func TestDelegationsIterator(t *testing.T) { {Name: "d", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, - file: "", - resultOrder: []string{"a", "b", "c", "d"}, + file: "", + resultOrder: []string{"targets", "b", "c", "d"}, }, { testName: "path does not match b", roles: map[string][]data.DelegatedRole{ - "a": { + "targets": { {Name: "b", Paths: noMatchPathPatterns}, {Name: "e", Paths: defaultPathPatterns}, }, @@ -84,35 +81,33 @@ func TestDelegationsIterator(t *testing.T) { {Name: "d", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, - file: "", - resultOrder: []string{"a", "e"}, + file: "", + resultOrder: []string{"targets", "e"}, }, { testName: "cycle avoided 1", roles: map[string][]data.DelegatedRole{ - "a": { + "targets": { {Name: "b", Paths: defaultPathPatterns}, {Name: "e", Paths: defaultPathPatterns}, }, "b": { - {Name: "a", Paths: defaultPathPatterns}, + {Name: "targets", Paths: defaultPathPatterns}, {Name: "d", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, - file: "", - resultOrder: []string{"a", "b", "a", "e", "d"}, + file: "", + resultOrder: []string{"targets", "b", "targets", "e", "d"}, }, { testName: "cycle avoided 2", roles: map[string][]data.DelegatedRole{ - "a": { - {Name: "a", Paths: defaultPathPatterns}, + "targets": { + {Name: "targets", Paths: defaultPathPatterns}, {Name: "b", Paths: defaultPathPatterns}, }, "b": { - {Name: "a", Paths: defaultPathPatterns}, + {Name: "targets", Paths: defaultPathPatterns}, {Name: "b", Paths: defaultPathPatterns}, {Name: "c", Paths: defaultPathPatterns}, }, @@ -120,14 +115,13 @@ func TestDelegationsIterator(t *testing.T) { {Name: "c", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, - file: "", - resultOrder: []string{"a", "a", "b", "a", "b", "c", "c"}, + file: "", + resultOrder: []string{"targets", "targets", "b", "targets", "b", "c", "c"}, }, { testName: "diamond delegation", roles: map[string][]data.DelegatedRole{ - "a": { + "targets": { {Name: "b", Paths: defaultPathPatterns}, {Name: "c", Paths: defaultPathPatterns}, }, @@ -138,15 +132,14 @@ func TestDelegationsIterator(t *testing.T) { {Name: "d", Paths: defaultPathPatterns}, }, }, - rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns}, - file: "", - resultOrder: []string{"a", "b", "d", "c", "d"}, + file: "", + resultOrder: []string{"targets", "b", "d", "c", "d"}, }, } for _, tt := range iteratorTests { t.Run(tt.testName, func(t *testing.T) { - d := newDelegationsIterator(tt.rootDelegation, "root", tt.file) + d := newDelegationsIterator(tt.file) var iterationOrder []string for { r, ok := d.next() @@ -234,20 +227,6 @@ func TestTargetsNotFound(t *testing.T) { assert.Equal(t, ErrMissingRemoteMetadata{Name: "c.json"}, err) } -func TestRootDelegationMatchesAll(t *testing.T) { - c := &Client{db: verify.NewDB()} - c.db.AddRole("targets", &data.Role{Threshold: 1}) - d := c.rootTargetDelegation() - - matchesPath, err := d.MatchesPath("a.txt") - assert.NoError(t, err) - assert.True(t, matchesPath) - - matchesPath, err = d.MatchesPath("var/b//g") - assert.NoError(t, err) - assert.True(t, matchesPath) -} - func TestUnverifiedTargets(t *testing.T) { verify.IsExpired = func(t time.Time) bool { return false } c, closer := initTestDelegationClient(t, "testdata/php-tuf-fixtures/TUFTestFixture3LevelDelegation") diff --git a/data/types.go b/data/types.go index 4ccc1702..12d0ca25 100644 --- a/data/types.go +++ b/data/types.go @@ -262,7 +262,8 @@ func (d *DelegatedRole) MatchesPath(file string) (bool, error) { return false, nil } -// validateFields enforces the spec 1.0.19 section 4.5: +// validateFields enforces the spec +// https://theupdateframework.github.io/specification/v1.0.19/index.html#file-formats-targets // 'role MUST specify only one of the "path_hash_prefixes" or "paths"' // Marshalling and unmarshalling JSON will fail and return // ErrPathsAndPathHashesSet if both fields are set and not empty. diff --git a/verify/db.go b/verify/db.go index f8562c95..1565f99b 100644 --- a/verify/db.go +++ b/verify/db.go @@ -40,7 +40,7 @@ func (d *DelegationsVerifier) Unmarshal(b []byte, v interface{}, role string, mi func NewDelegationsVerifier(d *data.Delegations) (DelegationsVerifier, error) { db := &DB{ roles: make(map[string]*Role, len(d.Roles)), - keys: make(map[string]*data.Key), + keys: make(map[string]*data.Key, len(d.Keys)), } for _, r := range d.Roles { role := &data.Role{Threshold: r.Threshold, KeyIDs: r.KeyIDs} diff --git a/verify/verify.go b/verify/verify.go index 3e9c10fd..b6d4162d 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -2,6 +2,7 @@ package verify import ( "encoding/json" + "strings" "time" cjson "github.com/tent/canonical-json-go" @@ -27,12 +28,12 @@ func (db *DB) Verify(s *data.Signed, role string, minVersion int) error { if isTopLevelRole(role) { // Top-level roles can only sign metadata of the same type (e.g. snapshot // metadata must be signed by the snapshot role). - if sm.Type != role { + if strings.ToLower(sm.Type) != strings.ToLower(role) { return ErrWrongMetaType } } else { // Delegated (non-top-level) roles may only sign targets metadata. - if sm.Type != "targets" { + if strings.ToLower(sm.Type) != "targets" { return ErrWrongMetaType } } From 80371eac647bf5c8c711bd8ddc2fd90add7f98e2 Mon Sep 17 00:00:00 2001 From: Ethan Lowman <53835328+ethan-lowman-dd@users.noreply.github.com> Date: Fri, 16 Jul 2021 13:26:04 -0400 Subject: [PATCH 16/22] Revert back to "nodes seen" interpretation of delegation traversal spec (#6) (instead of true cycle detection with "edges seen"). This reverts commit cfbb024baeaa9f69159ef580e078747496eb0e62. --- client/delegations.go | 22 +++++++++------------- client/delegations_test.go | 6 +++--- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index 464f83c2..7198792c 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -135,15 +135,10 @@ type delegation struct { child data.DelegatedRole } -type delegationID struct { - parent string - child string -} - type delegationsIterator struct { - stack []delegation - file string - visited map[delegationID]struct{} + stack []delegation + file string + visitedRoles map[string]struct{} } // newDelegationsIterator initialises an iterator with a first step @@ -156,7 +151,7 @@ func newDelegationsIterator(file string) *delegationsIterator { child: data.DelegatedRole{Name: "targets"}, }, }, - visited: make(map[delegationID]struct{}), + visitedRoles: make(map[string]struct{}), } return i } @@ -168,12 +163,13 @@ func (d *delegationsIterator) next() (delegation, bool) { delegation := d.stack[len(d.stack)-1] d.stack = d.stack[:len(d.stack)-1] - // 5.6.7.1 cycles protection - id := delegationID{delegation.parent, delegation.child.Name} - if _, ok := d.visited[id]; ok { + // 5.6.7.1: If this role has been visited before, then skip this role (so + // that cycles in the delegation graph are avoided). + roleName := delegation.child.Name + if _, ok := d.visitedRoles[roleName]; ok { return d.next() } - d.visited[id] = struct{}{} + d.visitedRoles[roleName] = struct{}{} // 5.6.7.2 trim delegations to visit, only the current role and its delegations // will be considered diff --git a/client/delegations_test.go b/client/delegations_test.go index 2bbeb8c5..4e5ccbb4 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -97,7 +97,7 @@ func TestDelegationsIterator(t *testing.T) { }, }, file: "", - resultOrder: []string{"targets", "b", "targets", "e", "d"}, + resultOrder: []string{"targets", "b", "d", "e"}, }, { testName: "cycle avoided 2", @@ -116,7 +116,7 @@ func TestDelegationsIterator(t *testing.T) { }, }, file: "", - resultOrder: []string{"targets", "targets", "b", "targets", "b", "c", "c"}, + resultOrder: []string{"targets", "b", "c"}, }, { testName: "diamond delegation", @@ -133,7 +133,7 @@ func TestDelegationsIterator(t *testing.T) { }, }, file: "", - resultOrder: []string{"targets", "b", "d", "c", "d"}, + resultOrder: []string{"targets", "b", "d", "c"}, }, } From 033408424b64e5be17cde1876c05bf175cd07138 Mon Sep 17 00:00:00 2001 From: Raphael Gavache Date: Fri, 16 Jul 2021 19:28:18 +0200 Subject: [PATCH 17/22] Update client/delegations.go Co-authored-by: Ethan Lowman <53835328+ethan-lowman-dd@users.noreply.github.com> --- client/delegations.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/delegations.go b/client/delegations.go index 7198792c..0d23289e 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -175,6 +175,7 @@ func (d *delegationsIterator) next() (delegation, bool) { // will be considered // https://github.com/theupdateframework/specification/issues/168 if delegation.child.Terminating { + // Empty the stack. d.stack = d.stack[0:0] } return delegation, true From f5a27f3ef55011b7f7684df2d4d93112c1f2b70c Mon Sep 17 00:00:00 2001 From: Raphael Gavache Date: Fri, 16 Jul 2021 19:39:26 +0200 Subject: [PATCH 18/22] Update following reviews of 16th of july (#7) * Update name * Rename file to target * Nits * Move verifier in iterator and rename fields --- client/delegations.go | 64 ++++++++++++++++++-------------------- client/delegations_test.go | 8 ++--- client/errors.go | 4 +-- data/types.go | 10 +++--- data/types_test.go | 2 +- 5 files changed, 42 insertions(+), 46 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index 0d23289e..cd327c37 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -5,59 +5,49 @@ import ( "github.com/theupdateframework/go-tuf/verify" ) -// getTargetFileMeta searches for a verified TargetFileMeta matching a file name +// 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(file string) (data.TargetFileMeta, error) { +func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) { snapshot, err := c.loadLocalSnapshot() if err != nil { return data.TargetFileMeta{}, err } - // verifiers is map of parent targets name to an associated DelegationsVerifier - // that can verify all child targets pointed by delegatedRoles in the parent targets - verifiers := make(map[string]verify.DelegationsVerifier) - // 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 file + // - filter delegations with paths or path_hash_prefixes matching searched target // - 5.6.7.1 cycles protection // - 5.6.7.2 terminations - delegations := newDelegationsIterator(file) + delegations := newDelegationsIterator(target) for i := 0; i < c.MaxDelegations; i++ { d, ok := delegations.next() if !ok { - return data.TargetFileMeta{}, ErrUnknownTarget{file, snapshot.Version} + return data.TargetFileMeta{}, ErrUnknownTarget{target, snapshot.Version} } // covers 5.6.{1,2,3,4,5,6} - verifier := verifiers[d.parent] - target, err := c.loadDelegatedTargets(snapshot, d.child.Name, verifier) + targets, err := c.loadDelegatedTargets(snapshot, d.delegatee.Name, d.verifier) if err != nil { return data.TargetFileMeta{}, err } // stop when the searched TargetFileMeta is found - if m, ok := target.Targets[file]; ok { + if m, ok := targets.Targets[target]; ok { return m, nil } - if target.Delegations != nil { - err := delegations.add(target.Delegations.Roles, d.child.Name) - if err != nil { - return data.TargetFileMeta{}, err - } - - targetVerifier, err := verify.NewDelegationsVerifier(target.Delegations) + if targets.Delegations != nil { + delegationsVerifier, err := verify.NewDelegationsVerifier(targets.Delegations) if err != nil { return data.TargetFileMeta{}, err } - verifiers[d.child.Name] = targetVerifier + err = delegations.add(targets.Delegations.Roles, d.delegatee.Name, delegationsVerifier) } } return data.TargetFileMeta{}, ErrMaxDelegations{ - File: file, + Target: target, MaxDelegations: c.MaxDelegations, SnapshotVersion: snapshot.Version, } @@ -131,24 +121,25 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri } type delegation struct { - parent string - child data.DelegatedRole + delegator string + verifier verify.DelegationsVerifier + delegatee data.DelegatedRole } type delegationsIterator struct { stack []delegation - file string + target string visitedRoles map[string]struct{} } // newDelegationsIterator initialises an iterator with a first step // on top level targets -func newDelegationsIterator(file string) *delegationsIterator { +func newDelegationsIterator(target string) *delegationsIterator { i := &delegationsIterator{ - file: file, + target: target, stack: []delegation{ { - child: data.DelegatedRole{Name: "targets"}, + delegatee: data.DelegatedRole{Name: "targets"}, }, }, visitedRoles: make(map[string]struct{}), @@ -156,7 +147,7 @@ func newDelegationsIterator(file string) *delegationsIterator { return i } -func (d *delegationsIterator) next() (delegation, bool) { +func (d *delegationsIterator) next() (value delegation, ok bool) { if len(d.stack) == 0 { return delegation{}, false } @@ -165,7 +156,7 @@ func (d *delegationsIterator) next() (delegation, bool) { // 5.6.7.1: If this role has been visited before, then skip this role (so // that cycles in the delegation graph are avoided). - roleName := delegation.child.Name + roleName := delegation.delegatee.Name if _, ok := d.visitedRoles[roleName]; ok { return d.next() } @@ -174,24 +165,29 @@ func (d *delegationsIterator) next() (delegation, bool) { // 5.6.7.2 trim delegations to visit, only the current role and its delegations // will be considered // https://github.com/theupdateframework/specification/issues/168 - if delegation.child.Terminating { + if delegation.delegatee.Terminating { // Empty the stack. d.stack = d.stack[0:0] } return delegation, true } -func (d *delegationsIterator) add(roles []data.DelegatedRole, parent string) error { +func (d *delegationsIterator) add(roles []data.DelegatedRole, delegator string, verifier verify.DelegationsVerifier) error { for i := len(roles) - 1; i >= 0; i-- { - // Push the roles onto the stack in reverse so we get an in-order traversal + // Push the roles onto the stack in reverse so we get an preorder traversal // of the delegations graph. r := roles[i] - matchesPath, err := r.MatchesPath(d.file) + matchesPath, err := r.MatchesPath(d.target) if err != nil { return err } if matchesPath { - d.stack = append(d.stack, delegation{parent, r}) + delegation := delegation{ + delegator: delegator, + delegatee: r, + verifier: verifier, + } + d.stack = append(d.stack, delegation) } } diff --git a/client/delegations_test.go b/client/delegations_test.go index 4e5ccbb4..3ed114c3 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -146,12 +146,12 @@ func TestDelegationsIterator(t *testing.T) { if !ok { break } - iterationOrder = append(iterationOrder, r.child.Name) - delegations, ok := tt.roles[r.child.Name] + iterationOrder = append(iterationOrder, r.delegatee.Name) + delegations, ok := tt.roles[r.delegatee.Name] if !ok { continue } - d.add(delegations, r.child.Name) + d.add(delegations, r.delegatee.Name, verify.DelegationsVerifier{}) } assert.Equal(t, tt.resultOrder, iterationOrder) }) @@ -178,7 +178,7 @@ func TestMaxDelegations(t *testing.T) { assert.Nil(t, err) c.MaxDelegations = 2 _, err = c.getTargetFileMeta("c.txt") - assert.Equal(t, ErrMaxDelegations{File: "c.txt", MaxDelegations: 2, SnapshotVersion: 2}, err) + assert.Equal(t, ErrMaxDelegations{Target: "c.txt", MaxDelegations: 2, SnapshotVersion: 2}, err) } func TestMetaNotFound(t *testing.T) { diff --git a/client/errors.go b/client/errors.go index dc2db6ac..3d39e6b2 100644 --- a/client/errors.go +++ b/client/errors.go @@ -40,13 +40,13 @@ func (e ErrDecodeFailed) Error() string { } type ErrMaxDelegations struct { - File string + Target string MaxDelegations int SnapshotVersion int } func (e ErrMaxDelegations) Error() string { - return fmt.Sprintf("tuf: max delegation of %d reached searching for %s with snapshot version %d", e.MaxDelegations, e.File, e.SnapshotVersion) + return fmt.Sprintf("tuf: max delegation of %d reached searching for %s with snapshot version %d", e.MaxDelegations, e.Target, e.SnapshotVersion) } func isDecodeFailedWithErrRoleThreshold(err error) bool { diff --git a/data/types.go b/data/types.go index 12d0ca25..ff231e80 100644 --- a/data/types.go +++ b/data/types.go @@ -242,7 +242,7 @@ type DelegatedRole struct { // a given file. This determines whether a delegated role is responsible for // signing and verifying the file. func (d *DelegatedRole) MatchesPath(file string) (bool, error) { - if err := d.validateFields(); err != nil { + if err := d.validatePaths(); err != nil { return false, err } @@ -262,12 +262,12 @@ func (d *DelegatedRole) MatchesPath(file string) (bool, error) { return false, nil } -// validateFields enforces the spec +// validatePaths enforces the spec // https://theupdateframework.github.io/specification/v1.0.19/index.html#file-formats-targets // 'role MUST specify only one of the "path_hash_prefixes" or "paths"' // Marshalling and unmarshalling JSON will fail and return // ErrPathsAndPathHashesSet if both fields are set and not empty. -func (d *DelegatedRole) validateFields() error { +func (d *DelegatedRole) validatePaths() error { if len(d.PathHashPrefixes) > 0 && len(d.Paths) > 0 { return ErrPathsAndPathHashesSet } @@ -281,7 +281,7 @@ func (d *DelegatedRole) validateFields() error { func (d *DelegatedRole) MarshalJSON() ([]byte, error) { type delegatedRoleAlias DelegatedRole - if err := d.validateFields(); err != nil { + if err := d.validatePaths(); err != nil { return nil, err } @@ -298,7 +298,7 @@ func (d *DelegatedRole) UnmarshalJSON(b []byte) error { return err } - return d.validateFields() + return d.validatePaths() } func NewTargets() *Targets { diff --git a/data/types_test.go b/data/types_test.go index a8c5038b..4b64cec8 100644 --- a/data/types_test.go +++ b/data/types_test.go @@ -163,7 +163,7 @@ func TestDelegatedRolePathMatch(t *testing.T) { Paths: tt.pathPatterns, PathHashPrefixes: tt.pathHashPrefixes, } - assert.NoError(t, d.validateFields()) + assert.NoError(t, d.validatePaths()) matchesPath, err := d.MatchesPath(tt.file) assert.NoError(t, err) From 9c997b3b9395a18a6a6b3e295ce65b10b5e8ac5d Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Sat, 17 Jul 2021 00:39:21 +0200 Subject: [PATCH 19/22] Add tests --- client/delegations.go | 17 ++++++++++------- client/delegations_test.go | 31 ++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index cd327c37..156f1a40 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -43,6 +43,9 @@ func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) { return data.TargetFileMeta{}, err } err = delegations.add(targets.Delegations.Roles, d.delegatee.Name, delegationsVerifier) + if err != nil { + return data.TargetFileMeta{}, err + } } } @@ -89,25 +92,25 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri } } - target := &data.Targets{} + 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 the topTargets verified by root roles loaded in the client db + // role "targets" is a top role verified by root keys loaded in the client db if role == "targets" { - err = c.db.Unmarshal(raw, target, role, fileMeta.Version) + err = c.db.Unmarshal(raw, targets, role, fileMeta.Version) } else { - err = verifier.Unmarshal(raw, target, role, fileMeta.Version) + err = verifier.Unmarshal(raw, targets, role, fileMeta.Version) } if err != nil { return nil, ErrDecodeFailed{fileName, err} } // 5.6.4 check against snapshot version - if target.Version != fileMeta.Version { + if targets.Version != fileMeta.Version { return nil, ErrTargetsSnapshotVersionMismatch{ Role: fileName, DownloadedTargetsVersion: fileMeta.Version, - TargetsSnapshotVersion: target.Version, + TargetsSnapshotVersion: targets.Version, SnapshotVersion: snapshot.Version, } } @@ -117,7 +120,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri return nil, err } } - return target, nil + return targets, nil } type delegation struct { diff --git a/client/delegations_test.go b/client/delegations_test.go index 3ed114c3..a0007dbf 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -27,6 +27,7 @@ func TestDelegationsIterator(t *testing.T) { roles map[string][]data.DelegatedRole file string resultOrder []string + err error }{ { testName: "no termination", @@ -84,6 +85,33 @@ func TestDelegationsIterator(t *testing.T) { file: "", resultOrder: []string{"targets", "e"}, }, + { + testName: "path does not match b - path prefixes", + roles: map[string][]data.DelegatedRole{ + "targets": { + {Name: "b", PathHashPrefixes: []string{"33472a4909"}}, + {Name: "c", PathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633"}}, + }, + "c": { + {Name: "d", PathHashPrefixes: []string{"8baf"}}, + {Name: "e", PathHashPrefixes: []string{"34c85d1ee84f61f10d7dc633472a49096ed87f8f764bd597831eac371f40ac39"}}, + }, + }, + file: "/e/f/g.txt", + resultOrder: []string{"targets", "c", "e"}, + }, + { + testName: "err paths and pathHashPrefixes are set", + roles: map[string][]data.DelegatedRole{ + "targets": { + {Name: "b", Paths: defaultPathPatterns, PathHashPrefixes: defaultPathPatterns}, + }, + "b": {}, + }, + file: "", + resultOrder: []string{"targets"}, + err: data.ErrPathsAndPathHashesSet, + }, { testName: "cycle avoided 1", roles: map[string][]data.DelegatedRole{ @@ -151,7 +179,8 @@ func TestDelegationsIterator(t *testing.T) { if !ok { continue } - d.add(delegations, r.delegatee.Name, verify.DelegationsVerifier{}) + err := d.add(delegations, r.delegatee.Name, verify.DelegationsVerifier{}) + assert.Equal(t, tt.err, err) } assert.Equal(t, tt.resultOrder, iterationOrder) }) From e9f9750f38e285c7c37ff7c934e1d3749bc350f0 Mon Sep 17 00:00:00 2001 From: Raphael Gavache Date: Mon, 19 Jul 2021 18:59:55 +0200 Subject: [PATCH 20/22] Update after 19th july review (#9) * Update delegations to err on top level role * Add simple cycle test * Remove duplicate check --- client/delegations.go | 10 +------ client/delegations_test.go | 13 +++++++++ client/errors.go | 11 -------- verify/db.go | 3 ++ verify/db_test.go | 57 ++++++++++++++++++++++++++++++++++++++ verify/errors.go | 21 +++++++------- 6 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 verify/db_test.go diff --git a/client/delegations.go b/client/delegations.go index 156f1a40..88eef2fe 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -94,6 +94,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri targets := &data.Targets{} // 5.6.3 verify signature with parent public keys + // 5.6.4 check against snapshot version // 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" { @@ -105,15 +106,6 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri return nil, ErrDecodeFailed{fileName, err} } - // 5.6.4 check against snapshot version - if targets.Version != fileMeta.Version { - return nil, ErrTargetsSnapshotVersionMismatch{ - Role: fileName, - DownloadedTargetsVersion: fileMeta.Version, - TargetsSnapshotVersion: targets.Version, - SnapshotVersion: snapshot.Version, - } - } // 5.6.6 persist if !alreadyStored { if err := c.local.SetMeta(fileName, raw); err != nil { diff --git a/client/delegations_test.go b/client/delegations_test.go index a0007dbf..ff9b1def 100644 --- a/client/delegations_test.go +++ b/client/delegations_test.go @@ -163,6 +163,19 @@ func TestDelegationsIterator(t *testing.T) { file: "", resultOrder: []string{"targets", "b", "d", "c"}, }, + { + testName: "simple cycle", + roles: map[string][]data.DelegatedRole{ + "targets": { + {Name: "a", Paths: defaultPathPatterns}, + }, + "a": { + {Name: "a", Paths: defaultPathPatterns}, + }, + }, + file: "", + resultOrder: []string{"targets", "a"}, + }, } for _, tt := range iteratorTests { diff --git a/client/errors.go b/client/errors.go index 3d39e6b2..723a6ea9 100644 --- a/client/errors.go +++ b/client/errors.go @@ -133,14 +133,3 @@ type ErrRoleNotInSnapshot struct { func (e ErrRoleNotInSnapshot) Error() string { return fmt.Sprintf("tuf: role %s not in snapshot version %d", e.Role, e.SnapshotVersion) } - -type ErrTargetsSnapshotVersionMismatch struct { - Role string - DownloadedTargetsVersion int - TargetsSnapshotVersion int - SnapshotVersion int -} - -func (e ErrTargetsSnapshotVersionMismatch) Error() string { - return fmt.Sprintf("tuf: downloaded %s version %d expected %d in snapshot v%d ", e.Role, e.DownloadedTargetsVersion, e.TargetsSnapshotVersion, e.SnapshotVersion) -} diff --git a/verify/db.go b/verify/db.go index 1565f99b..7c6e91e1 100644 --- a/verify/db.go +++ b/verify/db.go @@ -43,6 +43,9 @@ func NewDelegationsVerifier(d *data.Delegations) (DelegationsVerifier, error) { keys: make(map[string]*data.Key, len(d.Keys)), } for _, r := range d.Roles { + if _, ok := topLevelRoles[r.Name]; ok { + return DelegationsVerifier{}, ErrInvalidDelegatedRole + } role := &data.Role{Threshold: r.Threshold, KeyIDs: r.KeyIDs} if err := db.addRole(r.Name, role); err != nil { return DelegationsVerifier{}, err diff --git a/verify/db_test.go b/verify/db_test.go new file mode 100644 index 00000000..8921993d --- /dev/null +++ b/verify/db_test.go @@ -0,0 +1,57 @@ +package verify + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/theupdateframework/go-tuf/data" +) + +func TestDelegationsVerifier(t *testing.T) { + var verifierTests = []struct { + testName string + delegations *data.Delegations + initErr error + unmarshalErr error + }{ + { + testName: "empty state", + delegations: &data.Delegations{}, + unmarshalErr: ErrNoSignatures, + }, + { + testName: "top level role", + delegations: &data.Delegations{Roles: []data.DelegatedRole{ + {Name: "root"}, + }}, + initErr: ErrInvalidDelegatedRole, + }, + { + testName: "invalid role", + delegations: &data.Delegations{Roles: []data.DelegatedRole{ + {Threshold: 0}, + }}, + initErr: ErrInvalidThreshold, + }, + { + testName: "invalid keys", + delegations: &data.Delegations{Keys: map[string]*data.Key{ + "a": &data.Key{Type: data.KeySchemeEd25519}, + }}, + initErr: ErrWrongID{}, + }, + } + + for _, tt := range verifierTests { + t.Run(tt.testName, func(t *testing.T) { + verifier, err := NewDelegationsVerifier(tt.delegations) + assert.NotNil(t, verifier) + assert.Equal(t, tt.initErr, err) + if err == nil { + var targets data.Targets + err = verifier.Unmarshal([]byte(`{"a":"b"}`), targets, "tree", 0) + assert.Equal(t, tt.unmarshalErr, err) + } + }) + } +} diff --git a/verify/errors.go b/verify/errors.go index 6e4587be..78556c7a 100644 --- a/verify/errors.go +++ b/verify/errors.go @@ -7,16 +7,17 @@ import ( ) var ( - ErrMissingKey = errors.New("tuf: missing key") - ErrNoSignatures = errors.New("tuf: data has no signatures") - ErrInvalid = errors.New("tuf: signature verification failed") - ErrWrongMethod = errors.New("tuf: invalid signature type") - ErrWrongMetaType = errors.New("tuf: meta file has wrong type") - ErrExists = errors.New("tuf: key already in db") - ErrInvalidKey = errors.New("tuf: invalid key") - ErrInvalidRole = errors.New("tuf: invalid role") - ErrInvalidKeyID = errors.New("tuf: invalid key id") - ErrInvalidThreshold = errors.New("tuf: invalid role threshold") + ErrMissingKey = errors.New("tuf: missing key") + ErrNoSignatures = errors.New("tuf: data has no signatures") + ErrInvalid = errors.New("tuf: signature verification failed") + ErrWrongMethod = errors.New("tuf: invalid signature type") + ErrWrongMetaType = errors.New("tuf: meta file has wrong type") + ErrExists = errors.New("tuf: key already in db") + ErrInvalidKey = errors.New("tuf: invalid key") + ErrInvalidRole = errors.New("tuf: invalid role") + ErrInvalidDelegatedRole = errors.New("tuf: invalid delegated role") + ErrInvalidKeyID = errors.New("tuf: invalid key id") + ErrInvalidThreshold = errors.New("tuf: invalid role threshold") ) type ErrWrongID struct{} From e7cf8b79b94026b151dfa621290fbd139fb14713 Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Mon, 19 Jul 2021 19:38:19 +0200 Subject: [PATCH 21/22] Fix comment --- client/delegations.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/delegations.go b/client/delegations.go index 88eef2fe..9bc6d411 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -84,6 +84,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri // 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) @@ -94,7 +95,6 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, veri targets := &data.Targets{} // 5.6.3 verify signature with parent public keys - // 5.6.4 check against snapshot version // 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" { From deff639c29a188612b87f95ba6afa84b194239ef Mon Sep 17 00:00:00 2001 From: "raphael.gavache" Date: Tue, 20 Jul 2021 00:08:49 +0200 Subject: [PATCH 22/22] Update comment --- client/client.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/client.go b/client/client.go index f088b9f9..991fc5ce 100644 --- a/client/client.go +++ b/client/client.go @@ -195,11 +195,13 @@ func (c *Client) update(latestRoot bool) (data.TargetFiles, error) { return nil, ErrLatestSnapshot{c.snapshotVer} } - // Get snapshot.json, then extract root.json and targets.json file meta. + // Get snapshot.json, then extract file metas. // - // The snapshot.json is only saved locally after checking root.json and + // The snapshot.json is only saved locally after checking // targets.json so that it will be re-downloaded on subsequent updates // if this update fails. + // root.json meta should not be stored in the snapshot, if it is, + // the root will be checked, re-downloaded snapshotJSON, err := c.downloadMetaFromTimestamp("snapshot.json", snapshotMeta) if err != nil { return nil, err