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