Skip to content

Commit

Permalink
Merge pull request #96 from flynn/refactor-keys
Browse files Browse the repository at this point in the history
Refactor key handling
  • Loading branch information
titanous authored Aug 12, 2016
2 parents 82ed859 + 47aba77 commit d65801f
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 192 deletions.
2 changes: 1 addition & 1 deletion client/interop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import (
"path/filepath"
"strings"

"github.com/agl/ed25519"
"github.com/flynn/go-tuf"
"github.com/flynn/go-tuf/data"
"github.com/flynn/go-tuf/util"
"golang.org/x/crypto/ed25519"
. "gopkg.in/check.v1"
)

Expand Down
25 changes: 15 additions & 10 deletions data/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"sync"
"time"

"github.com/tent/canonical-json-go"
cjson "github.com/tent/canonical-json-go"
)

const KeyIDLength = sha256.Size * 2
const (
KeyIDLength = sha256.Size * 2
KeyTypeEd25519 = "ed25519"
)

type Signed struct {
Signed json.RawMessage `json:"signed"`
Expand All @@ -25,21 +29,22 @@ type Signature struct {
type Key struct {
Type string `json:"keytype"`
Value KeyValue `json:"keyval"`

id string
idOnce sync.Once
}

func (k *Key) ID() string {
// create a copy so the private key is not included
data, _ := cjson.Marshal(&Key{
Type: k.Type,
Value: KeyValue{Public: k.Value.Public},
k.idOnce.Do(func() {
data, _ := cjson.Marshal(k)
digest := sha256.Sum256(data)
k.id = hex.EncodeToString(digest[:])
})
digest := sha256.Sum256(data)
return hex.EncodeToString(digest[:])
return k.id
}

type KeyValue struct {
Public HexBytes `json:"public"`
Private HexBytes `json:"private,omitempty"`
Public HexBytes `json:"public"`
}

func DefaultExpires(role string) time.Time {
Expand Down
49 changes: 14 additions & 35 deletions keys/db.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Package keys implements an in-memory public key database for TUF.
package keys

import (
"crypto/rand"
"errors"

"github.com/agl/ed25519"
"github.com/flynn/go-tuf/data"
"golang.org/x/crypto/ed25519"
)

var (
Expand All @@ -18,42 +18,19 @@ var (
ErrInvalidThreshold = errors.New("tuf: invalid role threshold")
)

func NewKey() (*Key, error) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
k := &Key{
Public: *pub,
Private: priv,
}
k.ID = k.Serialize().ID()
return k, nil
}

type Key struct {
ID string
Public [ed25519.PublicKeySize]byte
Private *[ed25519.PrivateKeySize]byte
ID string
Type string
Public []byte
}

func (k *Key) Serialize() *data.Key {
return &data.Key{
Type: "ed25519",
Type: k.Type,
Value: data.KeyValue{Public: k.Public[:]},
}
}

func (k *Key) SerializePrivate() *data.Key {
return &data.Key{
Type: "ed25519",
Value: data.KeyValue{
Public: k.Public[:],
Private: k.Private[:],
},
}
}

type Role struct {
KeyIDs map[string]struct{}
Threshold int
Expand All @@ -77,8 +54,8 @@ func NewDB() *DB {
}

func (db *DB) AddKey(id string, k *data.Key) error {
if k.Type != "ed25519" {
return ErrWrongType
if k.Type != data.KeyTypeEd25519 {
return nil
}
if id != k.ID() {
return ErrWrongID
Expand All @@ -87,10 +64,12 @@ func (db *DB) AddKey(id string, k *data.Key) error {
return ErrInvalidKey
}

var key Key
copy(key.Public[:], k.Value.Public)
key.ID = id
db.keys[id] = &key
db.keys[id] = &Key{
ID: k.ID(),
Type: k.Type,
Public: k.Value.Public,
}

return nil
}

Expand Down
53 changes: 30 additions & 23 deletions local_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/flynn/go-tuf/data"
"github.com/flynn/go-tuf/encrypted"
"github.com/flynn/go-tuf/signed"
"github.com/flynn/go-tuf/util"
)

Expand All @@ -19,16 +20,16 @@ func MemoryStore(meta map[string]json.RawMessage, files map[string][]byte) Local
meta = make(map[string]json.RawMessage)
}
return &memoryStore{
meta: meta,
files: files,
keys: make(map[string][]*data.Key),
meta: meta,
files: files,
signers: make(map[string][]signed.Signer),
}
}

type memoryStore struct {
meta map[string]json.RawMessage
files map[string][]byte
keys map[string][]*data.Key
meta map[string]json.RawMessage
files map[string][]byte
signers map[string][]signed.Signer
}

func (m *memoryStore) GetMeta() (map[string]json.RawMessage, error) {
Expand Down Expand Up @@ -66,15 +67,12 @@ func (m *memoryStore) Commit(map[string]json.RawMessage, bool, map[string]data.H
return nil
}

func (m *memoryStore) GetKeys(role string) ([]*data.Key, error) {
return m.keys[role], nil
func (m *memoryStore) GetSigningKeys(role string) ([]signed.Signer, error) {
return m.signers[role], nil
}

func (m *memoryStore) SaveKey(role string, key *data.Key) error {
if _, ok := m.keys[role]; !ok {
m.keys[role] = make([]*data.Key, 0)
}
m.keys[role] = append(m.keys[role], key)
func (m *memoryStore) SavePrivateKey(role string, key *signed.PrivateKey) error {
m.signers[role] = append(m.signers[role], key.Signer())
return nil
}

Expand All @@ -91,16 +89,16 @@ func FileSystemStore(dir string, p util.PassphraseFunc) LocalStore {
return &fileSystemStore{
dir: dir,
passphraseFunc: p,
keys: make(map[string][]*data.Key),
signers: make(map[string][]signed.Signer),
}
}

type fileSystemStore struct {
dir string
passphraseFunc util.PassphraseFunc

// keys is a cache of persisted keys to avoid decrypting multiple times
keys map[string][]*data.Key
// signers is a cache of persisted keys to avoid decrypting multiple times
signers map[string][]signed.Signer
}

func (f *fileSystemStore) repoDir() string {
Expand Down Expand Up @@ -297,8 +295,8 @@ func (f *fileSystemStore) Commit(meta map[string]json.RawMessage, consistentSnap
return f.Clean()
}

func (f *fileSystemStore) GetKeys(role string) ([]*data.Key, error) {
if keys, ok := f.keys[role]; ok {
func (f *fileSystemStore) GetSigningKeys(role string) ([]signed.Signer, error) {
if keys, ok := f.signers[role]; ok {
return keys, nil
}
keys, _, err := f.loadKeys(role)
Expand All @@ -308,10 +306,11 @@ func (f *fileSystemStore) GetKeys(role string) ([]*data.Key, error) {
}
return nil, err
}
return keys, nil
f.signers[role] = f.privateKeySigners(keys)
return f.signers[role], nil
}

func (f *fileSystemStore) SaveKey(role string, key *data.Key) error {
func (f *fileSystemStore) SavePrivateKey(role string, key *signed.PrivateKey) error {
if err := f.createDirs(); err != nil {
return err
}
Expand Down Expand Up @@ -354,13 +353,21 @@ func (f *fileSystemStore) SaveKey(role string, key *data.Key) error {
if err := ioutil.WriteFile(f.keysPath(role), append(data, '\n'), 0600); err != nil {
return err
}
f.keys[role] = keys
f.signers[role] = f.privateKeySigners(keys)
return nil
}

func (f *fileSystemStore) privateKeySigners(keys []*signed.PrivateKey) []signed.Signer {
res := make([]signed.Signer, len(keys))
for i, k := range keys {
res[i] = k.Signer()
}
return res
}

// loadKeys loads keys for the given role and returns them along with the
// passphrase (if read) so that callers don't need to re-read it.
func (f *fileSystemStore) loadKeys(role string) ([]*data.Key, []byte, error) {
func (f *fileSystemStore) loadKeys(role string) ([]*signed.PrivateKey, []byte, error) {
file, err := os.Open(f.keysPath(role))
if err != nil {
return nil, nil, err
Expand All @@ -372,7 +379,7 @@ func (f *fileSystemStore) loadKeys(role string) ([]*data.Key, []byte, error) {
return nil, nil, err
}

var keys []*data.Key
var keys []*signed.PrivateKey
if !pk.Encrypted {
if err := json.Unmarshal(pk.Data, &keys); err != nil {
return nil, nil, err
Expand Down
31 changes: 16 additions & 15 deletions repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ type LocalStore interface {
WalkStagedTargets(paths []string, targetsFn targetsWalkFunc) error

Commit(map[string]json.RawMessage, bool, map[string]data.Hashes) error
GetKeys(string) ([]*data.Key, error)
SaveKey(string, *data.Key) error
GetSigningKeys(string) ([]signed.Signer, error)
SavePrivateKey(string, *signed.PrivateKey) error
Clean() error
}

Expand Down Expand Up @@ -183,26 +183,27 @@ func (r *Repo) GenKeyWithExpires(keyRole string, expires time.Time) (string, err
return "", err
}

key, err := keys.NewKey()
key, err := signed.GenerateEd25519Key()
if err != nil {
return "", err
}
if err := r.local.SaveKey(keyRole, key.SerializePrivate()); err != nil {
if err := r.local.SavePrivateKey(keyRole, key); err != nil {
return "", err
}
pk := key.PublicData()

role, ok := root.Roles[keyRole]
if !ok {
role = &data.Role{KeyIDs: []string{}, Threshold: 1}
root.Roles[keyRole] = role
}
role.KeyIDs = append(role.KeyIDs, key.ID)
role.KeyIDs = append(role.KeyIDs, pk.ID())

root.Keys[key.ID] = key.Serialize()
root.Keys[pk.ID()] = pk
root.Expires = expires.Round(time.Second)
root.Version++

return key.ID, r.setMeta("root.json", root)
return pk.ID(), r.setMeta("root.json", root)
}

func validExpires(expires time.Time) bool {
Expand Down Expand Up @@ -277,7 +278,7 @@ func (r *Repo) RevokeKeyWithExpires(keyRole, id string, expires time.Time) error
}

func (r *Repo) setMeta(name string, meta interface{}) error {
keys, err := r.getKeys(strings.TrimSuffix(name, ".json"))
keys, err := r.getSigningKeys(strings.TrimSuffix(name, ".json"))
if err != nil {
return err
}
Expand All @@ -304,7 +305,7 @@ func (r *Repo) Sign(name string) error {
return err
}

keys, err := r.getKeys(role)
keys, err := r.getSigningKeys(role)
if err != nil {
return err
}
Expand All @@ -323,19 +324,19 @@ func (r *Repo) Sign(name string) error {
return r.local.SetMeta(name, b)
}

// getKeys returns signing keys from local storage.
// getSigningKeys returns available signing keys.
//
// Only keys contained in the keys db are returned (i.e. local keys which have
// been revoked are omitted), except for the root role in which case all local
// keys are returned (revoked root keys still need to sign new root metadata so
// clients can verify the new root.json and update their keys db accordingly).
func (r *Repo) getKeys(name string) ([]*data.Key, error) {
localKeys, err := r.local.GetKeys(name)
func (r *Repo) getSigningKeys(name string) ([]signed.Signer, error) {
signingKeys, err := r.local.GetSigningKeys(name)
if err != nil {
return nil, err
}
if name == "root" {
return localKeys, nil
return signingKeys, nil
}
db, err := r.db()
if err != nil {
Expand All @@ -348,8 +349,8 @@ func (r *Repo) getKeys(name string) ([]*data.Key, error) {
if len(role.KeyIDs) == 0 {
return nil, nil
}
keys := make([]*data.Key, 0, len(role.KeyIDs))
for _, key := range localKeys {
keys := make([]signed.Signer, 0, len(role.KeyIDs))
for _, key := range signingKeys {
if _, ok := role.KeyIDs[key.ID()]; ok {
keys = append(keys, key)
}
Expand Down
Loading

0 comments on commit d65801f

Please sign in to comment.