This repository was archived by the owner on Feb 14, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathroles.go
More file actions
256 lines (220 loc) · 6.75 KB
/
roles.go
File metadata and controls
256 lines (220 loc) · 6.75 KB
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package tuf
import (
"bytes"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"hash"
"io"
"io/ioutil"
"time"
cjson "github.com/docker/go/canonical/json"
"github.com/pkg/errors"
)
// targetNameType is the path to the target. Mirror host + targetNameType is the
// download location of the file. For example if the mirror is https://releases.kolide.co
// and the targetNameType is kolide/agent/linux/installer then you'd download
// https://releases.kolide.co/kolide/agent/linux/installer
type targetNameType string
type keyID string
type hashingMethod string
type role string
type signingMethod string
const (
// Signing Methods
methodRSA signingMethod = "rsa"
methodED25519 signingMethod = "ed25519"
methodECDSA signingMethod = "ecdsa"
// Roles
roleRoot role = "root"
roleSnapshot role = "snapshot"
roleTargets role = "targets"
roleTimestamp role = "timestamp"
// Key Types
keyTypeRSAx509 = "rsa-x509"
keyTypeECDSA = "ecdsa"
keyTypeECDSAx509 = "ecdsa-x509"
keyTypeED25519 = "ed25519"
hashSHA256 hashingMethod = "sha256"
hashSHA512 hashingMethod = "sha512"
filetimeFormat = "20060102150405"
)
type marshaller interface {
canonicalJSON() ([]byte, error)
}
type base64decoder interface {
base64Decoded() ([]byte, error)
}
type keyfinder interface {
keyMap() map[keyID]Key
}
type keyed interface {
keys() map[keyID]Key
}
type signed interface {
sigs() []Signature
}
type signedkeyed interface {
keyed
signed
}
// Root is the root role. It indicates
// which keys are authorized for all top-level roles, including the root
// role itself.
type Root struct {
Signed SignedRoot `json:"signed"`
Signatures []Signature `json:"signatures"`
}
// Keys get key map for root role
func (r *Root) keys() map[keyID]Key {
return r.Signed.Keys
}
// Sigs get signatures for root role
func (r *Root) sigs() []Signature {
return r.Signatures
}
// SignedRoot signed contents of the root role
type SignedRoot struct {
Type string `json:"_type"`
ConsistentSnapshot bool `json:"consistent_snapshot"`
Expires time.Time `json:"expires"`
Keys map[keyID]Key `json:"keys"`
Roles map[role]Role `json:"roles"`
Version int `json:"version"`
}
func (sr SignedRoot) canonicalJSON() ([]byte, error) {
return cjson.MarshalCanonical(sr)
}
// Snapshot is the snapshot role. It lists the version
// numbers of all metadata on the repository, excluding timestamp.json and
// mirrors.json.
type Snapshot struct {
Signed SignedSnapshot `json:"signed"`
Signatures []Signature `json:"signatures"`
}
// SignedSnapshot is the signed portion of the snapshot
type SignedSnapshot struct {
Type string `json:"_type"`
Expires time.Time `json:"expires"`
Version int `json:"version"`
Meta map[role]FileIntegrityMeta `json:"meta"`
}
func (sr SignedSnapshot) canonicalJSON() ([]byte, error) {
return cjson.MarshalCanonical(sr)
}
// Timestamp role indicates the latest versions of other files and is frequently resigned to limit the
// amount of time a client can be kept unaware of interference with obtaining updates.
type Timestamp struct {
Signed SignedTimestamp `json:"signed"`
Signatures []Signature `json:"signatures"`
}
// SignedTimestamp signed portion of timestamp role.
type SignedTimestamp struct {
Type string `json:"_type"`
Expires time.Time `json:"expires"`
Version int `json:"version"`
Meta map[role]FileIntegrityMeta `json:"meta"`
}
func (sr SignedTimestamp) canonicalJSON() ([]byte, error) {
return cjson.MarshalCanonical(sr)
}
// Targets represents TUF role of the same name.
// See https://github.com/theupdateframework/tuf/blob/develop/docs/tuf-spec.txt
type Targets struct {
Signed SignedTarget `json:"signed"`
Signatures []Signature `json:"signatures"`
}
// SignedTarget specifics of the Targets
type SignedTarget struct {
Type string `json:"_type"`
Delegations Delegations `json:"delegations"`
Expires time.Time `json:"expires"`
Targets map[targetNameType]FileIntegrityMeta `json:"targets"`
Version int `json:"version"`
}
func (sr SignedTarget) canonicalJSON() ([]byte, error) {
return cjson.MarshalCanonical(sr)
}
// Signature information to validate digital signatures
type Signature struct {
KeyID keyID `json:"keyid"`
SigningMethod signingMethod `json:"method"`
Value string `json:"sig"`
}
func (sig *Signature) base64Decoded() ([]byte, error) {
return base64.StdEncoding.DecodeString(sig.Value)
}
// FileIntegrityMeta hashes and length of a file based resource to help ensure
// the binary footprint of the file hasn't been tampered with
type FileIntegrityMeta struct {
Hashes map[hashingMethod]string `json:"hashes"`
Length int `json:"length"`
}
type hashInfo struct {
h hash.Hash
valid []byte
}
// File hash and length validation per TUF 5.5.2
func (fim FileIntegrityMeta) verify(rdr io.Reader) error {
var hashes []hashInfo
for algo, expectedHash := range fim.Hashes {
var hashFunc hash.Hash
valid, err := base64.StdEncoding.DecodeString(expectedHash)
if err != nil {
return errors.New("invalid hash in verify")
}
switch algo {
case hashSHA256:
hashFunc = sha256.New()
case hashSHA512:
hashFunc = sha512.New()
default:
return errUnsupportedHash
}
rdr = io.TeeReader(rdr, hashFunc)
hashes = append(hashes, hashInfo{hashFunc, valid})
}
length, err := io.Copy(ioutil.Discard, rdr)
if err != nil {
return err
}
if length != int64(fim.Length) {
return errLengthIncorrect
}
for _, h := range hashes {
if !bytes.Equal(h.valid, h.h.Sum(nil)) {
return errHashIncorrect
}
}
return nil
}
// Delegations signing information for targets hosted by external principals
type Delegations struct {
Keys map[keyID]Key `json:"keys"`
Roles []DelegationRole `json:"roles"`
}
// Role maps keys in role that are needed to check signatures.
type Role struct {
KeyIDs []string `json:"keyids"`
Threshold int `json:"threshold"`
}
// DelegationRole contains information about targets delegated to other mirrors.
type DelegationRole struct {
Role
Name string `json:"name"`
Paths []string `json:"paths"`
}
// Key signing key with key type
type Key struct {
KeyType string `json:"keytype"`
KeyVal KeyVal `json:"keyval"`
}
// we only really care about the public key
func (k *Key) base64Decoded() ([]byte, error) {
return base64.StdEncoding.DecodeString(k.KeyVal.Public)
}
// KeyVal the contents of the private and/or public keys
type KeyVal struct {
Private *string `json:"private"`
Public string `json:"public"`
}