1 package data
2
3 import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "path"
11 "strings"
12 "sync"
13 "time"
14
15 "github.com/secure-systems-lab/go-securesystemslib/cjson"
16 )
17
18 type KeyType string
19
20 type KeyScheme string
21
22 type HashAlgorithm string
23
24 const (
25 KeyIDLength = sha256.Size * 2
26
27 KeyTypeEd25519 KeyType = "ed25519"
28
29
30 KeyTypeECDSA_SHA2_P256 KeyType = "ecdsa"
31 KeyTypeECDSA_SHA2_P256_OLD_FMT KeyType = "ecdsa-sha2-nistp256"
32 KeyTypeRSASSA_PSS_SHA256 KeyType = "rsa"
33
34 KeySchemeEd25519 KeyScheme = "ed25519"
35 KeySchemeECDSA_SHA2_P256 KeyScheme = "ecdsa-sha2-nistp256"
36 KeySchemeRSASSA_PSS_SHA256 KeyScheme = "rsassa-pss-sha256"
37
38 HashAlgorithmSHA256 HashAlgorithm = "sha256"
39 HashAlgorithmSHA512 HashAlgorithm = "sha512"
40 )
41
42 var (
43 HashAlgorithms = []HashAlgorithm{HashAlgorithmSHA256, HashAlgorithmSHA512}
44 ErrPathsAndPathHashesSet = errors.New("tuf: failed validation of delegated target: paths and path_hash_prefixes are both set")
45 )
46
47 type Signed struct {
48 Signed json.RawMessage `json:"signed"`
49 Signatures []Signature `json:"signatures"`
50 }
51
52 type Signature struct {
53 KeyID string `json:"keyid"`
54 Signature HexBytes `json:"sig"`
55 }
56
57 type PublicKey struct {
58 Type KeyType `json:"keytype"`
59 Scheme KeyScheme `json:"scheme"`
60 Algorithms []HashAlgorithm `json:"keyid_hash_algorithms,omitempty"`
61 Value json.RawMessage `json:"keyval"`
62
63 ids []string
64 idOnce sync.Once
65 }
66
67 type PrivateKey struct {
68 Type KeyType `json:"keytype"`
69 Scheme KeyScheme `json:"scheme,omitempty"`
70 Algorithms []HashAlgorithm `json:"keyid_hash_algorithms,omitempty"`
71 Value json.RawMessage `json:"keyval"`
72 }
73
74 func (k *PublicKey) IDs() []string {
75 k.idOnce.Do(func() {
76 data, err := cjson.EncodeCanonical(k)
77 if err != nil {
78 panic(fmt.Errorf("tuf: error creating key ID: %w", err))
79 }
80 digest := sha256.Sum256(data)
81 k.ids = []string{hex.EncodeToString(digest[:])}
82 })
83 return k.ids
84 }
85
86 func (k *PublicKey) ContainsID(id string) bool {
87 for _, keyid := range k.IDs() {
88 if id == keyid {
89 return true
90 }
91 }
92 return false
93 }
94
95 func DefaultExpires(role string) time.Time {
96 var t time.Time
97 switch role {
98 case "root":
99 t = time.Now().AddDate(1, 0, 0)
100 case "snapshot":
101 t = time.Now().AddDate(0, 0, 7)
102 case "timestamp":
103 t = time.Now().AddDate(0, 0, 1)
104 default:
105
106 t = time.Now().AddDate(0, 3, 0)
107 }
108 return t.UTC().Round(time.Second)
109 }
110
111 type Root struct {
112 Type string `json:"_type"`
113 SpecVersion string `json:"spec_version"`
114 Version int64 `json:"version"`
115 Expires time.Time `json:"expires"`
116 Keys map[string]*PublicKey `json:"keys"`
117 Roles map[string]*Role `json:"roles"`
118 Custom *json.RawMessage `json:"custom,omitempty"`
119
120 ConsistentSnapshot bool `json:"consistent_snapshot"`
121 }
122
123 func NewRoot() *Root {
124 return &Root{
125 Type: "root",
126 SpecVersion: "1.0",
127 Expires: DefaultExpires("root"),
128 Keys: make(map[string]*PublicKey),
129 Roles: make(map[string]*Role),
130 ConsistentSnapshot: true,
131 }
132 }
133
134 func (r *Root) AddKey(key *PublicKey) bool {
135 changed := false
136 for _, id := range key.IDs() {
137 if _, ok := r.Keys[id]; !ok {
138 changed = true
139 r.Keys[id] = key
140 }
141 }
142 return changed
143 }
144
145 type Role struct {
146 KeyIDs []string `json:"keyids"`
147 Threshold int `json:"threshold"`
148 }
149
150 func (r *Role) AddKeyIDs(ids []string) bool {
151 roleIDs := make(map[string]struct{})
152 for _, id := range r.KeyIDs {
153 roleIDs[id] = struct{}{}
154 }
155 changed := false
156 for _, id := range ids {
157 if _, ok := roleIDs[id]; !ok {
158 changed = true
159 r.KeyIDs = append(r.KeyIDs, id)
160 }
161 }
162 return changed
163 }
164
165 type Files map[string]TargetFileMeta
166
167 type Hashes map[string]HexBytes
168
169 func (f Hashes) HashAlgorithms() []string {
170 funcs := make([]string, 0, len(f))
171 for name := range f {
172 funcs = append(funcs, name)
173 }
174 return funcs
175 }
176
177 type metapathFileMeta struct {
178 Length int64 `json:"length,omitempty"`
179 Hashes Hashes `json:"hashes,omitempty"`
180 Version int64 `json:"version"`
181 Custom *json.RawMessage `json:"custom,omitempty"`
182 }
183
184
185
186 type SnapshotFileMeta metapathFileMeta
187
188 type SnapshotFiles map[string]SnapshotFileMeta
189
190 type Snapshot struct {
191 Type string `json:"_type"`
192 SpecVersion string `json:"spec_version"`
193 Version int64 `json:"version"`
194 Expires time.Time `json:"expires"`
195 Meta SnapshotFiles `json:"meta"`
196 Custom *json.RawMessage `json:"custom,omitempty"`
197 }
198
199 func NewSnapshot() *Snapshot {
200 return &Snapshot{
201 Type: "snapshot",
202 SpecVersion: "1.0",
203 Expires: DefaultExpires("snapshot"),
204 Meta: make(SnapshotFiles),
205 }
206 }
207
208 type FileMeta struct {
209 Length int64 `json:"length"`
210 Hashes Hashes `json:"hashes"`
211 }
212
213 type TargetFiles map[string]TargetFileMeta
214
215 type TargetFileMeta struct {
216 FileMeta
217 Custom *json.RawMessage `json:"custom,omitempty"`
218 }
219
220 func (f TargetFileMeta) HashAlgorithms() []string {
221 return f.FileMeta.Hashes.HashAlgorithms()
222 }
223
224 type Targets struct {
225 Type string `json:"_type"`
226 SpecVersion string `json:"spec_version"`
227 Version int64 `json:"version"`
228 Expires time.Time `json:"expires"`
229 Targets TargetFiles `json:"targets"`
230 Delegations *Delegations `json:"delegations,omitempty"`
231 Custom *json.RawMessage `json:"custom,omitempty"`
232 }
233
234
235
236 type Delegations struct {
237 Keys map[string]*PublicKey `json:"keys"`
238 Roles []DelegatedRole `json:"roles"`
239 }
240
241
242
243 type DelegatedRole struct {
244 Name string `json:"name"`
245 KeyIDs []string `json:"keyids"`
246 Threshold int `json:"threshold"`
247 Terminating bool `json:"terminating"`
248 PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"`
249 Paths []string `json:"paths"`
250 }
251
252
253
254
255 func (d *DelegatedRole) MatchesPath(file string) (bool, error) {
256 if err := d.validatePaths(); err != nil {
257 return false, err
258 }
259
260 for _, pattern := range d.Paths {
261 if matched, _ := path.Match(pattern, file); matched {
262 return true, nil
263 }
264 }
265
266 pathHash := PathHexDigest(file)
267 for _, hashPrefix := range d.PathHashPrefixes {
268 if strings.HasPrefix(pathHash, hashPrefix) {
269 return true, nil
270 }
271 }
272
273 return false, nil
274 }
275
276
277
278
279
280
281 func (d *DelegatedRole) validatePaths() error {
282 if len(d.PathHashPrefixes) > 0 && len(d.Paths) > 0 {
283 return ErrPathsAndPathHashesSet
284 }
285
286 return nil
287 }
288
289
290
291
292 func (d *DelegatedRole) MarshalJSON() ([]byte, error) {
293 type delegatedRoleAlias DelegatedRole
294
295 if err := d.validatePaths(); err != nil {
296 return nil, err
297 }
298
299 return json.Marshal((*delegatedRoleAlias)(d))
300 }
301
302
303
304
305 func (d *DelegatedRole) UnmarshalJSON(b []byte) error {
306 type delegatedRoleAlias DelegatedRole
307
308
309 dec := json.NewDecoder(bytes.NewReader(b))
310
311
312 if err := dec.Decode((*delegatedRoleAlias)(d)); err != nil {
313 return err
314 }
315
316 return d.validatePaths()
317 }
318
319 func NewTargets() *Targets {
320 return &Targets{
321 Type: "targets",
322 SpecVersion: "1.0",
323 Expires: DefaultExpires("targets"),
324 Targets: make(TargetFiles),
325 }
326 }
327
328 type TimestampFileMeta metapathFileMeta
329
330 type TimestampFiles map[string]TimestampFileMeta
331
332 type Timestamp struct {
333 Type string `json:"_type"`
334 SpecVersion string `json:"spec_version"`
335 Version int64 `json:"version"`
336 Expires time.Time `json:"expires"`
337 Meta TimestampFiles `json:"meta"`
338 Custom *json.RawMessage `json:"custom,omitempty"`
339 }
340
341 func NewTimestamp() *Timestamp {
342 return &Timestamp{
343 Type: "timestamp",
344 SpecVersion: "1.0",
345 Expires: DefaultExpires("timestamp"),
346 Meta: make(TimestampFiles),
347 }
348 }
349
View as plain text