add custom fields to all top level roles (#162)

* add custom fields to all top level roles

* add tests

* update Key => PublicKey
diff --git a/data/types.go b/data/types.go
index b21e4b4..700fa15 100644
--- a/data/types.go
+++ b/data/types.go
@@ -99,6 +99,7 @@
 	Expires     time.Time             `json:"expires"`
 	Keys        map[string]*PublicKey `json:"keys"`
 	Roles       map[string]*Role      `json:"roles"`
+	Custom      *json.RawMessage      `json:"custom,omitempty"`
 
 	ConsistentSnapshot bool `json:"consistent_snapshot"`
 }
@@ -171,11 +172,12 @@
 type SnapshotFiles map[string]SnapshotFileMeta
 
 type Snapshot struct {
-	Type        string        `json:"_type"`
-	SpecVersion string        `json:"spec_version"`
-	Version     int           `json:"version"`
-	Expires     time.Time     `json:"expires"`
-	Meta        SnapshotFiles `json:"meta"`
+	Type        string           `json:"_type"`
+	SpecVersion string           `json:"spec_version"`
+	Version     int              `json:"version"`
+	Expires     time.Time        `json:"expires"`
+	Meta        SnapshotFiles    `json:"meta"`
+	Custom      *json.RawMessage `json:"custom,omitempty"`
 }
 
 func NewSnapshot() *Snapshot {
@@ -198,12 +200,13 @@
 }
 
 type Targets struct {
-	Type        string       `json:"_type"`
-	SpecVersion string       `json:"spec_version"`
-	Version     int          `json:"version"`
-	Expires     time.Time    `json:"expires"`
-	Targets     TargetFiles  `json:"targets"`
-	Delegations *Delegations `json:"delegations,omitempty"`
+	Type        string           `json:"_type"`
+	SpecVersion string           `json:"spec_version"`
+	Version     int              `json:"version"`
+	Expires     time.Time        `json:"expires"`
+	Targets     TargetFiles      `json:"targets"`
+	Delegations *Delegations     `json:"delegations,omitempty"`
+	Custom      *json.RawMessage `json:"custom,omitempty"`
 }
 
 // Delegations represents the edges from a parent Targets role to one or more
@@ -304,11 +307,12 @@
 type TimestampFiles map[string]TimestampFileMeta
 
 type Timestamp struct {
-	Type        string         `json:"_type"`
-	SpecVersion string         `json:"spec_version"`
-	Version     int            `json:"version"`
-	Expires     time.Time      `json:"expires"`
-	Meta        TimestampFiles `json:"meta"`
+	Type        string           `json:"_type"`
+	SpecVersion string           `json:"spec_version"`
+	Version     int              `json:"version"`
+	Expires     time.Time        `json:"expires"`
+	Meta        TimestampFiles   `json:"meta"`
+	Custom      *json.RawMessage `json:"custom,omitempty"`
 }
 
 func NewTimestamp() *Timestamp {
diff --git a/data/types_test.go b/data/types_test.go
index 7bf4595..c7022d6 100644
--- a/data/types_test.go
+++ b/data/types_test.go
@@ -239,3 +239,49 @@
 	err := json.Unmarshal([]byte(`{"keyids":"a"}`), &d)
 	assert.Equal(t, "keyids", err.(*json.UnmarshalTypeError).Field)
 }
+
+func TestCustomField(t *testing.T) {
+	testCustomJSON := json.RawMessage([]byte(`{"test":true}`))
+
+	root := Root{
+		Type:               "root",
+		SpecVersion:        "1.0",
+		Keys:               make(map[string]*PublicKey),
+		Roles:              make(map[string]*Role),
+		ConsistentSnapshot: true,
+		Custom:             &testCustomJSON,
+	}
+	rootJSON, err := json.Marshal(&root)
+	assert.NoError(t, err)
+	assert.Equal(t, []byte("{\"_type\":\"root\",\"spec_version\":\"1.0\",\"version\":0,\"expires\":\"0001-01-01T00:00:00Z\",\"keys\":{},\"roles\":{},\"custom\":{\"test\":true},\"consistent_snapshot\":true}"), rootJSON)
+
+	targets := Targets{
+		Type:        "targets",
+		SpecVersion: "1.0",
+		Targets:     make(TargetFiles),
+		Custom:      &testCustomJSON,
+	}
+	targetsJSON, err := json.Marshal(&targets)
+	assert.NoError(t, err)
+	assert.Equal(t, []byte("{\"_type\":\"targets\",\"spec_version\":\"1.0\",\"version\":0,\"expires\":\"0001-01-01T00:00:00Z\",\"targets\":{},\"custom\":{\"test\":true}}"), targetsJSON)
+
+	snapshot := Snapshot{
+		Type:        "snapshot",
+		SpecVersion: "1.0",
+		Meta:        make(SnapshotFiles),
+		Custom:      &testCustomJSON,
+	}
+	snapshotJSON, err := json.Marshal(&snapshot)
+	assert.NoError(t, err)
+	assert.Equal(t, []byte("{\"_type\":\"snapshot\",\"spec_version\":\"1.0\",\"version\":0,\"expires\":\"0001-01-01T00:00:00Z\",\"meta\":{},\"custom\":{\"test\":true}}"), snapshotJSON)
+
+	timestamp := Timestamp{
+		Type:        "timestamp",
+		SpecVersion: "1.0",
+		Meta:        make(TimestampFiles),
+		Custom:      &testCustomJSON,
+	}
+	timestampJSON, err := json.Marshal(&timestamp)
+	assert.NoError(t, err)
+	assert.Equal(t, []byte("{\"_type\":\"timestamp\",\"spec_version\":\"1.0\",\"version\":0,\"expires\":\"0001-01-01T00:00:00Z\",\"meta\":{},\"custom\":{\"test\":true}}"), timestampJSON)
+}