| /* |
| * Copyright (c) 2023, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| */ |
| |
| #include "dpe_cmd_decode.h" |
| |
| #include <string.h> |
| |
| #include "dpe_client.h" |
| #include "dpe_impl.h" |
| #include "qcbor/qcbor_encode.h" |
| #include "qcbor/qcbor_decode.h" |
| #include "qcbor/qcbor_spiffy_decode.h" |
| |
| static dpe_error_t decode_dice_inputs(QCBORDecodeContext *decode_ctx, |
| DiceInputValues *input) |
| { |
| QCBORError qcbor_err; |
| UsefulBufC out = { NULL, 0 }; |
| int64_t out_int; |
| |
| /* The DICE inputs are encoded as a map wrapped into a byte string */ |
| QCBORDecode_EnterBstrWrappedFromMapN(decode_ctx, |
| DPE_DERIVE_CHILD_INPUT_DATA, |
| QCBOR_TAG_REQUIREMENT_NOT_A_TAG, NULL); |
| QCBORDecode_EnterMap(decode_ctx, NULL); |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_CODE_HASH, &out); |
| if (out.len != sizeof(input->code_hash)) { |
| return DPE_INVALID_COMMAND; |
| } |
| memcpy(input->code_hash, out.ptr, out.len); |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_CODE_DESCRIPTOR, &out); |
| input->code_descriptor = out.ptr; |
| input->code_descriptor_size = out.len; |
| |
| QCBORDecode_GetInt64InMapN(decode_ctx, DICE_CONFIG_TYPE, &out_int); |
| |
| /* Check error state before interpreting config type */ |
| qcbor_err = QCBORDecode_GetError(decode_ctx); |
| if (qcbor_err != QCBOR_SUCCESS) { |
| return DPE_INVALID_COMMAND; |
| } |
| |
| if (out_int < kDiceConfigTypeInline || |
| out_int > kDiceConfigTypeDescriptor) { |
| return DPE_INVALID_COMMAND; |
| } |
| input->config_type = (DiceConfigType)out_int; |
| |
| /* Only one of config value or config descriptor needs to be provided */ |
| if (input->config_type == kDiceConfigTypeInline) { |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_CONFIG_VALUE, &out); |
| if (out.len != sizeof(input->config_value)) { |
| return DPE_INVALID_COMMAND; |
| } |
| memcpy(input->config_value, out.ptr, out.len); |
| |
| /* Config descriptor is not provided */ |
| input->config_descriptor = NULL; |
| input->config_descriptor_size = 0; |
| } else { |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_CONFIG_DESCRIPTOR, |
| &out); |
| input->config_descriptor = out.ptr; |
| input->config_descriptor_size = out.len; |
| |
| /* Config value is not provided */ |
| memset(input->config_value, 0, sizeof(input->config_value)); |
| } |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_AUTHORITY_HASH, &out); |
| if (out.len != sizeof(input->authority_hash)) { |
| return DPE_INVALID_COMMAND; |
| } |
| memcpy(input->authority_hash, out.ptr, out.len); |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_AUTHORITY_DESCRIPTOR, |
| &out); |
| input->authority_descriptor = out.ptr; |
| input->authority_descriptor_size = out.len; |
| |
| QCBORDecode_GetInt64InMapN(decode_ctx, DICE_MODE, &out_int); |
| if (out_int < kDiceModeNotInitialized || out_int > kDiceModeMaintenance) { |
| return DPE_INVALID_COMMAND; |
| } |
| input->mode = (DiceMode)out_int; |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DICE_HIDDEN, &out); |
| if (out.len != sizeof(input->hidden)) { |
| return DPE_INVALID_COMMAND; |
| } |
| memcpy(input->hidden, out.ptr, out.len); |
| |
| QCBORDecode_ExitMap(decode_ctx); |
| QCBORDecode_ExitBstrWrapped(decode_ctx); |
| |
| return DPE_NO_ERROR; |
| } |
| |
| static dpe_error_t decode_derive_child(QCBORDecodeContext *decode_ctx, |
| QCBOREncodeContext *encode_ctx) |
| { |
| dpe_error_t dpe_err; |
| QCBORError qcbor_err; |
| UsefulBufC out; |
| int context_handle; |
| bool retain_parent_context; |
| bool allow_child_to_derive; |
| bool create_certificate; |
| DiceInputValues dice_inputs; |
| int new_child_context_handle; |
| int new_parent_context_handle; |
| |
| /* Decode DeriveChild command */ |
| QCBORDecode_EnterMap(decode_ctx, NULL); |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_DERIVE_CHILD_CONTEXT_HANDLE, |
| &out); |
| if (out.len != sizeof(context_handle)) { |
| return DPE_INVALID_COMMAND; |
| } |
| memcpy(&context_handle, out.ptr, out.len); |
| |
| QCBORDecode_GetBoolInMapN(decode_ctx, DPE_DERIVE_CHILD_RETAIN_PARENT_CONTEXT, |
| &retain_parent_context); |
| |
| QCBORDecode_GetBoolInMapN(decode_ctx, DPE_DERIVE_CHILD_ALLOW_CHILD_TO_DERIVE, |
| &allow_child_to_derive); |
| |
| QCBORDecode_GetBoolInMapN(decode_ctx, DPE_DERIVE_CHILD_CREATE_CERTIFICATE, |
| &create_certificate); |
| |
| dpe_err = decode_dice_inputs(decode_ctx, &dice_inputs); |
| if (dpe_err != DPE_NO_ERROR) { |
| return dpe_err; |
| } |
| |
| QCBORDecode_ExitMap(decode_ctx); |
| |
| /* Exit top level array */ |
| QCBORDecode_ExitArray(decode_ctx); |
| |
| /* Finish and check for errors before using decoded values */ |
| qcbor_err = QCBORDecode_Finish(decode_ctx); |
| if (qcbor_err != QCBOR_SUCCESS) { |
| return DPE_INVALID_COMMAND; |
| } |
| |
| dpe_err = dpe_derive_child_impl(context_handle, retain_parent_context, |
| allow_child_to_derive, create_certificate, |
| &dice_inputs, &new_child_context_handle, |
| &new_parent_context_handle); |
| if (dpe_err != DPE_NO_ERROR) { |
| return dpe_err; |
| } |
| |
| /* Encode response */ |
| QCBOREncode_OpenArray(encode_ctx); |
| QCBOREncode_AddInt64(encode_ctx, DPE_NO_ERROR); |
| |
| QCBOREncode_OpenMap(encode_ctx); |
| QCBOREncode_AddBytesToMapN(encode_ctx, DPE_DERIVE_CHILD_NEW_CONTEXT_HANDLE, |
| (UsefulBufC){ &new_child_context_handle, |
| sizeof(new_child_context_handle) }); |
| QCBOREncode_AddBytesToMapN(encode_ctx, |
| DPE_DERIVE_CHILD_PARENT_CONTEXT_HANDLE, |
| (UsefulBufC){ &new_parent_context_handle, |
| sizeof(new_parent_context_handle) }); |
| QCBOREncode_CloseMap(encode_ctx); |
| |
| QCBOREncode_CloseArray(encode_ctx); |
| |
| return DPE_NO_ERROR; |
| } |
| |
| static dpe_error_t decode_certify_key(QCBORDecodeContext *decode_ctx, |
| QCBOREncodeContext *encode_ctx) |
| { |
| QCBORError qcbor_err; |
| UsefulBufC out; |
| dpe_error_t dpe_err; |
| int context_handle; |
| bool retain_context; |
| const uint8_t *public_key; |
| size_t public_key_size; |
| const uint8_t *label; |
| size_t label_size; |
| uint8_t certificate_chain_buf[DPE_CERTIFICATE_CHAIN_MAX_SIZE]; |
| size_t certificate_chain_actual_size; |
| uint8_t derived_public_key_buf[DPE_PUBLIC_KEY_MAX_SIZE]; |
| size_t derived_public_key_actual_size; |
| int new_context_handle; |
| |
| /* Decode CertifyKey command */ |
| QCBORDecode_EnterMap(decode_ctx, NULL); |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_CERTIFY_KEY_CONTEXT_HANDLE, |
| &out); |
| if (out.len != sizeof(context_handle)) { |
| return DPE_INVALID_COMMAND; |
| } |
| memcpy(&context_handle, out.ptr, out.len); |
| |
| QCBORDecode_GetBoolInMapN(decode_ctx, DPE_CERTIFY_KEY_RETAIN_CONTEXT, |
| &retain_context); |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_CERTIFY_KEY_PUBLIC_KEY, |
| &out); |
| public_key = out.ptr; |
| public_key_size = out.len; |
| |
| QCBORDecode_GetByteStringInMapN(decode_ctx, DPE_CERTIFY_KEY_LABEL, &out); |
| label = out.ptr; |
| label_size = out.len; |
| |
| QCBORDecode_ExitMap(decode_ctx); |
| |
| /* Exit top level array */ |
| QCBORDecode_ExitArray(decode_ctx); |
| |
| /* Finish and check for errors before using decoded values */ |
| qcbor_err = QCBORDecode_Finish(decode_ctx); |
| if (qcbor_err != QCBOR_SUCCESS) { |
| return DPE_INVALID_COMMAND; |
| } |
| |
| dpe_err = dpe_certify_key_impl(context_handle, retain_context, public_key, |
| public_key_size, label, label_size, |
| certificate_chain_buf, |
| sizeof(certificate_chain_buf), |
| &certificate_chain_actual_size, |
| derived_public_key_buf, |
| sizeof(derived_public_key_buf), |
| &derived_public_key_actual_size, |
| &new_context_handle); |
| if (dpe_err != DPE_NO_ERROR) { |
| return dpe_err; |
| } |
| |
| /* Encode response */ |
| QCBOREncode_OpenArray(encode_ctx); |
| QCBOREncode_AddInt64(encode_ctx, DPE_NO_ERROR); |
| |
| QCBOREncode_OpenMap(encode_ctx); |
| |
| /* The certificate chain is already encoded into a CBOR array by the certify |
| * key implementation. Add it as a byte string so that its decoding can be |
| * skipped and the CBOR returned to the caller. |
| */ |
| QCBOREncode_AddBytesToMapN(encode_ctx, DPE_CERTIFY_KEY_CERTIFICATE_CHAIN, |
| (UsefulBufC){ certificate_chain_buf, |
| certificate_chain_actual_size }); |
| |
| QCBOREncode_AddBytesToMapN(encode_ctx, DPE_CERTIFY_KEY_DERIVED_PUBLIC_KEY, |
| (UsefulBufC){ derived_public_key_buf, |
| derived_public_key_actual_size }); |
| QCBOREncode_AddBytesToMapN(encode_ctx, DPE_CERTIFY_KEY_NEW_CONTEXT_HANDLE, |
| (UsefulBufC){ &new_context_handle, |
| sizeof(new_context_handle) }); |
| |
| QCBOREncode_CloseMap(encode_ctx); |
| |
| QCBOREncode_CloseArray(encode_ctx); |
| |
| return DPE_NO_ERROR; |
| } |
| |
| static void encode_error_only(QCBOREncodeContext *encode_ctx, |
| dpe_error_t dpe_err) |
| { |
| QCBOREncode_OpenArray(encode_ctx); |
| QCBOREncode_AddInt64(encode_ctx, dpe_err); |
| QCBOREncode_CloseArray(encode_ctx); |
| } |
| |
| int32_t dpe_command_decode(int32_t client_id, |
| const char *cmd_input, size_t cmd_input_size, |
| char *cmd_output, size_t *cmd_output_size) |
| { |
| dpe_error_t dpe_err; |
| QCBORError qcbor_err; |
| QCBORDecodeContext decode_ctx; |
| QCBOREncodeContext encode_ctx; |
| UsefulBufC out; |
| uint64_t command_id; |
| |
| QCBORDecode_Init(&decode_ctx, (UsefulBufC){ cmd_input, cmd_input_size }, |
| QCBOR_DECODE_MODE_NORMAL); |
| QCBOREncode_Init(&encode_ctx, (UsefulBuf){ cmd_output, *cmd_output_size }); |
| |
| /* Enter top level array */ |
| QCBORDecode_EnterArray(&decode_ctx, NULL); |
| |
| /* Get the command ID */ |
| QCBORDecode_GetUInt64(&decode_ctx, &command_id); |
| |
| /* Check for errors before interpreting the decoded command ID */ |
| qcbor_err = QCBORDecode_GetError(&decode_ctx); |
| |
| if (qcbor_err == QCBOR_SUCCESS) { |
| switch (command_id) { |
| case DPE_DERIVE_CHILD: |
| dpe_err = decode_derive_child(&decode_ctx, &encode_ctx); |
| break; |
| case DPE_CERTIFY_KEY: |
| dpe_err = decode_certify_key(&decode_ctx, &encode_ctx); |
| break; |
| default: |
| dpe_err = DPE_INVALID_COMMAND; |
| break; |
| } |
| } else { |
| dpe_err = DPE_INVALID_COMMAND; |
| } |
| |
| /* If an unhandled DPE error was returned, then encode it into a response */ |
| if (dpe_err != DPE_NO_ERROR) { |
| encode_error_only(&encode_ctx, dpe_err); |
| } |
| |
| qcbor_err = QCBOREncode_Finish(&encode_ctx, &out); |
| if (qcbor_err != QCBOR_SUCCESS) { |
| return -1; |
| } |
| |
| *cmd_output_size = out.len; |
| |
| return 0; |
| } |