mirror of https://github.com/ARMmbed/mbed-os.git
386 lines
13 KiB
C
386 lines
13 KiB
C
/** @file
|
|
* Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved.
|
|
* SPDX-License-Identifier : Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
**/
|
|
|
|
#include "pal_attestation_eat.h"
|
|
|
|
uint32_t mandatory_claims = 0, mandaroty_sw_components = 0;
|
|
bool_t sw_component_present = 0;
|
|
|
|
static int get_items_in_map(QCBORDecodeContext *decode_context,
|
|
struct items_to_get_t *item_list)
|
|
{
|
|
int item_index;
|
|
QCBORItem item;
|
|
struct items_to_get_t *item_ptr = item_list;
|
|
|
|
/* initialize the data type of all items in the list */
|
|
while (item_ptr->label != 0)
|
|
{
|
|
item_ptr->item.uDataType = QCBOR_TYPE_NONE;
|
|
item_ptr++;
|
|
}
|
|
|
|
QCBORDecode_GetNext(decode_context, &item);
|
|
if (item.uDataType != QCBOR_TYPE_MAP)
|
|
{
|
|
return PAL_ATTEST_ERROR;
|
|
}
|
|
|
|
for (item_index = item.val.uCount; item_index != 0; item_index--)
|
|
{
|
|
if (QCBORDecode_GetNext(decode_context, &item) != QCBOR_SUCCESS)
|
|
{
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
if (item.uLabelType != QCBOR_TYPE_INT64)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
item_ptr = item_list;
|
|
while (item_ptr->label != 0)
|
|
{
|
|
if (item.label.int64 == item_ptr->label)
|
|
{
|
|
item_ptr->item = item;
|
|
}
|
|
item_ptr++;
|
|
}
|
|
}
|
|
|
|
return PAL_ATTEST_SUCCESS;
|
|
}
|
|
|
|
static int get_item_in_map(QCBORDecodeContext *decode_context,
|
|
int32_t label,
|
|
QCBORItem *item)
|
|
{
|
|
struct items_to_get_t item_list[2];
|
|
|
|
item_list[0].label = label;
|
|
item_list[1].label = 0;
|
|
|
|
if (get_items_in_map(decode_context, item_list))
|
|
{
|
|
return PAL_ATTEST_ERROR;
|
|
}
|
|
|
|
if (item_list[0].item.uDataType == QCBOR_TYPE_NONE)
|
|
{
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
|
|
*item = item_list[0].item;
|
|
|
|
return PAL_ATTEST_SUCCESS;
|
|
}
|
|
|
|
static int parse_unprotected_headers(QCBORDecodeContext *decode_context,
|
|
struct useful_buf_c *child,
|
|
bool *loop_back)
|
|
{
|
|
struct items_to_get_t item_list[3];
|
|
|
|
item_list[0].label = COSE_HEADER_PARAM_KID;
|
|
item_list[1].label = T_COSE_SHORT_CIRCUIT_LABEL;
|
|
item_list[2].label = 0;
|
|
*loop_back = false;
|
|
|
|
if (get_items_in_map(decode_context, item_list))
|
|
{
|
|
return PAL_ATTEST_ERROR;
|
|
}
|
|
|
|
if (item_list[1].item.uDataType == QCBOR_TYPE_TRUE)
|
|
{
|
|
*loop_back = true;
|
|
}
|
|
|
|
if (item_list[0].item.uDataType != QCBOR_TYPE_BYTE_STRING)
|
|
{
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
|
|
*child = item_list[0].item.val.string;
|
|
|
|
return PAL_ATTEST_SUCCESS;
|
|
}
|
|
|
|
static int parse_protected_headers(struct useful_buf_c protected_headers,
|
|
int32_t *alg_id)
|
|
{
|
|
QCBORDecodeContext decode_context;
|
|
QCBORItem item;
|
|
|
|
QCBORDecode_Init(&decode_context, protected_headers, 0);
|
|
|
|
if (get_item_in_map(&decode_context, COSE_HEADER_PARAM_ALG, &item))
|
|
{
|
|
return PAL_ATTEST_ERROR;
|
|
}
|
|
|
|
if (QCBORDecode_Finish(&decode_context))
|
|
{
|
|
return PAL_ATTEST_ERROR;
|
|
}
|
|
|
|
if ((item.uDataType != QCBOR_TYPE_INT64) || (item.val.int64 > INT32_MAX))
|
|
{
|
|
return PAL_ATTEST_ERROR;
|
|
}
|
|
|
|
*alg_id = (int32_t)item.val.int64;
|
|
|
|
return PAL_ATTEST_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
@brief - This API will verify the claims
|
|
@param - decode_context : The buffer containing the challenge
|
|
item : context for decoding the data items
|
|
completed_challenge : Buffer containing the challenge
|
|
@return - error status
|
|
**/
|
|
static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item,
|
|
struct useful_buf_c completed_challenge)
|
|
{
|
|
int i, count = 0;
|
|
int status = PAL_ATTEST_SUCCESS;
|
|
|
|
/* Parse each claim and validate their data type */
|
|
while (status == PAL_ATTEST_SUCCESS)
|
|
{
|
|
status = QCBORDecode_GetNext(decode_context, &item);
|
|
if (status != PAL_ATTEST_SUCCESS)
|
|
break;
|
|
|
|
mandatory_claims |= 1 << (EAT_CBOR_ARM_RANGE_BASE - item.label.int64);
|
|
if (item.uLabelType == QCBOR_TYPE_INT64)
|
|
{
|
|
if (item.label.int64 == EAT_CBOR_ARM_LABEL_NONCE)
|
|
{
|
|
if (item.uDataType == QCBOR_TYPE_BYTE_STRING)
|
|
{
|
|
/* Given challenge vs challenge in token */
|
|
if (UsefulBuf_Compare(item.val.string, completed_challenge))
|
|
return PAL_ATTEST_TOKEN_CHALLENGE_MISMATCH;
|
|
}
|
|
else
|
|
return PAL_ATTEST_TOKEN_NOT_SUPPORTED;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_ARM_LABEL_BOOT_SEED ||
|
|
item.label.int64 == EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID ||
|
|
item.label.int64 == EAT_CBOR_ARM_LABEL_UEID)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_ARM_LABEL_ORIGINATION ||
|
|
item.label.int64 == EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION ||
|
|
item.label.int64 == EAT_CBOR_ARM_LABEL_HW_VERSION)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_ARM_LABEL_CLIENT_ID ||
|
|
item.label.int64 == EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_INT64)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_ARM_LABEL_SW_COMPONENTS)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_ARRAY)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
|
|
sw_component_present = 1;
|
|
status = QCBORDecode_GetNext(decode_context, &item);
|
|
if (status != PAL_ATTEST_SUCCESS)
|
|
continue;
|
|
|
|
count = item.val.uCount;
|
|
for (i = 0; i <= count; i++)
|
|
{
|
|
mandaroty_sw_components |= 1 << item.label.int64;
|
|
|
|
if (item.label.int64 == EAT_CBOR_SW_COMPONENT_MEASUREMENT)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_VERSION)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_SIGNER_ID)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_EPOCH)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_INT64)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_TYPE)
|
|
{
|
|
if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
|
|
if (i < count)
|
|
{
|
|
status = QCBORDecode_GetNext(decode_context, &item);
|
|
if (status != PAL_ATTEST_SUCCESS)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* ToDo: Add other claim types */
|
|
}
|
|
}
|
|
|
|
if (status == QCBOR_ERR_HIT_END)
|
|
return PAL_ATTEST_SUCCESS;
|
|
else
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
}
|
|
|
|
/**
|
|
@brief - This API will verify the attestation token
|
|
@param - challenge : The buffer containing the challenge
|
|
challenge_size : Size of the challenge buffer
|
|
token : The buffer containing the attestation token
|
|
token_size : Size of the token buffer
|
|
@return - error status
|
|
**/
|
|
int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_size,
|
|
uint8_t *token, uint32_t token_size)
|
|
{
|
|
int status = PAL_ATTEST_SUCCESS;
|
|
bool short_circuit;
|
|
int32_t cose_algorithm_id;
|
|
QCBORItem item;
|
|
QCBORDecodeContext decode_context;
|
|
struct useful_buf_c completed_challenge;
|
|
struct useful_buf_c completed_token;
|
|
struct useful_buf_c payload;
|
|
struct useful_buf_c protected_headers;
|
|
struct useful_buf_c kid;
|
|
|
|
/* Construct the token buffer for validation */
|
|
completed_token.ptr = token;
|
|
completed_token.len = token_size;
|
|
|
|
/* Construct the challenge buffer for validation */
|
|
completed_challenge.ptr = challenge;
|
|
completed_challenge.len = challenge_size;
|
|
|
|
/*
|
|
-------------------------
|
|
| CBOR Array Type |
|
|
-------------------------
|
|
| Protected Headers |
|
|
-------------------------
|
|
| Unprotected Headers |
|
|
-------------------------
|
|
| Payload |
|
|
-------------------------
|
|
| Signature |
|
|
-------------------------
|
|
*/
|
|
|
|
/* Initialize the decorder */
|
|
QCBORDecode_Init(&decode_context, completed_token, QCBOR_DECODE_MODE_NORMAL);
|
|
|
|
/* Get the Header */
|
|
QCBORDecode_GetNext(&decode_context, &item);
|
|
|
|
/* Check the CBOR Array type. Check if the count is 4.
|
|
* Only COSE_SIGN1 is supported now.
|
|
*/
|
|
if (item.uDataType != QCBOR_TYPE_ARRAY || item.val.uCount != 4 ||
|
|
!QCBORDecode_IsTagged(&decode_context, &item, CBOR_TAG_COSE_SIGN1))
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
|
|
/* Get the next headers */
|
|
QCBORDecode_GetNext(&decode_context, &item);
|
|
if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
|
|
protected_headers = item.val.string;
|
|
|
|
/* Parse the protected headers and check the data type and value*/
|
|
status = parse_protected_headers(protected_headers, &cose_algorithm_id);
|
|
if (status != PAL_ATTEST_SUCCESS)
|
|
return status;
|
|
|
|
/* Parse the unprotected headers and check the data type and value */
|
|
short_circuit = false;
|
|
status = parse_unprotected_headers(&decode_context, &kid, &short_circuit);
|
|
if (status != PAL_ATTEST_SUCCESS)
|
|
return status;
|
|
|
|
/* Get the payload */
|
|
QCBORDecode_GetNext(&decode_context, &item);
|
|
if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
|
|
payload = item.val.string;
|
|
|
|
/* Get the digital signature */
|
|
QCBORDecode_GetNext(&decode_context, &item);
|
|
if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
|
|
/* Initialize the Decoder and validate the payload format */
|
|
QCBORDecode_Init(&decode_context, payload, QCBOR_DECODE_MODE_NORMAL);
|
|
status = QCBORDecode_GetNext(&decode_context, &item);
|
|
if (status != PAL_ATTEST_SUCCESS)
|
|
return status;
|
|
|
|
if (item.uDataType != QCBOR_TYPE_MAP)
|
|
return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
|
|
|
/* Parse the payload and check the data type of each claim */
|
|
status = parse_claims(&decode_context, item, completed_challenge);
|
|
if (status != PAL_ATTEST_SUCCESS)
|
|
return status;
|
|
|
|
if ((mandatory_claims & MANDATORY_CLAIM_WITH_SW_COMP) == MANDATORY_CLAIM_WITH_SW_COMP)
|
|
{
|
|
if ((mandaroty_sw_components & MANDATORY_SW_COMP) != MANDATORY_SW_COMP)
|
|
return PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS;
|
|
}
|
|
else if ((mandatory_claims & MANDATORY_CLAIM_NO_SW_COMP) != MANDATORY_CLAIM_NO_SW_COMP)
|
|
{
|
|
return PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS;
|
|
}
|
|
|
|
return PAL_ATTEST_SUCCESS;
|
|
}
|