1 /*
2 * attest_token_decode_common.c
3 *
4 * Copyright (c) 2019, Laurence Lundblade.
5 * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 * See BSD-3-Clause license in README.md
10 */
11
12 #include "attest_token_decode.h"
13 #include "attest.h"
14 #include "q_useful_buf.h"
15 #include "qcbor_util.h"
16 #include "config_attest.h"
17
18 /**
19 * \file attest_token_decode_common.c
20 *
21 * \brief Common functions in attestation token decoder.
22 *
23 * This decodes and verifies an attestation token giving access to the
24 * data items in the token. The data items are also known as claims.
25 *
26 * This is written primarily as tests for the token encoder, though it
27 * close to a full commercial token decoder. The main thing missing is
28 * a thorough test suite for it. Test before commercial use is
29 * important as this is a parser / decoder and thus subject to attack
30 * by malicious input. It does however, use QCBOR for most base
31 * parsing, and QCBOR is thoroughly tested and commercial.
32 *
33 * This is oriented around the Arm-defined initial attestation token.
34 *
35 * \c uint_fast8_t is used for type and nest levels. They are
36 * 8-bit quantities, but making using uint8_t variables
37 * and parameters can result in bigger, slower code.
38 * \c uint_fast8_t is part of \c <stdint.h>. It is not
39 * used in structures where it is more important to keep
40 * the size smaller.
41 */
42
43
44 /**
45 * Compute the bit indicating a claim is present
46 */
47 #define CLAIM_PRESENT_BIT(item_index) (0x01U << (item_index))
48
49
50 /*
51 * Public function. See attest_token_decode.h
52 */
attest_token_decode_init(struct attest_token_decode_context * me,uint32_t options)53 void attest_token_decode_init(struct attest_token_decode_context *me,
54 uint32_t options)
55 {
56 memset(me, 0, sizeof(struct attest_token_decode_context));
57 me->options = options;
58 me->last_error = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
59 }
60
61 /*
62 * Public function. See attest_token_decode.h
63 */
64 enum attest_token_err_t
attest_token_decode_get_bstr(struct attest_token_decode_context * me,int32_t label,struct q_useful_buf_c * claim)65 attest_token_decode_get_bstr(struct attest_token_decode_context *me,
66 int32_t label,
67 struct q_useful_buf_c *claim)
68 {
69 enum attest_token_err_t return_value;
70 QCBORItem item;
71
72 if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
73 return_value = me->last_error;
74 *claim = NULL_Q_USEFUL_BUF_C;
75 goto Done;
76 }
77
78 return_value = qcbor_util_get_top_level_item_in_map(me->payload,
79 label,
80 QCBOR_TYPE_BYTE_STRING,
81 &item);
82 if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
83 goto Done;
84 }
85
86 *claim = item.val.string;
87
88 Done:
89 return return_value;
90 }
91
92
93 /*
94 * Public function. See attest_token_decode.h
95 */
96 enum attest_token_err_t
attest_token_decode_get_tstr(struct attest_token_decode_context * me,int32_t label,struct q_useful_buf_c * claim)97 attest_token_decode_get_tstr(struct attest_token_decode_context *me,
98 int32_t label,
99 struct q_useful_buf_c *claim)
100 {
101 enum attest_token_err_t return_value;
102 QCBORItem item;
103
104 if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
105 return_value = me->last_error;
106 *claim = NULL_Q_USEFUL_BUF_C;
107 goto Done;
108 }
109
110 return_value = qcbor_util_get_top_level_item_in_map(me->payload,
111 label,
112 QCBOR_TYPE_TEXT_STRING,
113 &item);
114 if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
115 goto Done;
116 }
117
118 *claim = item.val.string;
119
120 Done:
121 return return_value;
122 }
123
124
125 /*
126 * Public function. See attest_token_decode.h
127 */
128 enum attest_token_err_t
attest_token_decode_get_int(struct attest_token_decode_context * me,int32_t label,int64_t * integer)129 attest_token_decode_get_int(struct attest_token_decode_context *me,
130 int32_t label,
131 int64_t *integer)
132 {
133 enum attest_token_err_t return_value;
134 QCBORItem item;
135 QCBORDecodeContext decode_context;
136
137 if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
138 return_value = me->last_error;
139 *integer = 0;
140 goto Done;
141 }
142
143 QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
144
145 return_value = qcbor_util_get_item_in_map(&decode_context,
146 label,
147 &item);
148 if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
149 goto Done;
150 }
151
152 if(QCBORDecode_Finish(&decode_context)) {
153 return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
154 }
155
156 if(item.uDataType == QCBOR_TYPE_INT64) {
157 *integer = item.val.int64;
158 } else if(item.uDataType == QCBOR_TYPE_UINT64) {
159 if(item.val.uint64 < INT64_MAX) {
160 *integer = (int64_t)item.val.uint64;
161 } else {
162 return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
163 }
164 } else {
165 return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
166 }
167
168 Done:
169 return return_value;
170 }
171
172
173 /*
174 * Public function. See attest_token_decode.h
175 */
176 enum attest_token_err_t
attest_token_decode_get_uint(struct attest_token_decode_context * me,int32_t label,uint64_t * integer)177 attest_token_decode_get_uint(struct attest_token_decode_context *me,
178 int32_t label,
179 uint64_t *integer)
180 {
181 enum attest_token_err_t return_value;
182 QCBORItem item;
183 QCBORDecodeContext decode_context;
184
185 if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
186 return_value = me->last_error;
187 *integer = 0;
188 goto Done;
189 }
190
191 QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
192
193 return_value = qcbor_util_get_item_in_map(&decode_context,
194 label,
195 &item);
196 if(return_value != 0) {
197 goto Done;
198 }
199
200 if(QCBORDecode_Finish(&decode_context)) {
201 return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
202 }
203
204 if(item.uDataType == QCBOR_TYPE_UINT64) {
205 *integer = item.val.uint64;
206 } else if(item.uDataType == QCBOR_TYPE_INT64) {
207 if(item.val.int64 >= 0) {
208 *integer = (uint64_t)item.val.int64;
209 } else {
210 return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
211 }
212 } else {
213 return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
214 }
215
216 Done:
217 return return_value;
218 }
219
220
221 /*
222 * Public function. See attest_token_decode.h
223 */
224 enum attest_token_err_t
attest_token_decode_get_payload(struct attest_token_decode_context * me,struct q_useful_buf_c * payload)225 attest_token_decode_get_payload(struct attest_token_decode_context *me,
226 struct q_useful_buf_c *payload)
227 {
228 enum attest_token_err_t return_value;
229
230 if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
231 return_value = me->last_error;
232 *payload = NULL_Q_USEFUL_BUF_C;
233 goto Done;
234 }
235
236 if(q_useful_buf_c_is_null_or_empty(me->payload)) {
237 return_value = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
238 goto Done;
239 }
240
241 *payload = me->payload;
242 return_value = ATTEST_TOKEN_ERR_SUCCESS;
243
244 Done:
245 return return_value;
246 }
247
248
249 /*
250 * Public function. See attest_token_decode.h
251 */
252 enum attest_token_err_t
attest_token_decode_get_iat_simple(struct attest_token_decode_context * me,struct attest_token_iat_simple_t * items)253 attest_token_decode_get_iat_simple(struct attest_token_decode_context *me,
254 struct attest_token_iat_simple_t *items)
255 {
256 struct qcbor_util_items_to_get_t list[NUMBER_OF_ITEMS+1];
257 QCBORDecodeContext decode_context;
258 int64_t client_id_64;
259 enum attest_token_err_t return_value;
260
261 /* Set all q_useful_bufs to NULL and flags to 0 */
262 memset(items, 0, sizeof(struct attest_token_iat_simple_t));
263
264 /* Re use flags as array indexes because it works nicely */
265 list[NONCE_FLAG].label = IAT_NONCE;
266 list[INSTANCE_ID_FLAG].label = IAT_INSTANCE_ID;
267 list[BOOT_SEED_FLAG].label = IAT_BOOT_SEED;
268 list[CERT_REF_FLAG].label = IAT_CERTIFICATION_REFERENCE;
269 list[IMPLEMENTATION_ID_FLAG].label = IAT_IMPLEMENTATION_ID;
270 list[CLIENT_ID_FLAG].label = IAT_CLIENT_ID;
271 list[SECURITY_LIFECYCLE_FLAG].label = IAT_SECURITY_LIFECYCLE;
272 list[PROFILE_DEFINITION_FLAG].label = IAT_PROFILE_DEFINITION;
273 list[VERIFICATION_SERVICE_FLAG].label = IAT_VERIFICATION_SERVICE;
274 #if ATTEST_TOKEN_PROFILE_ARM_CCA
275 list[PLAT_HASH_ALGO_ID].label = IAT_PLATFORM_HASH_ALGO_ID;
276 list[PLAT_CONFIG].label = IAT_PLATFORM_CONFIG;
277 #endif
278 list[NUMBER_OF_ITEMS].label = 0; /* terminate the list. */
279
280 if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
281 return_value = me->last_error;
282 goto Done;
283 }
284
285 QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
286
287 return_value = qcbor_util_get_items_in_map(&decode_context,
288 list);
289 if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
290 goto Done;
291 }
292
293 /* ---- NONCE ---- */
294 if(list[NONCE_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
295 items->nonce = list[NONCE_FLAG].item.val.string;
296 items->item_flags |= CLAIM_PRESENT_BIT(NONCE_FLAG);
297 }
298
299 /* ---- Instance ID -------*/
300 if(list[INSTANCE_ID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
301 items->instance_id = list[INSTANCE_ID_FLAG].item.val.string;
302 items->item_flags |= CLAIM_PRESENT_BIT(INSTANCE_ID_FLAG);
303 }
304
305 /* ---- BOOT SEED -------*/
306 if(list[BOOT_SEED_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
307 items->boot_seed = list[BOOT_SEED_FLAG].item.val.string;
308 items->item_flags |= CLAIM_PRESENT_BIT(BOOT_SEED_FLAG);\
309 }
310
311 /* ---- CERTIFICATION REFERENCE -------*/
312 if(list[CERT_REF_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
313 items->cert_ref = list[CERT_REF_FLAG].item.val.string;
314 items->item_flags |= CLAIM_PRESENT_BIT(CERT_REF_FLAG);
315
316 }
317
318 /* ----IMPLEMENTATION ID -------*/
319 if(list[IMPLEMENTATION_ID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
320 items->implementation_id = list[IMPLEMENTATION_ID_FLAG].item.val.string;
321 items->item_flags |= CLAIM_PRESENT_BIT(IMPLEMENTATION_ID_FLAG);
322 }
323
324 /* ----CLIENT ID -------*/
325 if(list[CLIENT_ID_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
326 client_id_64 = list[CLIENT_ID_FLAG].item.val.int64;
327 if(client_id_64 < INT32_MAX || client_id_64 > INT32_MIN) {
328 items->client_id = (int32_t)client_id_64;
329 items->item_flags |= CLAIM_PRESENT_BIT(CLIENT_ID_FLAG);
330 }
331 }
332
333 /* ----SECURITY LIFECYCLE -------*/
334 if(list[SECURITY_LIFECYCLE_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
335 if(list[SECURITY_LIFECYCLE_FLAG].item.val.int64 < UINT32_MAX) {
336 items->security_lifecycle =
337 (uint32_t)list[SECURITY_LIFECYCLE_FLAG].item.val.int64;
338 items->item_flags |=CLAIM_PRESENT_BIT(SECURITY_LIFECYCLE_FLAG);
339 }
340 }
341
342 /* ---- PROFILE_DEFINITION -------*/
343 if(list[PROFILE_DEFINITION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
344 items->profile_definition=list[PROFILE_DEFINITION_FLAG].item.val.string;
345 items->item_flags |= CLAIM_PRESENT_BIT(PROFILE_DEFINITION_FLAG);
346 }
347
348 /* ---- VERIFICATION_SERVICE -------*/
349 if(list[VERIFICATION_SERVICE_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
350 items->verif_serv =
351 list[VERIFICATION_SERVICE_FLAG].item.val.string;
352 items->item_flags |= CLAIM_PRESENT_BIT(VERIFICATION_SERVICE_FLAG);
353 }
354
355 Done:
356 return return_value;
357 }
358
359
360 /*
361 * Public function. See attest_token_decode.h
362 */
363 enum attest_token_err_t
attest_token_get_num_sw_components(struct attest_token_decode_context * me,uint32_t * num_sw_components)364 attest_token_get_num_sw_components(struct attest_token_decode_context *me,
365 uint32_t *num_sw_components)
366 {
367 enum attest_token_err_t return_value;
368 QCBORItem item;
369
370 if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
371 return_value = me->last_error;
372 goto Done;
373 }
374
375 return_value = qcbor_util_get_top_level_item_in_map(me->payload,
376 IAT_SW_COMPONENTS,
377 QCBOR_TYPE_ARRAY,
378 &item);
379 if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
380 #if ATTEST_TOKEN_PROFILE_PSA_IOT_1 /* Other profiles mandates the SW comp*/
381 if(return_value == ATTEST_TOKEN_ERR_NOT_FOUND) {
382 /* Now decide if it was intentionally left out. */
383 return_value = qcbor_util_get_top_level_item_in_map(me->payload,
384 IAT_NO_SW_COMPONENTS,
385 QCBOR_TYPE_INT64,
386 &item);
387 if(return_value == ATTEST_TOKEN_ERR_SUCCESS) {
388 if(item.val.int64 == NO_SW_COMPONENT_FIXED_VALUE) {
389 /* Successful omission of SW components. Pass on the
390 * success return_value */
391 *num_sw_components = 0;
392 } else {
393 /* Indicator for no SW components malformed */
394 return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
395 }
396 } else if(return_value == ATTEST_TOKEN_ERR_NOT_FOUND) {
397 /* Should have been an indicator for no SW components */
398 return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
399 }
400 }
401 #endif /* ATTEST_TOKEN_PROFILE_PSA_IOT_1 */
402 goto Done;
403 } else {
404 /* The SW components claim exists */
405 if(item.val.uCount == 0) {
406 /* Empty SW component not allowed */
407 return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
408 } else {
409 /* SUCCESS! Pass on the success return_value */
410 /* Note that this assumes the array is definite length */
411 *num_sw_components = item.val.uCount;
412 }
413 }
414
415 Done:
416 return return_value;
417 }
418
419
420 /**
421 * \brief Decode a single SW component
422 *
423 * \param[in] decode_context The CBOR decoder context to decode from
424 * \param[in] sw_component_item The top-level map item for this SW
425 * component.
426 * \param[out] sw_component The structure to fill in with decoded data.
427 *
428 * \return An error from \ref attest_token_err_t.
429 *
430 */
431 static inline enum attest_token_err_t
decode_sw_component(QCBORDecodeContext * decode_context,const QCBORItem * sw_component_item,struct attest_token_sw_component_t * sw_component)432 decode_sw_component(QCBORDecodeContext *decode_context,
433 const QCBORItem *sw_component_item,
434 struct attest_token_sw_component_t *sw_component)
435 {
436 enum attest_token_err_t return_value;
437 QCBORItem claim_item;
438 QCBORError cbor_error;
439 uint_fast8_t next_nest_level; /* nest levels are 8-bit, but a uint8_t
440 var is often slower and more code */
441
442 if(sw_component_item->uDataType != QCBOR_TYPE_MAP) {
443 return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
444 goto Done;
445 }
446
447 /* Zero it, setting booleans to false, pointers to NULL and
448 lengths to 0 */
449 memset(sw_component, 0, sizeof(struct attest_token_sw_component_t));
450
451 return_value = ATTEST_TOKEN_ERR_SUCCESS;
452
453 while(1) {
454 cbor_error = QCBORDecode_GetNext(decode_context, &claim_item);
455 if(cbor_error != QCBOR_SUCCESS) {
456 /* no tolerance for any errors here */
457 return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
458 goto Done;
459 }
460
461 if(claim_item.uLabelType == QCBOR_TYPE_INT64) {
462 switch(claim_item.label.int64) {
463 case IAT_SW_COMPONENT_MEASUREMENT_TYPE:
464 if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
465 return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
466 goto Done;
467 }
468 sw_component->measurement_type = claim_item.val.string;
469 sw_component->item_flags |=
470 CLAIM_PRESENT_BIT(SW_MEASUREMENT_TYPE_FLAG);
471
472 break;
473
474 case IAT_SW_COMPONENT_MEASUREMENT_VALUE:
475 if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
476 return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
477 goto Done;
478 }
479 sw_component->measurement_val = claim_item.val.string;
480 sw_component->item_flags |=
481 CLAIM_PRESENT_BIT(SW_MEASURMENT_VAL_FLAG);
482 break;
483
484 case IAT_SW_COMPONENT_VERSION:
485 if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
486 return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
487 goto Done;
488 }
489 sw_component->version = claim_item.val.string;
490 sw_component->item_flags |=
491 CLAIM_PRESENT_BIT(SW_VERSION_FLAG);
492 break;
493
494 case IAT_SW_COMPONENT_SIGNER_ID:
495 if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
496 return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
497 goto Done;
498 }
499 sw_component->signer_id = claim_item.val.string;
500 sw_component->item_flags |=
501 CLAIM_PRESENT_BIT(SW_SIGNER_ID_FLAG);
502 break;
503
504 case IAT_SW_COMPONENT_MEASUREMENT_DESC:
505 if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
506 return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
507 goto Done;
508 }
509 sw_component->measurement_desc = claim_item.val.string;
510 sw_component->item_flags |=
511 CLAIM_PRESENT_BIT(SW_MEASUREMENT_DESC_FLAG);
512 break;
513 }
514 }
515
516 if(qcbor_util_consume_item(decode_context,
517 &claim_item,
518 &next_nest_level)) {
519 return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
520 goto Done;
521 }
522 if(next_nest_level < sw_component_item->uNextNestLevel) {
523 /* Got all the items in the map */
524 break;
525 }
526 }
527
528 Done:
529 return return_value;
530 }
531
532
533 /*
534 * Public function. See attest_token_decode.h
535 */
536 enum attest_token_err_t
attest_token_get_sw_component(struct attest_token_decode_context * me,uint32_t requested_index,struct attest_token_sw_component_t * sw_components)537 attest_token_get_sw_component(struct attest_token_decode_context *me,
538 uint32_t requested_index,
539 struct attest_token_sw_component_t *sw_components)
540 {
541 enum attest_token_err_t return_value;
542 QCBORItem sw_components_array_item;
543 QCBORDecodeContext decode_context;
544 QCBORItem sw_component_item;
545 QCBORError qcbor_error;
546 uint_fast8_t exit_array_level;
547
548 if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
549 return_value = me->last_error;
550 goto Done;
551 }
552
553 QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
554
555 /* Find the map containing all the SW Components */
556 return_value = qcbor_util_decode_to_labeled_item(&decode_context,
557 IAT_SW_COMPONENTS,
558 &sw_components_array_item);
559 if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
560 goto Done;
561 }
562
563 if(sw_components_array_item.uDataType != QCBOR_TYPE_ARRAY) {
564 return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
565 goto Done;
566 }
567
568 exit_array_level = sw_components_array_item.uNextNestLevel;
569
570 /* Loop over contents of SW Components array */
571 while(1) {
572 qcbor_error = QCBORDecode_GetNext(&decode_context, &sw_component_item);
573 if(qcbor_error) {
574 /* no tolerance for any errors here */
575 return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
576 goto Done;
577 }
578
579 if(sw_component_item.uNextNestLevel <= exit_array_level) {
580 /* Next item will be outside the array */
581 return_value = ATTEST_TOKEN_ERR_NOT_FOUND;
582 /* The end of the array containing SW components
583 and didn't get to the requested_index. */
584 goto Done;
585 }
586
587 if(requested_index == 0) {
588 /* Found the one of interest. Decode it and break out */
589 return_value = decode_sw_component(&decode_context,
590 &sw_component_item,
591 sw_components);
592 break; /* The normal, non-error exit from this loop */
593 }
594
595 /* Every member in the array counts even if they are not
596 * what is expected */
597 requested_index--;
598
599 if(qcbor_util_consume_item(&decode_context, &sw_component_item, NULL)) {
600 return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
601 goto Done;
602 }
603 }
604
605 Done:
606 return return_value;
607 }
608