Example
Dependencies: FXAS21002 FXOS8700Q
Diff: simple-mbed-cloud-client/mbed-cloud-client/certificate-enrollment-client/source/ce_tlv.c
- Revision:
- 0:11cc2b7889af
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simple-mbed-cloud-client/mbed-cloud-client/certificate-enrollment-client/source/ce_tlv.c Tue Nov 19 09:49:38 2019 +0000 @@ -0,0 +1,223 @@ +// ---------------------------------------------------------------------------- +// Copyright 2018 ARM Ltd. +// +// 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 <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "ce_tlv.h" +#include "pv_log.h" +#include "pal_macros.h" +#include "pv_error_handling.h" + +#define TYPE_LENGTH_IN_BYTES 2 +#define LEN_LENGTH_IN_BYTES 2 + +// Get the number of bit in a specific variable +#define CE_BITS(var) (sizeof(var) * 8) +// Get the MSB bit number +#define CE_MSB(var) (CE_BITS(var) - 1) + + +static bool is_element_in_range(const ce_tlv_element_s *element, uint16_t num_of_bytes_to_take) +{ + if (element->_end < element->_current) { + return false; + } + if ((element->_end - element->_current) >= num_of_bytes_to_take) { + return true; + } + return false; +} + +static ce_tlv_status_e take_16bit_number(ce_tlv_element_s *element, uint16_t *number_out) +{ + if (!is_element_in_range(element, sizeof(*number_out))) { + return CE_TLV_STATUS_MALFORMED_TLV; + } + + memcpy(number_out, element->_current, sizeof(*number_out)); + + // Convert from network endianity (big endian) to host endianity in a portable manner + *number_out = (uint16_t)PAL_NTOHS(*number_out); + element->_current += sizeof(*number_out); + return CE_TLV_STATUS_SUCCESS; +} + +static ce_tlv_status_e take_bytes(ce_tlv_element_s *element) +{ + if (!is_element_in_range(element, element->len)) { + return CE_TLV_STATUS_MALFORMED_TLV; + } + + element->val.bytes = element->_current; + element->_current += element->len; + return CE_TLV_STATUS_SUCCESS; +} + +bool is_required(const ce_tlv_element_s *element) +{ + return element->is_required; +} + +static ce_tlv_status_e take_type(ce_tlv_element_s *element) +{ + ce_tlv_status_e status = take_16bit_number(element, &element->type); + SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_16bit_number()"); + + // keep order, test "is required" and then clear the type MSB + element->is_required = (((element->type >> CE_MSB(element->type)) & 1) == 1) ? false : true; + element->type &= (uint16_t)(~(1 << CE_MSB(element->type))); // clear the MSB bit + return CE_TLV_STATUS_SUCCESS; +} + +static ce_tlv_status_e take_length(ce_tlv_element_s *element) +{ + return take_16bit_number(element, &element->len); +} + +// Element where element->len is the length of the string +static ce_tlv_status_e take_string(ce_tlv_element_s *element) +{ + // Take the bytes + ce_tlv_status_e status = take_bytes(element); + SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_bytes()"); + + // Assert null terminator at the end + if (element->val.bytes[element->len - 1] != '\0') { + return CE_TLV_STATUS_TEXT_NOT_TERMINATED; + } + + return CE_TLV_STATUS_SUCCESS; +} + +static ce_tlv_status_e take_value(ce_tlv_element_s *element) +{ + switch (element->type) { + case CE_TLV_TYPE_CERT_NAME: + return take_string(element); + default: + // Skip next + element->_current += element->len; + break; + } + + return CE_TLV_STATUS_SUCCESS; +} + + +ce_tlv_status_e ce_tlv_parser_init(const uint8_t *tlv_buf, size_t tlv_buf_len, ce_tlv_element_s *element_out) +{ + // Null check + SA_PV_ERR_RECOVERABLE_RETURN_IF((tlv_buf == NULL), CE_TLV_STATUS_INVALID_ARG, "Invalid tlv_buf"); + SA_PV_ERR_RECOVERABLE_RETURN_IF((element_out == NULL), CE_TLV_STATUS_INVALID_ARG, "Invalid element_out"); + SA_PV_ERR_RECOVERABLE_RETURN_IF((tlv_buf_len == 0), CE_TLV_STATUS_INVALID_ARG, "empty tlv_buf_len"); + + memset(element_out, 0, sizeof(ce_tlv_element_s)); + + element_out->_current = tlv_buf; + element_out->_end = (tlv_buf + tlv_buf_len); + + return CE_TLV_STATUS_SUCCESS; +} + +ce_tlv_status_e ce_tlv_parse_next(ce_tlv_element_s *element) +{ + ce_tlv_status_e status = CE_TLV_STATUS_SUCCESS; + + SA_PV_ERR_RECOVERABLE_RETURN_IF((element == NULL), CE_TLV_STATUS_INVALID_ARG, "Invalid element"); + + // Check if we are at the end of the buffer + if (element->_current == element->_end) { + return CE_TLV_STATUS_END; + } + + // If this is true then there is a bug in the code + // TBD: check if we need to remove this assert + assert(element->_current < element->_end); + + // Parse type + status = take_type(element); + SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_bytes()"); + + // Parse length + status = take_length(element); + SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_length()"); + + // Parse value + status = take_value(element); + SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_value()"); + + return CE_TLV_STATUS_SUCCESS; +} + +#ifdef CERT_RENEWAL_TEST +static void _append_16bit_number(uint16_t number, ce_tlv_encoder_s *encoder) +{ + uint16_t num_buf = (uint16_t)PAL_HTONS(number); + + memcpy(encoder->buf + (uint8_t)encoder->encoded_length, &num_buf, sizeof(num_buf)); + encoder->encoded_length = (uint16_t)(encoder->encoded_length +sizeof(num_buf)); +} + +static void _append_value_string(const char *str, uint16_t str_length, ce_tlv_encoder_s *encoder) +{ + // str_length should contain the '\0' + memcpy(encoder->buf + encoder->encoded_length, str, str_length); + // FIXME: Cast is needed here, need to check why getting compilation warning in Native GCC (Linux) + encoder->encoded_length = (uint16_t)(str_length + encoder->encoded_length); +} + +ce_tlv_status_e tlv_add_str(ce_tlv_type_e type, uint16_t length, const char *value, bool is_tlv_required, ce_tlv_encoder_s *encoder) +{ + uint16_t _type = type; + + // If out of range - update the length - and return CE_TLV_STATUS_ENCODER_INSUFFICIENT_BUFFER + // Next encoding will do the same and any time we may know how big the buffer must be: encoder->encoded_length + if (encoder->encoded_length + TYPE_LENGTH_IN_BYTES + LEN_LENGTH_IN_BYTES + length > encoder->_buf_size) { + encoder->encoded_length = (uint16_t)(encoder->encoded_length + (TYPE_LENGTH_IN_BYTES + LEN_LENGTH_IN_BYTES + length)); + return CE_TLV_STATUS_ENCODER_INSUFFICIENT_BUFFER; + } + + // Append type + + if (!is_tlv_required) { + // set MSB only if optional + _type |= 1 << CE_MSB(_type); + } + _append_16bit_number(_type, encoder); + + // Append length + _append_16bit_number(length, encoder); + + // Append value + _append_value_string(value, length, encoder); + + return CE_TLV_STATUS_SUCCESS; +} + +void ce_tlv_encoder_init(uint8_t *buf, uint16_t buf_size, ce_tlv_encoder_s *encoder) +{ + memset(buf, 0, buf_size); + memset(encoder, 0, sizeof(*encoder)); + encoder->buf = buf; + encoder->encoded_length = 0; // Explicit assignment for readability + encoder->_buf_size = buf_size; +} + +#endif // CERT_RENEWAL_TEST