docs/tuf/data/snapshot_test.go

204 lines
6.4 KiB
Go

package data
import (
"bytes"
"crypto/sha256"
"crypto/sha512"
rjson "encoding/json"
"reflect"
"testing"
"time"
cjson "github.com/docker/go/canonical/json"
"github.com/stretchr/testify/require"
)
func validSnapshotTemplate() *SignedSnapshot {
return &SignedSnapshot{
Signed: Snapshot{
Type: "Snapshot", Version: 1, Expires: time.Now(), Meta: Files{
CanonicalRootRole: FileMeta{Hashes: Hashes{"sha256": bytes.Repeat([]byte("a"), sha256.Size)}},
CanonicalTargetsRole: FileMeta{Hashes: Hashes{"sha256": bytes.Repeat([]byte("a"), sha256.Size)}},
"targets/a": FileMeta{},
}},
Signatures: []Signature{
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
},
}
}
func TestSnapshotToSignedMarshalsSignedPortionWithCanonicalJSON(t *testing.T) {
sn := SignedSnapshot{Signed: Snapshot{Type: "Snapshot", Version: 1, Expires: time.Now()}}
signedCanonical, err := sn.ToSigned()
require.NoError(t, err)
canonicalSignedPortion, err := cjson.MarshalCanonical(sn.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 TestSnapshotToSignCopiesSignatures(t *testing.T) {
sn := SignedSnapshot{
Signed: Snapshot{Type: "Snapshot", Version: 2, Expires: time.Now()},
Signatures: []Signature{
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
},
}
signed, err := sn.ToSigned()
require.NoError(t, err)
require.True(t, reflect.DeepEqual(sn.Signatures, signed.Signatures),
"expected %v == %v", sn.Signatures, signed.Signatures)
sn.Signatures[0].KeyID = "changed"
require.False(t, reflect.DeepEqual(sn.Signatures, signed.Signatures),
"expected %v != %v", sn.Signatures, signed.Signatures)
}
func TestSnapshotToSignedMarshallingErrorsPropagated(t *testing.T) {
setDefaultSerializer(errorSerializer{})
defer setDefaultSerializer(canonicalJSON{})
sn := SignedSnapshot{
Signed: Snapshot{Type: "Snapshot", Version: 2, Expires: time.Now()},
}
_, err := sn.ToSigned()
require.EqualError(t, err, "bad")
}
func TestSnapshotMarshalJSONMarshalsSignedWithRegularJSON(t *testing.T) {
sn := SignedSnapshot{
Signed: Snapshot{Type: "Snapshot", Version: 1, Expires: time.Now()},
Signatures: []Signature{
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
{KeyID: "key2", Method: "method2", Signature: []byte("there")},
},
}
serialized, err := sn.MarshalJSON()
require.NoError(t, err)
signed, err := sn.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 TestSnapshotMarshalJSONMarshallingErrorsPropagated(t *testing.T) {
setDefaultSerializer(errorSerializer{})
defer setDefaultSerializer(canonicalJSON{})
sn := SignedSnapshot{
Signed: Snapshot{Type: "Snapshot", Version: 2, Expires: time.Now()},
}
_, err := sn.MarshalJSON()
require.EqualError(t, err, "bad")
}
func TestSnapshotFromSignedUnmarshallingErrorsPropagated(t *testing.T) {
signed, err := validSnapshotTemplate().ToSigned()
require.NoError(t, err)
setDefaultSerializer(errorSerializer{})
defer setDefaultSerializer(canonicalJSON{})
_, err = SnapshotFromSigned(signed)
require.EqualError(t, err, "bad")
}
// SnapshotFromSigned succeeds if the snapshot is valid, and copies the signatures
// rather than assigns them
func TestSnapshotFromSignedCopiesSignatures(t *testing.T) {
signed, err := validSnapshotTemplate().ToSigned()
require.NoError(t, err)
signedSnapshot, err := SnapshotFromSigned(signed)
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", signedSnapshot.Signatures[0].KeyID)
}
func snapshotToSignedAndBack(t *testing.T, snapshot *SignedSnapshot) (*SignedSnapshot, error) {
s, err := snapshot.ToSigned()
require.NoError(t, err)
return SnapshotFromSigned(s)
}
// If the root or targets metadata is missing, the snapshot metadata fails to validate
// and thus fails to convert into a SignedSnapshot
func TestSnapshotFromSignedValidatesMeta(t *testing.T) {
var err error
for _, roleName := range []string{CanonicalRootRole, CanonicalTargetsRole} {
sn := validSnapshotTemplate()
// invalid checksum length
sn.Signed.Meta[roleName].Hashes["sha256"] = []byte("too short")
_, err = snapshotToSignedAndBack(t, sn)
require.IsType(t, ErrInvalidMetadata{}, err)
// missing sha256 checksum
delete(sn.Signed.Meta[roleName].Hashes, "sha256")
_, err = snapshotToSignedAndBack(t, sn)
require.IsType(t, ErrInvalidMetadata{}, err)
// add a different checksum to make sure it's not failing because of the hash length
sn.Signed.Meta[roleName].Hashes["sha512"] = bytes.Repeat([]byte("a"), sha512.Size)
_, err = snapshotToSignedAndBack(t, sn)
require.IsType(t, ErrInvalidMetadata{}, err)
// delete the ckechsum metadata entirely for the role
delete(sn.Signed.Meta, roleName)
_, err = snapshotToSignedAndBack(t, sn)
require.IsType(t, ErrInvalidMetadata{}, err)
// add some extra metadata to make sure it's not failing because the metadata
// is empty
sn.Signed.Meta[CanonicalSnapshotRole] = FileMeta{}
_, err = snapshotToSignedAndBack(t, sn)
require.IsType(t, ErrInvalidMetadata{}, err)
}
}
// Type must be "Snapshot"
func TestSnapshotFromSignedValidatesRoleType(t *testing.T) {
sn := validSnapshotTemplate()
for _, invalid := range []string{" Snapshot", CanonicalSnapshotRole, "TIMESTAMP"} {
sn.Signed.Type = invalid
_, err := snapshotToSignedAndBack(t, sn)
require.IsType(t, ErrInvalidMetadata{}, err)
}
sn = validSnapshotTemplate()
sn.Signed.Type = "Snapshot"
sSnapshot, err := snapshotToSignedAndBack(t, sn)
require.NoError(t, err)
require.Equal(t, "Snapshot", sSnapshot.Signed.Type)
}
// GetMeta returns the checksum, or an error if it is missing.
func TestSnapshotGetMeta(t *testing.T) {
ts := validSnapshotTemplate()
f, err := ts.GetMeta(CanonicalRootRole)
require.NoError(t, err)
require.IsType(t, &FileMeta{}, f)
// now one that doesn't exist
f, err = ts.GetMeta("targets/a/b")
require.Error(t, err)
require.IsType(t, ErrMissingMeta{}, err)
require.Nil(t, f)
}