forked from theupdateframework/go-tuf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
verify.go
175 lines (151 loc) · 4.27 KB
/
verify.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
170
171
172
173
174
175
package verify
import (
"encoding/json"
"strings"
"time"
"github.com/secure-systems-lab/go-securesystemslib/cjson"
"github.com/theupdateframework/go-tuf/data"
"github.com/theupdateframework/go-tuf/internal/roles"
)
type signedMeta struct {
Type string `json:"_type"`
Expires time.Time `json:"expires"`
Version int64 `json:"version"`
}
func (db *DB) VerifyIgnoreExpiredCheck(s *data.Signed, role string, minVersion int64) error {
if err := db.VerifySignatures(s, role); err != nil {
return err
}
sm := &signedMeta{}
if err := json.Unmarshal(s.Signed, sm); err != nil {
return err
}
if roles.IsTopLevelRole(role) {
// Top-level roles can only sign metadata of the same type (e.g. snapshot
// metadata must be signed by the snapshot role).
if !strings.EqualFold(sm.Type, role) {
return ErrWrongMetaType
}
} else {
// Delegated (non-top-level) roles may only sign targets metadata.
if strings.ToLower(sm.Type) != "targets" {
return ErrWrongMetaType
}
}
if sm.Version < minVersion {
return ErrLowVersion{sm.Version, minVersion}
}
return nil
}
func (db *DB) Verify(s *data.Signed, role string, minVersion int64) error {
// Verify signatures and versions
err := db.VerifyIgnoreExpiredCheck(s, role, minVersion)
if err != nil {
return err
}
sm := &signedMeta{}
if err := json.Unmarshal(s.Signed, sm); err != nil {
return err
}
// Verify expiration
if IsExpired(sm.Expires) {
return ErrExpired{sm.Expires}
}
return nil
}
var IsExpired = func(t time.Time) bool {
return time.Until(t) <= 0
}
func (db *DB) VerifySignatures(s *data.Signed, role string) error {
if len(s.Signatures) == 0 {
return ErrNoSignatures
}
roleData := db.GetRole(role)
if roleData == nil {
return ErrUnknownRole{role}
}
var decoded map[string]interface{}
if err := json.Unmarshal(s.Signed, &decoded); err != nil {
return err
}
msg, err := cjson.EncodeCanonical(decoded)
if err != nil {
return err
}
// Verify that a threshold of keys signed the data. Since keys can have
// multiple key ids, we need to protect against multiple attached
// signatures that just differ on the key id.
verifiedKeyIDs := make(map[string]struct{})
numVerifiedKeys := 0
for _, sig := range s.Signatures {
if !roleData.ValidKey(sig.KeyID) {
continue
}
verifier, err := db.GetVerifier(sig.KeyID)
if err != nil {
continue
}
if err := verifier.Verify(msg, sig.Signature); err != nil {
// FIXME: don't err out on the 1st bad signature.
return ErrInvalid
}
// Only consider this key valid if we haven't seen any of it's
// key ids before.
// Careful: we must not rely on the key IDs _declared in the file_,
// instead we get to decide what key IDs this key correspond to.
// XXX dangerous; better stop supporting multiple key IDs altogether.
keyIDs := verifier.MarshalPublicKey().IDs()
wasKeySeen := false
for _, keyID := range keyIDs {
if _, present := verifiedKeyIDs[keyID]; present {
wasKeySeen = true
}
}
if !wasKeySeen {
for _, id := range keyIDs {
verifiedKeyIDs[id] = struct{}{}
}
numVerifiedKeys++
}
}
if numVerifiedKeys < roleData.Threshold {
return ErrRoleThreshold{roleData.Threshold, numVerifiedKeys}
}
return nil
}
func (db *DB) Unmarshal(b []byte, v interface{}, role string, minVersion int64) error {
s := &data.Signed{}
if err := json.Unmarshal(b, s); err != nil {
return err
}
if err := db.Verify(s, role, minVersion); err != nil {
return err
}
return json.Unmarshal(s.Signed, v)
}
// UnmarshalExpired is exactly like Unmarshal except ignores expired timestamp error.
func (db *DB) UnmarshalIgnoreExpired(b []byte, v interface{}, role string, minVersion int64) error {
s := &data.Signed{}
if err := json.Unmarshal(b, s); err != nil {
return err
}
// Note: If verification fails, then we wont attempt to unmarshal
// unless when verification error is errExpired.
verifyErr := db.Verify(s, role, minVersion)
if verifyErr != nil {
if _, ok := verifyErr.(ErrExpired); !ok {
return verifyErr
}
}
return json.Unmarshal(s.Signed, v)
}
func (db *DB) UnmarshalTrusted(b []byte, v interface{}, role string) error {
s := &data.Signed{}
if err := json.Unmarshal(b, s); err != nil {
return err
}
if err := db.VerifySignatures(s, role); err != nil {
return err
}
return json.Unmarshal(s.Signed, v)
}