mirror of https://github.com/docker/docs.git
1032 lines
31 KiB
Go
1032 lines
31 KiB
Go
package handlers
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/docker/notary/trustmanager"
|
|
"github.com/docker/notary/tuf"
|
|
"github.com/docker/notary/tuf/data"
|
|
"github.com/docker/notary/tuf/keys"
|
|
"github.com/docker/notary/tuf/signed"
|
|
"github.com/docker/notary/tuf/testutils"
|
|
"github.com/docker/notary/tuf/validation"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/docker/notary/server/storage"
|
|
)
|
|
|
|
func copyTimestampKey(t *testing.T, fromKeyDB *keys.KeyDB,
|
|
toStore storage.MetaStore, gun string) {
|
|
|
|
role := fromKeyDB.GetRole(data.CanonicalTimestampRole)
|
|
assert.NotNil(t, role, "No timestamp role in the KeyDB")
|
|
assert.Len(t, role.KeyIDs, 1, fmt.Sprintf(
|
|
"Expected 1 timestamp key in timestamp role, got %d", len(role.KeyIDs)))
|
|
|
|
pubTimestampKey := fromKeyDB.GetKey(role.KeyIDs[0])
|
|
assert.NotNil(t, pubTimestampKey,
|
|
"Timestamp key specified by KeyDB role not in KeysDB")
|
|
|
|
err := toStore.SetKey(gun, data.CanonicalTimestampRole, pubTimestampKey.Algorithm(),
|
|
pubTimestampKey.Public())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// Returns a mapping of role name to `MetaUpdate` objects
|
|
func getUpdates(r, tg, sn, ts *data.Signed) (
|
|
root, targets, snapshot, timestamp storage.MetaUpdate, err error) {
|
|
|
|
rs, tgs, sns, tss, err := testutils.Serialize(r, tg, sn, ts)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
root = storage.MetaUpdate{
|
|
Role: data.CanonicalRootRole,
|
|
Version: 1,
|
|
Data: rs,
|
|
}
|
|
targets = storage.MetaUpdate{
|
|
Role: data.CanonicalTargetsRole,
|
|
Version: 1,
|
|
Data: tgs,
|
|
}
|
|
snapshot = storage.MetaUpdate{
|
|
Role: data.CanonicalSnapshotRole,
|
|
Version: 1,
|
|
Data: sns,
|
|
}
|
|
timestamp = storage.MetaUpdate{
|
|
Role: data.CanonicalTimestampRole,
|
|
Version: 1,
|
|
Data: tss,
|
|
}
|
|
return
|
|
}
|
|
|
|
func TestValidateEmptyNew(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateNoNewRoot(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store.UpdateCurrent("testGUN", root)
|
|
updates := []storage.MetaUpdate{targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateNoNewTargets(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store.UpdateCurrent("testGUN", targets)
|
|
updates := []storage.MetaUpdate{root, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateOnlySnapshot(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store.UpdateCurrent("testGUN", root)
|
|
store.UpdateCurrent("testGUN", targets)
|
|
|
|
updates := []storage.MetaUpdate{snapshot}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateOldRoot(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store.UpdateCurrent("testGUN", root)
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateRootRotation(t *testing.T) {
|
|
kdb, repo, crypto := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store.UpdateCurrent("testGUN", root)
|
|
|
|
oldRootRole := repo.Root.Signed.Roles["root"]
|
|
oldRootKey := repo.Root.Signed.Keys[oldRootRole.KeyIDs[0]]
|
|
|
|
rootKey, err := crypto.Create("root", data.ED25519Key)
|
|
assert.NoError(t, err)
|
|
rootRole, err := data.NewRole("root", 1, []string{rootKey.ID()}, nil, nil)
|
|
assert.NoError(t, err)
|
|
|
|
delete(repo.Root.Signed.Keys, oldRootRole.KeyIDs[0])
|
|
|
|
repo.Root.Signed.Roles["root"] = &rootRole.RootRole
|
|
repo.Root.Signed.Keys[rootKey.ID()] = rootKey
|
|
|
|
r, err = repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole))
|
|
assert.NoError(t, err)
|
|
err = signed.Sign(crypto, r, rootKey, oldRootKey)
|
|
assert.NoError(t, err)
|
|
|
|
rt, err := data.RootFromSigned(r)
|
|
assert.NoError(t, err)
|
|
repo.SetRoot(rt)
|
|
|
|
sn, err = repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole))
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err = getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(crypto, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateNoRoot(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
_, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrValidation{}, err)
|
|
}
|
|
|
|
func TestValidateSnapshotMissing(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, _, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadHierarchy{}, err)
|
|
}
|
|
|
|
func TestValidateSnapshotGenerateNoPrev(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
snapRole := kdb.GetRole(data.CanonicalSnapshotRole)
|
|
|
|
for _, id := range snapRole.KeyIDs {
|
|
k := kdb.GetKey(id)
|
|
assert.NotNil(t, k)
|
|
err := store.SetKey("testGUN", data.CanonicalSnapshotRole, k.Algorithm(), k.Public())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, _, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateSnapshotGenerateWithPrev(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
snapRole := kdb.GetRole(data.CanonicalSnapshotRole)
|
|
|
|
for _, id := range snapRole.KeyIDs {
|
|
k := kdb.GetKey(id)
|
|
assert.NotNil(t, k)
|
|
err := store.SetKey("testGUN", data.CanonicalSnapshotRole, k.Algorithm(), k.Public())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets}
|
|
|
|
// set the current snapshot in the store manually so we find it when generating
|
|
// the next version
|
|
store.UpdateCurrent("testGUN", snapshot)
|
|
|
|
prev, err := data.SnapshotFromSigned(sn)
|
|
assert.NoError(t, err)
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
updates, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
|
|
for _, u := range updates {
|
|
if u.Role == data.CanonicalSnapshotRole {
|
|
curr := &data.SignedSnapshot{}
|
|
err = json.Unmarshal(u.Data, curr)
|
|
assert.Equal(t, prev.Signed.Version+1, curr.Signed.Version)
|
|
assert.Equal(t, u.Version, curr.Signed.Version)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateSnapshotGeneratePrevCorrupt(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
snapRole := kdb.GetRole(data.CanonicalSnapshotRole)
|
|
|
|
for _, id := range snapRole.KeyIDs {
|
|
k := kdb.GetKey(id)
|
|
assert.NotNil(t, k)
|
|
err := store.SetKey("testGUN", data.CanonicalSnapshotRole, k.Algorithm(), k.Public())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets}
|
|
|
|
// corrupt the JSON structure of prev snapshot
|
|
snapshot.Data = snapshot.Data[1:]
|
|
// set the current snapshot in the store manually so we find it when generating
|
|
// the next version
|
|
store.UpdateCurrent("testGUN", snapshot)
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
updates, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestValidateSnapshotGenerateNoTargets(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
snapRole := kdb.GetRole(data.CanonicalSnapshotRole)
|
|
|
|
for _, id := range snapRole.KeyIDs {
|
|
k := kdb.GetKey(id)
|
|
assert.NotNil(t, k)
|
|
err := store.SetKey("testGUN", data.CanonicalSnapshotRole, k.Algorithm(), k.Public())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, _, _, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
updates, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestValidateSnapshotGenerate(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
snapRole := kdb.GetRole(data.CanonicalSnapshotRole)
|
|
|
|
for _, id := range snapRole.KeyIDs {
|
|
k := kdb.GetKey(id)
|
|
assert.NotNil(t, k)
|
|
err := store.SetKey("testGUN", data.CanonicalSnapshotRole, k.Algorithm(), k.Public())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, _, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{targets}
|
|
|
|
store.UpdateCurrent("testGUN", root)
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
updates, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// If there is no timestamp key in the store, validation fails. This could
|
|
// happen if pushing an existing repository from one server to another that
|
|
// does not have the repo.
|
|
func TestValidateRootNoTimestampKey(t *testing.T) {
|
|
_, oldRepo, cs := testutils.EmptyRepo()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(oldRepo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store := storage.NewMemStorage()
|
|
updates := []storage.MetaUpdate{root, targets, snapshot}
|
|
|
|
// sanity check - no timestamp keys for the GUN
|
|
_, _, err = store.GetKey("testGUN", data.CanonicalTimestampRole)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, &storage.ErrNoKey{}, err)
|
|
|
|
// do not copy the targets key to the storage, and try to update the root
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
|
|
// there should still be no timestamp keys - one should not have been
|
|
// created
|
|
_, _, err = store.GetKey("testGUN", data.CanonicalTimestampRole)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
// If the timestamp key in the store does not match the timestamp key in
|
|
// the root.json, validation fails. This could happen if pushing an existing
|
|
// repository from one server to another that had already initialized the same
|
|
// repo.
|
|
func TestValidateRootInvalidTimestampKey(t *testing.T) {
|
|
_, oldRepo, cs := testutils.EmptyRepo()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(oldRepo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store := storage.NewMemStorage()
|
|
updates := []storage.MetaUpdate{root, targets, snapshot}
|
|
|
|
key, err := trustmanager.GenerateECDSAKey(rand.Reader)
|
|
assert.NoError(t, err)
|
|
err = store.SetKey("testGUN", data.CanonicalRootRole, key.Algorithm(), key.Public())
|
|
assert.NoError(t, err)
|
|
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
}
|
|
|
|
// If the timestamp role has a threshold > 1, validation fails.
|
|
func TestValidateRootInvalidTimestampThreshold(t *testing.T) {
|
|
kdb, oldRepo, cs := testutils.EmptyRepo()
|
|
tsRole, ok := oldRepo.Root.Signed.Roles[data.CanonicalTimestampRole]
|
|
assert.True(t, ok)
|
|
tsRole.Threshold = 2
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(oldRepo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store := storage.NewMemStorage()
|
|
updates := []storage.MetaUpdate{root, targets, snapshot}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "timestamp role has invalid threshold")
|
|
}
|
|
|
|
// If any role has a threshold < 1, validation fails
|
|
func TestValidateRootInvalidZeroThreshold(t *testing.T) {
|
|
for _, role := range data.BaseRoles {
|
|
kdb, oldRepo, cs := testutils.EmptyRepo()
|
|
tsRole, ok := oldRepo.Root.Signed.Roles[role]
|
|
assert.True(t, ok)
|
|
tsRole.Threshold = 0
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(oldRepo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
store := storage.NewMemStorage()
|
|
updates := []storage.MetaUpdate{root, targets, snapshot}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "role has invalid threshold")
|
|
}
|
|
}
|
|
|
|
// ### Role missing negative tests ###
|
|
// These tests remove a role from the Root file and
|
|
// check for a validation.ErrBadRoot
|
|
func TestValidateRootRoleMissing(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
delete(repo.Root.Signed.Roles, "root")
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
}
|
|
|
|
func TestValidateTargetsRoleMissing(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
delete(repo.Root.Signed.Roles, "targets")
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
}
|
|
|
|
func TestValidateSnapshotRoleMissing(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
delete(repo.Root.Signed.Roles, "snapshot")
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
}
|
|
|
|
// ### End role missing negative tests ###
|
|
|
|
// ### Signature missing negative tests ###
|
|
func TestValidateRootSigMissing(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
delete(repo.Root.Signed.Roles, "snapshot")
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
|
|
r.Signatures = nil
|
|
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
}
|
|
|
|
func TestValidateTargetsSigMissing(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
|
|
tg.Signatures = nil
|
|
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadTargets{}, err)
|
|
}
|
|
|
|
func TestValidateSnapshotSigMissing(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
|
|
sn.Signatures = nil
|
|
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadSnapshot{}, err)
|
|
}
|
|
|
|
// ### End signature missing negative tests ###
|
|
|
|
// ### Corrupted metadata negative tests ###
|
|
func TestValidateRootCorrupt(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
// flip all the bits in the first byte
|
|
root.Data[0] = root.Data[0] ^ 0xff
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
}
|
|
|
|
func TestValidateTargetsCorrupt(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
// flip all the bits in the first byte
|
|
targets.Data[0] = targets.Data[0] ^ 0xff
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadTargets{}, err)
|
|
}
|
|
|
|
func TestValidateSnapshotCorrupt(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
// flip all the bits in the first byte
|
|
snapshot.Data[0] = snapshot.Data[0] ^ 0xff
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadSnapshot{}, err)
|
|
}
|
|
|
|
// ### End corrupted metadata negative tests ###
|
|
|
|
// ### Snapshot size mismatch negative tests ###
|
|
func TestValidateRootModifiedSize(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
|
|
// add another copy of the signature so the hash is different
|
|
r.Signatures = append(r.Signatures, r.Signatures[0])
|
|
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
// flip all the bits in the first byte
|
|
root.Data[0] = root.Data[0] ^ 0xff
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
}
|
|
|
|
func TestValidateTargetsModifiedSize(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
|
|
// add another copy of the signature so the hash is different
|
|
tg.Signatures = append(tg.Signatures, tg.Signatures[0])
|
|
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadSnapshot{}, err)
|
|
}
|
|
|
|
// ### End snapshot size mismatch negative tests ###
|
|
|
|
// ### Snapshot hash mismatch negative tests ###
|
|
func TestValidateRootModifiedHash(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
|
|
snap, err := data.SnapshotFromSigned(sn)
|
|
assert.NoError(t, err)
|
|
snap.Signed.Meta["root"].Hashes["sha256"][0] = snap.Signed.Meta["root"].Hashes["sha256"][0] ^ 0xff
|
|
|
|
sn, err = snap.ToSigned()
|
|
assert.NoError(t, err)
|
|
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadSnapshot{}, err)
|
|
}
|
|
|
|
func TestValidateTargetsModifiedHash(t *testing.T) {
|
|
kdb, repo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
r, tg, sn, ts, err := testutils.Sign(repo)
|
|
assert.NoError(t, err)
|
|
|
|
snap, err := data.SnapshotFromSigned(sn)
|
|
assert.NoError(t, err)
|
|
snap.Signed.Meta["targets"].Hashes["sha256"][0] = snap.Signed.Meta["targets"].Hashes["sha256"][0] ^ 0xff
|
|
|
|
sn, err = snap.ToSigned()
|
|
assert.NoError(t, err)
|
|
|
|
root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts)
|
|
assert.NoError(t, err)
|
|
|
|
updates := []storage.MetaUpdate{root, targets, snapshot, timestamp}
|
|
|
|
copyTimestampKey(t, kdb, store, "testGUN")
|
|
_, err = validateUpdate(cs, "testGUN", updates, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadSnapshot{}, err)
|
|
}
|
|
|
|
// ### End snapshot hash mismatch negative tests ###
|
|
|
|
// ### generateSnapshot tests ###
|
|
func TestGenerateSnapshotNoRole(t *testing.T) {
|
|
kdb := keys.NewDB()
|
|
_, err := generateSnapshot("gun", kdb, nil, nil)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadRoot{}, err)
|
|
}
|
|
|
|
func TestGenerateSnapshotNoKey(t *testing.T) {
|
|
kdb, _, _ := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
_, err := generateSnapshot("gun", kdb, nil, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, validation.ErrBadHierarchy{}, err)
|
|
}
|
|
|
|
// ### End generateSnapshot tests ###
|
|
|
|
// ### Target validation with delegations tests
|
|
func TestLoadTargetsFromStore(t *testing.T) {
|
|
_, repo, _ := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
st, err := repo.SignTargets(
|
|
data.CanonicalTargetsRole,
|
|
data.DefaultExpires(data.CanonicalTargetsRole),
|
|
)
|
|
assert.NoError(t, err)
|
|
|
|
tgs, err := json.Marshal(st)
|
|
assert.NoError(t, err)
|
|
update := storage.MetaUpdate{
|
|
Role: data.CanonicalTargetsRole,
|
|
Version: 1,
|
|
Data: tgs,
|
|
}
|
|
store.UpdateCurrent("gun", update)
|
|
|
|
generated := repo.Targets[data.CanonicalTargetsRole]
|
|
delete(repo.Targets, data.CanonicalTargetsRole)
|
|
_, ok := repo.Targets[data.CanonicalTargetsRole]
|
|
assert.False(t, ok)
|
|
|
|
err = loadTargetsFromStore("gun", data.CanonicalTargetsRole, repo, store)
|
|
assert.NoError(t, err)
|
|
loaded, ok := repo.Targets[data.CanonicalTargetsRole]
|
|
assert.True(t, ok)
|
|
assert.True(t, reflect.DeepEqual(generated.Signatures, loaded.Signatures))
|
|
assert.Len(t, loaded.Signed.Targets, 0)
|
|
assert.Equal(t, len(generated.Signed.Targets), len(loaded.Signed.Targets))
|
|
assert.Len(t, loaded.Signed.Delegations.Roles, 0)
|
|
assert.Equal(t, len(generated.Signed.Delegations.Roles), len(loaded.Signed.Delegations.Roles))
|
|
assert.Len(t, loaded.Signed.Delegations.Keys, 0)
|
|
assert.Equal(t, len(generated.Signed.Delegations.Keys), len(loaded.Signed.Delegations.Keys))
|
|
assert.True(t, generated.Signed.Expires.Equal(loaded.Signed.Expires))
|
|
assert.Equal(t, generated.Signed.Type, loaded.Signed.Type)
|
|
assert.Equal(t, generated.Signed.Version, loaded.Signed.Version)
|
|
}
|
|
|
|
func TestValidateTargetsLoadParent(t *testing.T) {
|
|
_, baseRepo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
k, err := cs.Create("targets/level1", data.ED25519Key)
|
|
assert.NoError(t, err)
|
|
r, err := data.NewRole("targets/level1", 1, []string{k.ID()}, []string{""}, nil)
|
|
assert.NoError(t, err)
|
|
|
|
err = baseRepo.UpdateDelegations(r, []data.PublicKey{k})
|
|
assert.NoError(t, err)
|
|
|
|
// no targets file is created for the new delegations, so force one
|
|
baseRepo.InitTargets("targets/level1")
|
|
|
|
// we're not going to validate things loaded from storage, so no need
|
|
// to sign the base targets, just Marshal it and set it into storage
|
|
tgtsJSON, err := json.Marshal(baseRepo.Targets["targets"])
|
|
assert.NoError(t, err)
|
|
update := storage.MetaUpdate{
|
|
Role: data.CanonicalTargetsRole,
|
|
Version: 1,
|
|
Data: tgtsJSON,
|
|
}
|
|
store.UpdateCurrent("gun", update)
|
|
|
|
// generate the update object we're doing to use to call loadAndValidateTargets
|
|
del, err := baseRepo.SignTargets("targets/level1", data.DefaultExpires(data.CanonicalTargetsRole))
|
|
assert.NoError(t, err)
|
|
delJSON, err := json.Marshal(del)
|
|
assert.NoError(t, err)
|
|
|
|
delUpdate := storage.MetaUpdate{
|
|
Role: "targets/level1",
|
|
Version: 1,
|
|
Data: delJSON,
|
|
}
|
|
|
|
roles := map[string]storage.MetaUpdate{"targets/level1": delUpdate}
|
|
|
|
kdb := keys.NewDB()
|
|
valRepo := tuf.NewRepo(kdb, nil)
|
|
valRepo.SetRoot(baseRepo.Root)
|
|
|
|
updates, err := loadAndValidateTargets("gun", valRepo, roles, kdb, store)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, updates, 1)
|
|
assert.Equal(t, "targets/level1", updates[0].Role)
|
|
assert.Equal(t, delJSON, updates[0].Data)
|
|
}
|
|
|
|
func TestValidateTargetsParentInUpdate(t *testing.T) {
|
|
_, baseRepo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
k, err := cs.Create("targets/level1", data.ED25519Key)
|
|
assert.NoError(t, err)
|
|
r, err := data.NewRole("targets/level1", 1, []string{k.ID()}, []string{""}, nil)
|
|
assert.NoError(t, err)
|
|
|
|
baseRepo.UpdateDelegations(r, []data.PublicKey{k})
|
|
|
|
// no targets file is created for the new delegations, so force one
|
|
baseRepo.InitTargets("targets/level1")
|
|
|
|
targets, err := baseRepo.SignTargets("targets", data.DefaultExpires(data.CanonicalTargetsRole))
|
|
|
|
tgtsJSON, err := json.Marshal(targets)
|
|
assert.NoError(t, err)
|
|
update := storage.MetaUpdate{
|
|
Role: data.CanonicalTargetsRole,
|
|
Version: 1,
|
|
Data: tgtsJSON,
|
|
}
|
|
store.UpdateCurrent("gun", update)
|
|
|
|
del, err := baseRepo.SignTargets("targets/level1", data.DefaultExpires(data.CanonicalTargetsRole))
|
|
assert.NoError(t, err)
|
|
delJSON, err := json.Marshal(del)
|
|
assert.NoError(t, err)
|
|
|
|
delUpdate := storage.MetaUpdate{
|
|
Role: "targets/level1",
|
|
Version: 1,
|
|
Data: delJSON,
|
|
}
|
|
|
|
roles := map[string]storage.MetaUpdate{
|
|
"targets/level1": delUpdate,
|
|
"targets": update,
|
|
}
|
|
|
|
kdb := keys.NewDB()
|
|
valRepo := tuf.NewRepo(kdb, nil)
|
|
valRepo.SetRoot(baseRepo.Root)
|
|
|
|
// because we sort the roles, the list of returned updates
|
|
// will contain shallower roles first, in this case "targets",
|
|
// and then "targets/level1"
|
|
updates, err := loadAndValidateTargets("gun", valRepo, roles, kdb, store)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, updates, 2)
|
|
assert.Equal(t, "targets", updates[0].Role)
|
|
assert.Equal(t, tgtsJSON, updates[0].Data)
|
|
assert.Equal(t, "targets/level1", updates[1].Role)
|
|
assert.Equal(t, delJSON, updates[1].Data)
|
|
}
|
|
|
|
func TestValidateTargetsParentNotFound(t *testing.T) {
|
|
_, baseRepo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
k, err := cs.Create("targets/level1", data.ED25519Key)
|
|
assert.NoError(t, err)
|
|
r, err := data.NewRole("targets/level1", 1, []string{k.ID()}, []string{""}, nil)
|
|
assert.NoError(t, err)
|
|
|
|
baseRepo.UpdateDelegations(r, []data.PublicKey{k})
|
|
|
|
// no targets file is created for the new delegations, so force one
|
|
baseRepo.InitTargets("targets/level1")
|
|
|
|
// generate the update object we're doing to use to call loadAndValidateTargets
|
|
del, err := baseRepo.SignTargets("targets/level1", data.DefaultExpires(data.CanonicalTargetsRole))
|
|
assert.NoError(t, err)
|
|
delJSON, err := json.Marshal(del)
|
|
assert.NoError(t, err)
|
|
|
|
delUpdate := storage.MetaUpdate{
|
|
Role: "targets/level1",
|
|
Version: 1,
|
|
Data: delJSON,
|
|
}
|
|
|
|
roles := map[string]storage.MetaUpdate{"targets/level1": delUpdate}
|
|
|
|
kdb := keys.NewDB()
|
|
valRepo := tuf.NewRepo(kdb, nil)
|
|
valRepo.SetRoot(baseRepo.Root)
|
|
|
|
_, err = loadAndValidateTargets("gun", valRepo, roles, kdb, store)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, storage.ErrNotFound{}, err)
|
|
}
|
|
|
|
func TestValidateTargetsRoleNotInParent(t *testing.T) {
|
|
kdb, baseRepo, cs := testutils.EmptyRepo()
|
|
store := storage.NewMemStorage()
|
|
|
|
k, err := cs.Create("targets/level1", data.ED25519Key)
|
|
assert.NoError(t, err)
|
|
r, err := data.NewRole("targets/level1", 1, []string{k.ID()}, []string{""}, nil)
|
|
assert.NoError(t, err)
|
|
|
|
kdb.AddKey(k)
|
|
err = kdb.AddRole(r)
|
|
assert.NoError(t, err)
|
|
|
|
baseRepo.InitTargets("targets/level1")
|
|
|
|
targets, err := baseRepo.SignTargets("targets", data.DefaultExpires(data.CanonicalTargetsRole))
|
|
|
|
tgtsJSON, err := json.Marshal(targets)
|
|
assert.NoError(t, err)
|
|
update := storage.MetaUpdate{
|
|
Role: data.CanonicalTargetsRole,
|
|
Version: 1,
|
|
Data: tgtsJSON,
|
|
}
|
|
store.UpdateCurrent("gun", update)
|
|
|
|
del, err := baseRepo.SignTargets("targets/level1", data.DefaultExpires(data.CanonicalTargetsRole))
|
|
assert.NoError(t, err)
|
|
delJSON, err := json.Marshal(del)
|
|
assert.NoError(t, err)
|
|
|
|
delUpdate := storage.MetaUpdate{
|
|
Role: "targets/level1",
|
|
Version: 1,
|
|
Data: delJSON,
|
|
}
|
|
|
|
roles := map[string]storage.MetaUpdate{
|
|
"targets/level1": delUpdate,
|
|
"targets": update,
|
|
}
|
|
|
|
kdb = keys.NewDB()
|
|
valRepo := tuf.NewRepo(kdb, nil)
|
|
valRepo.SetRoot(baseRepo.Root)
|
|
|
|
// because we sort the roles, the list of returned updates
|
|
// will contain shallower roles first, in this case "targets",
|
|
// and then "targets/level1"
|
|
updates, err := loadAndValidateTargets("gun", valRepo, roles, kdb, store)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, updates, 1)
|
|
assert.Equal(t, "targets", updates[0].Role)
|
|
assert.Equal(t, tgtsJSON, updates[0].Data)
|
|
}
|
|
|
|
// ### End target validation with delegations tests
|