-
Notifications
You must be signed in to change notification settings - Fork 0
/
iclaims.go
272 lines (224 loc) · 8.47 KB
/
iclaims.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
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// Copyright 2021-2024 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0
package psatoken
import (
"encoding/json"
"errors"
"fmt"
)
// IClaimsBase defines an interface for working with all EAT-based claims
type IClaimsBase interface {
// Semantic validation
Validate() error
}
// IClaims defines a uniform interface for dealing with claims common to all
// PSA profiles
type IClaims interface {
IClaimsBase
GetProfile() (string, error)
GetClientID() (int32, error)
GetSecurityLifeCycle() (uint16, error)
GetImplID() ([]byte, error)
GetBootSeed() ([]byte, error)
GetCertificationReference() (string, error)
GetSoftwareComponents() ([]ISwComponent, error)
GetNonce() ([]byte, error)
GetInstID() ([]byte, error)
GetVSI() (string, error)
SetClientID(int32) error
SetSecurityLifeCycle(uint16) error
SetImplID([]byte) error
SetBootSeed([]byte) error
SetCertificationReference(string) error
SetSoftwareComponents([]ISwComponent) error
SetNonce([]byte) error
SetInstID([]byte) error
SetVSI(string) error
}
// NewClaims returns a new IClaims implementation instance associated with the
// specified profile name. An error is returned if the specified name does not
// correspond to any of the previously registered profiles.
func NewClaims(profile string) (IClaims, error) {
entry, ok := profilesRegister[profile]
if !ok {
return nil, fmt.Errorf("unsupported profile %q", profile)
}
return entry.Profile.GetClaims(), nil
}
// ValidateClaims returns an error if validation fails for any of the standard
// PSA cliams defined by IClaims. This function may be used by new profiles
// whos implementations do not embed existing IClaims implementations and so
// cannot rely on their existing Validate() methods.
func ValidateClaims(c IClaims) error {
if err := FilterError(c.GetProfile()); err != nil {
return fmt.Errorf("validating profile: %w", err)
}
if err := FilterError(c.GetSecurityLifeCycle()); err != nil {
return fmt.Errorf("validating security lifecycle: %w", err)
}
if err := FilterError(c.GetImplID()); err != nil {
return fmt.Errorf("validating implementation id: %w", err)
}
if err := FilterError(c.GetSoftwareComponents()); err != nil {
return fmt.Errorf("validating software components: %w", err)
}
if err := FilterError(c.GetNonce()); err != nil {
return fmt.Errorf("validating nonce: %w", err)
}
if err := FilterError(c.GetInstID()); err != nil {
return fmt.Errorf("validating instance id: %w", err)
}
if err := FilterError(c.GetVSI()); err != nil {
return fmt.Errorf("validating verification service indicator: %w", err)
}
if err := FilterError(c.GetClientID()); err != nil {
return fmt.Errorf("validating client id: %w", err)
}
if err := FilterError(c.GetBootSeed()); err != nil {
return fmt.Errorf("validating boot seed: %w", err)
}
if err := FilterError(c.GetCertificationReference()); err != nil {
return fmt.Errorf("validating certification reference: %w", err)
}
return nil
}
// ValidateAndEncodeClaimsToCBOR returns a []byte containing CBOR-encoded
// IClaims that were provided as input. An error is returned instead if the
// provided claims are invalid or if the encoding fails.
func ValidateAndEncodeClaimsToCBOR(c IClaims) ([]byte, error) {
if err := c.Validate(); err != nil {
return nil, err
}
return em.Marshal(c)
}
// EncodeClaimsToCBOR returns a []byte containing CBOR-encoded IClaims, or an
// error if encoding fails.
func EncodeClaimsToCBOR(c IClaims) ([]byte, error) {
return em.Marshal(c)
}
// ValidateAndEncodeClaimsToJSON returns a []byte containing JSON-encoded
// IClaims that were provided as input. An error is returned instead if the
// provided claims are invalid or if the encoding fails.
func ValidateAndEncodeClaimsToJSON(c IClaims) ([]byte, error) {
if err := c.Validate(); err != nil {
return nil, err
}
return json.Marshal(c)
}
// EncodeClaimsToJSON returns a []byte containing JSON-encoded IClaims, or an
// error if encoding fails.
func EncodeClaimsToJSON(c IClaims) ([]byte, error) {
return json.Marshal(c)
}
// DecodeAndValidateClaimsFromCBOR returns an IClaims implementation instance populated
// from the provided CBOR buf. The implementation used is determined by value
// of the eat_profile (key 265) in the provided CBOR object. In the absence of
// this claim, Profile1 (PSA_IOT_PROFILE_1) is assumed. Claims are validated
// according to the profile as part of decoding.
func DecodeAndValidateClaimsFromCBOR(buf []byte) (IClaims, error) {
claims, err := DecodeClaimsFromCBOR(buf)
if err != nil {
return nil, err
}
if err := claims.Validate(); err != nil {
return nil, err
}
return claims, nil
}
// DecodeClaimsFromCBOR returns an IClaims implementation instance
// populated from the provided CBOR buf. The implementation used is determined
// by value of the eat_profile (key 265) in the provided CBOR object. In the
// absence of this key, Profile1 (PSA_IOT_PROFILE_1) is assumed. No validation
// is performed to confirm that the decoded claims actually conform to the
// stated profile.
func DecodeClaimsFromCBOR(buf []byte) (IClaims, error) {
selector := struct {
// note: code point 265 is defined as the eat_profile claim in
// EAT(https://datatracker.ietf.org/doc/draft-ietf-rats-eat/).
// This is not specific to PSA, and so is not something that we
// expect PSA profiles to be able to override.
// The only exception to this P1, which does not use the
// eat_profile claim (code point 265). It instead defines its
// own pofile at code point -7500. This will not be unmarshaled
// into this field. For P1, the dm.Unmarshal below will leave
// the field in its default value "" (however, it will not
// result in an error). As the P1 profile field is optional, if
// a profile is not present in the claims, they are assumed to
// be for P1 (as that would make them automatically invalid for
// any other profile). Because of this, the P1 claims will be
// selected from the register if the Profile field here is
// empty, and the decoding will proceed correctly. P1's -7500
// profile field will then be validated as part of the full
// claims decoding in UnmarshalCBOR() further down.
Profile string `cbor:"265,keyasint"`
}{}
err := dm.Unmarshal(buf, &selector)
if err != nil {
return nil, err
}
entry, ok := profilesRegister[selector.Profile]
if !ok {
return nil, fmt.Errorf("unknown profile: %q", selector.Profile)
}
claims := entry.Profile.GetClaims()
if err := dm.Unmarshal(buf, claims); err != nil {
return nil, err
}
return claims, nil
}
// Deprecated: use DecodeAndValidateClaimsFromJSON instead.
func DecodeJSONClaims(buf []byte) (IClaims, error) {
return DecodeAndValidateClaimsFromJSON(buf)
}
// DecodeAndValidateClaimsFromJSON returns an IClaims implementation instance populated
// from the provided JSON buf. The implementation used is determined by value
// of the profile field in the provided JSON object. In the absence of
// this field, Profile1 (PSA_IOT_PROFILE_1) is assumed. Claims are validated
// according to the profile as part of decoding.
func DecodeAndValidateClaimsFromJSON(buf []byte) (IClaims, error) {
claims, err := DecodeClaimsFromJSON(buf)
if err != nil {
return nil, err
}
if err := claims.Validate(); err != nil {
return nil, err
}
return claims, nil
}
// Deprecated: use DecodeClaimsFromJSON instead.
func DecodeUnvalidatedJSONClaims(buf []byte) (IClaims, error) {
return DecodeClaimsFromJSON(buf)
}
// DecodeClaimsFromJSON returns an IClaims implementation instance
// populated from the provided JSON buf. The implementation used is determined
// by value of the profile field in the provided JSON object. In the absence
// of this field, Profile1 (PSA_IOT_PROFILE_1) is assumed. No validation is
// performed to confirm that the decoded claims actually conform to the stated
// profile.
func DecodeClaimsFromJSON(buf []byte) (IClaims, error) {
var decoded map[string]interface{}
if err := json.Unmarshal(buf, &decoded); err != nil {
return nil, err
}
var found IProfile
for name, entry := range profilesRegister {
if profileTag, ok := decoded[entry.JSONTag]; ok {
if profileTag != entry.Profile.GetName() {
continue
}
if found != nil && found.GetName() != entry.Profile.GetName() {
return nil, fmt.Errorf("matched multiple profiles: %s and %s",
name, found.GetName())
}
found = entry.Profile
}
}
if found == nil {
return nil, errors.New(`could not match profile`)
}
claims := found.GetClaims()
if err := json.Unmarshal(buf, claims); err != nil {
return nil, err
}
return claims, nil
}