Skip to content

Commit e688697

Browse files
[Delegations prereq] Make signers addressible by key ID in LocalStore
Splitting up #175
1 parent 344f4ee commit e688697

File tree

2 files changed

+158
-60
lines changed

2 files changed

+158
-60
lines changed

local_store.go

Lines changed: 158 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,63 @@ import (
1212

1313
"github.com/theupdateframework/go-tuf/data"
1414
"github.com/theupdateframework/go-tuf/encrypted"
15+
"github.com/theupdateframework/go-tuf/internal/sets"
1516
"github.com/theupdateframework/go-tuf/pkg/keys"
1617
"github.com/theupdateframework/go-tuf/util"
1718
)
1819

19-
func signers(privateKeys []*data.PrivateKey) []keys.Signer {
20-
res := make([]keys.Signer, 0, len(privateKeys))
21-
for _, k := range privateKeys {
22-
signer, err := keys.GetSigner(k)
23-
if err != nil {
24-
continue
25-
}
26-
res = append(res, signer)
27-
}
28-
return res
20+
type LocalStore interface {
21+
// GetMeta returns a map from metadata file names (e.g. root.json) to their raw JSON payload or an error.
22+
GetMeta() (map[string]json.RawMessage, error)
23+
24+
// SetMeta is used to update a metadata file name with a JSON payload.
25+
SetMeta(name string, meta json.RawMessage) error
26+
27+
// WalkStagedTargets calls targetsFn for each staged target file in paths.
28+
// If paths is empty, all staged target files will be walked.
29+
WalkStagedTargets(paths []string, targetsFn TargetsWalkFunc) error
30+
31+
// Commit is used to publish staged files to the repository
32+
Commit(consistentSnapshot bool, versions map[string]int, hashes map[string]data.Hashes) error
33+
34+
// GetSigners return a list of signers for a role.
35+
GetSigners(role string) ([]keys.Signer, error)
36+
37+
// SaveSigner adds a signer to a role.
38+
SaveSigner(role string, signer keys.Signer) error
39+
40+
// SignersForRole return a list of signing keys for a role.
41+
SignersForKeyIDs(keyIDs []string) []keys.Signer
42+
43+
// Clean is used to remove all staged manifests.
44+
Clean() error
45+
}
46+
47+
type PassphraseChanger interface {
48+
// ChangePassphrase changes the passphrase for a role keys file.
49+
ChangePassphrase(string) error
2950
}
3051

3152
func MemoryStore(meta map[string]json.RawMessage, files map[string][]byte) LocalStore {
3253
if meta == nil {
3354
meta = make(map[string]json.RawMessage)
3455
}
3556
return &memoryStore{
36-
meta: meta,
37-
stagedMeta: make(map[string]json.RawMessage),
38-
files: files,
39-
signers: make(map[string][]keys.Signer),
57+
meta: meta,
58+
stagedMeta: make(map[string]json.RawMessage),
59+
files: files,
60+
signerForKeyID: make(map[string]keys.Signer),
61+
keyIDsForRole: make(map[string][]string),
4062
}
4163
}
4264

4365
type memoryStore struct {
4466
meta map[string]json.RawMessage
4567
stagedMeta map[string]json.RawMessage
4668
files map[string][]byte
47-
signers map[string][]keys.Signer
69+
70+
signerForKeyID map[string]keys.Signer
71+
keyIDsForRole map[string][]string
4872
}
4973

5074
func (m *memoryStore) GetMeta() (map[string]json.RawMessage, error) {
@@ -96,14 +120,53 @@ func (m *memoryStore) Commit(consistentSnapshot bool, versions map[string]int, h
96120
}
97121

98122
func (m *memoryStore) GetSigners(role string) ([]keys.Signer, error) {
99-
return m.signers[role], nil
123+
keyIDs, ok := m.keyIDsForRole[role]
124+
if ok {
125+
return m.SignersForKeyIDs(keyIDs), nil
126+
}
127+
128+
return nil, nil
100129
}
101130

102131
func (m *memoryStore) SaveSigner(role string, signer keys.Signer) error {
103-
m.signers[role] = append(m.signers[role], signer)
132+
keyIDs := signer.PublicData().IDs()
133+
134+
for _, keyID := range keyIDs {
135+
m.signerForKeyID[keyID] = signer
136+
}
137+
138+
mergedKeyIDs := sets.DeduplicateStrings(append(m.keyIDsForRole[role], keyIDs...))
139+
m.keyIDsForRole[role] = mergedKeyIDs
104140
return nil
105141
}
106142

143+
func (m *memoryStore) SignersForKeyIDs(keyIDs []string) []keys.Signer {
144+
signers := []keys.Signer{}
145+
keyIDsSeen := map[string]struct{}{}
146+
147+
for _, keyID := range keyIDs {
148+
signer, ok := m.signerForKeyID[keyID]
149+
if !ok {
150+
continue
151+
}
152+
addSigner := false
153+
154+
for _, skid := range signer.PublicData().IDs() {
155+
if _, seen := keyIDsSeen[skid]; !seen {
156+
addSigner = true
157+
}
158+
159+
keyIDsSeen[skid] = struct{}{}
160+
}
161+
162+
if addSigner {
163+
signers = append(signers, signer)
164+
}
165+
}
166+
167+
return signers
168+
}
169+
107170
func (m *memoryStore) Clean() error {
108171
return nil
109172
}
@@ -117,16 +180,17 @@ func FileSystemStore(dir string, p util.PassphraseFunc) LocalStore {
117180
return &fileSystemStore{
118181
dir: dir,
119182
passphraseFunc: p,
120-
signers: make(map[string][]keys.Signer),
183+
signerForKeyID: make(map[string]keys.Signer),
184+
keyIDsForRole: make(map[string][]string),
121185
}
122186
}
123187

124188
type fileSystemStore struct {
125189
dir string
126190
passphraseFunc util.PassphraseFunc
127191

128-
// signers is a cache of persisted keys to avoid decrypting multiple times
129-
signers map[string][]keys.Signer
192+
signerForKeyID map[string]keys.Signer
193+
keyIDsForRole map[string][]string
130194
}
131195

132196
func (f *fileSystemStore) repoDir() string {
@@ -319,18 +383,63 @@ func (f *fileSystemStore) Commit(consistentSnapshot bool, versions map[string]in
319383
}
320384

321385
func (f *fileSystemStore) GetSigners(role string) ([]keys.Signer, error) {
322-
if keys, ok := f.signers[role]; ok {
323-
return keys, nil
386+
keyIDs, ok := f.keyIDsForRole[role]
387+
if ok {
388+
return f.SignersForKeyIDs(keyIDs), nil
324389
}
325-
keys, _, err := f.loadPrivateKeys(role)
390+
391+
privKeys, _, err := f.loadPrivateKeys(role)
326392
if err != nil {
327393
if os.IsNotExist(err) {
328394
return nil, nil
329395
}
330396
return nil, err
331397
}
332-
f.signers[role] = signers(keys)
333-
return f.signers[role], nil
398+
399+
signers := []keys.Signer{}
400+
for _, key := range privKeys {
401+
signer, err := keys.GetSigner(key)
402+
if err != nil {
403+
return nil, err
404+
}
405+
406+
// Cache the signers.
407+
for _, keyID := range signer.PublicData().IDs() {
408+
f.keyIDsForRole[role] = append(f.keyIDsForRole[role], keyID)
409+
f.signerForKeyID[keyID] = signer
410+
}
411+
signers = append(signers, signer)
412+
}
413+
414+
return signers, nil
415+
}
416+
417+
func (f *fileSystemStore) SignersForKeyIDs(keyIDs []string) []keys.Signer {
418+
signers := []keys.Signer{}
419+
keyIDsSeen := map[string]struct{}{}
420+
421+
for _, keyID := range keyIDs {
422+
signer, ok := f.signerForKeyID[keyID]
423+
if !ok {
424+
continue
425+
}
426+
427+
addSigner := false
428+
429+
for _, skid := range signer.PublicData().IDs() {
430+
if _, seen := keyIDsSeen[skid]; !seen {
431+
addSigner = true
432+
}
433+
434+
keyIDsSeen[skid] = struct{}{}
435+
}
436+
437+
if addSigner {
438+
signers = append(signers, signer)
439+
}
440+
}
441+
442+
return signers
334443
}
335444

336445
// ChangePassphrase changes the passphrase for a role keys file. Implements
@@ -377,15 +486,15 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error {
377486
}
378487

379488
// add the key to the existing keys (if any)
380-
keys, pass, err := f.loadPrivateKeys(role)
489+
privKeys, pass, err := f.loadPrivateKeys(role)
381490
if err != nil && !os.IsNotExist(err) {
382491
return err
383492
}
384493
key, err := signer.MarshalPrivateKey()
385494
if err != nil {
386495
return err
387496
}
388-
keys = append(keys, key)
497+
privKeys = append(privKeys, key)
389498

390499
// if loadPrivateKeys didn't return a passphrase (because no keys yet exist)
391500
// and passphraseFunc is set, get the passphrase so the keys file can
@@ -400,13 +509,13 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error {
400509

401510
pk := &persistedKeys{}
402511
if pass != nil {
403-
pk.Data, err = encrypted.Marshal(keys, pass)
512+
pk.Data, err = encrypted.Marshal(privKeys, pass)
404513
if err != nil {
405514
return err
406515
}
407516
pk.Encrypted = true
408517
} else {
409-
pk.Data, err = json.MarshalIndent(keys, "", "\t")
518+
pk.Data, err = json.MarshalIndent(privKeys, "", "\t")
410519
if err != nil {
411520
return err
412521
}
@@ -418,7 +527,26 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error {
418527
if err := util.AtomicallyWriteFile(f.keysPath(role), append(data, '\n'), 0600); err != nil {
419528
return err
420529
}
421-
f.signers[role] = append(f.signers[role], signer)
530+
531+
innerKeyIdsForRole := f.keyIDsForRole[role]
532+
533+
for _, key := range privKeys {
534+
signer, err := keys.GetSigner(key)
535+
if err != nil {
536+
return err
537+
}
538+
539+
keyIDs := signer.PublicData().IDs()
540+
541+
for _, keyID := range keyIDs {
542+
f.signerForKeyID[keyID] = signer
543+
}
544+
545+
innerKeyIdsForRole = append(innerKeyIdsForRole, keyIDs...)
546+
}
547+
548+
f.keyIDsForRole[role] = sets.DeduplicateStrings(innerKeyIdsForRole)
549+
422550
return nil
423551
}
424552

repo.go

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,36 +38,6 @@ var snapshotMetadata = []string{
3838
// names and generate target file metadata with additional custom metadata.
3939
type TargetsWalkFunc func(path string, target io.Reader) error
4040

41-
type LocalStore interface {
42-
// GetMeta returns a map from metadata file names (e.g. root.json) to their raw JSON payload or an error.
43-
GetMeta() (map[string]json.RawMessage, error)
44-
45-
// SetMeta is used to update a metadata file name with a JSON payload.
46-
SetMeta(string, json.RawMessage) error
47-
48-
// WalkStagedTargets calls targetsFn for each staged target file in paths.
49-
//
50-
// If paths is empty, all staged target files will be walked.
51-
WalkStagedTargets(paths []string, targetsFn TargetsWalkFunc) error
52-
53-
// Commit is used to publish staged files to the repository
54-
Commit(bool, map[string]int, map[string]data.Hashes) error
55-
56-
// GetSigners return a list of signers for a role.
57-
GetSigners(string) ([]keys.Signer, error)
58-
59-
// SaveSigner adds a signer to a role.
60-
SaveSigner(string, keys.Signer) error
61-
62-
// Clean is used to remove all staged metadata files.
63-
Clean() error
64-
}
65-
66-
type PassphraseChanger interface {
67-
// ChangePassphrase changes the passphrase for a role keys file.
68-
ChangePassphrase(string) error
69-
}
70-
7141
type Repo struct {
7242
local LocalStore
7343
hashAlgorithms []string

0 commit comments

Comments
 (0)