updating gotuf dep

Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
David Lawrence 2015-07-20 11:23:07 -07:00
parent 9b604d0a6c
commit 0e2fe2137b
11 changed files with 283 additions and 66 deletions

2
Godeps/Godeps.json generated
View File

@ -63,7 +63,7 @@
},
{
"ImportPath": "github.com/endophage/gotuf",
"Rev": "9640c9b3f2ff0ba75baf7d1a57632e16cb78d5e6"
"Rev": "b1fb060403583500ba06b11e35130b7c16c74c92"
},
{
"ImportPath": "github.com/go-sql-driver/mysql",

View File

@ -1,2 +1,3 @@
/db/
*.bkp
*.swp

View File

@ -174,25 +174,7 @@ func (c *Client) downloadRoot() error {
logrus.Debug("using cached root")
s = old
}
// this will confirm that the root has been signed by the old root role
// as c.keysDB contains the root keys we bootstrapped with.
// Still need to determine if there has been a root key update and
// confirm signature with new root key
err = signed.Verify(s, role, version, c.keysDB)
if err != nil {
logrus.Debug("root did not verify with existing keys")
return err
}
// This will cause keyDB to get updated, overwriting any keyIDs associated
// with the roles in root.json
c.local.SetRoot(s)
// verify again now that the old keys have been replaced with the new keys.
// TODO(endophage): be more intelligent and only re-verify if we detect
// there has been a change in root keys
err = signed.Verify(s, role, version, c.keysDB)
if err != nil {
logrus.Debug("root did not verify with new keys")
if err := c.verifyRoot(role, s, version); err != nil {
return err
}
if download {
@ -205,6 +187,39 @@ func (c *Client) downloadRoot() error {
return nil
}
func (c Client) verifyRoot(role string, s *data.Signed, minVersion int) error {
// this will confirm that the root has been signed by the old root role
// as c.keysDB contains the root keys we bootstrapped with.
// Still need to determine if there has been a root key update and
// confirm signature with new root key
logrus.Debug("verifying root with existing keys")
err := signed.Verify(s, role, minVersion, c.keysDB)
if err != nil {
logrus.Debug("root did not verify with existing keys")
return err
}
// This will cause keyDB to get updated, overwriting any keyIDs associated
// with the roles in root.json
logrus.Debug("updating known root roles and keys")
err = c.local.SetRoot(s)
if err != nil {
logrus.Error(err.Error())
return err
}
// verify again now that the old keys have been replaced with the new keys.
// TODO(endophage): be more intelligent and only re-verify if we detect
// there has been a change in root keys
logrus.Debug("verifying root with updated keys")
err = signed.Verify(s, role, minVersion, c.keysDB)
if err != nil {
logrus.Debug("root did not verify with new keys")
return err
}
logrus.Debug("successfully verified root")
return nil
}
// downloadTimestamp is responsible for downloading the timestamp.json
func (c *Client) downloadTimestamp() error {
logrus.Debug("downloadTimestamp")
@ -228,9 +243,13 @@ func (c *Client) downloadTimestamp() error {
}
// unlike root, targets and snapshot, always try and download timestamps
// from remote, only using the cache one if we couldn't reach remote.
logrus.Debug("Downloading timestamp")
raw, err := c.remote.GetMeta(role, maxSize)
var s *data.Signed
if err != nil || len(raw) == 0 {
if err, ok := err.(*store.ErrMetaNotFound); ok {
return err
}
s = old
} else {
download = true
@ -244,6 +263,7 @@ func (c *Client) downloadTimestamp() error {
if err != nil {
return err
}
logrus.Debug("successfully verified timestamp")
if download {
c.cache.SetMeta(role, raw)
}
@ -314,6 +334,7 @@ func (c *Client) downloadSnapshot() error {
if err != nil {
return err
}
logrus.Debug("successfully verified snapshot")
c.local.SetSnapshot(s)
if download {
err = c.cache.SetMeta(role, raw)
@ -349,24 +370,24 @@ func (c *Client) downloadTargets(role string) error {
return nil
}
func (c Client) GetTargetsFile(roleName string, keyIDs []string, snapshotMeta data.Files, consistent bool, threshold int) (*data.Signed, error) {
func (c Client) GetTargetsFile(role string, keyIDs []string, snapshotMeta data.Files, consistent bool, threshold int) (*data.Signed, error) {
// require role exists in snapshots
roleMeta, ok := snapshotMeta[roleName]
roleMeta, ok := snapshotMeta[role]
if !ok {
return nil, fmt.Errorf("Snapshot does not contain target role")
}
expectedSha256, ok := snapshotMeta[roleName].Hashes["sha256"]
expectedSha256, ok := snapshotMeta[role].Hashes["sha256"]
if !ok {
return nil, fmt.Errorf("Sha256 is currently the only hash supported by this client. No Sha256 found for targets role %s", roleName)
return nil, fmt.Errorf("Sha256 is currently the only hash supported by this client. No Sha256 found for targets role %s", role)
}
// try to get meta file from content addressed cache
var download bool
old := &data.Signed{}
version := 0
raw, err := c.cache.GetMeta(roleName, roleMeta.Length)
raw, err := c.cache.GetMeta(role, roleMeta.Length)
if err != nil || raw == nil {
logrus.Debugf("Couldn't not find cached %s, must download", roleName)
logrus.Debugf("Couldn't not find cached %s, must download", role)
download = true
} else {
// file may have been tampered with on disk. Always check the hash!
@ -390,11 +411,11 @@ func (c Client) GetTargetsFile(roleName string, keyIDs []string, snapshotMeta da
var s *data.Signed
if download {
rolePath, err := c.RoleTargetsPath(roleName, hex.EncodeToString(expectedSha256), consistent)
rolePath, err := c.RoleTargetsPath(role, hex.EncodeToString(expectedSha256), consistent)
if err != nil {
return nil, err
}
raw, err = c.remote.GetMeta(rolePath, snapshotMeta[roleName].Length)
raw, err = c.remote.GetMeta(rolePath, snapshotMeta[role].Length)
if err != nil {
return nil, err
}
@ -405,17 +426,18 @@ func (c Client) GetTargetsFile(roleName string, keyIDs []string, snapshotMeta da
return nil, err
}
} else {
logrus.Debug("using cached ", roleName)
logrus.Debug("using cached ", role)
s = old
}
err = signed.Verify(s, roleName, version, c.keysDB)
err = signed.Verify(s, role, version, c.keysDB)
if err != nil {
return nil, err
}
logrus.Debugf("successfully verified %s", role)
if download {
// if we error when setting meta, we should continue.
err = c.cache.SetMeta(roleName, raw)
err = c.cache.SetMeta(role, raw)
if err != nil {
logrus.Errorf("Failed to write snapshot to local cache: %s", err.Error())
}
@ -425,19 +447,19 @@ func (c Client) GetTargetsFile(roleName string, keyIDs []string, snapshotMeta da
// RoleTargetsPath generates the appropriate filename for the targets file,
// based on whether the repo is marked as consistent.
func (c Client) RoleTargetsPath(roleName string, hashSha256 string, consistent bool) (string, error) {
func (c Client) RoleTargetsPath(role string, hashSha256 string, consistent bool) (string, error) {
if consistent {
dir := filepath.Dir(roleName)
if strings.Contains(roleName, "/") {
lastSlashIdx := strings.LastIndex(roleName, "/")
roleName = roleName[lastSlashIdx+1:]
dir := filepath.Dir(role)
if strings.Contains(role, "/") {
lastSlashIdx := strings.LastIndex(role, "/")
role = role[lastSlashIdx+1:]
}
roleName = path.Join(
role = path.Join(
dir,
fmt.Sprintf("%s.%s.json", hashSha256, roleName),
fmt.Sprintf("%s.%s.json", hashSha256, role),
)
}
return roleName, nil
return role, nil
}
// TargetMeta ensures the repo is up to date, downloading the minimum

View File

@ -0,0 +1,190 @@
package client
import (
"testing"
"github.com/Sirupsen/logrus"
tuf "github.com/endophage/gotuf"
"github.com/stretchr/testify/assert"
"github.com/endophage/gotuf/data"
"github.com/endophage/gotuf/keys"
"github.com/endophage/gotuf/signed"
"github.com/endophage/gotuf/store"
)
func TestRotation(t *testing.T) {
kdb := keys.NewDB()
signer := signed.NewEd25519()
repo := tuf.NewTufRepo(kdb, signer)
remote := store.NewMemoryStore(nil, nil)
cache := store.NewMemoryStore(nil, nil)
// Generate initial root key and role and add to key DB
rootKey, err := signer.Create("root", data.ED25519Key)
assert.NoError(t, err, "Error creating root key")
rootRole, err := data.NewRole("root", 1, []string{rootKey.ID()}, nil, nil)
assert.NoError(t, err, "Error creating root role")
kdb.AddKey(rootKey)
err = kdb.AddRole(rootRole)
assert.NoError(t, err, "Error adding root role to db")
// Generate new key and role. These will appear in the root.json
// but will not be added to the keyDB.
replacementKey, err := signer.Create("root", data.ED25519Key)
assert.NoError(t, err, "Error creating replacement root key")
replacementRole, err := data.NewRole("root", 1, []string{replacementKey.ID()}, nil, nil)
assert.NoError(t, err, "Error creating replacement root role")
// Generate a new root with the replacement key and role
testRoot, err := data.NewRoot(
map[string]data.PublicKey{replacementKey.ID(): replacementKey},
map[string]*data.RootRole{"root": &replacementRole.RootRole},
false,
)
assert.NoError(t, err, "Failed to create new root")
// Sign testRoot with both old and new keys
signedRoot, err := testRoot.ToSigned()
err = signed.Sign(signer, signedRoot, rootKey, replacementKey)
assert.NoError(t, err, "Failed to sign root")
var origKeySig bool
var replKeySig bool
for _, sig := range signedRoot.Signatures {
if sig.KeyID == rootKey.ID() {
origKeySig = true
} else if sig.KeyID == replacementKey.ID() {
replKeySig = true
}
}
assert.True(t, origKeySig, "Original root key signature not present")
assert.True(t, replKeySig, "Replacement root key signature not present")
client := NewClient(repo, remote, kdb, cache)
err = client.verifyRoot("root", signedRoot, 0)
assert.NoError(t, err, "Failed to verify key rotated root")
}
func TestRotationNewSigMissing(t *testing.T) {
logrus.SetLevel(logrus.DebugLevel)
kdb := keys.NewDB()
signer := signed.NewEd25519()
repo := tuf.NewTufRepo(kdb, signer)
remote := store.NewMemoryStore(nil, nil)
cache := store.NewMemoryStore(nil, nil)
// Generate initial root key and role and add to key DB
rootKey, err := signer.Create("root", data.ED25519Key)
assert.NoError(t, err, "Error creating root key")
rootRole, err := data.NewRole("root", 1, []string{rootKey.ID()}, nil, nil)
assert.NoError(t, err, "Error creating root role")
kdb.AddKey(rootKey)
err = kdb.AddRole(rootRole)
assert.NoError(t, err, "Error adding root role to db")
// Generate new key and role. These will appear in the root.json
// but will not be added to the keyDB.
replacementKey, err := signer.Create("root", data.ED25519Key)
assert.NoError(t, err, "Error creating replacement root key")
replacementRole, err := data.NewRole("root", 1, []string{replacementKey.ID()}, nil, nil)
assert.NoError(t, err, "Error creating replacement root role")
assert.NotEqual(t, rootKey.ID(), replacementKey.ID(), "Key IDs are the same")
// Generate a new root with the replacement key and role
testRoot, err := data.NewRoot(
map[string]data.PublicKey{replacementKey.ID(): replacementKey},
map[string]*data.RootRole{"root": &replacementRole.RootRole},
false,
)
assert.NoError(t, err, "Failed to create new root")
_, ok := testRoot.Signed.Keys[rootKey.ID()]
assert.False(t, ok, "Old root key appeared in test root")
// Sign testRoot with both old and new keys
signedRoot, err := testRoot.ToSigned()
err = signed.Sign(signer, signedRoot, rootKey)
assert.NoError(t, err, "Failed to sign root")
var origKeySig bool
var replKeySig bool
for _, sig := range signedRoot.Signatures {
if sig.KeyID == rootKey.ID() {
origKeySig = true
} else if sig.KeyID == replacementKey.ID() {
replKeySig = true
}
}
assert.True(t, origKeySig, "Original root key signature not present")
assert.False(t, replKeySig, "Replacement root key signature was present and shouldn't be")
client := NewClient(repo, remote, kdb, cache)
err = client.verifyRoot("root", signedRoot, 0)
assert.Error(t, err, "Should have errored on verify as replacement signature was missing.")
}
func TestRotationOldSigMissing(t *testing.T) {
logrus.SetLevel(logrus.DebugLevel)
kdb := keys.NewDB()
signer := signed.NewEd25519()
repo := tuf.NewTufRepo(kdb, signer)
remote := store.NewMemoryStore(nil, nil)
cache := store.NewMemoryStore(nil, nil)
// Generate initial root key and role and add to key DB
rootKey, err := signer.Create("root", data.ED25519Key)
assert.NoError(t, err, "Error creating root key")
rootRole, err := data.NewRole("root", 1, []string{rootKey.ID()}, nil, nil)
assert.NoError(t, err, "Error creating root role")
kdb.AddKey(rootKey)
err = kdb.AddRole(rootRole)
assert.NoError(t, err, "Error adding root role to db")
// Generate new key and role. These will appear in the root.json
// but will not be added to the keyDB.
replacementKey, err := signer.Create("root", data.ED25519Key)
assert.NoError(t, err, "Error creating replacement root key")
replacementRole, err := data.NewRole("root", 1, []string{replacementKey.ID()}, nil, nil)
assert.NoError(t, err, "Error creating replacement root role")
assert.NotEqual(t, rootKey.ID(), replacementKey.ID(), "Key IDs are the same")
// Generate a new root with the replacement key and role
testRoot, err := data.NewRoot(
map[string]data.PublicKey{replacementKey.ID(): replacementKey},
map[string]*data.RootRole{"root": &replacementRole.RootRole},
false,
)
assert.NoError(t, err, "Failed to create new root")
_, ok := testRoot.Signed.Keys[rootKey.ID()]
assert.False(t, ok, "Old root key appeared in test root")
// Sign testRoot with both old and new keys
signedRoot, err := testRoot.ToSigned()
err = signed.Sign(signer, signedRoot, replacementKey)
assert.NoError(t, err, "Failed to sign root")
var origKeySig bool
var replKeySig bool
for _, sig := range signedRoot.Signatures {
if sig.KeyID == rootKey.ID() {
origKeySig = true
} else if sig.KeyID == replacementKey.ID() {
replKeySig = true
}
}
assert.False(t, origKeySig, "Original root key signature was present and shouldn't be")
assert.True(t, replKeySig, "Replacement root key signature was not present")
client := NewClient(repo, remote, kdb, cache)
err = client.verifyRoot("root", signedRoot, 0)
assert.Error(t, err, "Should have errored on verify as replacement signature was missing.")
}

View File

@ -3,6 +3,7 @@ package signed
import (
"encoding/json"
"errors"
"strings"
"time"
"github.com/Sirupsen/logrus"
@ -115,6 +116,11 @@ func VerifySignatures(s *data.Signed, role string, db *keys.KeyDB) error {
return ErrUnknownRole
}
if roleData.Threshold < 1 {
return ErrRoleThreshold
}
logrus.Debugf("%s role has key IDs: %s", role, strings.Join(roleData.KeyIDs, ","))
var decoded map[string]interface{}
if err := json.Unmarshal(s.Signed, &decoded); err != nil {
return err
@ -126,6 +132,7 @@ func VerifySignatures(s *data.Signed, role string, db *keys.KeyDB) error {
valid := make(map[string]struct{})
for _, sig := range s.Signatures {
logrus.Debug("verifying signature for key ID: ", sig.KeyID)
if !roleData.ValidKey(sig.KeyID) {
logrus.Debugf("continuing b/c keyid was invalid: %s for roledata %s\n", sig.KeyID, roleData)
continue

View File

@ -1,13 +1,7 @@
package store
import (
"fmt"
)
type ErrMetaNotFound struct {
role string
}
type ErrMetaNotFound struct{}
func (err ErrMetaNotFound) Error() string {
return fmt.Sprintf("no metadata for %s", err.role)
return "no trust data available"
}

View File

@ -67,8 +67,9 @@ func (s HTTPStore) GetMeta(name string, size int64) ([]byte, error) {
return nil, err
}
defer resp.Body.Close()
logrus.Debugf("%d when retrieving metadata for %s", resp.StatusCode, name)
if resp.StatusCode == http.StatusNotFound {
return nil, &ErrMetaNotFound{role: name}
return nil, &ErrMetaNotFound{}
}
b := io.LimitReader(resp.Body, int64(size))
body, err := ioutil.ReadAll(b)

View File

@ -47,7 +47,7 @@ func TestHTTPStoreGetMeta(t *testing.T) {
rootPem := "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEArvqUPYb6JJROPJQglPTj\n5uDrsxQKl34Mo+3pSlBVuD6puE4lDnG649a2YksJy+C8ZIPJgokn5w+C3alh+dMe\nzbdWHHxrY1h9CLpYz5cbMlE16303ubkt1rvwDqEezG0HDBzPaKj4oP9YJ9x7wbsq\ndvFcy+Qc3wWd7UWcieo6E0ihbJkYcY8chRXVLg1rL7EfZ+e3bq5+ojA2ECM5JqzZ\nzgDpqCv5hTCYYZp72MZcG7dfSPAHrcSGIrwg7whzz2UsEtCOpsJTuCl96FPN7kAu\n4w/WyM3+SPzzr4/RQXuY1SrLCFD8ebM2zHt/3ATLhPnGmyG5I0RGYoegFaZ2AViw\nlqZDOYnBtgDvKP0zakMtFMbkh2XuNBUBO7Sjs0YcZMjLkh9gYUHL1yWS3Aqus1Lw\nlI0gHS22oyGObVBWkZEgk/Foy08sECLGao+5VvhmGpfVuiz9OKFUmtPVjWzRE4ng\niekEu4drSxpH41inLGSvdByDWLpcTvWQI9nkgclh3AT/AgMBAAE=\n-----END PUBLIC KEY-----"
k := data.NewPublicKey("RSA", []byte(rootPem))
sigBytes, err := hex.DecodeString(p.Signatures[0].Signature.String())
sigBytes := p.Signatures[0].Signature
if err != nil {
t.Fatal(err)
}

View File

@ -2,12 +2,15 @@ package store
import (
"bytes"
"fmt"
"io"
"github.com/endophage/gotuf/data"
"github.com/endophage/gotuf/errors"
"github.com/endophage/gotuf/utils"
)
func NewMemoryStore(meta map[string][]byte, files map[string][]byte) LocalStore {
func NewMemoryStore(meta map[string][]byte, files map[string][]byte) *memoryStore {
if meta == nil {
meta = make(map[string][]byte)
}
@ -43,8 +46,8 @@ func (m *memoryStore) SetMultiMeta(metas map[string][]byte) error {
return nil
}
func (m *memoryStore) AddBlob(path string, meta data.FileMeta) {
func (m *memoryStore) GetTarget(path string) (io.ReadCloser, error) {
return &utils.NoopCloser{Reader: bytes.NewReader(m.files[path])}, nil
}
func (m *memoryStore) WalkStagedTargets(paths []string, targetsFn targetsWalkFunc) error {
@ -81,18 +84,6 @@ func (m *memoryStore) Commit(map[string][]byte, bool, map[string]data.Hashes) er
return nil
}
func (m *memoryStore) GetKeys(role string) ([]data.PrivateKey, error) {
return m.keys[role], nil
}
func (m *memoryStore) SaveKey(role string, key data.PrivateKey) error {
if _, ok := m.keys[role]; !ok {
m.keys[role] = make([]data.PrivateKey, 0)
}
m.keys[role] = append(m.keys[role], key)
return nil
}
func (m *memoryStore) Clean() error {
return nil
func (m *memoryStore) GetKey(role string) ([]byte, error) {
return nil, fmt.Errorf("GetKey is not implemented for the memoryStore")
}

View File

@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
"time"
"github.com/Sirupsen/logrus"
@ -263,9 +264,11 @@ func (tr *TufRepo) SetRoot(s *data.Signed) error {
return err
}
for _, key := range r.Signed.Keys {
logrus.Debug("Adding key ", key.ID())
tr.keysDB.AddKey(key)
}
for roleName, role := range r.Signed.Roles {
logrus.Debugf("Adding role %s with keys %s", roleName, strings.Join(role.KeyIDs, ","))
baseRole, err := data.NewRole(
roleName,
role.Threshold,

View File

@ -70,3 +70,11 @@ func FileExists(path string) bool {
_, err := os.Stat(path)
return os.IsNotExist(err)
}
type NoopCloser struct {
io.Reader
}
func (nc *NoopCloser) Close() error {
return nil
}