bug fix

Dependencies:   HTS221

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers fcc_bundle_csr_utils.c Source File

fcc_bundle_csr_utils.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 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 #ifndef USE_TINY_CBOR
00017 #include "fcc_bundle_utils.h"
00018 #include "fcc_bundle_handler.h"
00019 #include "fcc_malloc.h"
00020 #include "pv_error_handling.h"
00021 #include "fcc_utils.h"
00022 #include "kcm_internal.h"
00023 #include "fcc_bundle_fields.h"
00024 
00025 // For convenience when migrating to tinycbor
00026 #define CN_CBOR_NEXT_GET(cn) cn->next 
00027 
00028 // Initial attempt to allocate buffer for generated CSR will be <size (in bytes) of the encoded CSR request map (part of the CBOR)> + CSR_INITIAL_EXTRA_ALLOCATION_BYTES.
00029 // If the allocation is not enough we keep allocating an extra CSR_ALLOCATION_STEP until the buffer is sufficiently large, or, the allocation fails.
00030 #define CSR_INITIAL_EXTRA_ALLOCATION_BYTES 100
00031 #define CSR_ALLOCATION_STEP 100
00032 
00033 
00034 // FIXME: Temporary. This is a workaround so that the memory is still allocated when calling cn_cbor_encoder_write().
00035 // When we migrate to tinycbor we could either write the CSR directly into the preallocated encoder buffer, or write to a separate buffer, then write to the encoder, then immediately free the separate buffer.
00036 uint8_t *g_csr_buf[CSR_MAX_NUMBER_OF_CSRS] = { 0 };
00037 
00038 void g_csr_buf_free()
00039 {
00040     int i;
00041 
00042     for (i = 0; i < CSR_MAX_NUMBER_OF_CSRS; i++) {
00043         fcc_free(g_csr_buf[i]);
00044         g_csr_buf[i] = NULL;
00045     }
00046 }
00047 
00048 static uint8_t **g_csr_next_available()
00049 {
00050     int i;
00051 
00052     for (i = 0; i < CSR_MAX_NUMBER_OF_CSRS; i++) {
00053         if (!g_csr_buf[i]) {
00054             return &g_csr_buf[i];
00055         }
00056     }
00057 
00058     return NULL;
00059 }
00060 
00061 
00062 static fcc_status_e  csr_extensions_parse(const cn_cbor *parser, kcm_csr_params_s *csr_params_out)
00063 {
00064     cn_cbor *cbor_iterator, *cbor_extension_map;
00065 
00066     // Get the extensions from the map - a map (optional, return success if does not exist)
00067     cbor_extension_map = cn_cbor_mapget_string(parser, FCC_CSRREQ_INBOUND_EXTENSIONS_NAME);
00068     if (!cbor_extension_map) {
00069         return FCC_STATUS_SUCCESS;
00070     }
00071 
00072     SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor_extension_map->type != CN_CBOR_MAP), FCC_STATUS_BUNDLE_ERROR, "Extensions wrong format");
00073 
00074     // FIXME: Should parse the trust level from the extensions. Currently not in use
00075 
00076     // Parse key usage (optional)
00077     cbor_iterator = cn_cbor_mapget_string(cbor_extension_map, FCC_CSRREQ_INBOUND_EXTENSION_KEYUSAGE_NAME);
00078     if (cbor_iterator) {
00079         SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor_iterator->type != CN_CBOR_UINT), FCC_STATUS_BUNDLE_ERROR, "Key usage wrong format");
00080         csr_params_out->key_usage = (uint32_t)cbor_iterator->v.uint;
00081     }
00082 
00083     // Parse extended key usage (optional)
00084     cbor_iterator = cn_cbor_mapget_string(cbor_extension_map, FCC_CSRREQ_INBOUND_EXTENSION_EXTENDEDKEYUSAGE_NAME);
00085     if (cbor_iterator) {
00086         SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor_iterator->type != CN_CBOR_UINT), FCC_STATUS_BUNDLE_ERROR, "Extended Key usage wrong format");
00087         csr_params_out->ext_key_usage = (uint32_t)cbor_iterator->v.uint;
00088     }
00089 
00090     return FCC_STATUS_SUCCESS;
00091 }
00092 /*
00093 * parser - points to a CSR request map
00094 */
00095 static fcc_status_e  csr_params_parse(const cn_cbor *parser, kcm_csr_params_s *csr_params_out)
00096 {
00097     cn_cbor *cbor_iterator;
00098     fcc_status_e  fcc_status;
00099 
00100     // Parse the extension (optional - will return success if extensions do not exist)
00101     fcc_status = csr_extensions_parse(parser, csr_params_out);
00102     SA_PV_ERR_RECOVERABLE_RETURN_IF((fcc_status != FCC_STATUS_SUCCESS), fcc_status, "Error parsing CSR extensions");
00103 
00104     // Retrieve the MD type
00105     cbor_iterator = cn_cbor_mapget_string(parser, FCC_CSRREQ_INBOUND_MESSAGEDIGEST_NAME);
00106     SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor_iterator == NULL), FCC_STATUS_BUNDLE_ERROR, "No MD type");
00107     SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor_iterator->type != CN_CBOR_UINT), FCC_STATUS_BUNDLE_ERROR, "MD type wrong format");
00108 
00109     csr_params_out->md_type = (kcm_md_type_e)cbor_iterator->v.uint;
00110 
00111     // Retrieve the subject
00112     cbor_iterator = cn_cbor_mapget_string(parser, FCC_CSRREQ_INBOUND_SUBJECT_NAME);
00113     SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor_iterator == NULL), FCC_STATUS_BUNDLE_ERROR, "No subject");
00114     SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor_iterator->type != CN_CBOR_BYTES && cbor_iterator->type != CN_CBOR_TEXT), FCC_STATUS_BUNDLE_ERROR, "Subject wrong format");
00115 
00116     // Allocate memory for the subject, it may be large and must be terminated with a '\0' terminator
00117     csr_params_out->subject = fcc_malloc((size_t)cbor_iterator->length + 1);
00118     SA_PV_ERR_RECOVERABLE_RETURN_IF((csr_params_out->subject == NULL), FCC_STATUS_MEMORY_OUT, "Error allocating subject");
00119 
00120     memcpy(csr_params_out->subject, cbor_iterator->v.bytes, (size_t)cbor_iterator->length);
00121 
00122     // Append the NULL terminator
00123     csr_params_out->subject[cbor_iterator->length] = '\0';
00124 
00125     return FCC_STATUS_SUCCESS;
00126 }
00127 
00128 /** Parse, create, and encode the next CSR map into the CSR cbor array in the response CBOR
00129 * The outcome of this function is that the following map will be appended to the encoder: {"PrKN: "<name>", "Data": <CSR byte array>}
00130 * @param parser CSRrequest map:
00131 *    INBOUND MESSAGE:
00132 *    {
00133 *     ...
00134 *
00135 *     "CsrReqs" : [ { ... }, { ... }, ... , <YOU ARE HERE> ]
00136 *
00137 *     ...
00138 *    }
00139 *
00140 * @param encoder - points to the next place in the CSR CBOR array:
00141 *    OUTBOUND MESSAGE:
00142 *    {
00143 *     ...
00144 *
00145 *     "Csrs" : [ { ... }, { ... }, ... , <YOU ARE HERE> ]
00146 *
00147 *     ...
00148 *    }
00149 *
00150 *
00151 */
00152 static fcc_status_e  encode_next_csr(const cn_cbor *parser, cn_cbor *encoder)
00153 {
00154     kcm_csr_params_s csr_params;
00155     fcc_status_e  fcc_status = FCC_STATUS_SUCCESS;
00156     fcc_status_e  output_info_fcc_status = FCC_STATUS_SUCCESS;
00157     kcm_status_e  kcm_status = KCM_STATUS_SUCCESS;
00158     bool status;
00159     size_t csr_extra_bytes = CSR_INITIAL_EXTRA_ALLOCATION_BYTES;
00160     size_t csr_buf_len;
00161     const uint8_t *private_key_name, *public_key_name;
00162     size_t private_key_name_len, public_key_name_len;
00163     int approximated_csr_size = 0;
00164     uint8_t **csr_buf;
00165     size_t csr_len = 0;
00166 
00167     char *key;
00168     cn_cbor *val, *cbor, *encoder_iterator;
00169 
00170     memset(&csr_params, 0, sizeof(kcm_csr_params_s));
00171 
00172     // First, Create and open a new map for the encoder, that will eventually look like that {"PrKN: "<name>", "Data": <CSR byte array>}
00173     encoder_iterator = cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA NULL);
00174     SA_PV_ERR_RECOVERABLE_RETURN_IF((encoder_iterator == NULL), FCC_STATUS_MEMORY_OUT, "Error creating CBOR");
00175 
00176     // Push the empty map into the encoder (open the container part 2)
00177     status = cn_cbor_array_append(encoder, encoder_iterator, NULL);
00178     SA_PV_ERR_RECOVERABLE_RETURN_IF((status == false), FCC_STATUS_BUNDLE_ERROR, "CBOR error");
00179 
00180     // Get private key name
00181     cbor = cn_cbor_mapget_string(parser, FCC_CSRREQ_INBOUND_PRIVATE_KEY_NAME);
00182     SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor == NULL), FCC_STATUS_BUNDLE_ERROR, "No private key in message");
00183     SA_PV_ERR_RECOVERABLE_RETURN_IF((cbor->type != CN_CBOR_TEXT), FCC_STATUS_BUNDLE_ERROR, "No private key in message");
00184 
00185     private_key_name = cbor->v.bytes;
00186     private_key_name_len = (size_t)cbor->length;
00187 
00188     // Append Name key-value into the encoder
00189     val = cn_cbor_text_create(private_key_name, (int)private_key_name_len, CBOR_CONTEXT_PARAM_COMMA NULL);
00190     SA_PV_ERR_RECOVERABLE_RETURN_IF((val == NULL), FCC_STATUS_MEMORY_OUT, "Error creating CBOR");
00191     status = cn_cbor_mapput_string(encoder_iterator, FCC_CSR_OUTBOUND_MAP_PRIVATE_KEY_NAME, val, CBOR_CONTEXT_PARAM_COMMA NULL);
00192     SA_PV_ERR_RECOVERABLE_RETURN_IF((status == false), FCC_STATUS_BUNDLE_ERROR, "CBOR error");
00193 
00194     // Get public key name. Field is optional, if does not exist - set to NULL
00195     cbor = cn_cbor_mapget_string(parser, FCC_CSRREQ_INBOUND_PUBLIC_KEY_NAME);
00196     if (cbor == NULL) {
00197         public_key_name = NULL;
00198         public_key_name_len = 0;
00199     } else {
00200         public_key_name = cbor->v.bytes;
00201         public_key_name_len = (size_t)cbor->length;
00202     }
00203 
00204     // Extract CSR params
00205     fcc_status = csr_params_parse(parser, &csr_params);
00206     SA_PV_ERR_RECOVERABLE_GOTO_IF((fcc_status != FCC_STATUS_SUCCESS), fcc_status = fcc_status, Exit, "Error parsing CSR params");
00207 
00208     // Gets the size in bytes of the encoded CSR request map
00209     approximated_csr_size = cn_cbor_get_encoded_container_size(parser);
00210     SA_PV_ERR_RECOVERABLE_GOTO_IF((approximated_csr_size < 0), fcc_status = FCC_STATUS_BUNDLE_ERROR, Exit, "Error getting encoded CBOR size");
00211 
00212     approximated_csr_size += KCM_EC_SECP256R1_MAX_PUB_KEY_DER_SIZE + KCM_ECDSA_SECP256R1_MAX_SIGNATURE_SIZE_IN_BYTES;
00213 
00214     csr_buf = g_csr_next_available();
00215 
00216     // Start with an approximate allocation and keep trying to increase the allocation until it is sufficiently large, or some error occurres.
00217     while (true) {
00218         csr_buf_len = (size_t)approximated_csr_size + csr_extra_bytes;
00219         *csr_buf = fcc_malloc(csr_buf_len);
00220         SA_PV_ERR_RECOVERABLE_GOTO_IF((*csr_buf == NULL), fcc_status = FCC_STATUS_MEMORY_OUT, Exit, "Error generating CSR");
00221 
00222         // Generate the CSR into the encoder 
00223         // FIXME: when migrating to tinycbor we might want to try to encode it directly into the encoder buffer. This may require manually creating the cbor byte-array prefix (major type 3)
00224         // Requires understanding the CBOR mechanism but could save significant space since this way the CSR will not be duplicated.
00225         kcm_status = kcm_generate_keys_and_csr(KCM_SCHEME_EC_SECP256R1, private_key_name, private_key_name_len,
00226                                                public_key_name, public_key_name_len, true, &csr_params,
00227                                                *csr_buf, csr_buf_len, &csr_len,
00228                                                NULL);
00229         if (kcm_status == KCM_STATUS_SUCCESS) {
00230             break;
00231         } else if (kcm_status == KCM_STATUS_INSUFFICIENT_BUFFER) { // If buffer insufficient - attempt with larger buffer
00232             csr_extra_bytes += CSR_ALLOCATION_STEP;
00233         } else {
00234             fcc_status = fcc_convert_kcm_to_fcc_status(kcm_status);
00235             goto Exit;
00236         }
00237     }
00238 
00239     // Append the encoded CSR data key-value to the CSR response map
00240     key = FCC_CSR_OUTBOUND_MAP_DATA;
00241     val = cn_cbor_data_create(*csr_buf, (int)csr_len, CBOR_CONTEXT_PARAM_COMMA NULL);    
00242     SA_PV_ERR_RECOVERABLE_GOTO_IF((val == NULL), fcc_status = FCC_STATUS_MEMORY_OUT, Exit, "Error creating CBOR");
00243 
00244     status = cn_cbor_mapput_string(encoder_iterator, key, val, CBOR_CONTEXT_PARAM_COMMA NULL);
00245     SA_PV_ERR_RECOVERABLE_GOTO_IF((status == false), fcc_status = FCC_STATUS_BUNDLE_ERROR, Exit, "CBOR error");
00246 
00247     // FIXME: For tinycbor - this would be the time to close the map opened in the beginning - {"Name: "<name>", "Format": "der", "Data": <CSR byte array>}
00248 
00249 Exit:
00250     fcc_free(csr_params.subject);
00251     // If KCM error - store the KCM error, If FCC error, store the FCC error
00252     if (kcm_status != KCM_STATUS_SUCCESS) {
00253         output_info_fcc_status = fcc_bundle_store_error_info(private_key_name, private_key_name_len, kcm_status);
00254         SA_PV_ERR_RECOVERABLE_RETURN_IF((output_info_fcc_status != FCC_STATUS_SUCCESS),
00255                                         fcc_status = FCC_STATUS_OUTPUT_INFO_ERROR,
00256                                         "Failed to create output kcm_status error %d", kcm_status);
00257     } 
00258     
00259 
00260     return fcc_status;
00261 }
00262 
00263 /** Parse a CBOR array of CSR requests, for each CSR request - generate the keys and the CSR, save the keys in the persistent storage, and encode the CSR in the response encoder.
00264 * @param csrs_list_cb CSR Requests array:
00265 *    INBOUND MESSAGE:
00266 *    {
00267 *     ...
00268 *
00269 *     "CsrReqs" : <YOU ARE HERE>
00270 *
00271 *     ...
00272 *    }
00273 *
00274 * @param response_encoder - points to the next place in the CSR CBOR array:
00275 *    OUTBOUND MESSAGE:
00276 *    {
00277 *     "SchemeVersion": "0.0.1", 
00278 *     ...
00279 *
00280 *     <YOU ARE HERE>
00281 *
00282 *     ...
00283 *    }
00284 *
00285 *
00286 */
00287 fcc_status_e  fcc_bundle_process_csrs(const cn_cbor *csrs_list_cb, cn_cbor *response_encoder)
00288 {
00289     fcc_status_e  fcc_status = FCC_STATUS_SUCCESS;
00290     fcc_status_e  output_info_fcc_status = FCC_STATUS_SUCCESS;
00291     cn_cbor *cbor, *encoder_iterator;
00292     const cn_cbor *parser_iterator;
00293     bool status;
00294     int i = 0;
00295 
00296     SA_PV_ERR_RECOVERABLE_GOTO_IF((csrs_list_cb == NULL || response_encoder == NULL), fcc_status = FCC_STATUS_BUNDLE_ERROR, SetError, "Invalid cbor_blob");
00297     
00298     // Make sure we get a array of CSR requests
00299     SA_PV_ERR_RECOVERABLE_GOTO_IF((csrs_list_cb->type != CN_CBOR_ARRAY), fcc_status = FCC_STATUS_BUNDLE_ERROR, SetError, "CSR requests must be array");
00300     SA_PV_ERR_RECOVERABLE_GOTO_IF((csrs_list_cb->length > CSR_MAX_NUMBER_OF_CSRS), fcc_status = FCC_STATUS_TOO_MANY_CSR_REQUESTS, SetError, "More CSR requests than the maximum allowed");
00301 
00302     parser_iterator = csrs_list_cb;
00303 
00304     // Open a new map for the encoder (open the container part 1)
00305     cbor = cn_cbor_array_create(CBOR_CONTEXT_PARAM_COMMA NULL);
00306     SA_PV_ERR_RECOVERABLE_GOTO_IF((cbor == NULL), fcc_status = FCC_STATUS_MEMORY_OUT, SetError, "Error creating CBOR");
00307 
00308     // Push the empty array into the encoder (open the container part 2)
00309     status = cn_cbor_mapput_string(response_encoder, FCC_CSR_OUTBOUND_GROUP_NAME, cbor, CBOR_CONTEXT_PARAM_COMMA NULL);
00310     SA_PV_ERR_RECOVERABLE_GOTO_IF((status == false), fcc_status = FCC_STATUS_BUNDLE_ERROR, SetError, "Error appending");
00311 
00312     // Step into the encoder array (the last child is the value of the last appended KV pair which is the empty array)
00313     encoder_iterator = response_encoder->last_child;
00314 
00315     // Go to the first value of the array
00316     parser_iterator = parser_iterator->first_child;
00317 
00318     for (i = 0; i < csrs_list_cb->length; i++) {
00319 
00320         SA_PV_ERR_RECOVERABLE_GOTO_IF((parser_iterator == NULL), fcc_status = FCC_STATUS_BUNDLE_ERROR, SetError, "Error getting CBOR");
00321 
00322         fcc_status = encode_next_csr(parser_iterator, encoder_iterator);
00323         SA_PV_ERR_RECOVERABLE_GOTO_IF((fcc_status != FCC_STATUS_SUCCESS), fcc_status = fcc_status, SetError, "Error encoding CSR");
00324         
00325         // step into next value in the CBOR array
00326         parser_iterator = CN_CBOR_NEXT_GET(parser_iterator);
00327     }
00328 
00329 SetError:
00330     if (fcc_status != FCC_STATUS_SUCCESS) {
00331         output_info_fcc_status = fcc_store_error_info(NULL, 0, fcc_status);
00332         SA_PV_ERR_RECOVERABLE_RETURN_IF((output_info_fcc_status != FCC_STATUS_SUCCESS),
00333                                         fcc_status = FCC_STATUS_OUTPUT_INFO_ERROR,
00334                                         "Failed to create output fcc_status error %d", fcc_status);
00335     }
00336 
00337     return fcc_status;
00338 }
00339 #endif