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