docs/tuf/testutils/swizzler_test.go

743 lines
26 KiB
Go

// make sure that the swizzler actually sort of works, so our tests that use it actually test what we
// think
package testutils
import (
"bytes"
"crypto/sha256"
"encoding/json"
"reflect"
"testing"
"time"
"github.com/docker/notary/tuf"
"github.com/docker/notary/tuf/data"
"github.com/docker/notary/tuf/signed"
"github.com/docker/notary/tuf/store"
"github.com/stretchr/testify/require"
)
// creates a new swizzler with 3 delegation targets (and only 2 metadata files
// for those targets), and returns the swizzler along with a copy of the original
// metadata
func createNewSwizzler(t *testing.T) (*MetadataSwizzler, map[string][]byte) {
gun := "docker.com/notary"
m, cs, err := NewRepoMetadata(gun, "targets/a", "targets/a/b", "targets/a/b/c")
require.NoError(t, err)
return NewMetadataSwizzler(gun, m, cs), CopyRepoMetadata(m)
}
// A new swizzler should have metadata for all roles, and a snapshot of all roles
func TestNewSwizzler(t *testing.T) {
f, origMeta := createNewSwizzler(t)
for _, role := range f.Roles {
metaBytes, ok := origMeta[role]
require.True(t, ok)
require.NotNil(t, metaBytes)
require.NotEmpty(t, metaBytes)
}
snapshot, timestamp := &data.SignedSnapshot{}, &data.SignedTimestamp{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalSnapshotRole], snapshot))
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalTimestampRole], timestamp))
for _, role := range f.Roles {
filemeta, ok := snapshot.Signed.Meta[role]
if role != data.CanonicalTimestampRole && role != data.CanonicalSnapshotRole {
require.True(t, ok)
require.NotNil(t, filemeta)
require.NotEmpty(t, filemeta)
} else {
require.False(t, ok)
}
}
require.Len(t, timestamp.Signed.Meta, 1)
filemeta, ok := timestamp.Signed.Meta[data.CanonicalSnapshotRole]
require.True(t, ok)
require.NotNil(t, filemeta)
require.NotEmpty(t, filemeta)
// targets should have 1 delegated role, as should targets/a
targets, targetsA := &data.SignedTargets{}, &data.SignedTargets{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalTargetsRole], targets))
require.NoError(t, json.Unmarshal(origMeta["targets/a"], targetsA))
require.Len(t, targets.Signed.Delegations.Roles, 1)
require.Equal(t, "targets/a", targets.Signed.Delegations.Roles[0].Name)
require.Len(t, targetsA.Signed.Delegations.Roles, 1)
require.Equal(t, "targets/a/b", targetsA.Signed.Delegations.Roles[0].Name)
}
// This invalidates the metadata so that it can no longer be unmarshalled as
// JSON as any sort
func TestSwizzlerSetInvalidJSON(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.SetInvalidJSON(data.CanonicalSnapshotRole)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalSnapshotRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
// it should not be JSON unmarshallable
var generic interface{}
require.Error(t, json.Unmarshal(newMeta, &generic))
}
}
}
// This adds a single byte of whitespace to the metadata file, so it should be parsed
// and deserialized the same way, but checksums against snapshot/timestamp may fail
func TestSwizzlerAddExtraSpace(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.AddExtraSpace(data.CanonicalTargetsRole)
snapshot := &data.SignedSnapshot{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalSnapshotRole], snapshot))
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalTargetsRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
require.True(t, bytes.Equal(metaBytes, newMeta[1:len(metaBytes)+1]))
require.Equal(t, byte(' '), newMeta[0])
require.Equal(t, byte(' '), newMeta[len(newMeta)-1])
// make sure the hash is not the same as the hash in snapshot
newHash := sha256.Sum256(newMeta)
require.False(t, bytes.Equal(
snapshot.Signed.Meta[data.CanonicalTargetsRole].Hashes["sha256"],
newHash[:]))
require.NotEqual(t,
snapshot.Signed.Meta[data.CanonicalTargetsRole].Length,
len(newMeta))
}
}
}
// This modifies metdata so that it is unmarshallable as JSON, but cannot be
// unmarshalled as a Signed object
func TestSwizzlerSetInvalidSigned(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.SetInvalidSigned(data.CanonicalTargetsRole)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalTargetsRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
// it be JSON unmarshallable, but not data.Signed marshallable
var generic interface{}
require.NoError(t, json.Unmarshal(newMeta, &generic))
signedThing := data.Signed{}
require.Error(t, json.Unmarshal(newMeta, &signedThing))
}
}
}
// This modifies metdata so that it is unmarshallable as JSON, but cannot be
// unmarshalled as a Signed object
func TestSwizzlerSetInvalidSignedMeta(t *testing.T) {
f, origMeta := createNewSwizzler(t)
err := f.SetInvalidSignedMeta(data.CanonicalRootRole)
require.NoError(t, err)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalRootRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
// can be unmarshaled as Signed, but not as SignedMeta
signedThing := data.Signed{}
require.NoError(t, json.Unmarshal(newMeta, &signedThing))
signedMeta := data.SignedMeta{}
require.Error(t, json.Unmarshal(newMeta, &signedMeta))
}
}
}
// This modifies metdata so that it is unmarshallable as JSON, but cannot be
// unmarshalled as a Signed object
func TestSwizzlerSetInvalidMetadataType(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.SetInvalidMetadataType(data.CanonicalTargetsRole)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalTargetsRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
signedMeta := data.SignedMeta{}
require.NoError(t, json.Unmarshal(newMeta, &signedMeta))
require.NotEqual(t, data.CanonicalTargetsRole, signedMeta.Signed.Type)
}
}
}
// This modifies the metadata so that the signed part has an extra, extraneous
// field. This does not prevent it from being unmarshalled as Signed* object,
// but the signature is no longer valid because the hash is different.
func TestSwizzlerInvalidateMetadataSignatures(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.InvalidateMetadataSignatures(data.CanonicalRootRole)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalRootRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
// it be JSON unmarshallable into a data.Signed, and it's signed by
// root, but it is NOT the correct signature because the hash
// does not match
origSigned, newSigned := &data.Signed{}, &data.Signed{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.Len(t, newSigned.Signatures, len(origSigned.Signatures))
for i := range origSigned.Signatures {
require.Equal(t, origSigned.Signatures[i].KeyID, newSigned.Signatures[i].KeyID)
require.Equal(t, origSigned.Signatures[i].Method, newSigned.Signatures[i].Method)
require.NotEqual(t, origSigned.Signatures[i].Signature, newSigned.Signatures[i].Signature)
require.Equal(t, []byte("invalid signature"), newSigned.Signatures[i].Signature)
}
require.True(t, bytes.Equal(*origSigned.Signed, *newSigned.Signed))
}
}
}
// This just deletes the metadata entirely from the cache
func TestSwizzlerRemoveMetadata(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.RemoveMetadata("targets/a")
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
if role != "targets/a" {
require.NoError(t, err)
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.Error(t, err)
require.IsType(t, store.ErrMetaNotFound{}, err)
}
}
}
// This signs the metadata with the wrong key
func TestSwizzlerSignMetadataWithInvalidKey(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.SignMetadataWithInvalidKey(data.CanonicalTimestampRole)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalTimestampRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
// it is JSON unmarshallable as a timestamp, but the signature ID
// does not match.
require.NoError(t, json.Unmarshal(newMeta, &data.SignedTimestamp{}))
origSigned, newSigned := &data.Signed{}, &data.Signed{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.Len(t, origSigned.Signatures, 1)
require.Len(t, newSigned.Signatures, 1)
require.NotEqual(t, origSigned.Signatures[0].KeyID, newSigned.Signatures[0].KeyID)
}
}
}
// This updates the metadata version with a particular number
func TestSwizzlerOffsetMetadataVersion(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.OffsetMetadataVersion("targets/a", -2)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != "targets/a" {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
origSigned, newSigned := &data.SignedMeta{}, &data.SignedMeta{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.Equal(t, 1, origSigned.Signed.Version)
require.Equal(t, -1, newSigned.Signed.Version)
}
}
}
// This causes the metadata to be expired
func TestSwizzlerExpireMetadata(t *testing.T) {
f, origMeta := createNewSwizzler(t)
err := f.ExpireMetadata(data.CanonicalRootRole)
require.NoError(t, err)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalRootRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
origSigned, newSigned := &data.SignedMeta{}, &data.SignedMeta{}
now := time.Now()
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.True(t, now.Before(origSigned.Signed.Expires))
require.True(t, now.After(newSigned.Signed.Expires))
}
}
}
// This sets the threshold for a base role
func TestSwizzlerSetThresholdBaseRole(t *testing.T) {
f, origMeta := createNewSwizzler(t)
err := f.SetThreshold(data.CanonicalTargetsRole, 3)
require.NoError(t, err)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
// the threshold for base roles is set in root
if role != data.CanonicalRootRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
signedRoot := &data.SignedRoot{}
require.NoError(t, json.Unmarshal(newMeta, signedRoot))
for r, roleInfo := range signedRoot.Signed.Roles {
if r != data.CanonicalTargetsRole {
require.Equal(t, 1, roleInfo.Threshold)
} else {
require.Equal(t, 3, roleInfo.Threshold)
}
}
}
}
}
// This sets the threshold for a delegation
func TestSwizzlerSetThresholdDelegatedRole(t *testing.T) {
f, origMeta := createNewSwizzler(t)
f.SetThreshold("targets/a/b", 3)
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
// the threshold for "targets/a/b" is in "targets/a"
if role != "targets/a" {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
signedTargets := &data.SignedTargets{}
require.NoError(t, json.Unmarshal(newMeta, signedTargets))
require.Len(t, signedTargets.Signed.Delegations.Roles, 1)
require.Equal(t, "targets/a/b", signedTargets.Signed.Delegations.Roles[0].Name)
require.Equal(t, 3, signedTargets.Signed.Delegations.Roles[0].Threshold)
}
}
}
// This changes the root key
func TestSwizzlerChangeRootKey(t *testing.T) {
f, origMeta := createNewSwizzler(t)
err := f.ChangeRootKey()
require.NoError(t, err)
tufRepo := tuf.NewRepo(f.CryptoService)
// we want to test these in a specific order
roles := []string{data.CanonicalRootRole, data.CanonicalTargetsRole, data.CanonicalSnapshotRole,
data.CanonicalTimestampRole, "targets/a", "targets/a/b"}
for _, role := range roles {
origMeta := origMeta[role]
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
// the threshold for base roles is set in root
switch role {
case data.CanonicalRootRole:
require.False(t, bytes.Equal(origMeta, newMeta))
origRoot, newRoot := &data.SignedRoot{}, &data.SignedRoot{}
require.NoError(t, json.Unmarshal(origMeta, origRoot))
require.NoError(t, json.Unmarshal(newMeta, newRoot))
require.NotEqual(t, len(origRoot.Signed.Keys), len(newRoot.Signed.Keys))
var rootRole data.Role
for r, origRole := range origRoot.Signed.Roles {
newRole := newRoot.Signed.Roles[r]
require.Len(t, origRole.KeyIDs, 1)
require.Len(t, newRole.KeyIDs, 1)
if r == data.CanonicalRootRole {
require.NotEqual(t, origRole.KeyIDs[0], newRole.KeyIDs[0])
rootRole = data.Role{RootRole: *newRole, Name: data.CanonicalRootRole}
} else {
require.Equal(t, origRole.KeyIDs[0], newRole.KeyIDs[0])
}
}
require.NoError(t, tufRepo.SetRoot(newRoot))
signedThing, err := newRoot.ToSigned()
require.NoError(t, err)
newKey := newRoot.Signed.Keys[rootRole.KeyIDs[0]]
require.NoError(t, signed.Verify(signedThing,
data.BaseRole{Name: data.CanonicalRootRole, Keys: map[string]data.PublicKey{newKey.ID(): newKey}, Threshold: 1}, 1))
default:
require.True(t, bytes.Equal(origMeta, newMeta), "bytes have changed for role %s", role)
}
}
}
// UpdateSnapshotHashes will recreate all snapshot hashes, useful if some metadata
// has been fuzzed and we want all the hashes to be correct. If roles are provided,
// only hashes for those roles will be re-generated.
func TestSwizzlerUpdateSnapshotHashesSpecifiedRoles(t *testing.T) {
f, origMeta := createNewSwizzler(t)
// nothing has changed, signed data should be the same (signatures might
// change because signatures may have random elements
f.UpdateSnapshotHashes(data.CanonicalTargetsRole)
newMeta, err := f.MetadataCache.GetMeta(data.CanonicalSnapshotRole, -1)
origSigned, newSigned := &data.Signed{}, &data.Signed{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalSnapshotRole], origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.True(t, bytes.Equal(*origSigned.Signed, *newSigned.Signed))
// change these 3 metadata items
f.InvalidateMetadataSignatures(data.CanonicalTargetsRole)
f.InvalidateMetadataSignatures("targets/a")
f.InvalidateMetadataSignatures("targets/a/b")
// update the snapshot with just 1 role
f.UpdateSnapshotHashes(data.CanonicalTargetsRole)
newMeta, err = f.MetadataCache.GetMeta(data.CanonicalSnapshotRole, -1)
require.NoError(t, err)
require.False(t, bytes.Equal(origMeta[data.CanonicalSnapshotRole], newMeta))
origSnapshot, newSnapshot := &data.SignedSnapshot{}, &data.SignedSnapshot{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalSnapshotRole], origSnapshot))
require.NoError(t, json.Unmarshal(newMeta, newSnapshot))
// only the targets checksum was regenerated, since that was specified
for _, role := range f.Roles {
switch role {
case data.CanonicalTimestampRole:
continue
case data.CanonicalTargetsRole:
require.NotEqual(t, origSnapshot.Signed.Meta[role], newSnapshot.Signed.Meta[role])
default:
require.Equal(t, origSnapshot.Signed.Meta[role], newSnapshot.Signed.Meta[role])
}
}
}
// UpdateSnapshotHashes will recreate all snapshot hashes, useful if some metadata
// has been fuzzed and we want all the hashes to be correct. If no roles are provided,
// all hashes are regenerated
func TestSwizzlerUpdateSnapshotHashesNoSpecifiedRoles(t *testing.T) {
f, origMeta := createNewSwizzler(t)
// nothing has changed, signed data should be the same (signatures might
// change because signatures may have random elements
f.UpdateSnapshotHashes()
newMeta, err := f.MetadataCache.GetMeta(data.CanonicalSnapshotRole, -1)
require.NoError(t, err)
origSigned, newSigned := &data.Signed{}, &data.Signed{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalSnapshotRole], origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.True(t, bytes.Equal(*origSigned.Signed, *newSigned.Signed))
// change these 2 metadata items
f.InvalidateMetadataSignatures(data.CanonicalTargetsRole)
f.InvalidateMetadataSignatures("targets/a")
f.InvalidateMetadataSignatures("targets/a/b")
// update the snapshot with just no specified roles
f.UpdateSnapshotHashes()
newMeta, err = f.MetadataCache.GetMeta(data.CanonicalSnapshotRole, -1)
require.NoError(t, err)
require.False(t, bytes.Equal(origMeta[data.CanonicalSnapshotRole], newMeta))
origSnapshot, newSnapshot := &data.SignedSnapshot{}, &data.SignedSnapshot{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalSnapshotRole], origSnapshot))
require.NoError(t, json.Unmarshal(newMeta, newSnapshot))
for _, role := range f.Roles {
switch role {
case data.CanonicalTimestampRole:
continue
case data.CanonicalTargetsRole:
fallthrough
case "targets/a":
fallthrough
case "targets/a/b":
require.NotEqual(t, origSnapshot.Signed.Meta[role], newSnapshot.Signed.Meta[role])
default:
require.Equal(t, origSnapshot.Signed.Meta[role], newSnapshot.Signed.Meta[role])
}
}
}
// UpdateTimestamp will re-calculate the snapshot hash
func TestSwizzlerUpdateTimestamp(t *testing.T) {
f, origMeta := createNewSwizzler(t)
// nothing has changed, signed data should be the same (signatures might
// change because signatures may have random elements
f.UpdateTimestampHash()
newMeta, err := f.MetadataCache.GetMeta(data.CanonicalTimestampRole, -1)
require.NoError(t, err)
origSigned, newSigned := &data.Signed{}, &data.Signed{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalTimestampRole], origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.True(t, bytes.Equal(*origSigned.Signed, *newSigned.Signed))
// update snapshot
f.OffsetMetadataVersion(data.CanonicalSnapshotRole, 1)
// update the timestamp
f.UpdateTimestampHash()
newMeta, err = f.MetadataCache.GetMeta(data.CanonicalTimestampRole, -1)
require.NoError(t, err)
require.False(t, bytes.Equal(origMeta[data.CanonicalTimestampRole], newMeta))
origTimestamp, newTimestamp := &data.SignedTimestamp{}, &data.SignedTimestamp{}
require.NoError(t, json.Unmarshal(origMeta[data.CanonicalTimestampRole], origTimestamp))
require.NoError(t, json.Unmarshal(newMeta, newTimestamp))
require.Len(t, origTimestamp.Signed.Meta, 1)
require.Len(t, newTimestamp.Signed.Meta, 1)
require.False(t, reflect.DeepEqual(
origTimestamp.Signed.Meta[data.CanonicalSnapshotRole],
newTimestamp.Signed.Meta[data.CanonicalSnapshotRole]))
}
// functions which require re-signing the metadata will return ErrNoKeyForRole if
// the signing key is missing
func TestMissingSigningKey(t *testing.T) {
f, _ := createNewSwizzler(t)
// delete the snapshot, timestamp, and root keys
noKeys := []string{
data.CanonicalSnapshotRole, data.CanonicalTimestampRole, data.CanonicalRootRole}
for _, role := range noKeys {
k := f.CryptoService.ListKeys(role)
require.Len(t, k, 1)
require.NoError(t, f.CryptoService.RemoveKey(k[0]))
}
// these are all the functions that require re-signing
require.IsType(t, ErrNoKeyForRole{}, f.OffsetMetadataVersion(data.CanonicalSnapshotRole, 1))
require.IsType(t, ErrNoKeyForRole{}, f.ExpireMetadata(data.CanonicalSnapshotRole))
require.IsType(t, ErrNoKeyForRole{}, f.SetThreshold(data.CanonicalSnapshotRole, 2))
require.IsType(t, ErrNoKeyForRole{}, f.UpdateSnapshotHashes())
require.IsType(t, ErrNoKeyForRole{}, f.UpdateTimestampHash())
}
// This mutates the root
func TestSwizzlerMutateRoot(t *testing.T) {
f, origMeta := createNewSwizzler(t)
require.NoError(t, f.MutateRoot(func(r *data.Root) { r.Roles["hello"] = nil }))
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalRootRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
origSigned, newSigned := &data.SignedRoot{}, &data.SignedRoot{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
// it may not exactly equal 4 or 5 because if the metadata was
// produced by calling SignedRoot, it could have saved a previous
// root role
require.True(t, len(origSigned.Signed.Roles) >= 4)
require.True(t, len(newSigned.Signed.Roles) >= 5)
}
}
}
// This mutates the timestamp
func TestSwizzlerMutateTimestamp(t *testing.T) {
f, origMeta := createNewSwizzler(t)
require.NoError(t, f.MutateTimestamp(func(t *data.Timestamp) { t.Meta["hello"] = data.FileMeta{} }))
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalTimestampRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
origSigned, newSigned := &data.SignedTimestamp{}, &data.SignedTimestamp{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.Len(t, origSigned.Signed.Meta, 1)
require.Len(t, newSigned.Signed.Meta, 2)
}
}
}
// This mutates the snapshot
func TestSwizzlerMutateSnapshot(t *testing.T) {
f, origMeta := createNewSwizzler(t)
require.NoError(t, f.MutateSnapshot(func(s *data.Snapshot) { s.Meta["hello"] = data.FileMeta{} }))
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalSnapshotRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
origSigned, newSigned := &data.SignedSnapshot{}, &data.SignedSnapshot{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.Len(t, origSigned.Signed.Meta, 4)
require.Len(t, newSigned.Signed.Meta, 5)
}
}
}
// This mutates the targets
func TestSwizzlerMutateTargets(t *testing.T) {
f, origMeta := createNewSwizzler(t)
require.NoError(t, f.MutateTargets(func(t *data.Targets) { t.Targets["hello"] = data.FileMeta{} }))
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalTargetsRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
origSigned, newSigned := &data.SignedTargets{}, &data.SignedTargets{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.Len(t, origSigned.Signed.Targets, 0)
require.Len(t, newSigned.Signed.Targets, 1)
}
}
}
// This rotates the key of some base role
func TestSwizzlerRotateKeyBaseRole(t *testing.T) {
f, origMeta := createNewSwizzler(t)
theRole := data.CanonicalSnapshotRole
cs := signed.NewEd25519()
pubKey, err := cs.Create(theRole, f.Gun, data.ED25519Key)
require.NoError(t, err)
require.NoError(t, f.RotateKey(theRole, pubKey))
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != data.CanonicalRootRole {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
origSigned, newSigned := &data.SignedRoot{}, &data.SignedRoot{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.NotEqual(t, []string{pubKey.ID()}, origSigned.Signed.Roles[theRole].KeyIDs)
require.Equal(t, []string{pubKey.ID()}, newSigned.Signed.Roles[theRole].KeyIDs)
_, ok := origSigned.Signed.Keys[pubKey.ID()]
require.False(t, ok)
_, ok = newSigned.Signed.Keys[pubKey.ID()]
require.True(t, ok)
}
}
}
// This rotates the key of some delegation role
func TestSwizzlerRotateKeyDelegationRole(t *testing.T) {
f, origMeta := createNewSwizzler(t)
theRole := "targets/a/b"
cs := signed.NewEd25519()
pubKey, err := cs.Create(theRole, f.Gun, data.ED25519Key)
require.NoError(t, err)
require.NoError(t, f.RotateKey(theRole, pubKey))
for role, metaBytes := range origMeta {
newMeta, err := f.MetadataCache.GetMeta(role, -1)
require.NoError(t, err)
if role != "targets/a" {
require.True(t, bytes.Equal(metaBytes, newMeta), "bytes have changed for role %s", role)
} else {
require.False(t, bytes.Equal(metaBytes, newMeta))
origSigned, newSigned := &data.SignedTargets{}, &data.SignedTargets{}
require.NoError(t, json.Unmarshal(metaBytes, origSigned))
require.NoError(t, json.Unmarshal(newMeta, newSigned))
require.NotEqual(t, []string{pubKey.ID()}, origSigned.Signed.Delegations.Roles[0].KeyIDs)
require.Equal(t, []string{pubKey.ID()}, newSigned.Signed.Delegations.Roles[0].KeyIDs)
_, ok := origSigned.Signed.Delegations.Keys[pubKey.ID()]
require.False(t, ok)
_, ok = newSigned.Signed.Delegations.Keys[pubKey.ID()]
require.True(t, ok)
}
}
}