docs/tuf/data/targets_test.go

230 lines
7.1 KiB
Go

package data
import (
"bytes"
rjson "encoding/json"
"path"
"reflect"
"testing"
"time"
cjson "github.com/docker/go/canonical/json"
"github.com/stretchr/testify/require"
)
func validTargetsTemplate() *SignedTargets {
return &SignedTargets{
Signed: Targets{
SignedCommon: SignedCommon{Type: "Targets", Version: 1, Expires: time.Now()},
Targets: Files{},
Delegations: Delegations{
Roles: []*Role{},
Keys: Keys{
"key1": NewPublicKey(RSAKey, []byte("key1")),
"key2": NewPublicKey(RSAKey, []byte("key2")),
},
},
},
Signatures: []Signature{
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
},
}
}
func TestTargetsToSignedMarshalsSignedPortionWithCanonicalJSON(t *testing.T) {
tg := SignedTargets{Signed: Targets{SignedCommon: SignedCommon{Type: "Targets", Version: 1, Expires: time.Now()}}}
signedCanonical, err := tg.ToSigned()
require.NoError(t, err)
canonicalSignedPortion, err := cjson.MarshalCanonical(tg.Signed)
require.NoError(t, err)
castedCanonical := rjson.RawMessage(canonicalSignedPortion)
// don't bother testing regular JSON because it might not be different
require.True(t, bytes.Equal(signedCanonical.Signed, castedCanonical),
"expected %v == %v", signedCanonical.Signed, castedCanonical)
}
func TestTargetsToSignCopiesSignatures(t *testing.T) {
tg := SignedTargets{
Signed: Targets{SignedCommon: SignedCommon{Type: "Targets", Version: 2, Expires: time.Now()}},
Signatures: []Signature{
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
},
}
signed, err := tg.ToSigned()
require.NoError(t, err)
require.True(t, reflect.DeepEqual(tg.Signatures, signed.Signatures),
"expected %v == %v", tg.Signatures, signed.Signatures)
tg.Signatures[0].KeyID = "changed"
require.False(t, reflect.DeepEqual(tg.Signatures, signed.Signatures),
"expected %v != %v", tg.Signatures, signed.Signatures)
}
func TestTargetsToSignedMarshallingErrorsPropagated(t *testing.T) {
setDefaultSerializer(errorSerializer{})
defer setDefaultSerializer(canonicalJSON{})
tg := SignedTargets{
Signed: Targets{SignedCommon: SignedCommon{Type: "Targets", Version: 2, Expires: time.Now()}},
}
_, err := tg.ToSigned()
require.EqualError(t, err, "bad")
}
func TestTargetsMarshalJSONMarshalsSignedWithRegularJSON(t *testing.T) {
tg := SignedTargets{
Signed: Targets{SignedCommon: SignedCommon{Type: "Targets", Version: 1, Expires: time.Now()}},
Signatures: []Signature{
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
{KeyID: "key2", Method: "method2", Signature: []byte("there")},
},
}
serialized, err := tg.MarshalJSON()
require.NoError(t, err)
signed, err := tg.ToSigned()
require.NoError(t, err)
// don't bother testing canonical JSON because it might not be different
regular, err := rjson.Marshal(signed)
require.NoError(t, err)
require.True(t, bytes.Equal(serialized, regular),
"expected %v != %v", serialized, regular)
}
func TestTargetsMarshalJSONMarshallingErrorsPropagated(t *testing.T) {
setDefaultSerializer(errorSerializer{})
defer setDefaultSerializer(canonicalJSON{})
tg := SignedTargets{
Signed: Targets{SignedCommon: SignedCommon{Type: "Targets", Version: 2, Expires: time.Now()}},
}
_, err := tg.MarshalJSON()
require.EqualError(t, err, "bad")
}
func TestTargetsFromSignedUnmarshallingErrorsPropagated(t *testing.T) {
signed, err := validTargetsTemplate().ToSigned()
require.NoError(t, err)
setDefaultSerializer(errorSerializer{})
defer setDefaultSerializer(canonicalJSON{})
_, err = TargetsFromSigned(signed, CanonicalTargetsRole)
require.EqualError(t, err, "bad")
}
// TargetsFromSigned succeeds if the targets is valid, and copies the signatures
// rather than assigns them
func TestTargetsFromSignedCopiesSignatures(t *testing.T) {
for _, roleName := range []string{CanonicalTargetsRole, path.Join(CanonicalTargetsRole, "a")} {
signed, err := validTargetsTemplate().ToSigned()
require.NoError(t, err)
signedTargets, err := TargetsFromSigned(signed, roleName)
require.NoError(t, err)
signed.Signatures[0] = Signature{KeyID: "key3", Method: "method3", Signature: []byte("world")}
require.Equal(t, "key3", signed.Signatures[0].KeyID)
require.Equal(t, "key1", signedTargets.Signatures[0].KeyID)
}
}
// If the targets metadata contains delegations which are invalid, the targets metadata
// fails to validate and thus fails to convert into a SignedTargets
func TestTargetsFromSignedValidatesDelegations(t *testing.T) {
for _, roleName := range []string{CanonicalTargetsRole, path.Join(CanonicalTargetsRole, "a")} {
targets := validTargetsTemplate()
delgRole, err := NewRole(path.Join(roleName, "b"), 1, []string{"key1"}, nil)
require.NoError(t, err)
targets.Signed.Delegations.Roles = []*Role{delgRole}
// delegation has invalid threshold
delgRole.Threshold = 0
s, err := targets.ToSigned()
require.NoError(t, err)
_, err = TargetsFromSigned(s, roleName)
require.Error(t, err)
require.IsType(t, ErrInvalidMetadata{}, err)
delgRole.Threshold = 1
// Keys that aren't in the list of keys
delgRole.KeyIDs = []string{"keys11"}
s, err = targets.ToSigned()
require.NoError(t, err)
_, err = TargetsFromSigned(s, roleName)
require.Error(t, err)
require.IsType(t, ErrInvalidMetadata{}, err)
delgRole.KeyIDs = []string{"keys1"}
// not delegation role
delgRole.Name = CanonicalRootRole
s, err = targets.ToSigned()
require.NoError(t, err)
_, err = TargetsFromSigned(s, roleName)
require.Error(t, err)
require.IsType(t, ErrInvalidMetadata{}, err)
// more than one level deep
delgRole.Name = path.Join(roleName, "x", "y")
s, err = targets.ToSigned()
require.NoError(t, err)
_, err = TargetsFromSigned(s, roleName)
require.Error(t, err)
require.IsType(t, ErrInvalidMetadata{}, err)
// not in delegation hierarchy
if IsDelegation(roleName) {
delgRole.Name = path.Join(CanonicalTargetsRole, "z")
s, err := targets.ToSigned()
require.NoError(t, err)
_, err = TargetsFromSigned(s, roleName)
require.Error(t, err)
require.IsType(t, ErrInvalidMetadata{}, err)
}
}
}
// Type must be "Targets"
func TestTargetsFromSignedValidatesRoleType(t *testing.T) {
for _, roleName := range []string{CanonicalTargetsRole, path.Join(CanonicalTargetsRole, "a")} {
tg := validTargetsTemplate()
for _, invalid := range []string{" Targets", CanonicalTargetsRole, "TARGETS"} {
tg.Signed.Type = invalid
s, err := tg.ToSigned()
require.NoError(t, err)
_, err = TargetsFromSigned(s, roleName)
require.IsType(t, ErrInvalidMetadata{}, err)
}
tg = validTargetsTemplate()
tg.Signed.Type = "Targets"
s, err := tg.ToSigned()
require.NoError(t, err)
sTargets, err := TargetsFromSigned(s, roleName)
require.NoError(t, err)
require.Equal(t, "Targets", sTargets.Signed.Type)
}
}
// The rolename passed to TargetsFromSigned must be a valid targets role name
func TestTargetsFromSignedValidatesRoleName(t *testing.T) {
for _, roleName := range []string{"TARGETS", "root/a"} {
tg := validTargetsTemplate()
s, err := tg.ToSigned()
require.NoError(t, err)
_, err = TargetsFromSigned(s, roleName)
require.IsType(t, ErrInvalidRole{}, err)
}
}