Skip to content

Commit

Permalink
[Delegations prereq] Make signers addressible by key ID in LocalStore
Browse files Browse the repository at this point in the history
Splitting up #175
  • Loading branch information
ethan-lowman-dd committed Dec 10, 2021
1 parent 092aa7c commit 84c43e5
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 60 deletions.
188 changes: 158 additions & 30 deletions local_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,63 @@ 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

// Commit is used to publish staged files to the repository
Commit(consistentSnapshot bool, versions map[string]int, hashes 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 {
if meta == nil {
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),
}
}

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) {
Expand Down Expand Up @@ -96,14 +120,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
}
Expand All @@ -117,16 +180,17 @@ 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),
}
}

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 {
Expand Down Expand Up @@ -319,18 +383,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
Expand Down Expand Up @@ -377,15 +486,15 @@ 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
}
key, err := signer.MarshalPrivateKey()
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
Expand All @@ -400,13 +509,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
}
Expand All @@ -418,7 +527,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
}

Expand Down
30 changes: 0 additions & 30 deletions repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,36 +38,6 @@ var snapshotMetadata = []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

// Commit is used to publish staged files to the repository
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
Expand Down

0 comments on commit 84c43e5

Please sign in to comment.