leo hendrickson / Mbed OS example-Ethernet-mbed-Cloud-connect
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ce_tlv.c Source File

ce_tlv.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2018 ARM Ltd.
00003 //  
00004 // Licensed under the Apache License, Version 2.0 (the "License");
00005 // you may not use this file except in compliance with the License.
00006 // You may obtain a copy of the License at
00007 //  
00008 //     http://www.apache.org/licenses/LICENSE-2.0
00009 //  
00010 // Unless required by applicable law or agreed to in writing, software
00011 // distributed under the License is distributed on an "AS IS" BASIS,
00012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013 // See the License for the specific language governing permissions and
00014 // limitations under the License.
00015 // ----------------------------------------------------------------------------
00016 
00017 
00018 #include <stdbool.h>
00019 #include <stdint.h>
00020 #include <string.h>
00021 #include <stdio.h>
00022 
00023 #include "ce_tlv.h"
00024 #include "pv_log.h"
00025 #include "pal_macros.h"
00026 #include "pv_error_handling.h"
00027 
00028 #define TYPE_LENGTH_IN_BYTES 2
00029 #define LEN_LENGTH_IN_BYTES 2
00030 
00031 // Get the number of bit in a specific variable
00032 #define CE_BITS(var) (sizeof(var) * 8)
00033 // Get the MSB bit number
00034 #define CE_MSB(var) (CE_BITS(var) - 1)
00035 
00036 
00037 static bool is_element_in_range(const ce_tlv_element_s *element, uint16_t num_of_bytes_to_take)
00038 {
00039     if (element->_end < element->_current) {
00040         return false;
00041     }
00042     if ((element->_end - element->_current) >= num_of_bytes_to_take) {
00043         return true;
00044     }
00045     return false;
00046 }
00047 
00048 static ce_tlv_status_e take_16bit_number(ce_tlv_element_s *element, uint16_t *number_out)
00049 {
00050     if (!is_element_in_range(element, sizeof(*number_out))) {
00051         return CE_TLV_STATUS_MALFORMED_TLV;
00052     }
00053 
00054     memcpy(number_out, element->_current, sizeof(*number_out));
00055 
00056     // Convert from network endianity (big endian) to host endianity in a portable manner
00057     *number_out = (uint16_t)PAL_NTOHS(*number_out);
00058     element->_current += sizeof(*number_out);
00059     return CE_TLV_STATUS_SUCCESS;
00060 }
00061 
00062 static ce_tlv_status_e take_bytes(ce_tlv_element_s *element)
00063 {
00064     if (!is_element_in_range(element, element->len)) {
00065         return CE_TLV_STATUS_MALFORMED_TLV;
00066     }
00067 
00068     element->val.bytes = element->_current;
00069     element->_current += element->len;
00070     return CE_TLV_STATUS_SUCCESS;
00071 }
00072 
00073 bool is_required(const ce_tlv_element_s *element)
00074 {
00075     return element->is_required;
00076 }
00077 
00078 static ce_tlv_status_e take_type(ce_tlv_element_s *element)
00079 {
00080     ce_tlv_status_e status = take_16bit_number(element, &element->type);
00081     SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_16bit_number()");
00082 
00083     // keep order, test "is required" and then clear the type MSB
00084     element->is_required = (((element->type >> CE_MSB(element->type)) & 1) == 1) ? false : true;
00085     element->type &= (uint16_t)(~(1 << CE_MSB(element->type))); // clear the MSB bit
00086     return CE_TLV_STATUS_SUCCESS;
00087 }
00088 
00089 static ce_tlv_status_e take_length(ce_tlv_element_s *element)
00090 {
00091     return take_16bit_number(element, &element->len);
00092 }
00093 
00094 // Element where element->len is the length of the string
00095 static ce_tlv_status_e take_string(ce_tlv_element_s *element)
00096 {
00097     // Take the bytes
00098     ce_tlv_status_e status = take_bytes(element);
00099     SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_bytes()");
00100     
00101     // Assert null terminator at the end
00102     if (element->val.bytes[element->len - 1] != '\0') {
00103         return CE_TLV_STATUS_TEXT_NOT_TERMINATED;
00104     }
00105 
00106     return CE_TLV_STATUS_SUCCESS;
00107 }
00108 
00109 static ce_tlv_status_e take_value(ce_tlv_element_s *element)
00110 {
00111     switch (element->type) {
00112         case CE_TLV_TYPE_CERT_NAME:
00113             return take_string(element);
00114         default:
00115             // Skip next
00116             element->_current += element->len;
00117             break;
00118     }
00119 
00120     return CE_TLV_STATUS_SUCCESS;
00121 }
00122 
00123 
00124 ce_tlv_status_e ce_tlv_parser_init(const uint8_t *tlv_buf, size_t tlv_buf_len, ce_tlv_element_s *element_out)
00125 { 
00126     // Null check
00127     SA_PV_ERR_RECOVERABLE_RETURN_IF((tlv_buf == NULL), CE_TLV_STATUS_INVALID_ARG, "Invalid tlv_buf");
00128     SA_PV_ERR_RECOVERABLE_RETURN_IF((element_out == NULL), CE_TLV_STATUS_INVALID_ARG, "Invalid element_out");
00129     SA_PV_ERR_RECOVERABLE_RETURN_IF((tlv_buf_len == 0), CE_TLV_STATUS_INVALID_ARG, "empty tlv_buf_len");
00130 
00131     memset(element_out, 0, sizeof(ce_tlv_element_s));
00132 
00133     element_out->_current = tlv_buf;
00134     element_out->_end = (tlv_buf + tlv_buf_len);
00135 
00136     return CE_TLV_STATUS_SUCCESS;
00137 }
00138 
00139 ce_tlv_status_e ce_tlv_parse_next(ce_tlv_element_s *element)
00140 {
00141     ce_tlv_status_e status = CE_TLV_STATUS_SUCCESS;
00142 
00143     SA_PV_ERR_RECOVERABLE_RETURN_IF((element == NULL), CE_TLV_STATUS_INVALID_ARG, "Invalid element");
00144 
00145     // Check if we are at the end of the buffer
00146     if (element->_current == element->_end) {
00147         return CE_TLV_STATUS_END;
00148     }
00149 
00150     // If this is true then there is a bug in the code
00151     // TBD: check if we need to remove this assert
00152     assert(element->_current < element->_end);
00153 
00154     // Parse type
00155     status = take_type(element);
00156     SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_bytes()");
00157 
00158     // Parse length
00159     status = take_length(element);
00160     SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_length()");
00161 
00162     // Parse value
00163     status = take_value(element);
00164     SA_PV_ERR_RECOVERABLE_RETURN_IF((status != CE_TLV_STATUS_SUCCESS), status, "failed in take_value()");
00165 
00166     return CE_TLV_STATUS_SUCCESS;
00167 }
00168 
00169 #ifdef CERT_RENEWAL_TEST
00170 static void _append_16bit_number(uint16_t number, ce_tlv_encoder_s *encoder)
00171 {
00172     uint16_t num_buf = (uint16_t)PAL_HTONS(number);
00173 
00174     memcpy(encoder->buf + (uint8_t)encoder->encoded_length, &num_buf, sizeof(num_buf));
00175     encoder->encoded_length = (uint16_t)(encoder->encoded_length +sizeof(num_buf));
00176 }
00177 
00178 static void _append_value_string(const char *str, uint16_t str_length, ce_tlv_encoder_s *encoder)
00179 {
00180     // str_length should contain the '\0'
00181     memcpy(encoder->buf + encoder->encoded_length, str, str_length);
00182     // FIXME: Cast is needed here, need to check why getting compilation warning in Native GCC (Linux)
00183     encoder->encoded_length = (uint16_t)(str_length + encoder->encoded_length);
00184 }
00185 
00186 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)
00187 {
00188     uint16_t _type = type;
00189 
00190     // If out of range - update the length - and return CE_TLV_STATUS_ENCODER_INSUFFICIENT_BUFFER
00191     // Next encoding will do the same and any time we may know how big the buffer must be: encoder->encoded_length
00192     if (encoder->encoded_length + TYPE_LENGTH_IN_BYTES + LEN_LENGTH_IN_BYTES + length > encoder->_buf_size) {
00193         encoder->encoded_length = (uint16_t)(encoder->encoded_length + (TYPE_LENGTH_IN_BYTES + LEN_LENGTH_IN_BYTES + length));
00194         return CE_TLV_STATUS_ENCODER_INSUFFICIENT_BUFFER;
00195     }
00196 
00197     // Append type
00198 
00199     if (!is_tlv_required) {
00200         // set MSB only if optional
00201         _type |= 1 << CE_MSB(_type);
00202     }
00203     _append_16bit_number(_type, encoder);
00204 
00205     // Append length
00206     _append_16bit_number(length, encoder);
00207 
00208     // Append value
00209     _append_value_string(value, length, encoder);
00210 
00211     return CE_TLV_STATUS_SUCCESS;
00212 }
00213 
00214 void ce_tlv_encoder_init(uint8_t *buf, uint16_t buf_size, ce_tlv_encoder_s *encoder)
00215 {
00216     memset(buf, 0, buf_size);
00217     memset(encoder, 0, sizeof(*encoder));
00218     encoder->buf = buf;
00219     encoder->encoded_length = 0; // Explicit assignment for readability
00220     encoder->_buf_size = buf_size;
00221 }
00222 
00223 #endif // CERT_RENEWAL_TEST