Skip to content

Commit a1f2a82

Browse files
[Delegations prereq] Make signers addressible by key ID in LocalStore
Splitting up #175
1 parent 3cef410 commit a1f2a82

File tree

2 files changed

+166
-68
lines changed

2 files changed

+166
-68
lines changed

local_store.go

Lines changed: 166 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,71 @@ 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+
// FileIsStaged determines if a metadata file is currently staged, to avoid incrementing
32+
// version numbers repeatedly while staged.
33+
FileIsStaged(filename string) bool
34+
35+
// Commit is used to publish staged files to the repository
36+
//
37+
// This will also reset the staged meta to signal incrementing version numbers.
38+
// TUF 1.0 requires that the root metadata version numbers in the repository does not
39+
// gaps. To avoid this, we will only increment the number once until we commit.
40+
Commit(bool, map[string]int, map[string]data.Hashes) error
41+
42+
// GetSigners return a list of signers for a role.
43+
GetSigners(role string) ([]keys.Signer, error)
44+
45+
// SaveSigner adds a signer to a role.
46+
SaveSigner(role string, signer keys.Signer) error
47+
48+
// SignersForRole return a list of signing keys for a role.
49+
SignersForKeyIDs(keyIDs []string) []keys.Signer
50+
51+
// Clean is used to remove all staged manifests.
52+
Clean() error
53+
}
54+
55+
type PassphraseChanger interface {
56+
// ChangePassphrase changes the passphrase for a role keys file.
57+
ChangePassphrase(string) error
2958
}
3059

3160
func MemoryStore(meta map[string]json.RawMessage, files map[string][]byte) LocalStore {
3261
if meta == nil {
3362
meta = make(map[string]json.RawMessage)
3463
}
3564
return &memoryStore{
36-
meta: meta,
37-
stagedMeta: make(map[string]json.RawMessage),
38-
files: files,
39-
signers: make(map[string][]keys.Signer),
65+
meta: meta,
66+
stagedMeta: make(map[string]json.RawMessage),
67+
files: files,
68+
signerForKeyID: make(map[string]keys.Signer),
69+
keyIDsForRole: make(map[string][]string),
4070
}
4171
}
4272

4373
type memoryStore struct {
4474
meta map[string]json.RawMessage
4575
stagedMeta map[string]json.RawMessage
4676
files map[string][]byte
47-
signers map[string][]keys.Signer
77+
78+
signerForKeyID map[string]keys.Signer
79+
keyIDsForRole map[string][]string
4880
}
4981

