1 /** @file
2 * Copyright (c) 2019-2022, Arm Limited or its affiliates. All rights reserved.
3 * SPDX-License-Identifier : Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 **/
17
18 #include "val_target.h"
19 #include "pal_interfaces_ns.h"
20 #include "val_framework.h"
21 #include "val_client_defs.h"
22 #include "val_attestation.h"
23
24 #ifdef INITIAL_ATTESTATION
25
26 uint32_t mandatory_claims = 0;
27 uint32_t mandaroty_sw_components = 0;
28 bool_t sw_component_present = 0;
29
get_items_in_map(QCBORDecodeContext * decode_context,struct items_to_get_t * item_list)30 static int get_items_in_map(QCBORDecodeContext *decode_context,
31 struct items_to_get_t *item_list)
32 {
33 int item_index;
34 QCBORItem item;
35 struct items_to_get_t *item_ptr = item_list;
36
37 /* initialize the data type of all items in the list */
38 while (item_ptr->label != 0)
39 {
40 item_ptr->item.uDataType = QCBOR_TYPE_NONE;
41 item_ptr++;
42 }
43
44 QCBORDecode_GetNext(decode_context, &item);
45 if (item.uDataType != QCBOR_TYPE_MAP)
46 {
47 return VAL_ATTEST_ERROR;
48 }
49
50 for (item_index = item.val.uCount; item_index != 0; item_index--)
51 {
52 if (QCBORDecode_GetNext(decode_context, &item) != QCBOR_SUCCESS)
53 {
54 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
55 }
56 if (item.uLabelType != QCBOR_TYPE_INT64)
57 {
58 continue;
59 }
60
61 item_ptr = item_list;
62 while (item_ptr->label != 0)
63 {
64 if (item.label.int64 == item_ptr->label)
65 {
66 item_ptr->item = item;
67 }
68 item_ptr++;
69 }
70 }
71
72 return VAL_ATTEST_SUCCESS;
73 }
74
get_item_in_map(QCBORDecodeContext * decode_context,int32_t label,QCBORItem * item)75 static int get_item_in_map(QCBORDecodeContext *decode_context,
76 int32_t label,
77 QCBORItem *item)
78 {
79 struct items_to_get_t item_list[2];
80
81 item_list[0].label = label;
82 item_list[1].label = 0;
83
84 if (get_items_in_map(decode_context, item_list))
85 {
86 return VAL_ATTEST_ERROR;
87 }
88
89 if (item_list[0].item.uDataType == QCBOR_TYPE_NONE)
90 {
91 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
92 }
93
94 *item = item_list[0].item;
95
96 return VAL_ATTEST_SUCCESS;
97 }
98
parse_unprotected_headers(QCBORDecodeContext * decode_context,struct q_useful_buf_c * child __UNUSED)99 static int parse_unprotected_headers(QCBORDecodeContext *decode_context,
100 struct q_useful_buf_c *child __UNUSED)
101 {
102 struct items_to_get_t item_list[3];
103
104 item_list[0].label = COSE_HEADER_PARAM_KID;
105 item_list[1].label = 0;
106
107 if (get_items_in_map(decode_context, item_list))
108 {
109 return VAL_ATTEST_ERR_CBOR_STRUCTURE;
110 }
111
112 return VAL_ATTEST_SUCCESS;
113 }
114
parse_protected_headers(struct q_useful_buf_c protected_headers,int32_t * alg_id)115 static int parse_protected_headers(struct q_useful_buf_c protected_headers,
116 int32_t *alg_id)
117 {
118 QCBORDecodeContext decode_context;
119 QCBORItem item;
120
121 QCBORDecode_Init(&decode_context, protected_headers, 0);
122
123 if (get_item_in_map(&decode_context, COSE_HEADER_PARAM_ALG, &item))
124 {
125 return VAL_ATTEST_ERROR;
126 }
127
128 if (QCBORDecode_Finish(&decode_context))
129 {
130 return VAL_ATTEST_ERROR;
131 }
132
133 if ((item.uDataType != QCBOR_TYPE_INT64) || (item.val.int64 > INT32_MAX))
134 {
135 return VAL_ATTEST_ERROR;
136 }
137
138 *alg_id = (int32_t)item.val.int64;
139
140 return VAL_ATTEST_SUCCESS;
141 }
142
143 /**
144 @brief - This API will verify the claims
145 @param - decode_context : The buffer containing the challenge
146 item : context for decoding the data items
147 completed_challenge : Buffer containing the challenge
148 @return - error status
149 **/
parse_claims(QCBORDecodeContext * decode_context,QCBORItem item,struct q_useful_buf_c completed_challenge)150 static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item,
151 struct q_useful_buf_c completed_challenge)
152 {
153 int i, count = 0, sw_comp_count = 0, index = 0;
154 int status = VAL_ATTEST_SUCCESS;
155
156 /* Parse each claim and validate their data type */
157 while (status == VAL_ATTEST_SUCCESS)
158 {
159 status = QCBORDecode_GetNext(decode_context, &item);
160 if (status != VAL_ATTEST_SUCCESS)
161 break;
162
163 mandatory_claims |= 1 << (EAT_CBOR_ARM_RANGE_BASE - item.label.int64);
164 if (item.uLabelType == QCBOR_TYPE_INT64)
165 {
166 if (item.label.int64 == EAT_CBOR_ARM_LABEL_NONCE)
167 {
168 if (item.uDataType == QCBOR_TYPE_BYTE_STRING)
169 {
170 /* Given challenge vs challenge in token */
171 if (UsefulBuf_Compare(item.val.string, completed_challenge))
172 return VAL_ATTEST_TOKEN_CHALLENGE_MISMATCH;
173 }
174 else
175 return VAL_ATTEST_TOKEN_NOT_SUPPORTED;
176 }
177 else if (item.label.int64 == EAT_CBOR_ARM_LABEL_BOOT_SEED ||
178 item.label.int64 == EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID ||
179 item.label.int64 == EAT_CBOR_ARM_LABEL_UEID)
180 {
181 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
182 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
183 }
184 else if (item.label.int64 == EAT_CBOR_ARM_LABEL_ORIGINATION ||
185 item.label.int64 == EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION ||
186 item.label.int64 == EAT_CBOR_ARM_LABEL_HW_VERSION)
187 {
188 if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
189 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
190 }
191 else if (item.label.int64 == EAT_CBOR_ARM_LABEL_CLIENT_ID ||
192 item.label.int64 == EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE)
193 {
194 if (item.uDataType != QCBOR_TYPE_INT64)
195 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
196 }
197 else if (item.label.int64 == EAT_CBOR_ARM_LABEL_SW_COMPONENTS)
198 {
199 if (item.uDataType != QCBOR_TYPE_ARRAY)
200 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
201
202 sw_comp_count = item.val.uCount;
203 sw_component_present = 1;
204 for (index = 0; index < sw_comp_count; index++)
205 {
206 status = QCBORDecode_GetNext(decode_context, &item);
207 if (status != VAL_ATTEST_SUCCESS)
208 continue;
209
210 count = item.val.uCount;
211 for (i = 0; i <= count; i++)
212 {
213 //mandaroty_sw_components |= 1 << item.label.int64;
214 if (item.label.int64 == EAT_CBOR_SW_COMPONENT_MEASUREMENT)
215 {
216 if (index == 0)
217 mandaroty_sw_components |= 1 << item.label.int64;
218 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
219 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
220 }
221 else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC)
222 {
223 if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
224 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
225 }
226 else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_VERSION)
227 {
228 if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
229 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
230 }
231 else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_SIGNER_ID)
232 {
233 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
234 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
235 }
236 else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_EPOCH)
237 {
238 if (item.uDataType != QCBOR_TYPE_INT64)
239 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
240 }
241 else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_TYPE)
242 {
243 if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
244 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
245 }
246
247 if (i < count)
248 {
249 status = QCBORDecode_GetNext(decode_context, &item);
250 if (status != VAL_ATTEST_SUCCESS)
251 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
252 }
253 } /*for (i = 0; i <= count; i++)*/
254 } /*for (index = 0; index<sw_comp_count; index++)*/
255
256 }
257 }
258 else
259 {
260 /* For other claim types */
261 }
262 }
263
264 if (status == QCBOR_ERR_HIT_END || status == QCBOR_ERR_NO_MORE_ITEMS)
265 return VAL_ATTEST_SUCCESS;
266 else
267 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
268 }
269
270 /**
271 @brief - This API will verify the attestation token
272 @param - challenge : The buffer containing the challenge
273 challenge_size : Size of the challenge buffer
274 token : The buffer containing the attestation token
275 token_size : Size of the token buffer
276 @return - error status
277 **/
val_initial_attest_verify_token(uint8_t * challenge,size_t challenge_size,uint8_t * token,size_t token_size)278 int32_t val_initial_attest_verify_token(uint8_t *challenge, size_t challenge_size,
279 uint8_t *token, size_t token_size)
280 {
281 int32_t status = VAL_ATTEST_SUCCESS;
282 int32_t cose_algorithm_id;
283 QCBORItem item;
284 QCBORDecodeContext decode_context;
285 struct q_useful_buf_c completed_challenge;
286 struct q_useful_buf_c completed_token;
287 struct q_useful_buf_c payload;
288 struct q_useful_buf_c signature;
289 struct q_useful_buf_c protected_headers;
290 struct q_useful_buf_c kid;
291 struct q_useful_buf_c token_hash;
292
293 USEFUL_BUF_MAKE_STACK_UB(buffer_for_encoded_key, MAX_ENCODED_COSE_KEY_SIZE);
294 USEFUL_BUF_MAKE_STACK_UB(buffer_for_token_hash, T_COSE_CRYPTO_SHA256_SIZE);
295
296 kid.ptr = buffer_for_encoded_key.ptr;
297
298 /* Construct the token buffer for validation */
299 completed_token.ptr = token;
300 completed_token.len = token_size;
301
302 /* Construct the challenge buffer for validation */
303 completed_challenge.ptr = challenge;
304 completed_challenge.len = challenge_size;
305
306 /*
307 -------------------------
308 | CBOR Array Type |
309 -------------------------
310 | Protected Headers |
311 -------------------------
312 | Unprotected Headers |
313 -------------------------
314 | Payload |
315 -------------------------
316 | Signature |
317 -------------------------
318 */
319
320 /* Initialize the decorder */
321 QCBORDecode_Init(&decode_context, completed_token, QCBOR_DECODE_MODE_NORMAL);
322
323 /* Get the Header */
324 QCBORDecode_GetNext(&decode_context, &item);
325
326 /* Check the CBOR Array type. Check if the count is 4.
327 * Only COSE_SIGN1 is supported now.
328 */
329 if (item.uDataType != QCBOR_TYPE_ARRAY || item.val.uCount != 4 ||
330 !QCBORDecode_IsTagged(&decode_context, &item, CBOR_TAG_COSE_SIGN1))
331 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
332
333 /* Get the next headers */
334 QCBORDecode_GetNext(&decode_context, &item);
335 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
336 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
337
338 protected_headers = item.val.string;
339
340 /* Parse the protected headers and check the data type and value*/
341 status = parse_protected_headers(protected_headers, &cose_algorithm_id);
342 if (status != VAL_ATTEST_SUCCESS)
343 return status;
344
345 /* Parse the unprotected headers and check the data type and value */
346 status = parse_unprotected_headers(&decode_context, &kid);
347 if (status != VAL_ATTEST_SUCCESS)
348 return status;
349
350 /* Get the payload */
351 QCBORDecode_GetNext(&decode_context, &item);
352 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
353 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
354
355 payload = item.val.string;
356
357 /* Get the digital signature */
358 QCBORDecode_GetNext(&decode_context, &item);
359 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
360 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
361
362 signature = item.val.string;
363
364 /* Compute the hash from the token */
365 status = val_attestation_function(VAL_INITIAL_ATTEST_COMPUTE_HASH, cose_algorithm_id,
366 buffer_for_token_hash, &token_hash,
367 protected_headers, payload);
368 if (status != VAL_ATTEST_SUCCESS)
369 return status;
370
371 /* Verify the signature */
372 status = val_attestation_function(VAL_INITIAL_ATTEST_VERIFY_WITH_PK, cose_algorithm_id,
373 token_hash, signature);
374 if (status != VAL_ATTEST_SUCCESS)
375 return status;
376
377 /* Initialize the Decoder and validate the payload format */
378 QCBORDecode_Init(&decode_context, payload, QCBOR_DECODE_MODE_NORMAL);
379 status = QCBORDecode_GetNext(&decode_context, &item);
380 if (status != VAL_ATTEST_SUCCESS)
381 return status;
382
383 if (item.uDataType != QCBOR_TYPE_MAP)
384 return VAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
385
386 /* Parse the payload and check the data type of each claim */
387 status = parse_claims(&decode_context, item, completed_challenge);
388 if (status != VAL_ATTEST_SUCCESS)
389 return status;
390
391 if ((mandatory_claims & MANDATORY_CLAIM_WITH_SW_COMP) == MANDATORY_CLAIM_WITH_SW_COMP)
392 {
393 if ((mandaroty_sw_components & MANDATORY_SW_COMP) != MANDATORY_SW_COMP)
394 return VAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS;
395 mandaroty_sw_components = 0;
396 }
397 else if ((mandatory_claims & MANDATORY_CLAIM_NO_SW_COMP) != MANDATORY_CLAIM_NO_SW_COMP)
398 {
399 return VAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS;
400 }
401
402 return VAL_ATTEST_SUCCESS;
403 }
404 #endif /* INITIAL_ATTESTATION */
405
406 /**
407 @brief - This API will call the requested attestation function
408 @param - type : function code
409 ... : variable number of arguments
410 @return - Error status
411 **/
val_attestation_function(int type,...)412 int32_t val_attestation_function(int type, ...)
413 {
414 #ifdef INITIAL_ATTESTATION
415 va_list valist;
416 val_status_t status;
417 uint8_t *challenge, *token;
418 size_t challenge_size, verify_token_size;
419
420 va_start(valist, type);
421 switch (type)
422 {
423 case VAL_INITIAL_ATTEST_VERIFY_TOKEN:
424 challenge = va_arg(valist, uint8_t*);
425 challenge_size = va_arg(valist, size_t);
426 token = va_arg(valist, uint8_t*);
427 verify_token_size = va_arg(valist, size_t);
428 status = val_initial_attest_verify_token(challenge, challenge_size,
429 token, verify_token_size);
430 break;
431 default:
432 status = pal_attestation_function(type, valist);
433 break;
434 }
435
436 va_end(valist);
437 return status;
438 #else
439 (void)type;
440 return VAL_STATUS_ERROR;
441 #endif /* INITIAL_ATTESTATION */
442 }
443