mirror of https://github.com/docker/docs.git
215 lines
6.4 KiB
Go
215 lines
6.4 KiB
Go
package data
|
|
|
|
import (
|
|
"bytes"
|
|
rjson "encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
cjson "github.com/docker/go/canonical/json"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type errorSerializer struct {
|
|
canonicalJSON
|
|
}
|
|
|
|
func (e errorSerializer) MarshalCanonical(interface{}) ([]byte, error) {
|
|
return nil, fmt.Errorf("bad")
|
|
}
|
|
|
|
func (e errorSerializer) Unmarshal([]byte, interface{}) error {
|
|
return fmt.Errorf("bad")
|
|
}
|
|
|
|
func validRootTemplate() *SignedRoot {
|
|
return &SignedRoot{
|
|
Signed: Root{
|
|
Type: "Root",
|
|
Version: 1,
|
|
Expires: time.Now(),
|
|
Keys: Keys{
|
|
"key1": NewPublicKey(RSAKey, []byte("key1")),
|
|
"key2": NewPublicKey(RSAKey, []byte("key2")),
|
|
"key3": NewPublicKey(RSAKey, []byte("key3")),
|
|
"snKey": NewPublicKey(RSAKey, []byte("snKey")),
|
|
"tgKey": NewPublicKey(RSAKey, []byte("tgKey")),
|
|
"tsKey": NewPublicKey(RSAKey, []byte("tsKey")),
|
|
},
|
|
Roles: map[string]*RootRole{
|
|
CanonicalRootRole: {KeyIDs: []string{"key1"}, Threshold: 1},
|
|
CanonicalSnapshotRole: {KeyIDs: []string{"snKey"}, Threshold: 1},
|
|
CanonicalTimestampRole: {KeyIDs: []string{"tsKey"}, Threshold: 1},
|
|
CanonicalTargetsRole: {KeyIDs: []string{"tgKey"}, Threshold: 1},
|
|
},
|
|
},
|
|
Signatures: []Signature{
|
|
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestRootToSignedMarshalsSignedPortionWithCanonicalJSON(t *testing.T) {
|
|
r := SignedRoot{Signed: Root{Type: "root", Version: 2, Expires: time.Now()}}
|
|
signedCanonical, err := r.ToSigned()
|
|
require.NoError(t, err)
|
|
|
|
canonicalSignedPortion, err := cjson.MarshalCanonical(r.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 TestRootToSignCopiesSignatures(t *testing.T) {
|
|
r := SignedRoot{
|
|
Signed: Root{Type: "root", Version: 2, Expires: time.Now()},
|
|
Signatures: []Signature{
|
|
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
|
|
},
|
|
}
|
|
signed, err := r.ToSigned()
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, reflect.DeepEqual(r.Signatures, signed.Signatures),
|
|
"expected %v == %v", r.Signatures, signed.Signatures)
|
|
|
|
r.Signatures[0].KeyID = "changed"
|
|
require.False(t, reflect.DeepEqual(r.Signatures, signed.Signatures),
|
|
"expected %v != %v", r.Signatures, signed.Signatures)
|
|
}
|
|
|
|
func TestRootToSignedMarshallingErrorsPropagated(t *testing.T) {
|
|
setDefaultSerializer(errorSerializer{})
|
|
defer setDefaultSerializer(canonicalJSON{})
|
|
r := SignedRoot{
|
|
Signed: Root{Type: "root", Version: 2, Expires: time.Now()},
|
|
}
|
|
_, err := r.ToSigned()
|
|
require.EqualError(t, err, "bad")
|
|
}
|
|
|
|
func TestRootMarshalJSONMarshalsSignedWithRegularJSON(t *testing.T) {
|
|
r := SignedRoot{
|
|
Signed: Root{Type: "root", Version: 2, Expires: time.Now()},
|
|
Signatures: []Signature{
|
|
{KeyID: "key1", Method: "method1", Signature: []byte("hello")},
|
|
{KeyID: "key2", Method: "method2", Signature: []byte("there")},
|
|
},
|
|
}
|
|
serialized, err := r.MarshalJSON()
|
|
require.NoError(t, err)
|
|
|
|
signed, err := r.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 TestRootMarshalJSONMarshallingErrorsPropagated(t *testing.T) {
|
|
setDefaultSerializer(errorSerializer{})
|
|
defer setDefaultSerializer(canonicalJSON{})
|
|
r := SignedRoot{
|
|
Signed: Root{Type: "root", Version: 2, Expires: time.Now()},
|
|
}
|
|
_, err := r.MarshalJSON()
|
|
require.EqualError(t, err, "bad")
|
|
}
|
|
|
|
func TestRootFromSignedUnmarshallingErrorsPropagated(t *testing.T) {
|
|
signed, err := validRootTemplate().ToSigned()
|
|
require.NoError(t, err)
|
|
|
|
setDefaultSerializer(errorSerializer{})
|
|
defer setDefaultSerializer(canonicalJSON{})
|
|
|
|
_, err = RootFromSigned(signed)
|
|
require.EqualError(t, err, "bad")
|
|
}
|
|
|
|
// RootFromSigned succeeds if the root is valid, and copies the signatures
|
|
// rather than assigns them
|
|
func TestRootFromSignedCopiesSignatures(t *testing.T) {
|
|
signed, err := validRootTemplate().ToSigned()
|
|
require.NoError(t, err)
|
|
|
|
signedRoot, err := RootFromSigned(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", signedRoot.Signatures[0].KeyID)
|
|
}
|
|
|
|
func rootToSignedAndBack(t *testing.T, root *SignedRoot) (*SignedRoot, error) {
|
|
s, err := root.ToSigned()
|
|
require.NoError(t, err)
|
|
return RootFromSigned(s)
|
|
}
|
|
|
|
// If the role data specified is nil, or has an invalid threshold, or doesn't have enough
|
|
// keys to cover the threshold, or has key IDs that are not in the key list, the root
|
|
// metadata fails to validate and thus fails to convert into a SignedRoot
|
|
func TestRootFromSignedValidatesRoleData(t *testing.T) {
|
|
var err error
|
|
for _, roleName := range BaseRoles {
|
|
root := validRootTemplate()
|
|
|
|
// Invalid threshold
|
|
root.Signed.Roles[roleName].Threshold = 0
|
|
_, err = rootToSignedAndBack(t, root)
|
|
require.IsType(t, ErrInvalidMetadata{}, err)
|
|
|
|
// Keys that aren't in the list of keys
|
|
root.Signed.Roles[roleName].Threshold = 1
|
|
root.Signed.Roles[roleName].KeyIDs = []string{"key11"}
|
|
_, err = rootToSignedAndBack(t, root)
|
|
require.IsType(t, ErrInvalidMetadata{}, err)
|
|
|
|
// role is nil
|
|
root.Signed.Roles[roleName] = nil
|
|
_, err = rootToSignedAndBack(t, root)
|
|
require.IsType(t, ErrInvalidMetadata{}, err)
|
|
|
|
// too few roles
|
|
delete(root.Signed.Roles, roleName)
|
|
_, err = rootToSignedAndBack(t, root)
|
|
require.IsType(t, ErrInvalidMetadata{}, err)
|
|
|
|
// add an extra role that doesn't belong, so that the number of roles
|
|
// is correct a required one is still missing
|
|
root.Signed.Roles["extraneous"] = &RootRole{KeyIDs: []string{"key3"}, Threshold: 1}
|
|
_, err = rootToSignedAndBack(t, root)
|
|
require.IsType(t, ErrInvalidMetadata{}, err)
|
|
}
|
|
}
|
|
|
|
// The type must be "Root"
|
|
func TestRootFromSignedValidatesRoleType(t *testing.T) {
|
|
root := validRootTemplate()
|
|
|
|
for _, invalid := range []string{"Root ", CanonicalSnapshotRole, "rootroot", "RoOt", "root"} {
|
|
root.Signed.Type = invalid
|
|
_, err := rootToSignedAndBack(t, root)
|
|
require.IsType(t, ErrInvalidMetadata{}, err)
|
|
}
|
|
|
|
root.Signed.Type = "Root"
|
|
sRoot, err := rootToSignedAndBack(t, root)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Root", sRoot.Signed.Type)
|
|
}
|