diff --git a/client/client.go b/client/client.go index 129ec31b..991fc5ce 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, } } @@ -189,16 +195,18 @@ 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 } - rootMeta, 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 @@ -210,13 +218,17 @@ 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) { - return c.updateWithLatestRoot(&rootMeta) + // Root should no longer be pinned in snapshot meta https://github.com/theupdateframework/tuf/pull/988 + 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 { @@ -539,13 +551,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, 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{}, data.SnapshotFileMeta{}, ErrDecodeFailed{"snapshot.json", err} + return data.SnapshotFiles{}, ErrDecodeFailed{"snapshot.json", err} } c.snapshotVer = snapshot.Version - return snapshot.Meta["root.json"], snapshot.Meta["targets.json"], nil + return snapshot.Meta, nil } // decodeTargets decodes and verifies targets metadata, sets c.targets and @@ -582,18 +594,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 +652,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 +671,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..9bc6d411 --- /dev/null +++ b/client/delegations.go @@ -0,0 +1,190 @@ +package client + +import ( + "github.com/theupdateframework/go-tuf/data" + "github.com/theupdateframework/go-tuf/verify" +) + +// getTargetFileMeta searches for a verified TargetFileMeta matching a target +// Requires a local snapshot to be loaded and is locked to the snapshot versions. +// Searches through delegated targets following TUF spec 1.0.19 section 5.6. +func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) { + snapshot, err := c.loadLocalSnapshot() + if err != nil { + return data.TargetFileMeta{}, err + } + + // delegationsIterator covers 5.6.7 + // - pre-order depth-first search starting with the top targets + // - filter delegations with paths or path_hash_prefixes matching searched target + // - 5.6.7.1 cycles protection + // - 5.6.7.2 terminations + delegations := newDelegationsIterator(target) + for i := 0; i < c.MaxDelegations; i++ { + d, ok := delegations.next() + if !ok { + return data.TargetFileMeta{}, ErrUnknownTarget{target, snapshot.Version} + } + + // covers 5.6.{1,2,3,4,5,6} + targets, err := c.loadDelegatedTargets(snapshot, d.delegatee.Name, d.verifier) + if err != nil { + return data.TargetFileMeta{}, err + } + + // stop when the searched TargetFileMeta is found + if m, ok := targets.Targets[target]; ok { + return m, nil + } + + if targets.Delegations != nil { + delegationsVerifier, err := verify.NewDelegationsVerifier(targets.Delegations) + if err != nil { + return data.TargetFileMeta{}, err + } + err = delegations.add(targets.Delegations.Roles, d.delegatee.Name, delegationsVerifier) + if err != nil { + return data.TargetFileMeta{}, err + } + } + } + + return data.TargetFileMeta{}, ErrMaxDelegations{ + Target: target, + 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 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 + // 5.6.4 check against snapshot version + raw, alreadyStored := c.localMetaFromSnapshot(fileName, fileMeta) + if !alreadyStored { + raw, err = c.downloadMetaFromSnapshot(fileName, fileMeta) + if err != nil { + return nil, err + } + } + + targets := &data.Targets{} + // 5.6.3 verify signature with parent public keys + // 5.6.5 verify that the targets is not expired + // role "targets" is a top role verified by root keys loaded in the client db + if role == "targets" { + err = c.db.Unmarshal(raw, targets, role, fileMeta.Version) + } else { + err = verifier.Unmarshal(raw, targets, role, fileMeta.Version) + } + if err != nil { + return nil, ErrDecodeFailed{fileName, err} + } + + // 5.6.6 persist + if !alreadyStored { + if err := c.local.SetMeta(fileName, raw); err != nil { + return nil, err + } + } + return targets, nil +} + +type delegation struct { + delegator string + verifier verify.DelegationsVerifier + delegatee data.DelegatedRole +} + +type delegationsIterator struct { + stack []delegation + target string + visitedRoles map[string]struct{} +} + +// newDelegationsIterator initialises an iterator with a first step +// on top level targets +func newDelegationsIterator(target string) *delegationsIterator { + i := &delegationsIterator{ + target: target, + stack: []delegation{ + { + delegatee: data.DelegatedRole{Name: "targets"}, + }, + }, + visitedRoles: make(map[string]struct{}), + } + return i +} + +func (d *delegationsIterator) next() (value delegation, ok 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: If this role has been visited before, then skip this role (so + // that cycles in the delegation graph are avoided). + roleName := delegation.delegatee.Name + if _, ok := d.visitedRoles[roleName]; ok { + return d.next() + } + d.visitedRoles[roleName] = 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.delegatee.Terminating { + // Empty the stack. + d.stack = d.stack[0:0] + } + return delegation, true +} + +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 preorder traversal + // of the delegations graph. + r := roles[i] + matchesPath, err := r.MatchesPath(d.target) + if err != nil { + return err + } + if matchesPath { + delegation := delegation{ + delegator: delegator, + delegatee: r, + verifier: verifier, + } + d.stack = append(d.stack, delegation) + } + } + + return nil +} diff --git a/client/delegations_test.go b/client/delegations_test.go new file mode 100644 index 00000000..ff9b1def --- /dev/null +++ b/client/delegations_test.go @@ -0,0 +1,481 @@ +package client + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/theupdateframework/go-tuf/data" + "github.com/theupdateframework/go-tuf/verify" +) + +var ( + defaultPathPatterns = []string{"tmp", "*"} + noMatchPathPatterns = []string{"vars", "null"} +) + +func TestDelegationsIterator(t *testing.T) { + var iteratorTests = []struct { + testName string + roles map[string][]data.DelegatedRole + file string + resultOrder []string + err error + }{ + { + testName: "no termination", + roles: map[string][]data.DelegatedRole{ + "targets": { + {Name: "b", Paths: defaultPathPatterns}, + {Name: "e", Paths: defaultPathPatterns}, + }, + "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}, + }, + }, + file: "", + resultOrder: []string{"targets", "b", "c", "d", "e", "f", "g", "h", "i", "j"}, + }, + { + testName: "terminated in b", + roles: map[string][]data.DelegatedRole{ + "targets": { + {Name: "b", Paths: defaultPathPatterns, Terminating: true}, + {Name: "e", Paths: defaultPathPatterns}, + }, + "b": { + {Name: "c", Paths: defaultPathPatterns}, + {Name: "d", Paths: defaultPathPatterns}, + }, + }, + file: "", + resultOrder: []string{"targets", "b", "c", "d"}, + }, + { + testName: "path does not match b", + roles: map[string][]data.DelegatedRole{ + "targets": { + {Name: "b", Paths: noMatchPathPatterns}, + {Name: "e", Paths: defaultPathPatterns}, + }, + "b": { + {Name: "c", Paths: defaultPathPatterns}, + {Name: "d", Paths: defaultPathPatterns}, + }, + }, + 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{ + "targets": { + {Name: "b", Paths: defaultPathPatterns}, + {Name: "e", Paths: defaultPathPatterns}, + }, + "b": { + {Name: "targets", Paths: defaultPathPatterns}, + {Name: "d", Paths: defaultPathPatterns}, + }, + }, + file: "", + resultOrder: []string{"targets", "b", "d", "e"}, + }, + { + testName: "cycle avoided 2", + roles: map[string][]data.DelegatedRole{ + "targets": { + {Name: "targets", Paths: defaultPathPatterns}, + {Name: "b", Paths: defaultPathPatterns}, + }, + "b": { + {Name: "targets", Paths: defaultPathPatterns}, + {Name: "b", Paths: defaultPathPatterns}, + {Name: "c", Paths: defaultPathPatterns}, + }, + "c": { + {Name: "c", Paths: defaultPathPatterns}, + }, + }, + file: "", + resultOrder: []string{"targets", "b", "c"}, + }, + { + testName: "diamond delegation", + roles: map[string][]data.DelegatedRole{ + "targets": { + {Name: "b", Paths: defaultPathPatterns}, + {Name: "c", Paths: defaultPathPatterns}, + }, + "b": { + {Name: "d", Paths: defaultPathPatterns}, + }, + "c": { + {Name: "d", Paths: defaultPathPatterns}, + }, + }, + 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 { + t.Run(tt.testName, func(t *testing.T) { + d := newDelegationsIterator(tt.file) + var iterationOrder []string + for { + r, ok := d.next() + if !ok { + break + } + iterationOrder = append(iterationOrder, r.delegatee.Name) + delegations, ok := tt.roles[r.delegatee.Name] + if !ok { + continue + } + err := d.add(delegations, r.delegatee.Name, verify.DelegationsVerifier{}) + assert.Equal(t, tt.err, err) + } + assert.Equal(t, tt.resultOrder, iterationOrder) + }) + } +} + +func TestGetTargetMeta(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) + + f, err := c.getTargetFileMeta("f.txt") + assert.Nil(t, err) + assert.Equal(t, int64(15), f.Length) +} + +func TestMaxDelegations(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) + c.MaxDelegations = 2 + _, err = c.getTargetFileMeta("c.txt") + assert.Equal(t, ErrMaxDelegations{Target: "c.txt", MaxDelegations: 2, SnapshotVersion: 2}, err) +} + +func TestMetaNotFound(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) + _, err = c.getTargetFileMeta("unknown.txt") + assert.Equal(t, ErrUnknownTarget{Name: "unknown.txt", SnapshotVersion: 2}, err) +} + +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") + 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 + }{ + { + file: "unknown", + targets: []expectedTargets{ + { + name: "targets.json", + version: 2, + }, + }, + downloadError: ErrUnknownTarget{Name: "unknown", SnapshotVersion: 2}, + fileContent: "", + }, + { + file: "b.txt", + targets: []expectedTargets{ + { + name: "targets.json", + version: 2, + }, + { + name: "a.json", + version: 1, + }, + { + name: "b.json", + version: 1, + }, + }, + downloadError: nil, + fileContent: "Contents: b.txt", + }, + { + file: "f.txt", + targets: []expectedTargets{ + { + name: "targets.json", + version: 2, + }, + { + name: "a.json", + version: 1, + }, + { + name: "b.json", + version: 1, + }, + { + name: "c.json", + version: 1, + }, + { + name: "d.json", + version: 1, + }, + { + name: "e.json", + version: 1, + }, + { + name: "f.json", + version: 1, + }, + }, + downloadError: nil, + fileContent: "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, targets.version, storedVersion) + 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 := ioutil.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 := ioutil.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 := ioutil.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..723a6ea9 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 { + 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.Target, 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,12 @@ 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) +} 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.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 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..ff231e80 100644 --- a/data/types.go +++ b/data/types.go @@ -4,6 +4,9 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" + "errors" + "path/filepath" + "strings" "sync" "time" @@ -19,7 +22,8 @@ const ( ) var ( - KeyAlgorithms = []string{"sha256", "sha512"} + KeyAlgorithms = []string{"sha256", "sha512"} + ErrPathsAndPathHashesSet = errors.New("tuf: failed validation of delegated target: paths and path_hash_prefixes are both set") ) type Signed struct { @@ -208,11 +212,93 @@ 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"` +} + +// 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 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"` + Threshold int `json:"threshold"` + Terminating bool `json:"terminating"` + PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"` + Paths []string `json:"paths"` +} + +// 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.validatePaths(); err != nil { + return false, err + } + + for _, pattern := range d.Paths { + if matched, _ := filepath.Match(pattern, file); matched { + return true, nil + } + } + + pathHash := PathHexDigest(file) + for _, hashPrefix := range d.PathHashPrefixes { + if strings.HasPrefix(pathHash, hashPrefix) { + return true, nil + } + } + + return false, nil +} + +// 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) validatePaths() error { + if len(d.PathHashPrefixes) > 0 && len(d.Paths) > 0 { + return ErrPathsAndPathHashesSet + } + + 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 + + if err := d.validatePaths(); err != nil { + return nil, err + } + + 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 + + if err := json.Unmarshal(b, (*delegatedRoleAlias)(d)); err != nil { + return err + } + + return d.validatePaths() } func NewTargets() *Targets { diff --git a/data/types_test.go b/data/types_test.go index d5b00f2e..4b64cec8 100644 --- a/data/types_test.go +++ b/data/types_test.go @@ -2,7 +2,10 @@ package data import ( "encoding/json" + "testing" + "github.com/stretchr/testify/assert" + cjson "github.com/tent/canonical-json-go" . "gopkg.in/check.v1" ) @@ -91,3 +94,138 @@ 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 + pathPatterns []string + pathHashPrefixes []string + file string + shouldMatch bool + }{ + { + testName: "no path", + file: "licence.txt", + }, + { + testName: "match path *", + pathPatterns: []string{"null", "targets/*.tgz"}, + file: "targets/foo.tgz", + shouldMatch: true, + }, + { + testName: "does not match path *", + pathPatterns: []string{"null", "targets/*.tgz"}, + file: "targets/foo.txt", + shouldMatch: false, + }, + { + testName: "match path ?", + pathPatterns: []string{"foo-version-?.tgz"}, + file: "foo-version-a.tgz", + shouldMatch: true, + }, + { + testName: "does not match ?", + 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", + pathHashPrefixes: []string{"badd", "8baf"}, + file: "/file3.txt", + shouldMatch: true, + }, + { + testName: "does not match hash prefix", + pathHashPrefixes: []string{"badd"}, + file: "/file3.txt", + shouldMatch: false, + }, + { + testName: "hash prefix first char", + pathHashPrefixes: []string{"2"}, + file: "/a/b/c/file_d.txt", + shouldMatch: 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{ + Paths: tt.pathPatterns, + PathHashPrefixes: tt.pathHashPrefixes, + } + assert.NoError(t, d.validatePaths()) + + matchesPath, err := d.MatchesPath(tt.file) + assert.NoError(t, err) + assert.Equal(t, tt.shouldMatch, matchesPath) + }) + + } +} + +func TestDelegatedRoleJSON(t *testing.T) { + var tts = []struct { + testName string + d *DelegatedRole + rawCJSON string + }{{ + testName: "all fields with hashes", + d: &DelegatedRole{ + Name: "n1", + KeyIDs: []string{"k1"}, + Threshold: 5, + Terminating: true, + PathHashPrefixes: []string{"8f"}, + }, + rawCJSON: `{"keyids":["k1"],"name":"n1","path_hash_prefixes":["8f"],"paths":null,"terminating":true,"threshold":5}`, + }, + { + testName: "paths only", + d: &DelegatedRole{ + Name: "n2", + KeyIDs: []string{"k1", "k3"}, + Threshold: 12, + Paths: []string{"*.txt"}, + }, + rawCJSON: `{"keyids":["k1","k3"],"name":"n2","paths":["*.txt"],"terminating":false,"threshold":12}`, + }, + { + 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) { + 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) + }) + } +} + +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, ErrPathsAndPathHashesSet, json.Unmarshal(targetsWithBothMatchers, &d)) + + // test for type errors + err := json.Unmarshal([]byte(`{"keyids":"a"}`), &d) + assert.Equal(t, "keyids", err.(*json.UnmarshalTypeError).Field) +} 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..7c6e91e1 100644 --- a/verify/db.go +++ b/verify/db.go @@ -26,6 +26,39 @@ func NewDB() *DB { } } +type DelegationsVerifier struct { + DB *DB +} + +func (d *DelegationsVerifier) Unmarshal(b []byte, v interface{}, role string, minVersion int) error { + return d.DB.Unmarshal(b, v, role, minVersion) +} + +// NewDelegationsVerifier returns a DelegationsVerifier that verifies delegations +// of a given Targets. It reuses the DB struct to leverage verified keys, roles +// unmarshals. +func NewDelegationsVerifier(d *data.Delegations) (DelegationsVerifier, error) { + db := &DB{ + roles: make(map[string]*Role, len(d.Roles)), + 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 + } + } + for id, k := range d.Keys { + if err := db.AddKey(id, k); err != nil { + return DelegationsVerifier{}, err + } + } + return DelegationsVerifier{db}, nil +} + func (db *DB) AddKey(id string, k *data.Key) error { v, ok := Verifiers[k.Type] if !ok { @@ -43,22 +76,31 @@ 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) +} + +func (db *DB) addRole(name string, r *data.Role) error { if r.Threshold < 1 { return ErrInvalidThreshold } 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{} diff --git a/verify/verify.go b/verify/verify.go index eb618ae0..b6d4162d 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -24,12 +24,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 strings.ToLower(sm.Type) != strings.ToLower(role) { - 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 strings.ToLower(sm.Type) != strings.ToLower(role) { + return ErrWrongMetaType + } + } else { + // Delegated (non-top-level) roles may only sign targets metadata. + if strings.ToLower(sm.Type) != "targets" { + return ErrWrongMetaType + } } + if IsExpired(sm.Expires) { return ErrExpired{sm.Expires} } + if sm.Version < minVersion { return ErrLowVersion{sm.Version, minVersion} }