docs/tuf/tuf_test.go

1045 lines
34 KiB
Go

package tuf
import (
"crypto/sha256"
"encoding/json"
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"github.com/docker/notary/tuf/data"
"github.com/docker/notary/tuf/signed"
"github.com/stretchr/testify/assert"
)
var testGUN = "gun"
func initRepo(t *testing.T, cryptoService signed.CryptoService) *Repo {
rootKey, err := cryptoService.Create("root", testGUN, data.ED25519Key)
assert.NoError(t, err)
targetsKey, err := cryptoService.Create("targets", testGUN, data.ED25519Key)
assert.NoError(t, err)
snapshotKey, err := cryptoService.Create("snapshot", testGUN, data.ED25519Key)
assert.NoError(t, err)
timestampKey, err := cryptoService.Create("timestamp", testGUN, data.ED25519Key)
assert.NoError(t, err)
rootRole := data.NewBaseRole(
data.CanonicalRootRole,
1,
rootKey,
)
targetsRole := data.NewBaseRole(
data.CanonicalTargetsRole,
1,
targetsKey,
)
snapshotRole := data.NewBaseRole(
data.CanonicalSnapshotRole,
1,
snapshotKey,
)
timestampRole := data.NewBaseRole(
data.CanonicalTimestampRole,
1,
timestampKey,
)
repo := NewRepo(cryptoService)
err = repo.InitRoot(rootRole, timestampRole, snapshotRole, targetsRole, false)
assert.NoError(t, err)
_, err = repo.InitTargets(data.CanonicalTargetsRole)
assert.NoError(t, err)
err = repo.InitSnapshot()
assert.NoError(t, err)
err = repo.InitTimestamp()
assert.NoError(t, err)
return repo
}
func TestInitSnapshotNoTargets(t *testing.T) {
cs := signed.NewEd25519()
repo := initRepo(t, cs)
repo.Targets = make(map[string]*data.SignedTargets)
err := repo.InitSnapshot()
assert.Error(t, err)
assert.IsType(t, ErrNotLoaded{}, err)
}
func writeRepo(t *testing.T, dir string, repo *Repo) {
err := os.MkdirAll(dir, 0755)
assert.NoError(t, err)
signedRoot, err := repo.SignRoot(data.DefaultExpires("root"))
assert.NoError(t, err)
rootJSON, _ := json.Marshal(signedRoot)
ioutil.WriteFile(dir+"/root.json", rootJSON, 0755)
for r := range repo.Targets {
signedTargets, err := repo.SignTargets(r, data.DefaultExpires("targets"))
assert.NoError(t, err)
targetsJSON, _ := json.Marshal(signedTargets)
p := path.Join(dir, r+".json")
parentDir := filepath.Dir(p)
os.MkdirAll(parentDir, 0755)
ioutil.WriteFile(p, targetsJSON, 0755)
}
signedSnapshot, err := repo.SignSnapshot(data.DefaultExpires("snapshot"))
assert.NoError(t, err)
snapshotJSON, _ := json.Marshal(signedSnapshot)
ioutil.WriteFile(dir+"/snapshot.json", snapshotJSON, 0755)
signedTimestamp, err := repo.SignTimestamp(data.DefaultExpires("timestamp"))
assert.NoError(t, err)
timestampJSON, _ := json.Marshal(signedTimestamp)
ioutil.WriteFile(dir+"/timestamp.json", timestampJSON, 0755)
}
func TestInitRepo(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
writeRepo(t, "/tmp/tufrepo", repo)
}
func TestUpdateDelegations(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"test"}, []string{}, false)
assert.NoError(t, err)
// no empty metadata is created for this role
_, ok := repo.Targets["targets/test"]
assert.False(t, ok, "no empty targets file should be created for deepest delegation")
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 1)
assert.Len(t, r.Signed.Delegations.Keys, 1)
keyIDs := r.Signed.Delegations.Roles[0].KeyIDs
assert.Len(t, keyIDs, 1)
assert.Equal(t, testKey.ID(), keyIDs[0])
testDeepKey, err := ed25519.Create("targets/test/deep", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test/deep", []data.PublicKey{testDeepKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test/deep", []string{"test/deep"}, []string{}, false)
assert.NoError(t, err)
// this metadata didn't exist before, but creating targets/test/deep created
// the targets/test metadata
r, ok = repo.Targets["targets/test"]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 1)
assert.Len(t, r.Signed.Delegations.Keys, 1)
keyIDs = r.Signed.Delegations.Roles[0].KeyIDs
assert.Len(t, keyIDs, 1)
assert.Equal(t, testDeepKey.ID(), keyIDs[0])
assert.True(t, r.Dirty)
// no empty delegation metadata is created for targets/test/deep
_, ok = repo.Targets["targets/test/deep"]
assert.False(t, ok, "no empty targets file should be created for deepest delegation")
}
func TestUpdateDelegationsParentMissing(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testDeepKey, err := ed25519.Create("targets/test/deep", testGUN, data.ED25519Key)
err = repo.UpdateDelegationKeys("targets/test/deep", []data.PublicKey{testDeepKey}, []string{}, 1)
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 0)
// no delegation metadata created for non-existent parent
_, ok = repo.Targets["targets/test"]
assert.False(t, ok, "no targets file should be created for nonexistent parent delegation")
}
// Updating delegations needs to modify the parent of the role being updated.
// If there is no signing key for that parent, the delegation cannot be added.
func TestUpdateDelegationsMissingParentKey(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
// remove the target key (all keys)
repo.cryptoService = signed.NewEd25519()
roleKey, err := ed25519.Create("Invalid Role", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/role", []data.PublicKey{roleKey}, []string{}, 1)
assert.Error(t, err)
assert.IsType(t, signed.ErrNoKeys{}, err)
// no empty delegation metadata created for new delegation
_, ok := repo.Targets["targets/role"]
assert.False(t, ok, "no targets file should be created for empty delegation")
}
func TestUpdateDelegationsInvalidRole(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
roleKey, err := ed25519.Create("Invalid Role", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("root", []data.PublicKey{roleKey}, []string{}, 1)
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 0)
// no delegation metadata created for invalid delegation
_, ok = repo.Targets["root"]
assert.False(t, ok, "no targets file should be created since delegation failed")
}
// A delegation can be created with a role that is missing a signing key, so
// long as UpdateDelegations is called with the key
func TestUpdateDelegationsRoleThatIsMissingDelegationKey(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
roleKey, err := ed25519.Create("Invalid Role", testGUN, data.ED25519Key)
assert.NoError(t, err)
// key should get added to role as part of updating the delegation
err = repo.UpdateDelegationKeys("targets/role", []data.PublicKey{roleKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/role", []string{""}, []string{}, false)
assert.NoError(t, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 1)
assert.Len(t, r.Signed.Delegations.Keys, 1)
keyIDs := r.Signed.Delegations.Roles[0].KeyIDs
assert.Len(t, keyIDs, 1)
assert.Equal(t, roleKey.ID(), keyIDs[0])
assert.True(t, r.Dirty)
// no empty delegation metadata created for new delegation
_, ok = repo.Targets["targets/role"]
assert.False(t, ok, "no targets file should be created for empty delegation")
}
func TestUpdateDelegationsNotEnoughKeys(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
roleKey, err := ed25519.Create("Invalid Role", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/role", []data.PublicKey{roleKey}, []string{}, 2)
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
// no delegation metadata created for failed delegation
_, ok := repo.Targets["targets/role"]
assert.False(t, ok, "no targets file should be created since delegation failed")
}
func TestUpdateDelegationsAddKeyToRole(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"test"}, []string{}, false)
assert.NoError(t, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 1)
assert.Len(t, r.Signed.Delegations.Keys, 1)
keyIDs := r.Signed.Delegations.Roles[0].KeyIDs
assert.Len(t, keyIDs, 1)
assert.Equal(t, testKey.ID(), keyIDs[0])
testKey2, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey2}, []string{}, 1)
assert.NoError(t, err)
r, ok = repo.Targets["targets"]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 1)
assert.Len(t, r.Signed.Delegations.Keys, 2)
keyIDs = r.Signed.Delegations.Roles[0].KeyIDs
assert.Len(t, keyIDs, 2)
// it does an append so the order is deterministic (but not meaningful to TUF)
assert.Equal(t, testKey.ID(), keyIDs[0])
assert.Equal(t, testKey2.ID(), keyIDs[1])
assert.True(t, r.Dirty)
}
func TestDeleteDelegations(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"test"}, []string{}, false)
assert.NoError(t, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 1)
assert.Len(t, r.Signed.Delegations.Keys, 1)
keyIDs := r.Signed.Delegations.Roles[0].KeyIDs
assert.Len(t, keyIDs, 1)
assert.Equal(t, testKey.ID(), keyIDs[0])
// ensure that the metadata is there and snapshot is there
targets, err := repo.InitTargets("targets/test")
assert.NoError(t, err)
targetsSigned, err := targets.ToSigned()
assert.NoError(t, err)
assert.NoError(t, repo.UpdateSnapshot("targets/test", targetsSigned))
_, ok = repo.Snapshot.Signed.Meta["targets/test"]
assert.True(t, ok)
assert.NoError(t, repo.DeleteDelegation("targets/test"))
assert.Len(t, r.Signed.Delegations.Roles, 0)
assert.Len(t, r.Signed.Delegations.Keys, 0)
assert.True(t, r.Dirty)
// metadata should be deleted
_, ok = repo.Targets["targets/test"]
assert.False(t, ok)
_, ok = repo.Snapshot.Signed.Meta["targets/test"]
assert.False(t, ok)
}
func TestDeleteDelegationsRoleNotExistBecauseNoParentMeta(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"test"}, []string{}, false)
assert.NoError(t, err)
// no empty delegation metadata created for new delegation
_, ok := repo.Targets["targets/test"]
assert.False(t, ok, "no targets file should be created for empty delegation")
delRole, err := data.NewRole("targets/test/a", 1, []string{testKey.ID()}, []string{"test"})
err = repo.DeleteDelegation(delRole.Name)
assert.NoError(t, err)
// still no metadata
_, ok = repo.Targets["targets/test"]
assert.False(t, ok)
}
func TestDeleteDelegationsRoleNotExist(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
// initRepo leaves all the roles as Dirty. Set to false
// to test removing a non-existent role doesn't mark
// a role as dirty
repo.Targets[data.CanonicalTargetsRole].Dirty = false
role, err := data.NewRole("targets/test", 1, []string{}, []string{""})
assert.NoError(t, err)
err = repo.DeleteDelegation(role.Name)
assert.NoError(t, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 0)
assert.Len(t, r.Signed.Delegations.Keys, 0)
assert.False(t, r.Dirty)
}
func TestDeleteDelegationsInvalidRole(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
// data.NewRole errors if the role isn't a valid TUF role so use one of the non-delegation
// valid roles
invalidRole, err := data.NewRole("root", 1, []string{}, []string{""})
assert.NoError(t, err)
err = repo.DeleteDelegation(invalidRole.Name)
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 0)
}
func TestDeleteDelegationsParentMissing(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testRole, err := data.NewRole("targets/test/deep", 1, []string{}, []string{""})
assert.NoError(t, err)
err = repo.DeleteDelegation(testRole.Name)
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 0)
}
// Can't delete a delegation if we don't have the parent's signing key
func TestDeleteDelegationsMissingParentSigningKey(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"test"}, []string{}, false)
assert.NoError(t, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 1)
assert.Len(t, r.Signed.Delegations.Keys, 1)
keyIDs := r.Signed.Delegations.Roles[0].KeyIDs
assert.Len(t, keyIDs, 1)
assert.Equal(t, testKey.ID(), keyIDs[0])
// ensure that the metadata is there and snapshot is there
targets, err := repo.InitTargets("targets/test")
assert.NoError(t, err)
targetsSigned, err := targets.ToSigned()
assert.NoError(t, err)
assert.NoError(t, repo.UpdateSnapshot("targets/test", targetsSigned))
_, ok = repo.Snapshot.Signed.Meta["targets/test"]
assert.True(t, ok)
// delete all signing keys
repo.cryptoService = signed.NewEd25519()
err = repo.DeleteDelegation("targets/test")
assert.Error(t, err)
assert.IsType(t, signed.ErrNoKeys{}, err)
assert.Len(t, r.Signed.Delegations.Roles, 1)
assert.Len(t, r.Signed.Delegations.Keys, 1)
assert.True(t, r.Dirty)
// metadata should be here still
_, ok = repo.Targets["targets/test"]
assert.True(t, ok)
_, ok = repo.Snapshot.Signed.Meta["targets/test"]
assert.True(t, ok)
}
func TestDeleteDelegationsMidSliceRole(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{""}, []string{}, false)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test2", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test2", []string{""}, []string{}, false)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test3", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test3", []string{"test"}, []string{}, false)
assert.NoError(t, err)
err = repo.DeleteDelegation("targets/test2")
assert.NoError(t, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
assert.Len(t, r.Signed.Delegations.Roles, 2)
assert.Len(t, r.Signed.Delegations.Keys, 1)
assert.True(t, r.Dirty)
}
// If the parent exists, the metadata exists, and the delegation is in it,
// returns the role that was found
func TestGetDelegationRoleAndMetadataExistDelegationExists(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("meh", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/level1", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/level1", []string{""}, []string{}, false)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/level1/level2", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/level1/level2", []string{""}, []string{}, false)
assert.NoError(t, err)
gottenRole, err := repo.GetDelegationRole("targets/level1/level2")
assert.NoError(t, err)
assert.Equal(t, "targets/level1/level2", gottenRole.Name)
assert.Equal(t, 1, gottenRole.Threshold)
assert.Equal(t, []string{""}, gottenRole.Paths)
_, ok := gottenRole.Keys[testKey.ID()]
assert.True(t, ok)
}
// If the parent exists, the metadata exists, and the delegation isn't in it,
// returns an ErrNoSuchRole
func TestGetDelegationRoleAndMetadataExistDelegationDoesntExists(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("meh", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/level1", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/level1", []string{""}, []string{}, false)
assert.NoError(t, err)
// ensure metadata exists
repo.InitTargets("targets/level1")
_, err = repo.GetDelegationRole("targets/level1/level2")
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
// If the parent exists but the metadata doesn't exist, returns an ErrNoSuchRole
func TestGetDelegationRoleAndMetadataDoesntExists(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("meh", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/level1", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/level1", []string{""}, []string{}, false)
assert.NoError(t, err)
// no empty delegation metadata created for new delegation
_, ok := repo.Targets["targets/test"]
assert.False(t, ok, "no targets file should be created for empty delegation")
_, err = repo.GetDelegationRole("targets/level1/level2")
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
// If the parent role doesn't exist, GetDelegation fails with an ErrInvalidRole
func TestGetDelegationParentMissing(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
_, err := repo.GetDelegationRole("targets/level1/level2")
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
// Adding targets to a role that exists and has metadata (like targets)
// correctly adds the target
func TestAddTargetsRoleAndMetadataExist(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
hash := sha256.Sum256([]byte{})
f := data.FileMeta{
Length: 1,
Hashes: map[string][]byte{
"sha256": hash[:],
},
}
_, err := repo.AddTargets(data.CanonicalTargetsRole, data.Files{"f": f})
assert.NoError(t, err)
r, ok := repo.Targets[data.CanonicalTargetsRole]
assert.True(t, ok)
targetsF, ok := r.Signed.Targets["f"]
assert.True(t, ok)
assert.Equal(t, f, targetsF)
}
// Adding targets to a role that exists and has not metadata first creates the
// metadata and then correctly adds the target
func TestAddTargetsRoleExistsAndMetadataDoesntExist(t *testing.T) {
hash := sha256.Sum256([]byte{})
f := data.FileMeta{
Length: 1,
Hashes: map[string][]byte{
"sha256": hash[:],
},
}
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{""}, []string{}, false)
assert.NoError(t, err)
// no empty metadata is created for this role
_, ok := repo.Targets["targets/test"]
assert.False(t, ok, "no empty targets file should be created")
// adding the targets to the role should create the metadata though
_, err = repo.AddTargets("targets/test", data.Files{"f": f})
assert.NoError(t, err)
r, ok := repo.Targets["targets/test"]
assert.True(t, ok)
targetsF, ok := r.Signed.Targets["f"]
assert.True(t, ok)
assert.Equal(t, f, targetsF)
}
// Adding targets to a role that doesn't exist fails
func TestAddTargetsRoleDoesntExist(t *testing.T) {
hash := sha256.Sum256([]byte{})
f := data.FileMeta{
Length: 1,
Hashes: map[string][]byte{
"sha256": hash[:],
},
}
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
_, err := repo.AddTargets("targets/test", data.Files{"f": f})
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
// Adding targets to a role that we don't have signing keys for fails
func TestAddTargetsNoSigningKeys(t *testing.T) {
hash := sha256.Sum256([]byte{})
f := data.FileMeta{
Length: 1,
Hashes: map[string][]byte{
"sha256": hash[:],
},
}
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"test"}, []string{}, false)
assert.NoError(t, err)
// now delete the signing key (all keys)
repo.cryptoService = signed.NewEd25519()
// adding the targets to the role should create the metadata though
_, err = repo.AddTargets("targets/test", data.Files{"f": f})
assert.Error(t, err)
assert.IsType(t, signed.ErrNoKeys{}, err)
}
// Removing targets from a role that exists, has targets, and is signable
// should succeed, even if we also want to remove targets that don't exist.
func TestRemoveExistingAndNonexistingTargets(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"test"}, []string{}, false)
assert.NoError(t, err)
// no empty metadata is created for this role
_, ok := repo.Targets["targets/test"]
assert.False(t, ok, "no empty targets file should be created")
// now remove a target
assert.NoError(t, repo.RemoveTargets("targets/test", "f"))
// still no metadata
_, ok = repo.Targets["targets/test"]
assert.False(t, ok)
}
// Removing targets from a role that exists but without metadata succeeds.
func TestRemoveTargetsNonexistentMetadata(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
err := repo.RemoveTargets("targets/test", "f")
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
// Removing targets from a role that doesn't exist fails
func TestRemoveTargetsRoleDoesntExist(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
err := repo.RemoveTargets("targets/test", "f")
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
// Removing targets from a role that we don't have signing keys for fails
func TestRemoveTargetsNoSigningKeys(t *testing.T) {
hash := sha256.Sum256([]byte{})
f := data.FileMeta{
Length: 1,
Hashes: map[string][]byte{
"sha256": hash[:],
},
}
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{""}, []string{}, false)
assert.NoError(t, err)
// adding the targets to the role should create the metadata though
_, err = repo.AddTargets("targets/test", data.Files{"f": f})
assert.NoError(t, err)
r, ok := repo.Targets["targets/test"]
assert.True(t, ok)
_, ok = r.Signed.Targets["f"]
assert.True(t, ok)
// now delete the signing key (all keys)
repo.cryptoService = signed.NewEd25519()
// now remove the target - it should fail
err = repo.RemoveTargets("targets/test", "f")
assert.Error(t, err)
assert.IsType(t, signed.ErrNoKeys{}, err)
}
// adding a key to a role marks root as dirty as well as the role
func TestAddBaseKeysToRoot(t *testing.T) {
for _, role := range data.BaseRoles {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
key, err := ed25519.Create(role, testGUN, data.ED25519Key)
assert.NoError(t, err)
assert.Len(t, repo.Root.Signed.Roles[role].KeyIDs, 1)
assert.NoError(t, repo.AddBaseKeys(role, key))
_, ok := repo.Root.Signed.Keys[key.ID()]
assert.True(t, ok)
assert.Len(t, repo.Root.Signed.Roles[role].KeyIDs, 2)
assert.True(t, repo.Root.Dirty)
switch role {
case data.CanonicalSnapshotRole:
assert.True(t, repo.Snapshot.Dirty)
case data.CanonicalTargetsRole:
assert.True(t, repo.Targets[data.CanonicalTargetsRole].Dirty)
case data.CanonicalTimestampRole:
assert.True(t, repo.Timestamp.Dirty)
}
}
}
func TestGetAllRoles(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
// After we init, we get the base roles
roles := repo.GetAllLoadedRoles()
assert.Len(t, roles, len(data.BaseRoles))
}
func TestGetBaseRoles(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
// After we init, we get the base roles
for _, role := range data.BaseRoles {
baseRole, err := repo.GetBaseRole(role)
assert.NoError(t, err)
assert.Equal(t, role, baseRole.Name)
keyIDs := repo.cryptoService.ListKeys(role)
for _, keyID := range keyIDs {
_, ok := baseRole.Keys[keyID]
assert.True(t, ok)
assert.Contains(t, baseRole.ListKeyIDs(), keyID)
}
// initRepo should set all key thresholds to 1
assert.Equal(t, 1, baseRole.Threshold)
}
}
func TestGetBaseRolesInvalidName(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
_, err := repo.GetBaseRole("invalid")
assert.Error(t, err)
_, err = repo.GetBaseRole("targets/delegation")
assert.Error(t, err)
}
func TestGetDelegationValidRoles(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey1, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey1}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"path", "anotherpath"}, []string{}, false)
assert.NoError(t, err)
delgRole, err := repo.GetDelegationRole("targets/test")
assert.NoError(t, err)
assert.Equal(t, "targets/test", delgRole.Name)
assert.Equal(t, 1, delgRole.Threshold)
assert.Equal(t, []string{testKey1.ID()}, delgRole.ListKeyIDs())
assert.Equal(t, []string{"path", "anotherpath"}, delgRole.Paths)
assert.Equal(t, testKey1, delgRole.Keys[testKey1.ID()])
testKey2, err := ed25519.Create("targets/a", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/a", []data.PublicKey{testKey2}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/a", []string{""}, []string{}, false)
assert.NoError(t, err)
delgRole, err = repo.GetDelegationRole("targets/a")
assert.NoError(t, err)
assert.Equal(t, "targets/a", delgRole.Name)
assert.Equal(t, 1, delgRole.Threshold)
assert.Equal(t, []string{testKey2.ID()}, delgRole.ListKeyIDs())
assert.Equal(t, []string{""}, delgRole.Paths)
assert.Equal(t, testKey2, delgRole.Keys[testKey2.ID()])
testKey3, err := ed25519.Create("targets/test/b", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test/b", []data.PublicKey{testKey3}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test/b", []string{"path/subpath", "anotherpath"}, []string{}, false)
assert.NoError(t, err)
delgRole, err = repo.GetDelegationRole("targets/test/b")
assert.NoError(t, err)
assert.Equal(t, "targets/test/b", delgRole.Name)
assert.Equal(t, 1, delgRole.Threshold)
assert.Equal(t, []string{testKey3.ID()}, delgRole.ListKeyIDs())
assert.Equal(t, []string{"path/subpath", "anotherpath"}, delgRole.Paths)
assert.Equal(t, testKey3, delgRole.Keys[testKey3.ID()])
testKey4, err := ed25519.Create("targets/test/c", testGUN, data.ED25519Key)
assert.NoError(t, err)
// Try adding empty paths, ensure this is valid
err = repo.UpdateDelegationKeys("targets/test/c", []data.PublicKey{testKey4}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test/c", []string{}, []string{}, false)
assert.NoError(t, err)
}
func TestGetDelegationRolesInvalidName(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
_, err := repo.GetDelegationRole("invalid")
assert.Error(t, err)
for _, role := range data.BaseRoles {
_, err = repo.GetDelegationRole(role)
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
_, err = repo.GetDelegationRole("targets/doesnt_exist")
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
func TestGetDelegationRolesInvalidPaths(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
testKey1, err := ed25519.Create("targets/test", testGUN, data.ED25519Key)
assert.NoError(t, err)
err = repo.UpdateDelegationKeys("targets/test", []data.PublicKey{testKey1}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test", []string{"path", "anotherpath"}, []string{}, false)
assert.NoError(t, err)
testKey2, err := ed25519.Create("targets/test/b", testGUN, data.ED25519Key)
assert.NoError(t, err)
// Now we add a delegation with a path that is not prefixed by its parent delegation, the invalid path can't be added so there is an error
err = repo.UpdateDelegationKeys("targets/test/b", []data.PublicKey{testKey2}, []string{}, 1)
assert.NoError(t, err)
err = repo.UpdateDelegationPaths("targets/test/b", []string{"invalidpath"}, []string{}, false)
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
delgRole, err := repo.GetDelegationRole("targets/test")
assert.NoError(t, err)
assert.Contains(t, delgRole.Paths, "path")
assert.Contains(t, delgRole.Paths, "anotherpath")
}
func TestDelegationRolesParent(t *testing.T) {
delgA := data.DelegationRole{
BaseRole: data.BaseRole{
Keys: nil,
Name: "targets/a",
Threshold: 1,
},
Paths: []string{"path", "anotherpath"},
}
delgB := data.DelegationRole{
BaseRole: data.BaseRole{
Keys: nil,
Name: "targets/a/b",
Threshold: 1,
},
Paths: []string{"path/b", "anotherpath/b", "b/invalidpath"},
}
// Assert direct parent relationship
assert.True(t, delgA.IsParentOf(delgB))
assert.False(t, delgB.IsParentOf(delgA))
assert.False(t, delgA.IsParentOf(delgA))
delgC := data.DelegationRole{
BaseRole: data.BaseRole{
Keys: nil,
Name: "targets/a/b/c",
Threshold: 1,
},
Paths: []string{"path/b", "anotherpath/b/c", "c/invalidpath"},
}
// Assert direct parent relationship
assert.True(t, delgB.IsParentOf(delgC))
assert.False(t, delgB.IsParentOf(delgB))
assert.False(t, delgA.IsParentOf(delgC))
assert.False(t, delgC.IsParentOf(delgB))
assert.False(t, delgC.IsParentOf(delgA))
assert.False(t, delgC.IsParentOf(delgC))
// Check that parents correctly restrict paths
restrictedDelgB, err := delgA.Restrict(delgB)
assert.NoError(t, err)
assert.Contains(t, restrictedDelgB.Paths, "path/b")
assert.Contains(t, restrictedDelgB.Paths, "anotherpath/b")
assert.NotContains(t, restrictedDelgB.Paths, "b/invalidpath")
_, err = delgB.Restrict(delgA)
assert.Error(t, err)
_, err = delgA.Restrict(delgC)
assert.Error(t, err)
_, err = delgC.Restrict(delgB)
assert.Error(t, err)
_, err = delgC.Restrict(delgA)
assert.Error(t, err)
// Make delgA have no paths and check that it changes delgB and delgC accordingly when chained
delgA.Paths = []string{}
restrictedDelgB, err = delgA.Restrict(delgB)
assert.NoError(t, err)
assert.Empty(t, restrictedDelgB.Paths)
restrictedDelgC, err := restrictedDelgB.Restrict(delgC)
assert.NoError(t, err)
assert.Empty(t, restrictedDelgC.Paths)
}
func TestGetBaseRoleEmptyRepo(t *testing.T) {
repo := NewRepo(nil)
_, err := repo.GetBaseRole(data.CanonicalRootRole)
assert.Error(t, err)
assert.IsType(t, ErrNotLoaded{}, err)
}
func TestGetBaseRoleKeyMissing(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
// change root role to have a KeyID that doesn't exist
repo.Root.Signed.Roles[data.CanonicalRootRole].KeyIDs = []string{"abc"}
_, err := repo.GetBaseRole(data.CanonicalRootRole)
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}
func TestGetDelegationRoleKeyMissing(t *testing.T) {
ed25519 := signed.NewEd25519()
repo := initRepo(t, ed25519)
// add a delegation that has a KeyID that doesn't exist
// in the relevant key map
tar := repo.Targets[data.CanonicalTargetsRole]
tar.Signed.Delegations.Roles = []*data.Role{
{
RootRole: data.RootRole{
KeyIDs: []string{"abc"},
Threshold: 1,
},
Name: "targets/missing_key",
Paths: []string{""},
},
}
_, err := repo.GetDelegationRole("targets/missing_key")
assert.Error(t, err)
assert.IsType(t, data.ErrInvalidRole{}, err)
}