Split signed package into sign and verify packages
- Merge key database package keys into package verify
- Use Verifiers to check key validity instead of hard-coding
Ed25519.
Signed-off-by: Jonathan Rudenberg <jonathan@titanous.com>
diff --git a/client/client.go b/client/client.go
index 1e1de2e..acc0240 100644
--- a/client/client.go
+++ b/client/client.go
@@ -8,9 +8,8 @@
"io/ioutil"
"github.com/flynn/go-tuf/data"
- "github.com/flynn/go-tuf/keys"
- "github.com/flynn/go-tuf/signed"
"github.com/flynn/go-tuf/util"
+ "github.com/flynn/go-tuf/verify"
)
// LocalStore is local storage for downloaded top-level metadata.
@@ -69,7 +68,7 @@
localMeta map[string]json.RawMessage
// db is a key DB used for verifying metadata
- db *keys.DB
+ db *verify.DB
// consistentSnapshot indicates whether the remote storage is using
// consistent snapshots (as specified in root.json)
@@ -97,7 +96,7 @@
return err
}
- c.db = keys.NewDB()
+ c.db = verify.NewDB()
rootKeyIDs := make([]string, len(rootKeys))
for i, key := range rootKeys {
id := key.ID()
@@ -131,7 +130,7 @@
func (c *Client) update(latestRoot bool) (data.Files, error) {
// Always start the update using local metadata
if err := c.getLocalMeta(); err != nil {
- if _, ok := err.(signed.ErrExpired); ok {
+ if _, ok := err.(verify.ErrExpired); ok {
if !latestRoot {
return c.updateWithLatestRoot(nil)
}
@@ -140,7 +139,7 @@
// should not have continued the update
return nil, err
}
- if latestRoot && err == signed.ErrRoleThreshold {
+ if latestRoot && err == verify.ErrRoleThreshold {
// Root was updated with new keys, so our local metadata is no
// longer validating. Read only the versions from the local metadata
// and re-download everything.
@@ -162,7 +161,7 @@
if err != nil {
// ErrRoleThreshold could indicate timestamp keys have been
// revoked, so retry with the latest root.json
- if isDecodeFailedWithErr(err, signed.ErrRoleThreshold) && !latestRoot {
+ if isDecodeFailedWithErr(err, verify.ErrRoleThreshold) && !latestRoot {
return c.updateWithLatestRoot(nil)
}
return nil, err
@@ -189,7 +188,7 @@
if err != nil {
// ErrRoleThreshold could indicate snapshot keys have been
// revoked, so retry with the latest root.json
- if isDecodeFailedWithErr(err, signed.ErrRoleThreshold) && !latestRoot {
+ if isDecodeFailedWithErr(err, verify.ErrRoleThreshold) && !latestRoot {
return c.updateWithLatestRoot(nil)
}
return nil, err
@@ -267,7 +266,7 @@
if err := json.Unmarshal(s.Signed, root); err != nil {
return err
}
- c.db = keys.NewDB()
+ c.db = verify.NewDB()
for id, k := range root.Keys {
if err := c.db.AddKey(id, k); err != nil {
return err
@@ -278,7 +277,7 @@
return err
}
}
- if err := signed.Verify(s, "root", 0, c.db); err != nil {
+ if err := c.db.Verify(s, "root", 0); err != nil {
return err
}
c.consistentSnapshot = root.ConsistentSnapshot
@@ -288,7 +287,7 @@
if snapshotJSON, ok := meta["snapshot.json"]; ok {
snapshot := &data.Snapshot{}
- if err := signed.UnmarshalTrusted(snapshotJSON, snapshot, "snapshot", c.db); err != nil {
+ if err := verify.UnmarshalTrusted(snapshotJSON, snapshot, "snapshot", c.db); err != nil {
return err
}
c.snapshotVer = snapshot.Version
@@ -296,7 +295,7 @@
if targetsJSON, ok := meta["targets.json"]; ok {
targets := &data.Targets{}
- if err := signed.UnmarshalTrusted(targetsJSON, targets, "targets", c.db); err != nil {
+ if err := verify.UnmarshalTrusted(targetsJSON, targets, "targets", c.db); err != nil {
return err
}
c.targetsVer = targets.Version
@@ -305,7 +304,7 @@
if timestampJSON, ok := meta["timestamp.json"]; ok {
timestamp := &data.Timestamp{}
- if err := signed.UnmarshalTrusted(timestampJSON, timestamp, "timestamp", c.db); err != nil {
+ if err := verify.UnmarshalTrusted(timestampJSON, timestamp, "timestamp", c.db); err != nil {
return err
}
c.timestampVer = timestamp.Version
@@ -454,7 +453,7 @@
// decodeRoot decodes and verifies root metadata.
func (c *Client) decodeRoot(b json.RawMessage) error {
root := &data.Root{}
- if err := signed.Unmarshal(b, root, "root", c.rootVer, c.db); err != nil {
+ if err := verify.Unmarshal(b, root, "root", c.rootVer, c.db); err != nil {
return ErrDecodeFailed{"root.json", err}
}
c.rootVer = root.Version
@@ -466,7 +465,7 @@
// root and targets file meta.
func (c *Client) decodeSnapshot(b json.RawMessage) (data.FileMeta, data.FileMeta, error) {
snapshot := &data.Snapshot{}
- if err := signed.Unmarshal(b, snapshot, "snapshot", c.snapshotVer, c.db); err != nil {
+ if err := verify.Unmarshal(b, snapshot, "snapshot", c.snapshotVer, c.db); err != nil {
return data.FileMeta{}, data.FileMeta{}, ErrDecodeFailed{"snapshot.json", err}
}
c.snapshotVer = snapshot.Version
@@ -477,7 +476,7 @@
// returns updated targets.
func (c *Client) decodeTargets(b json.RawMessage) (data.Files, error) {
targets := &data.Targets{}
- if err := signed.Unmarshal(b, targets, "targets", c.targetsVer, c.db); err != nil {
+ if err := verify.Unmarshal(b, targets, "targets", c.targetsVer, c.db); err != nil {
return nil, ErrDecodeFailed{"targets.json", err}
}
updatedTargets := make(data.Files)
@@ -498,7 +497,7 @@
// new snapshot file meta.
func (c *Client) decodeTimestamp(b json.RawMessage) (data.FileMeta, error) {
timestamp := &data.Timestamp{}
- if err := signed.Unmarshal(b, timestamp, "timestamp", c.timestampVer, c.db); err != nil {
+ if err := verify.Unmarshal(b, timestamp, "timestamp", c.timestampVer, c.db); err != nil {
return data.FileMeta{}, ErrDecodeFailed{"timestamp.json", err}
}
c.timestampVer = timestamp.Version
diff --git a/client/client_test.go b/client/client_test.go
index 846fb84..d715ea7 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -12,9 +12,8 @@
"github.com/flynn/go-tuf"
"github.com/flynn/go-tuf/data"
- "github.com/flynn/go-tuf/keys"
- "github.com/flynn/go-tuf/signed"
"github.com/flynn/go-tuf/util"
+ "github.com/flynn/go-tuf/verify"
. "gopkg.in/check.v1"
)
@@ -133,9 +132,9 @@
// any metadata marked to expire at s.expiredTime will be expired (this avoids
// the need to sleep in the tests).
func (s *ClientSuite) withMetaExpired(f func()) {
- e := signed.IsExpired
- defer func() { signed.IsExpired = e }()
- signed.IsExpired = func(t time.Time) bool {
+ e := verify.IsExpired
+ defer func() { verify.IsExpired = e }()
+ verify.IsExpired = func(t time.Time) bool {
return t.Unix() == s.expiredTime.Round(time.Second).Unix()
}
f()
@@ -220,7 +219,7 @@
c.Fatalf("expected err to have type ErrDecodeFailed, got %T", err)
}
c.Assert(decodeErr.File, Equals, file)
- expiredErr, ok := decodeErr.Err.(signed.ErrExpired)
+ expiredErr, ok := decodeErr.Err.(verify.ErrExpired)
if !ok {
c.Fatalf("expected err.Err to have type signed.ErrExpired, got %T", err)
}
@@ -246,7 +245,7 @@
client := NewClient(MemoryLocalStore(), s.remote)
// check Init() returns keys.ErrInvalidThreshold with an invalid threshold
- c.Assert(client.Init(s.rootKeys(c), 0), Equals, keys.ErrInvalidThreshold)
+ c.Assert(client.Init(s.rootKeys(c), 0), Equals, verify.ErrInvalidThreshold)
// check Init() returns signed.ErrRoleThreshold when not enough keys
c.Assert(client.Init(s.rootKeys(c), 2), Equals, ErrInsufficientKeys)
@@ -332,7 +331,7 @@
for name, id := range newKeyIDs {
key := client.db.GetKey(id)
c.Assert(key, NotNil)
- c.Assert(key.ID, Equals, id)
+ c.Assert(key.ID(), Equals, id)
role := client.db.GetRole(name)
c.Assert(role, NotNil)
c.Assert(role.KeyIDs, DeepEquals, map[string]struct{}{id: {}})
@@ -385,7 +384,7 @@
c.Assert(client.db.GetKey(oldID), IsNil)
key := client.db.GetKey(newID)
c.Assert(key, NotNil)
- c.Assert(key.ID, Equals, newID)
+ c.Assert(key.ID(), Equals, newID)
role := client.db.GetRole("timestamp")
c.Assert(role, NotNil)
c.Assert(role.KeyIDs, DeepEquals, map[string]struct{}{newID: {}})
@@ -419,7 +418,7 @@
c.Assert(client.db.GetKey(oldID), IsNil)
key := client.db.GetKey(newID)
c.Assert(key, NotNil)
- c.Assert(key.ID, Equals, newID)
+ c.Assert(key.ID(), Equals, newID)
role := client.db.GetRole("snapshot")
c.Assert(role, NotNil)
c.Assert(role.KeyIDs, DeepEquals, map[string]struct{}{newID: {}})
@@ -456,7 +455,7 @@
c.Assert(client.db.GetKey(oldID), IsNil)
key := client.db.GetKey(newID)
c.Assert(key, NotNil)
- c.Assert(key.ID, Equals, newID)
+ c.Assert(key.ID(), Equals, newID)
role := client.db.GetRole("targets")
c.Assert(role, NotNil)
c.Assert(role.KeyIDs, DeepEquals, map[string]struct{}{newID: {}})
@@ -498,7 +497,7 @@
s.syncLocal(c)
s.withMetaExpired(func() {
err := client.getLocalMeta()
- if _, ok := err.(signed.ErrExpired); !ok {
+ if _, ok := err.(verify.ErrExpired); !ok {
c.Fatalf("expected err to have type signed.ErrExpired, got %T", err)
}
c.Assert(client.rootVer, Equals, version)
@@ -528,7 +527,7 @@
// restarts itself, thus successfully updating
s.withMetaExpired(func() {
err := client.getLocalMeta()
- if _, ok := err.(signed.ErrExpired); !ok {
+ if _, ok := err.(verify.ErrExpired); !ok {
c.Fatalf("expected err to have type signed.ErrExpired, got %T", err)
}
@@ -602,7 +601,7 @@
// restarts itself, thus successfully updating
s.withMetaExpired(func() {
err := client.getLocalMeta()
- c.Assert(err, FitsTypeOf, signed.ErrExpired{})
+ c.Assert(err, FitsTypeOf, verify.ErrExpired{})
_, err = client.Update()
c.Assert(err, IsNil)
@@ -674,7 +673,7 @@
// check update returns ErrLowVersion
_, err = client.Update()
- c.Assert(err, DeepEquals, ErrDecodeFailed{"timestamp.json", signed.ErrLowVersion{version, client.timestampVer}})
+ c.Assert(err, DeepEquals, ErrDecodeFailed{"timestamp.json", verify.ErrLowVersion{version, client.timestampVer}})
}
func (s *ClientSuite) TestUpdateTamperedTargets(c *C) {
diff --git a/keys/db.go b/keys/db.go
deleted file mode 100644
index 7613d5c..0000000
--- a/keys/db.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// Package keys implements an in-memory public key database for TUF.
-package keys
-
-import (
- "errors"
-
- "github.com/flynn/go-tuf/data"
- "github.com/flynn/go-tuf/signed"
-)
-
-var (
- ErrWrongType = errors.New("tuf: invalid key type")
- ErrExists = errors.New("tuf: key already in db")
- ErrWrongID = errors.New("tuf: key id mismatch")
- ErrInvalidKey = errors.New("tuf: invalid key")
- ErrInvalidRole = errors.New("tuf: invalid role")
- ErrInvalidKeyID = errors.New("tuf: invalid key id")
- ErrInvalidThreshold = errors.New("tuf: invalid role threshold")
-)
-
-type Key struct {
- ID string
- Type string
- Public []byte
-}
-
-func (k *Key) Serialize() *data.Key {
- return &data.Key{
- Type: k.Type,
- Value: data.KeyValue{Public: k.Public[:]},
- }
-}
-
-type Role struct {
- KeyIDs map[string]struct{}
- Threshold int
-}
-
-func (r *Role) ValidKey(id string) bool {
- _, ok := r.KeyIDs[id]
- return ok
-}
-
-type DB struct {
- roles map[string]*Role
- keys map[string]*Key
-}
-
-func NewDB() *DB {
- return &DB{
- roles: make(map[string]*Role),
- keys: make(map[string]*Key),
- }
-}
-
-func (db *DB) AddKey(id string, k *data.Key) error {
- v, ok := signed.Verifiers[k.Type]
- if !ok {
- return nil
- }
- if id != k.ID() {
- return ErrWrongID
- }
- if !v.ValidKey(k.Value.Public) {
- return ErrInvalidKey
- }
-
- db.keys[id] = &Key{
- ID: k.ID(),
- Type: k.Type,
- Public: k.Value.Public,
- }
-
- return nil
-}
-
-var validRoles = map[string]struct{}{
- "root": {},
- "targets": {},
- "snapshot": {},
- "timestamp": {},
-}
-
-func ValidRole(name string) bool {
- _, ok := validRoles[name]
- return ok
-}
-
-func (db *DB) AddRole(name string, r *data.Role) error {
- if !ValidRole(name) {
- return ErrInvalidRole
- }
- if r.Threshold < 1 {
- return ErrInvalidThreshold
- }
-
- role := &Role{
- KeyIDs: make(map[string]struct{}),
- Threshold: r.Threshold,
- }
- for _, id := range r.KeyIDs {
- if len(id) != data.KeyIDLength {
- return ErrInvalidKeyID
- }
- role.KeyIDs[id] = struct{}{}
- }
-
- db.roles[name] = role
- return nil
-}
-
-func (db *DB) GetKey(id string) *Key {
- return db.keys[id]
-}
-
-func (db *DB) GetRole(name string) *Role {
- return db.roles[name]
-}
diff --git a/local_store.go b/local_store.go
index 9da6734..98d69e7 100644
--- a/local_store.go
+++ b/local_store.go
@@ -11,7 +11,7 @@
"github.com/flynn/go-tuf/data"
"github.com/flynn/go-tuf/encrypted"
- "github.com/flynn/go-tuf/signed"
+ "github.com/flynn/go-tuf/sign"
"github.com/flynn/go-tuf/util"
)
@@ -22,14 +22,14 @@
return &memoryStore{
meta: meta,
files: files,
- signers: make(map[string][]signed.Signer),
+ signers: make(map[string][]sign.Signer),
}
}
type memoryStore struct {
meta map[string]json.RawMessage
files map[string][]byte
- signers map[string][]signed.Signer
+ signers map[string][]sign.Signer
}
func (m *memoryStore) GetMeta() (map[string]json.RawMessage, error) {
@@ -67,11 +67,11 @@
return nil
}
-func (m *memoryStore) GetSigningKeys(role string) ([]signed.Signer, error) {
+func (m *memoryStore) GetSigningKeys(role string) ([]sign.Signer, error) {
return m.signers[role], nil
}
-func (m *memoryStore) SavePrivateKey(role string, key *signed.PrivateKey) error {
+func (m *memoryStore) SavePrivateKey(role string, key *sign.PrivateKey) error {
m.signers[role] = append(m.signers[role], key.Signer())
return nil
}
@@ -89,7 +89,7 @@
return &fileSystemStore{
dir: dir,
passphraseFunc: p,
- signers: make(map[string][]signed.Signer),
+ signers: make(map[string][]sign.Signer),
}
}
@@ -98,7 +98,7 @@
passphraseFunc util.PassphraseFunc
// signers is a cache of persisted keys to avoid decrypting multiple times
- signers map[string][]signed.Signer
+ signers map[string][]sign.Signer
}
func (f *fileSystemStore) repoDir() string {
@@ -295,7 +295,7 @@
return f.Clean()
}
-func (f *fileSystemStore) GetSigningKeys(role string) ([]signed.Signer, error) {
+func (f *fileSystemStore) GetSigningKeys(role string) ([]sign.Signer, error) {
if keys, ok := f.signers[role]; ok {
return keys, nil
}
@@ -310,7 +310,7 @@
return f.signers[role], nil
}
-func (f *fileSystemStore) SavePrivateKey(role string, key *signed.PrivateKey) error {
+func (f *fileSystemStore) SavePrivateKey(role string, key *sign.PrivateKey) error {
if err := f.createDirs(); err != nil {
return err
}
@@ -357,8 +357,8 @@
return nil
}
-func (f *fileSystemStore) privateKeySigners(keys []*signed.PrivateKey) []signed.Signer {
- res := make([]signed.Signer, len(keys))
+func (f *fileSystemStore) privateKeySigners(keys []*sign.PrivateKey) []sign.Signer {
+ res := make([]sign.Signer, len(keys))
for i, k := range keys {
res[i] = k.Signer()
}
@@ -367,7 +367,7 @@
// 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) ([]*signed.PrivateKey, []byte, error) {
+func (f *fileSystemStore) loadKeys(role string) ([]*sign.PrivateKey, []byte, error) {
file, err := os.Open(f.keysPath(role))
if err != nil {
return nil, nil, err
@@ -379,7 +379,7 @@
return nil, nil, err
}
- var keys []*signed.PrivateKey
+ var keys []*sign.PrivateKey
if !pk.Encrypted {
if err := json.Unmarshal(pk.Data, &keys); err != nil {
return nil, nil, err
diff --git a/repo.go b/repo.go
index aad9b02..9fcf53f 100644
--- a/repo.go
+++ b/repo.go
@@ -10,9 +10,9 @@
"time"
"github.com/flynn/go-tuf/data"
- "github.com/flynn/go-tuf/keys"
- "github.com/flynn/go-tuf/signed"
+ "github.com/flynn/go-tuf/sign"
"github.com/flynn/go-tuf/util"
+ "github.com/flynn/go-tuf/verify"
)
type CompressionType uint8
@@ -47,8 +47,8 @@
WalkStagedTargets(paths []string, targetsFn targetsWalkFunc) error
Commit(map[string]json.RawMessage, bool, map[string]data.Hashes) error
- GetSigningKeys(string) ([]signed.Signer, error)
- SavePrivateKey(string, *signed.PrivateKey) error
+ GetSigningKeys(string) ([]sign.Signer, error)
+ SavePrivateKey(string, *sign.PrivateKey) error
Clean() error
}
@@ -82,8 +82,8 @@
return r.setMeta("root.json", root)
}
-func (r *Repo) db() (*keys.DB, error) {
- db := keys.NewDB()
+func (r *Repo) db() (*verify.DB, error) {
+ db := verify.NewDB()
root, err := r.root()
if err != nil {
return nil, err
@@ -170,7 +170,7 @@
}
func (r *Repo) GenKeyWithExpires(keyRole string, expires time.Time) (string, error) {
- if !keys.ValidRole(keyRole) {
+ if !verify.ValidRole(keyRole) {
return "", ErrInvalidRole{keyRole}
}
@@ -183,7 +183,7 @@
return "", err
}
- key, err := signed.GenerateEd25519Key()
+ key, err := sign.GenerateEd25519Key()
if err != nil {
return "", err
}
@@ -235,7 +235,7 @@
}
func (r *Repo) RevokeKeyWithExpires(keyRole, id string, expires time.Time) error {
- if !keys.ValidRole(keyRole) {
+ if !verify.ValidRole(keyRole) {
return ErrInvalidRole{keyRole}
}
@@ -282,7 +282,7 @@
if err != nil {
return err
}
- s, err := signed.Marshal(meta, keys...)
+ s, err := sign.Marshal(meta, keys...)
if err != nil {
return err
}
@@ -296,7 +296,7 @@
func (r *Repo) Sign(name string) error {
role := strings.TrimSuffix(name, ".json")
- if !keys.ValidRole(role) {
+ if !verify.ValidRole(role) {
return ErrInvalidRole{role}
}
@@ -313,7 +313,7 @@
return ErrInsufficientKeys{name}
}
for _, k := range keys {
- signed.Sign(s, k)
+ sign.Sign(s, k)
}
b, err := json.Marshal(s)
@@ -330,7 +330,7 @@
// 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) getSigningKeys(name string) ([]signed.Signer, error) {
+func (r *Repo) getSigningKeys(name string) ([]sign.Signer, error) {
signingKeys, err := r.local.GetSigningKeys(name)
if err != nil {
return nil, err
@@ -349,7 +349,7 @@
if len(role.KeyIDs) == 0 {
return nil, nil
}
- keys := make([]signed.Signer, 0, len(role.KeyIDs))
+ keys := make([]sign.Signer, 0, len(role.KeyIDs))
for _, key := range signingKeys {
if _, ok := role.KeyIDs[key.ID()]; ok {
keys = append(keys, key)
@@ -635,13 +635,13 @@
return r.local.Clean()
}
-func (r *Repo) verifySignature(name string, db *keys.DB) error {
+func (r *Repo) verifySignature(name string, db *verify.DB) error {
s, err := r.signedMeta(name)
if err != nil {
return err
}
role := strings.TrimSuffix(name, ".json")
- if err := signed.Verify(s, role, 0, db); err != nil {
+ if err := db.Verify(s, role, 0); err != nil {
return ErrInsufficientSignatures{name, err}
}
return nil
diff --git a/repo_test.go b/repo_test.go
index 420b368..8ca4540 100644
--- a/repo_test.go
+++ b/repo_test.go
@@ -12,8 +12,9 @@
"github.com/flynn/go-tuf/data"
"github.com/flynn/go-tuf/encrypted"
- "github.com/flynn/go-tuf/signed"
+ "github.com/flynn/go-tuf/sign"
"github.com/flynn/go-tuf/util"
+ "github.com/flynn/go-tuf/verify"
"golang.org/x/crypto/ed25519"
. "gopkg.in/check.v1"
)
@@ -163,7 +164,7 @@
c.Assert(err, IsNil)
rootKey := db.GetKey(keyID)
c.Assert(rootKey, NotNil)
- c.Assert(rootKey.ID, Equals, keyID)
+ c.Assert(rootKey.ID(), Equals, keyID)
role := db.GetRole("root")
c.Assert(role.KeyIDs, DeepEquals, map[string]struct{}{keyID: {}})
@@ -177,8 +178,8 @@
rootKeys, err := r.RootKeys()
c.Assert(err, IsNil)
c.Assert(rootKeys, HasLen, 1)
- c.Assert(rootKeys[0].ID(), Equals, rootKey.ID)
- c.Assert(rootKeys[0].Value.Public, DeepEquals, rootKey.Serialize().Value.Public)
+ c.Assert(rootKeys[0].ID(), Equals, rootKey.ID())
+ c.Assert(rootKeys[0].Value.Public, DeepEquals, rootKey.Value.Public)
// generate two targets keys
genKey(c, r, "targets")
@@ -205,7 +206,7 @@
}
key := db.GetKey(id)
c.Assert(key, NotNil)
- c.Assert(key.ID, Equals, id)
+ c.Assert(key.ID(), Equals, id)
}
role = db.GetRole("targets")
c.Assert(role.KeyIDs, DeepEquals, targetKeyIDs)
@@ -214,7 +215,7 @@
rootKeys, err = r.RootKeys()
c.Assert(err, IsNil)
c.Assert(rootKeys, HasLen, 1)
- c.Assert(rootKeys[0].ID(), Equals, rootKey.ID)
+ c.Assert(rootKeys[0].ID(), Equals, rootKey.ID())
// check the keys were saved correctly
localKeys, err = local.GetSigningKeys("targets")
@@ -321,7 +322,7 @@
}
// signing with an available key generates a signature
- key, err := signed.GenerateEd25519Key()
+ key, err := sign.GenerateEd25519Key()
c.Assert(err, IsNil)
c.Assert(local.SavePrivateKey("root", key), IsNil)
c.Assert(r.Sign("root.json"), IsNil)
@@ -332,7 +333,7 @@
checkSigIDs(key.PublicData().ID())
// signing with a new available key generates another signature
- newKey, err := signed.GenerateEd25519Key()
+ newKey, err := sign.GenerateEd25519Key()
c.Assert(err, IsNil)
c.Assert(local.SavePrivateKey("root", newKey), IsNil)
c.Assert(r.Sign("root.json"), IsNil)
@@ -364,7 +365,7 @@
// commit with timestamp.json but no timestamp key
c.Assert(r.Timestamp(), IsNil)
- c.Assert(r.Commit(), DeepEquals, ErrInsufficientSignatures{"timestamp.json", signed.ErrNoSignatures})
+ c.Assert(r.Commit(), DeepEquals, ErrInsufficientSignatures{"timestamp.json", verify.ErrNoSignatures})
// commit success
genKey(c, r, "timestamp")
@@ -761,13 +762,13 @@
passphrase := []byte("s3cr3t")
store := FileSystemStore(tmp.path, testPassphraseFunc(passphrase))
- assertKeys := func(role string, enc bool, expected []*signed.PrivateKey) {
+ assertKeys := func(role string, enc bool, expected []*sign.PrivateKey) {
keysJSON := tmp.readFile("keys/" + role + ".json")
pk := &persistedKeys{}
c.Assert(json.Unmarshal(keysJSON, pk), IsNil)
// check the persisted keys are correct
- var actual []*signed.PrivateKey
+ var actual []*sign.PrivateKey
if enc {
c.Assert(pk.Encrypted, Equals, true)
decrypted, err := encrypted.Decrypt(pk.Data, passphrase)
@@ -792,28 +793,28 @@
}
// save a key and check it gets encrypted
- key, err := signed.GenerateEd25519Key()
+ key, err := sign.GenerateEd25519Key()
c.Assert(err, IsNil)
c.Assert(store.SavePrivateKey("root", key), IsNil)
- assertKeys("root", true, []*signed.PrivateKey{key})
+ assertKeys("root", true, []*sign.PrivateKey{key})
// save another key and check it gets added to the existing keys
- newKey, err := signed.GenerateEd25519Key()
+ newKey, err := sign.GenerateEd25519Key()
c.Assert(err, IsNil)
c.Assert(store.SavePrivateKey("root", newKey), IsNil)
- assertKeys("root", true, []*signed.PrivateKey{key, newKey})
+ assertKeys("root", true, []*sign.PrivateKey{key, newKey})
// check saving a key to an encrypted file without a passphrase fails
insecureStore := FileSystemStore(tmp.path, nil)
- key, err = signed.GenerateEd25519Key()
+ key, err = sign.GenerateEd25519Key()
c.Assert(err, IsNil)
c.Assert(insecureStore.SavePrivateKey("root", key), Equals, ErrPassphraseRequired{"root"})
// save a key to an insecure store and check it is not encrypted
- key, err = signed.GenerateEd25519Key()
+ key, err = sign.GenerateEd25519Key()
c.Assert(err, IsNil)
c.Assert(insecureStore.SavePrivateKey("targets", key), IsNil)
- assertKeys("targets", false, []*signed.PrivateKey{key})
+ assertKeys("targets", false, []*sign.PrivateKey{key})
}
func (RepoSuite) TestManageMultipleTargets(c *C) {
diff --git a/signed/keys.go b/sign/keys.go
similarity index 98%
rename from signed/keys.go
rename to sign/keys.go
index 7e4fd3a..8afd2ae 100644
--- a/signed/keys.go
+++ b/sign/keys.go
@@ -1,4 +1,4 @@
-package signed
+package sign
import (
"crypto/rand"
diff --git a/signed/sign.go b/sign/sign.go
similarity index 98%
rename from signed/sign.go
rename to sign/sign.go
index 41dd20b..f135e04 100644
--- a/signed/sign.go
+++ b/sign/sign.go
@@ -1,4 +1,4 @@
-package signed
+package sign
import (
"crypto"
diff --git a/signed/errors.go b/signed/errors.go
deleted file mode 100644
index 10bdfd6..0000000
--- a/signed/errors.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package signed
-
-import (
- "fmt"
- "time"
-)
-
-type ErrExpired struct {
- Expired time.Time
-}
-
-func (e ErrExpired) Error() string {
- return fmt.Sprintf("expired at %s", e.Expired)
-}
-
-type ErrLowVersion struct {
- Actual int
- Current int
-}
-
-func (e ErrLowVersion) Error() string {
- return fmt.Sprintf("version %d is lower than current version %d", e.Actual, e.Current)
-}
diff --git a/verify/db.go b/verify/db.go
new file mode 100644
index 0000000..b0c287d
--- /dev/null
+++ b/verify/db.go
@@ -0,0 +1,87 @@
+package verify
+
+import (
+ "github.com/flynn/go-tuf/data"
+)
+
+type Role struct {
+ KeyIDs map[string]struct{}
+ Threshold int
+}
+
+func (r *Role) ValidKey(id string) bool {
+ _, ok := r.KeyIDs[id]
+ return ok
+}
+
+type DB struct {
+ roles map[string]*Role
+ keys map[string]*data.Key
+}
+
+func NewDB() *DB {
+ return &DB{
+ roles: make(map[string]*Role),
+ keys: make(map[string]*data.Key),
+ }
+}
+
+func (db *DB) AddKey(id string, k *data.Key) error {
+ v, ok := Verifiers[k.Type]
+ if !ok {
+ return nil
+ }
+ if id != k.ID() {
+ return ErrWrongID
+ }
+ if !v.ValidKey(k.Value.Public) {
+ return ErrInvalidKey
+ }
+
+ db.keys[id] = k
+
+ return nil
+}
+
+var validRoles = map[string]struct{}{
+ "root": {},
+ "targets": {},
+ "snapshot": {},
+ "timestamp": {},
+}
+
+func ValidRole(name string) bool {
+ _, ok := validRoles[name]
+ return ok
+}
+
+func (db *DB) AddRole(name string, r *data.Role) error {
+ if !ValidRole(name) {
+ return ErrInvalidRole
+ }
+ if r.Threshold < 1 {
+ return ErrInvalidThreshold
+ }
+
+ role := &Role{
+ KeyIDs: make(map[string]struct{}),
+ Threshold: r.Threshold,
+ }
+ for _, id := range r.KeyIDs {
+ if len(id) != data.KeyIDLength {
+ return ErrInvalidKeyID
+ }
+ role.KeyIDs[id] = struct{}{}
+ }
+
+ db.roles[name] = role
+ return nil
+}
+
+func (db *DB) GetKey(id string) *data.Key {
+ return db.keys[id]
+}
+
+func (db *DB) GetRole(name string) *Role {
+ return db.roles[name]
+}
diff --git a/verify/errors.go b/verify/errors.go
new file mode 100644
index 0000000..08ef217
--- /dev/null
+++ b/verify/errors.go
@@ -0,0 +1,40 @@
+package verify
+
+import (
+ "errors"
+ "fmt"
+ "time"
+)
+
+var (
+ ErrMissingKey = errors.New("tuf: missing key")
+ ErrNoSignatures = errors.New("tuf: data has no signatures")
+ ErrInvalid = errors.New("tuf: signature verification failed")
+ ErrWrongMethod = errors.New("tuf: invalid signature type")
+ ErrUnknownRole = errors.New("tuf: unknown role")
+ ErrRoleThreshold = errors.New("tuf: valid signatures did not meet threshold")
+ ErrWrongMetaType = errors.New("tuf: meta file has wrong type")
+ ErrExists = errors.New("tuf: key already in db")
+ ErrWrongID = errors.New("tuf: key id mismatch")
+ ErrInvalidKey = errors.New("tuf: invalid key")
+ ErrInvalidRole = errors.New("tuf: invalid role")
+ ErrInvalidKeyID = errors.New("tuf: invalid key id")
+ ErrInvalidThreshold = errors.New("tuf: invalid role threshold")
+)
+
+type ErrExpired struct {
+ Expired time.Time
+}
+
+func (e ErrExpired) Error() string {
+ return fmt.Sprintf("expired at %s", e.Expired)
+}
+
+type ErrLowVersion struct {
+ Actual int
+ Current int
+}
+
+func (e ErrLowVersion) Error() string {
+ return fmt.Sprintf("version %d is lower than current version %d", e.Actual, e.Current)
+}
diff --git a/signed/verifiers.go b/verify/verifiers.go
similarity index 75%
rename from signed/verifiers.go
rename to verify/verifiers.go
index cb09345..ac9ce34 100644
--- a/signed/verifiers.go
+++ b/verify/verifiers.go
@@ -1,4 +1,4 @@
-package signed
+package verify
import (
"github.com/flynn/go-tuf/data"
@@ -22,12 +22,6 @@
data.KeyTypeEd25519: ed25519Verifier{},
}
-// RegisterVerifier provides a convenience function for init() functions
-// to register additional verifiers or replace existing ones.
-func RegisterVerifier(name string, v Verifier) {
- Verifiers[name] = v
-}
-
type ed25519Verifier struct{}
func (ed25519Verifier) Verify(key, msg, sig []byte) error {
@@ -37,6 +31,6 @@
return nil
}
-func (ed25519Verifier) ValidKey(k []byte) {
+func (ed25519Verifier) ValidKey(k []byte) bool {
return len(k) == ed25519.PublicKeySize
}
diff --git a/signed/verify.go b/verify/verify.go
similarity index 66%
rename from signed/verify.go
rename to verify/verify.go
index 7a52aae..b0823c7 100644
--- a/signed/verify.go
+++ b/verify/verify.go
@@ -1,35 +1,23 @@
-package signed
+package verify
import (
"encoding/json"
- "errors"
"strings"
"time"
"github.com/flynn/go-tuf/data"
- "github.com/flynn/go-tuf/keys"
"github.com/tent/canonical-json-go"
"golang.org/x/crypto/ed25519"
)
-var (
- ErrMissingKey = errors.New("tuf: missing key")
- ErrNoSignatures = errors.New("tuf: data has no signatures")
- ErrInvalid = errors.New("tuf: signature verification failed")
- ErrWrongMethod = errors.New("tuf: invalid signature type")
- ErrUnknownRole = errors.New("tuf: unknown role")
- ErrRoleThreshold = errors.New("tuf: valid signatures did not meet threshold")
- ErrWrongType = errors.New("tuf: meta file has wrong type")
-)
-
type signedMeta struct {
Type string `json:"_type"`
Expires time.Time `json:"expires"`
Version int `json:"version"`
}
-func Verify(s *data.Signed, role string, minVersion int, db *keys.DB) error {
- if err := VerifySignatures(s, role, db); err != nil {
+func (db *DB) Verify(s *data.Signed, role string, minVersion int) error {
+ if err := db.VerifySignatures(s, role); err != nil {
return err
}
@@ -38,7 +26,7 @@
return err
}
if strings.ToLower(sm.Type) != strings.ToLower(role) {
- return ErrWrongType
+ return ErrWrongMetaType
}
if IsExpired(sm.Expires) {
return ErrExpired{sm.Expires}
@@ -54,7 +42,7 @@
return t.Sub(time.Now()) <= 0
}
-func VerifySignatures(s *data.Signed, role string, db *keys.DB) error {
+func (db *DB) VerifySignatures(s *data.Signed, role string) error {
if len(s.Signatures) == 0 {
return ErrNoSignatures
}
@@ -92,7 +80,7 @@
}
copy(sigBytes[:], sig.Signature)
- if err := Verifiers[sig.Method].Verify(key.Public[:], msg, sigBytes[:]); err != nil {
+ if err := Verifiers[sig.Method].Verify(key.Value.Public, msg, sigBytes[:]); err != nil {
return err
}
valid[sig.KeyID] = struct{}{}
@@ -104,23 +92,23 @@
return nil
}
-func Unmarshal(b []byte, v interface{}, role string, minVersion int, db *keys.DB) error {
+func Unmarshal(b []byte, v interface{}, role string, minVersion int, db *DB) error {
s := &data.Signed{}
if err := json.Unmarshal(b, s); err != nil {
return err
}
- if err := Verify(s, role, minVersion, db); err != nil {
+ if err := db.Verify(s, role, minVersion); err != nil {
return err
}
return json.Unmarshal(s.Signed, v)
}
-func UnmarshalTrusted(b []byte, v interface{}, role string, db *keys.DB) error {
+func UnmarshalTrusted(b []byte, v interface{}, role string, db *DB) error {
s := &data.Signed{}
if err := json.Unmarshal(b, s); err != nil {
return err
}
- if err := VerifySignatures(s, role, db); err != nil {
+ if err := db.VerifySignatures(s, role); err != nil {
return err
}
return json.Unmarshal(s.Signed, v)
diff --git a/signed/verify_test.go b/verify/verify_test.go
similarity index 85%
rename from signed/verify_test.go
rename to verify/verify_test.go
index 58a00dd..2e15dc0 100644
--- a/signed/verify_test.go
+++ b/verify/verify_test.go
@@ -1,11 +1,11 @@
-package signed
+package verify
import (
"testing"
"time"
"github.com/flynn/go-tuf/data"
- "github.com/flynn/go-tuf/keys"
+ "github.com/flynn/go-tuf/sign"
"golang.org/x/crypto/ed25519"
. "gopkg.in/check.v1"
@@ -76,8 +76,8 @@
{
name: "more than enough signatures",
mut: func(t *test) {
- k, _ := GenerateEd25519Key()
- Sign(t.s, k.Signer())
+ k, _ := sign.GenerateEd25519Key()
+ sign.Sign(t.s, k.Signer())
t.keys = append(t.keys, k.PublicData())
t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, k.PublicData().ID())
},
@@ -93,15 +93,15 @@
{
name: "unknown key",
mut: func(t *test) {
- k, _ := GenerateEd25519Key()
- Sign(t.s, k.Signer())
+ k, _ := sign.GenerateEd25519Key()
+ sign.Sign(t.s, k.Signer())
},
},
{
name: "unknown key below threshold",
mut: func(t *test) {
- k, _ := GenerateEd25519Key()
- Sign(t.s, k.Signer())
+ k, _ := sign.GenerateEd25519Key()
+ sign.Sign(t.s, k.Signer())
t.roles["root"].Threshold = 2
},
err: ErrRoleThreshold,
@@ -109,16 +109,16 @@
{
name: "unknown keys in db",
mut: func(t *test) {
- k, _ := GenerateEd25519Key()
- Sign(t.s, k.Signer())
+ k, _ := sign.GenerateEd25519Key()
+ sign.Sign(t.s, k.Signer())
t.keys = append(t.keys, k.PublicData())
},
},
{
name: "unknown keys in db below threshold",
mut: func(t *test) {
- k, _ := GenerateEd25519Key()
- Sign(t.s, k.Signer())
+ k, _ := sign.GenerateEd25519Key()
+ sign.Sign(t.s, k.Signer())
t.keys = append(t.keys, k.PublicData())
t.roles["root"].Threshold = 2
},
@@ -127,7 +127,7 @@
{
name: "wrong type",
typ: "bar",
- err: ErrWrongType,
+ err: ErrWrongMetaType,
},
{
name: "low version",
@@ -155,8 +155,8 @@
t.typ = t.role
}
if t.keys == nil && t.s == nil {
- k, _ := GenerateEd25519Key()
- t.s, _ = Marshal(&signedMeta{Type: t.typ, Version: t.ver, Expires: *t.exp}, k.Signer())
+ k, _ := sign.GenerateEd25519Key()
+ t.s, _ = sign.Marshal(&signedMeta{Type: t.typ, Version: t.ver, Expires: *t.exp}, k.Signer())
t.keys = []*data.Key{k.PublicData()}
}
if t.roles == nil {
@@ -171,7 +171,7 @@
t.mut(&t)
}
- db := keys.NewDB()
+ db := NewDB()
for _, k := range t.keys {
err := db.AddKey(k.ID(), k)
c.Assert(err, IsNil)
@@ -181,7 +181,7 @@
c.Assert(err, IsNil)
}
- err := Verify(t.s, t.role, minVer, db)
+ err := db.Verify(t.s, t.role, minVer)
if e, ok := t.err.(ErrExpired); ok {
assertErrExpired(c, err, e)
} else {