-
Notifications
You must be signed in to change notification settings - Fork 0
/
snapshot.go
169 lines (155 loc) · 4.7 KB
/
snapshot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package data
import (
"bytes"
"fmt"
"github.com/Sirupsen/logrus"
"github.com/docker/go/canonical/json"
"github.com/docker/notary"
)
// SignedSnapshot is a fully unpacked snapshot.json
type SignedSnapshot struct {
Signatures []Signature
Signed Snapshot
Dirty bool
}
// Snapshot is the Signed component of a snapshot.json
type Snapshot struct {
SignedCommon
Meta Files `json:"meta"`
}
// IsValidSnapshotStructure returns an error, or nil, depending on whether the content of the
// struct is valid for snapshot metadata. This does not check signatures or expiry, just that
// the metadata content is valid.
func IsValidSnapshotStructure(s Snapshot) error {
expectedType := TUFTypes[CanonicalSnapshotRole]
if s.Type != expectedType {
return ErrInvalidMetadata{
role: CanonicalSnapshotRole, msg: fmt.Sprintf("expected type %s, not %s", expectedType, s.Type)}
}
if s.Version < 1 {
return ErrInvalidMetadata{
role: CanonicalSnapshotRole, msg: "version cannot be less than one"}
}
for _, file := range []RoleName{CanonicalRootRole, CanonicalTargetsRole} {
// Meta is a map of FileMeta, so if the role isn't in the map it returns
// an empty FileMeta, which has an empty map, and you can check on keys
// from an empty map.
//
// For now sha256 is required and sha512 is not.
if _, ok := s.Meta[file.String()].Hashes[notary.SHA256]; !ok {
return ErrInvalidMetadata{
role: CanonicalSnapshotRole,
msg: fmt.Sprintf("missing %s sha256 checksum information", file.String()),
}
}
if err := CheckValidHashStructures(s.Meta[file.String()].Hashes); err != nil {
return ErrInvalidMetadata{
role: CanonicalSnapshotRole,
msg: fmt.Sprintf("invalid %s checksum information, %v", file.String(), err),
}
}
}
return nil
}
// NewSnapshot initilizes a SignedSnapshot with a given top level root
// and targets objects
func NewSnapshot(root *Signed, targets *Signed) (*SignedSnapshot, error) {
logrus.Debug("generating new snapshot...")
targetsJSON, err := json.Marshal(targets)
if err != nil {
logrus.Debug("Error Marshalling Targets")
return nil, err
}
rootJSON, err := json.Marshal(root)
if err != nil {
logrus.Debug("Error Marshalling Root")
return nil, err
}
rootMeta, err := NewFileMeta(bytes.NewReader(rootJSON), NotaryDefaultHashes...)
if err != nil {
return nil, err
}
targetsMeta, err := NewFileMeta(bytes.NewReader(targetsJSON), NotaryDefaultHashes...)
if err != nil {
return nil, err
}
return &SignedSnapshot{
Signatures: make([]Signature, 0),
Signed: Snapshot{
SignedCommon: SignedCommon{
Type: TUFTypes[CanonicalSnapshotRole],
Version: 0,
Expires: DefaultExpires(CanonicalSnapshotRole),
},
Meta: Files{
CanonicalRootRole.String(): rootMeta,
CanonicalTargetsRole.String(): targetsMeta,
},
},
}, nil
}
// ToSigned partially serializes a SignedSnapshot for further signing
func (sp *SignedSnapshot) ToSigned() (*Signed, error) {
s, err := defaultSerializer.MarshalCanonical(sp.Signed)
if err != nil {
return nil, err
}
signed := json.RawMessage{}
err = signed.UnmarshalJSON(s)
if err != nil {
return nil, err
}
sigs := make([]Signature, len(sp.Signatures))
copy(sigs, sp.Signatures)
return &Signed{
Signatures: sigs,
Signed: &signed,
}, nil
}
// AddMeta updates a role in the snapshot with new meta
func (sp *SignedSnapshot) AddMeta(role RoleName, meta FileMeta) {
sp.Signed.Meta[role.String()] = meta
sp.Dirty = true
}
// GetMeta gets the metadata for a particular role, returning an error if it's
// not found
func (sp *SignedSnapshot) GetMeta(role RoleName) (*FileMeta, error) {
if meta, ok := sp.Signed.Meta[role.String()]; ok {
if _, ok := meta.Hashes["sha256"]; ok {
return &meta, nil
}
}
return nil, ErrMissingMeta{Role: role.String()}
}
// DeleteMeta removes a role from the snapshot. If the role doesn't
// exist in the snapshot, it's a noop.
func (sp *SignedSnapshot) DeleteMeta(role RoleName) {
if _, ok := sp.Signed.Meta[role.String()]; ok {
delete(sp.Signed.Meta, role.String())
sp.Dirty = true
}
}
// MarshalJSON returns the serialized form of SignedSnapshot as bytes
func (sp *SignedSnapshot) MarshalJSON() ([]byte, error) {
signed, err := sp.ToSigned()
if err != nil {
return nil, err
}
return defaultSerializer.Marshal(signed)
}
// SnapshotFromSigned fully unpacks a Signed object into a SignedSnapshot
func SnapshotFromSigned(s *Signed) (*SignedSnapshot, error) {
sp := Snapshot{}
if err := defaultSerializer.Unmarshal(*s.Signed, &sp); err != nil {
return nil, err
}
if err := IsValidSnapshotStructure(sp); err != nil {
return nil, err
}
sigs := make([]Signature, len(s.Signatures))
copy(sigs, s.Signatures)
return &SignedSnapshot{
Signatures: sigs,
Signed: sp,
}, nil
}