From ba4552e0e92dd8bca5242bdcc57dc28204f856de Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Fri, 10 Dec 2021 17:42:41 -0500 Subject: [PATCH 1/7] [Delegations prereq] Use a verify.DB for delegation in client Splitting up https://github.com/theupdateframework/go-tuf/pull/175 --- client/delegations.go | 14 ++++---------- pkg/targets/delegation.go | 14 +++++++------- pkg/targets/delegation_test.go | 28 ++++++++-------------------- 3 files changed, 19 insertions(+), 37 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index f1ec67a9..9190aa35 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -20,11 +20,7 @@ func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) { // - filter delegations with paths or path_hash_prefixes matching searched target // - 5.6.7.1 cycles protection // - 5.6.7.2 terminations - delegations, err := targets.NewDelegationsIterator(target, c.db) - if err != nil { - return data.TargetFileMeta{}, err - } - + delegations := targets.NewDelegationsIterator(target, c.db) for i := 0; i < c.MaxDelegations; i++ { d, ok := delegations.Next() if !ok { @@ -32,7 +28,7 @@ func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) { } // covers 5.6.{1,2,3,4,5,6} - targets, err := c.loadDelegatedTargets(snapshot, d) + targets, err := c.loadDelegatedTargets(snapshot, d.Delegatee.Name, d.DB) if err != nil { return data.TargetFileMeta{}, err } @@ -79,9 +75,7 @@ func (c *Client) loadLocalSnapshot() (*data.Snapshot, error) { } // loadDelegatedTargets downloads, decodes, verifies and stores targets -func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, delegation targets.Delegation) (*data.Targets, error) { - role := delegation.Delegatee.Name - +func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, role string, db *verify.DB) (*data.Targets, error) { var err error fileName := role + ".json" fileMeta, ok := snapshot.Meta[fileName] @@ -104,7 +98,7 @@ func (c *Client) loadDelegatedTargets(snapshot *data.Snapshot, delegation target // 5.6.3 verify signature with parent public keys // 5.6.5 verify that the targets is not expired // role "targets" is a top role verified by root keys loaded in the client db - err = delegation.DB.Unmarshal(raw, targets, role, fileMeta.Version) + err = db.Unmarshal(raw, targets, role, fileMeta.Version) if err != nil { return nil, ErrDecodeFailed{fileName, err} } diff --git a/pkg/targets/delegation.go b/pkg/targets/delegation.go index ccd52bae..92597cab 100644 --- a/pkg/targets/delegation.go +++ b/pkg/targets/delegation.go @@ -24,10 +24,11 @@ var ErrTopLevelTargetsRoleMissing = errors.New("tuf: top level targets role miss // NewDelegationsIterator initialises an iterator with a first step // on top level targets. -func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) (*delegationsIterator, error) { - targetsRole := topLevelKeysDB.GetRole("targets") - if targetsRole == nil { - return nil, ErrTopLevelTargetsRoleMissing +func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) *delegationsIterator { + role := topLevelKeysDB.GetRole("targets") + keyIDs := []string{} + if role != nil { + keyIDs = sets.StringSetToSlice(role.KeyIDs) } i := &delegationsIterator{ @@ -35,9 +36,8 @@ func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) (*delegati stack: []Delegation{ { Delegatee: data.DelegatedRole{ - Name: "targets", - KeyIDs: sets.StringSetToSlice(targetsRole.KeyIDs), - Threshold: targetsRole.Threshold, + Name: "targets", + KeyIDs: keyIDs, }, DB: topLevelKeysDB, }, diff --git a/pkg/targets/delegation_test.go b/pkg/targets/delegation_test.go index 2e0c42b6..316d8cde 100644 --- a/pkg/targets/delegation_test.go +++ b/pkg/targets/delegation_test.go @@ -14,20 +14,7 @@ var ( ) func TestDelegationsIterator(t *testing.T) { - topTargetsPubKey := &data.PublicKey{ - Type: data.KeyTypeEd25519, - Scheme: data.KeySchemeEd25519, - Algorithms: data.HashAlgorithms, - Value: []byte(`{"public":"aaaaec567e5901ba3976c34f7cd5169704292439bf71e6aa19c64b96706f95ef"}`), - } - delTargetsPubKey := &data.PublicKey{ - Type: data.KeyTypeEd25519, - Scheme: data.KeySchemeEd25519, - Algorithms: data.HashAlgorithms, - Value: []byte(`{"public":"bbbbec567e5901ba3976c34f7cd5169704292439bf71e6aa19c64b96706f95ef"}`), - } - - defaultKeyIDs := delTargetsPubKey.IDs() + defaultKeyIDs := []string{"26b878ad73362774b8b69dd4fdeb2cc6a2688e4133ed5ace9e18a06e9d998a6d"} var iteratorTests = []struct { testName string roles map[string][]data.DelegatedRole @@ -201,15 +188,16 @@ func TestDelegationsIterator(t *testing.T) { for _, tt := range iteratorTests { t.Run(tt.testName, func(t *testing.T) { - topLevelDB := verify.NewDB() - topLevelDB.AddKey(topTargetsPubKey.IDs()[0], topTargetsPubKey) - topLevelDB.AddRole("targets", &data.Role{ - KeyIDs: topTargetsPubKey.IDs(), - Threshold: 1, + flattened := []data.DelegatedRole{} + for _, roles := range tt.roles { + flattened = append(flattened, roles...) + } + db, err := verify.NewDBFromDelegations(&data.Delegations{ + Roles: flattened, }) - d, err := NewDelegationsIterator(tt.file, topLevelDB) assert.NoError(t, err) + d := NewDelegationsIterator(tt.file, db) var iterationOrder []string for { From 38b372908c49212125b87fc49a0053f80587b94e Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Fri, 7 Jan 2022 11:45:28 -0500 Subject: [PATCH 2/7] stash --- pkg/targets/delegation.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/targets/delegation.go b/pkg/targets/delegation.go index 92597cab..cee7047d 100644 --- a/pkg/targets/delegation.go +++ b/pkg/targets/delegation.go @@ -4,7 +4,6 @@ import ( "errors" "github.com/theupdateframework/go-tuf/data" - "github.com/theupdateframework/go-tuf/internal/sets" "github.com/theupdateframework/go-tuf/verify" ) @@ -25,11 +24,12 @@ var ErrTopLevelTargetsRoleMissing = errors.New("tuf: top level targets role miss // NewDelegationsIterator initialises an iterator with a first step // on top level targets. func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) *delegationsIterator { - role := topLevelKeysDB.GetRole("targets") + // role := topLevelKeysDB.GetRole("targets") keyIDs := []string{} - if role != nil { - keyIDs = sets.StringSetToSlice(role.KeyIDs) - } + + // if role != nil { + // keyIDs = sets.StringSetToSlice(role.KeyIDs) + // } i := &delegationsIterator{ target: target, From 8e83e4f0fbfcf1e1d16aef5f4742ba05bd8ebc02 Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Mon, 28 Feb 2022 14:24:26 -0500 Subject: [PATCH 3/7] Add tests to make sure the top level targets 'delegation' edge has associated keys. Make NewDelegationsIterator return an error if the passed DB is missing the top level targets role --- client/delegations.go | 6 +++++- pkg/targets/delegation.go | 18 +++++++++--------- pkg/targets/delegation_test.go | 28 ++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/client/delegations.go b/client/delegations.go index 9190aa35..de3e6647 100644 --- a/client/delegations.go +++ b/client/delegations.go @@ -20,7 +20,11 @@ func (c *Client) getTargetFileMeta(target string) (data.TargetFileMeta, error) { // - filter delegations with paths or path_hash_prefixes matching searched target // - 5.6.7.1 cycles protection // - 5.6.7.2 terminations - delegations := targets.NewDelegationsIterator(target, c.db) + delegations, err := targets.NewDelegationsIterator(target, c.db) + if err != nil { + return data.TargetFileMeta{}, err + } + for i := 0; i < c.MaxDelegations; i++ { d, ok := delegations.Next() if !ok { diff --git a/pkg/targets/delegation.go b/pkg/targets/delegation.go index cee7047d..ccd52bae 100644 --- a/pkg/targets/delegation.go +++ b/pkg/targets/delegation.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/theupdateframework/go-tuf/data" + "github.com/theupdateframework/go-tuf/internal/sets" "github.com/theupdateframework/go-tuf/verify" ) @@ -23,21 +24,20 @@ var ErrTopLevelTargetsRoleMissing = errors.New("tuf: top level targets role miss // NewDelegationsIterator initialises an iterator with a first step // on top level targets. -func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) *delegationsIterator { - // role := topLevelKeysDB.GetRole("targets") - keyIDs := []string{} - - // if role != nil { - // keyIDs = sets.StringSetToSlice(role.KeyIDs) - // } +func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) (*delegationsIterator, error) { + targetsRole := topLevelKeysDB.GetRole("targets") + if targetsRole == nil { + return nil, ErrTopLevelTargetsRoleMissing + } i := &delegationsIterator{ target: target, stack: []Delegation{ { Delegatee: data.DelegatedRole{ - Name: "targets", - KeyIDs: keyIDs, + Name: "targets", + KeyIDs: sets.StringSetToSlice(targetsRole.KeyIDs), + Threshold: targetsRole.Threshold, }, DB: topLevelKeysDB, }, diff --git a/pkg/targets/delegation_test.go b/pkg/targets/delegation_test.go index 316d8cde..2e0c42b6 100644 --- a/pkg/targets/delegation_test.go +++ b/pkg/targets/delegation_test.go @@ -14,7 +14,20 @@ var ( ) func TestDelegationsIterator(t *testing.T) { - defaultKeyIDs := []string{"26b878ad73362774b8b69dd4fdeb2cc6a2688e4133ed5ace9e18a06e9d998a6d"} + topTargetsPubKey := &data.PublicKey{ + Type: data.KeyTypeEd25519, + Scheme: data.KeySchemeEd25519, + Algorithms: data.HashAlgorithms, + Value: []byte(`{"public":"aaaaec567e5901ba3976c34f7cd5169704292439bf71e6aa19c64b96706f95ef"}`), + } + delTargetsPubKey := &data.PublicKey{ + Type: data.KeyTypeEd25519, + Scheme: data.KeySchemeEd25519, + Algorithms: data.HashAlgorithms, + Value: []byte(`{"public":"bbbbec567e5901ba3976c34f7cd5169704292439bf71e6aa19c64b96706f95ef"}`), + } + + defaultKeyIDs := delTargetsPubKey.IDs() var iteratorTests = []struct { testName string roles map[string][]data.DelegatedRole @@ -188,16 +201,15 @@ func TestDelegationsIterator(t *testing.T) { for _, tt := range iteratorTests { t.Run(tt.testName, func(t *testing.T) { - flattened := []data.DelegatedRole{} - for _, roles := range tt.roles { - flattened = append(flattened, roles...) - } - db, err := verify.NewDBFromDelegations(&data.Delegations{ - Roles: flattened, + topLevelDB := verify.NewDB() + topLevelDB.AddKey(topTargetsPubKey.IDs()[0], topTargetsPubKey) + topLevelDB.AddRole("targets", &data.Role{ + KeyIDs: topTargetsPubKey.IDs(), + Threshold: 1, }) + d, err := NewDelegationsIterator(tt.file, topLevelDB) assert.NoError(t, err) - d := NewDelegationsIterator(tt.file, db) var iterationOrder []string for { From d4194264f007c34c12da9e04640b53f79d28af4c Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Fri, 10 Dec 2021 17:48:28 -0500 Subject: [PATCH 4/7] [Delegations prereq] Make signers addressible by key ID in LocalStore Splitting up https://github.com/theupdateframework/go-tuf/pull/175 --- local_store.go | 196 +++++++++++++++++++++++++++++++++++++++++-------- repo.go | 38 ---------- 2 files changed, 166 insertions(+), 68 deletions(-) diff --git a/local_store.go b/local_store.go index 139f436f..f729d42d 100644 --- a/local_store.go +++ b/local_store.go @@ -12,20 +12,49 @@ import ( "github.com/theupdateframework/go-tuf/data" "github.com/theupdateframework/go-tuf/encrypted" + "github.com/theupdateframework/go-tuf/internal/sets" "github.com/theupdateframework/go-tuf/pkg/keys" "github.com/theupdateframework/go-tuf/util" ) -func signers(privateKeys []*data.PrivateKey) []keys.Signer { - res := make([]keys.Signer, 0, len(privateKeys)) - for _, k := range privateKeys { - signer, err := keys.GetSigner(k) - if err != nil { - continue - } - res = append(res, signer) - } - return res +type LocalStore interface { + // GetMeta returns a map from metadata file names (e.g. root.json) to their raw JSON payload or an error. + GetMeta() (map[string]json.RawMessage, error) + + // SetMeta is used to update a metadata file name with a JSON payload. + SetMeta(name string, meta json.RawMessage) error + + // WalkStagedTargets calls targetsFn for each staged target file in paths. + // If paths is empty, all staged target files will be walked. + WalkStagedTargets(paths []string, targetsFn TargetsWalkFunc) error + + // FileIsStaged determines if a metadata file is currently staged, to avoid incrementing + // version numbers repeatedly while staged. + FileIsStaged(filename string) bool + + // Commit is used to publish staged files to the repository + // + // This will also reset the staged meta to signal incrementing version numbers. + // TUF 1.0 requires that the root metadata version numbers in the repository does not + // gaps. To avoid this, we will only increment the number once until we commit. + Commit(bool, map[string]int, map[string]data.Hashes) error + + // GetSigners return a list of signers for a role. + GetSigners(role string) ([]keys.Signer, error) + + // SaveSigner adds a signer to a role. + SaveSigner(role string, signer keys.Signer) error + + // SignersForRole return a list of signing keys for a role. + SignersForKeyIDs(keyIDs []string) []keys.Signer + + // Clean is used to remove all staged manifests. + Clean() error +} + +type PassphraseChanger interface { + // ChangePassphrase changes the passphrase for a role keys file. + ChangePassphrase(string) error } func MemoryStore(meta map[string]json.RawMessage, files map[string][]byte) LocalStore { @@ -33,10 +62,11 @@ func MemoryStore(meta map[string]json.RawMessage, files map[string][]byte) Local meta = make(map[string]json.RawMessage) } return &memoryStore{ - meta: meta, - stagedMeta: make(map[string]json.RawMessage), - files: files, - signers: make(map[string][]keys.Signer), + meta: meta, + stagedMeta: make(map[string]json.RawMessage), + files: files, + signerForKeyID: make(map[string]keys.Signer), + keyIDsForRole: make(map[string][]string), } } @@ -44,7 +74,9 @@ type memoryStore struct { meta map[string]json.RawMessage stagedMeta map[string]json.RawMessage files map[string][]byte - signers map[string][]keys.Signer + + signerForKeyID map[string]keys.Signer + keyIDsForRole map[string][]string } func (m *memoryStore) GetMeta() (map[string]json.RawMessage, error) { @@ -105,14 +137,53 @@ func (m *memoryStore) Commit(consistentSnapshot bool, versions map[string]int, h } func (m *memoryStore) GetSigners(role string) ([]keys.Signer, error) { - return m.signers[role], nil + keyIDs, ok := m.keyIDsForRole[role] + if ok { + return m.SignersForKeyIDs(keyIDs), nil + } + + return nil, nil } func (m *memoryStore) SaveSigner(role string, signer keys.Signer) error { - m.signers[role] = append(m.signers[role], signer) + keyIDs := signer.PublicData().IDs() + + for _, keyID := range keyIDs { + m.signerForKeyID[keyID] = signer + } + + mergedKeyIDs := sets.DeduplicateStrings(append(m.keyIDsForRole[role], keyIDs...)) + m.keyIDsForRole[role] = mergedKeyIDs return nil } +func (m *memoryStore) SignersForKeyIDs(keyIDs []string) []keys.Signer { + signers := []keys.Signer{} + keyIDsSeen := map[string]struct{}{} + + for _, keyID := range keyIDs { + signer, ok := m.signerForKeyID[keyID] + if !ok { + continue + } + addSigner := false + + for _, skid := range signer.PublicData().IDs() { + if _, seen := keyIDsSeen[skid]; !seen { + addSigner = true + } + + keyIDsSeen[skid] = struct{}{} + } + + if addSigner { + signers = append(signers, signer) + } + } + + return signers +} + func (m *memoryStore) Clean() error { return nil } @@ -126,7 +197,8 @@ func FileSystemStore(dir string, p util.PassphraseFunc) LocalStore { return &fileSystemStore{ dir: dir, passphraseFunc: p, - signers: make(map[string][]keys.Signer), + signerForKeyID: make(map[string]keys.Signer), + keyIDsForRole: make(map[string][]string), } } @@ -134,8 +206,8 @@ type fileSystemStore struct { dir string passphraseFunc util.PassphraseFunc - // signers is a cache of persisted keys to avoid decrypting multiple times - signers map[string][]keys.Signer + signerForKeyID map[string]keys.Signer + keyIDsForRole map[string][]string } func (f *fileSystemStore) repoDir() string { @@ -333,18 +405,63 @@ func (f *fileSystemStore) Commit(consistentSnapshot bool, versions map[string]in } func (f *fileSystemStore) GetSigners(role string) ([]keys.Signer, error) { - if keys, ok := f.signers[role]; ok { - return keys, nil + keyIDs, ok := f.keyIDsForRole[role] + if ok { + return f.SignersForKeyIDs(keyIDs), nil } - keys, _, err := f.loadPrivateKeys(role) + + privKeys, _, err := f.loadPrivateKeys(role) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, err } - f.signers[role] = signers(keys) - return f.signers[role], nil + + signers := []keys.Signer{} + for _, key := range privKeys { + signer, err := keys.GetSigner(key) + if err != nil { + return nil, err + } + + // Cache the signers. + for _, keyID := range signer.PublicData().IDs() { + f.keyIDsForRole[role] = append(f.keyIDsForRole[role], keyID) + f.signerForKeyID[keyID] = signer + } + signers = append(signers, signer) + } + + return signers, nil +} + +func (f *fileSystemStore) SignersForKeyIDs(keyIDs []string) []keys.Signer { + signers := []keys.Signer{} + keyIDsSeen := map[string]struct{}{} + + for _, keyID := range keyIDs { + signer, ok := f.signerForKeyID[keyID] + if !ok { + continue + } + + addSigner := false + + for _, skid := range signer.PublicData().IDs() { + if _, seen := keyIDsSeen[skid]; !seen { + addSigner = true + } + + keyIDsSeen[skid] = struct{}{} + } + + if addSigner { + signers = append(signers, signer) + } + } + + return signers } // ChangePassphrase changes the passphrase for a role keys file. Implements @@ -391,7 +508,7 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error { } // add the key to the existing keys (if any) - keys, pass, err := f.loadPrivateKeys(role) + privKeys, pass, err := f.loadPrivateKeys(role) if err != nil && !os.IsNotExist(err) { return err } @@ -399,7 +516,7 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error { if err != nil { return err } - keys = append(keys, key) + privKeys = append(privKeys, key) // if loadPrivateKeys didn't return a passphrase (because no keys yet exist) // and passphraseFunc is set, get the passphrase so the keys file can @@ -414,13 +531,13 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error { pk := &persistedKeys{} if pass != nil { - pk.Data, err = encrypted.Marshal(keys, pass) + pk.Data, err = encrypted.Marshal(privKeys, pass) if err != nil { return err } pk.Encrypted = true } else { - pk.Data, err = json.MarshalIndent(keys, "", "\t") + pk.Data, err = json.MarshalIndent(privKeys, "", "\t") if err != nil { return err } @@ -432,7 +549,26 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error { if err := util.AtomicallyWriteFile(f.keysPath(role), append(data, '\n'), 0600); err != nil { return err } - f.signers[role] = append(f.signers[role], signer) + + innerKeyIdsForRole := f.keyIDsForRole[role] + + for _, key := range privKeys { + signer, err := keys.GetSigner(key) + if err != nil { + return err + } + + keyIDs := signer.PublicData().IDs() + + for _, keyID := range keyIDs { + f.signerForKeyID[keyID] = signer + } + + innerKeyIdsForRole = append(innerKeyIdsForRole, keyIDs...) + } + + f.keyIDsForRole[role] = sets.DeduplicateStrings(innerKeyIdsForRole) + return nil } diff --git a/repo.go b/repo.go index 482cdf00..c436195e 100644 --- a/repo.go +++ b/repo.go @@ -34,44 +34,6 @@ var topLevelMetadata = []string{ // names and generate target file metadata with additional custom metadata. type TargetsWalkFunc func(path string, target io.Reader) error -type LocalStore interface { - // GetMeta returns a map from metadata file names (e.g. root.json) to their raw JSON payload or an error. - GetMeta() (map[string]json.RawMessage, error) - - // SetMeta is used to update a metadata file name with a JSON payload. - SetMeta(string, json.RawMessage) error - - // WalkStagedTargets calls targetsFn for each staged target file in paths. - // - // If paths is empty, all staged target files will be walked. - WalkStagedTargets(paths []string, targetsFn TargetsWalkFunc) error - - // FileIsStaged determines if a metadata file is currently staged, to avoid incrementing - // version numbers repeatedly while staged. - FileIsStaged(filename string) bool - - // Commit is used to publish staged files to the repository - // - // This will also reset the staged meta to signal incrementing version numbers. - // TUF 1.0 requires that the root metadata version numbers in the repository does not - // gaps. To avoid this, we will only increment the number once until we commit. - Commit(bool, map[string]int, map[string]data.Hashes) error - - // GetSigners return a list of signers for a role. - GetSigners(string) ([]keys.Signer, error) - - // SaveSigner adds a signer to a role. - SaveSigner(string, keys.Signer) error - - // Clean is used to remove all staged metadata files. - Clean() error -} - -type PassphraseChanger interface { - // ChangePassphrase changes the passphrase for a role keys file. - ChangePassphrase(string) error -} - type Repo struct { local LocalStore hashAlgorithms []string From b9e20fe7b8103b82c1cc4cac4fb72c0fe18bef8c Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Mon, 28 Feb 2022 19:03:21 -0500 Subject: [PATCH 5/7] Clarify naming --- local_store.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/local_store.go b/local_store.go index f729d42d..bd1175b6 100644 --- a/local_store.go +++ b/local_store.go @@ -550,8 +550,9 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error { return err } - innerKeyIdsForRole := f.keyIDsForRole[role] - + // Merge privKeys into f.keyIDsForRole and register signers with + // f.signerForKeyID. + keyIDsForRole := f.keyIDsForRole[role] for _, key := range privKeys { signer, err := keys.GetSigner(key) if err != nil { @@ -564,10 +565,10 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error { f.signerForKeyID[keyID] = signer } - innerKeyIdsForRole = append(innerKeyIdsForRole, keyIDs...) + keyIDsForRole = append(keyIDsForRole, keyIDs...) } - f.keyIDsForRole[role] = sets.DeduplicateStrings(innerKeyIdsForRole) + f.keyIDsForRole[role] = sets.DeduplicateStrings(keyIDsForRole) return nil } From 4b520eb5c1efa2bedb5dde37d6cd2af1e6d8f955 Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Mon, 28 Feb 2022 19:03:39 -0500 Subject: [PATCH 6/7] Add local_store_test.go --- local_store_test.go | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 local_store_test.go diff --git a/local_store_test.go b/local_store_test.go new file mode 100644 index 00000000..bc69f18e --- /dev/null +++ b/local_store_test.go @@ -0,0 +1,69 @@ +package tuf + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/theupdateframework/go-tuf/pkg/keys" +) + +func TestLocalStoreSigners(t *testing.T) { + tmpdir, err := os.MkdirTemp("", "") + if err != nil { + t.Fatal(err) + } + defer func() { + os.RemoveAll(tmpdir) + }() + + stores := map[string]LocalStore{ + "MemoryStore": MemoryStore(nil, nil), + "FileSystemStore": FileSystemStore(tmpdir, nil), + } + + for name, store := range stores { + t.Run(name, func(t *testing.T) { + signers, err := store.GetSigners("abc") + assert.NoError(t, err) + assert.Equal(t, len(signers), 0) + + // Add two signers to role "a". + aSigner1, err := keys.GenerateEd25519Key() + assert.NoError(t, err) + err = store.SaveSigner("a", aSigner1) + assert.NoError(t, err) + + aSigner2, err := keys.GenerateEd25519Key() + assert.NoError(t, err) + err = store.SaveSigner("a", aSigner2) + assert.NoError(t, err) + + // Add one signer to role "b". + bSigner, err := keys.GenerateEd25519Key() + assert.NoError(t, err) + err = store.SaveSigner("b", bSigner) + assert.NoError(t, err) + + // Add to b again to test deduplication. + err = store.SaveSigner("b", bSigner) + assert.NoError(t, err) + + signers, err = store.GetSigners("a") + assert.NoError(t, err) + assert.ElementsMatch(t, []keys.Signer{aSigner1, aSigner2}, signers) + + signers, err = store.GetSigners("b") + assert.NoError(t, err) + assert.ElementsMatch(t, []keys.Signer{bSigner}, signers) + + a1KeyIDs := aSigner1.PublicData().IDs() + a2KeyIDs := aSigner2.PublicData().IDs() + bKeyIDs := bSigner.PublicData().IDs() + + assert.Equal(t, []keys.Signer{aSigner1}, store.SignersForKeyIDs(a1KeyIDs)) + assert.Equal(t, []keys.Signer{aSigner2}, store.SignersForKeyIDs(a2KeyIDs)) + assert.Equal(t, []keys.Signer{bSigner}, store.SignersForKeyIDs(bKeyIDs)) + }) + } +} From eaefa2e826379443cb626328bba3c90e4792260d Mon Sep 17 00:00:00 2001 From: Ethan Lowman Date: Mon, 28 Feb 2022 19:05:12 -0500 Subject: [PATCH 7/7] Another test case --- local_store_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/local_store_test.go b/local_store_test.go index bc69f18e..8badcfc8 100644 --- a/local_store_test.go +++ b/local_store_test.go @@ -63,6 +63,7 @@ func TestLocalStoreSigners(t *testing.T) { assert.Equal(t, []keys.Signer{aSigner1}, store.SignersForKeyIDs(a1KeyIDs)) assert.Equal(t, []keys.Signer{aSigner2}, store.SignersForKeyIDs(a2KeyIDs)) + assert.ElementsMatch(t, []keys.Signer{aSigner1, aSigner2}, store.SignersForKeyIDs(append(a1KeyIDs, a2KeyIDs...))) assert.Equal(t, []keys.Signer{bSigner}, store.SignersForKeyIDs(bKeyIDs)) }) }