Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

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