5082
func (m *memoryStore) GetMeta() (map[string]json.RawMessage, error) {
@@ -105,14 +137,53 @@ func (m *memoryStore) Commit(consistentSnapshot bool, versions map[string]int, h
105137
}
106138

107139
func (m *memoryStore) GetSigners(role string) ([]keys.Signer, error) {
108-
return m.signers[role], nil
140+
keyIDs, ok := m.keyIDsForRole[role]
141+
if ok {
142+
return m.SignersForKeyIDs(keyIDs), nil
143+
}
144+
145+
return nil, nil
109146
}
110147

111148
func (m *memoryStore) SaveSigner(role string, signer keys.Signer) error {
112-
m.signers[role] = append(m.signers[role], signer)
149+
keyIDs := signer.PublicData().IDs()
150+
151+
for _, keyID := range keyIDs {
152+
m.signerForKeyID[keyID] = signer
153+
}
154+
155+
mergedKeyIDs := sets.DeduplicateStrings(append(m.keyIDsForRole[role], keyIDs...))
156+
m.keyIDsForRole[role] = mergedKeyIDs
113157
return nil
114158
}
115159

160+
func (m *memoryStore) SignersForKeyIDs(keyIDs []string) []keys.Signer {
161+
signers := []keys.Signer{}
162+
keyIDsSeen := map[string]struct{}{}
163+
164+
for _, keyID := range keyIDs {
165+
signer, ok := m.signerForKeyID[keyID]
166+
if !ok {
167+
continue
168+
}
169+
addSigner := false
170+
171+
for _, skid := range signer.PublicData().IDs() {
172+
if _, seen := keyIDsSeen[skid]; !seen {
173+
addSigner = true
174+
}
175+
176+
keyIDsSeen[skid] = struct{}{}
177+
}
178+
179+
if addSigner {
180+
signers = append(signers, signer)
181+
}
182+
}
183+
184+
return signers
185+
}
186+
116187
func (m *memoryStore) Clean() error {
117188
return nil
118189
}
@@ -126,16 +197,17 @@ func FileSystemStore(dir string, p util.PassphraseFunc) LocalStore {
126197
return &fileSystemStore{
127198
dir: dir,
128199
passphraseFunc: p,
129-
signers: make(map[string][]keys.Signer),
200+
signerForKeyID: make(map[string]keys.Signer),
201+
keyIDsForRole: make(map[string][]string),
130202
}
131203
}
132204

133205
type fileSystemStore struct {
134206
dir string
135207
passphraseFunc util.PassphraseFunc
136208

137-
// signers is a cache of persisted keys to avoid decrypting multiple times
138-
signers map[string][]keys.Signer
209+
signerForKeyID map[string]keys.Signer
210+
keyIDsForRole map[string][]string
139211
}
140212

141213
func (f *fileSystemStore) repoDir() string {
@@ -333,18 +405,63 @@ func (f *fileSystemStore) Commit(consistentSnapshot bool, versions map[string]in
333405
}
334406

335407
func (f *fileSystemStore) GetSigners(role string) ([]keys.Signer, error) {
336-
if keys, ok := f.signers[role]; ok {
337-
return keys, nil
408+
keyIDs, ok := f.keyIDsForRole[role]
409+
if ok {
410+
return f.SignersForKeyIDs(keyIDs), nil
338411
}
339-
keys, _, err := f.loadPrivateKeys(role)
412+
413+
privKeys, _, err := f.loadPrivateKeys(role)
340414
if err != nil {
341415
if os.IsNotExist(err) {
342416
return nil, nil
343417
}
344418
return nil, err
345419
}
346-
f.signers[role] = signers(keys)
347-
return f.signers[role], nil
420+
421+
signers := []keys.Signer{}
422+
for _, key := range privKeys {
423+
signer, err := keys.GetSigner(key)
424+
if err != nil {
425+
return nil, err
426+
}
427+
428+
// Cache the signers.
429+
for _, keyID := range signer.PublicData().IDs() {
430+
f.keyIDsForRole[role] = append(f.keyIDsForRole[role], keyID)
431+
f.signerForKeyID[keyID] = signer
432+
}
433+
signers = append(signers, signer)
434+
}
435+
436+
return signers, nil
437+
}
438+
439+
func (f *fileSystemStore) SignersForKeyIDs(keyIDs []string) []keys.Signer {
440+
signers := []keys.Signer{}
441+
keyIDsSeen := map[string]struct{}{}
442+
443+
for _, keyID := range keyIDs {
444+
signer, ok := f.signerForKeyID[keyID]
445+
if !ok {
446+
continue
447+
}
448+
449+
addSigner := false
450+
451+
for _, skid := range signer.PublicData().IDs() {
452+
if _, seen := keyIDsSeen[skid]; !seen {
453+
addSigner = true
454+
}
455+
456+
keyIDsSeen[skid] = struct{}{}
457+
}
458+
459+
if addSigner {
460+
signers = append(signers, signer)
461+
}
462+
}
463+
464+
return signers
348465
}
349466

350467
// ChangePassphrase changes the passphrase for a role keys file. Implements
@@ -391,15 +508,15 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error {
391508
}
392509

393510
// add the key to the existing keys (if any)
394-
keys, pass, err := f.loadPrivateKeys(role)
511+
privKeys, pass, err := f.loadPrivateKeys(role)
395512
if err != nil && !os.IsNotExist(err) {
396513
return err
397514
}
398515
key, err := signer.MarshalPrivateKey()
399516
if err != nil {
400517
return err
401518
}
402-
keys = append(keys, key)
519+
privKeys = append(privKeys, key)
403520

404521
// if loadPrivateKeys didn't return a passphrase (because no keys yet exist)
405522
// 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 {
414531

415532
pk := &persistedKeys{}
416533
if pass != nil {
417-
pk.Data, err = encrypted.Marshal(keys, pass)
534+
pk.Data, err = encrypted.Marshal(privKeys, pass)
418535
if err != nil {
419536
return err
420537
}
421538
pk.Encrypted = true
422539
} else {
423-
pk.Data, err = json.MarshalIndent(keys, "", "\t")
540+
pk.Data, err = json.MarshalIndent(privKeys, "", "\t")
424541
if err != nil {
425542
return err
426543
}
@@ -432,7 +549,26 @@ func (f *fileSystemStore) SaveSigner(role string, signer keys.Signer) error {
432549
if err := util.AtomicallyWriteFile(f.keysPath(role), append(data, '\n'), 0600); err != nil {
433550
return err
434551
}
435-
f.signers[role] = append(f.signers[role], signer)
552+
553+
innerKeyIdsForRole := f.keyIDsForRole[role]
554+
555+
for _, key := range privKeys {
556+
signer, err := keys.GetSigner(key)
557+
if err != nil {
558+
return err
559+
}
560+
561+
keyIDs := signer.PublicData().IDs()
562+
563+
for _, keyID := range keyIDs {
564+
f.signerForKeyID[keyID] = signer
565+
}
566+
567+
innerKeyIdsForRole = append(innerKeyIdsForRole, keyIDs...)
568+
}
569+
570+
f.keyIDsForRole[role] = sets.DeduplicateStrings(innerKeyIdsForRole)
571+
436572
return nil
437573
}
438574

repo.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,44 +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-
// FileIsStaged determines if a metadata file is currently staged, to avoid incrementing
54-
// version numbers repeatedly while staged.
55-
FileIsStaged(filename string) bool
56-
57-
// Commit is used to publish staged files to the repository
58-
//
59-
// This will also reset the staged meta to signal incrementing version numbers.
60-
// TUF 1.0 requires that the root metadata version numbers in the repository does not
61-
// gaps. To avoid this, we will only increment the number once until we commit.
62-
Commit(bool, map[string]int, map[string]data.Hashes) error
63-
64-
// GetSigners return a list of signers for a role.
65-
GetSigners(string) ([]keys.Signer, error)
66-
67-
// SaveSigner adds a signer to a role.
68-
SaveSigner(string, keys.Signer) error
69-
70-
// Clean is used to remove all staged metadata files.
71-
Clean() error
72-
}
73-
74-
type PassphraseChanger interface {
75-
// ChangePassphrase changes the passphrase for a role keys file.
76-
ChangePassphrase(string) error
77-
}
78-
7941
type Repo struct {
8042
local LocalStore
8143
hashAlgorithms []string

0 commit comments

Comments
 (0)