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

« Back to documentation index

Show/hide line numbers certificate_enrollment.c Source File

certificate_enrollment.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 #include <stdio.h>
00018 #include <stdbool.h>
00019 #include "pv_error_handling.h"
00020 #include "certificate_enrollment.h"
00021 #include "key_config_manager.h"
00022 #include "pv_macros.h"
00023 #include "fcc_defs.h"
00024 #include "ce_internal.h"
00025 #include "storage.h"
00026 
00027 extern const char g_renewal_status_file[];
00028 
00029 ce_status_e  ce_init(void)
00030 {
00031     return kcm_init() == KCM_STATUS_SUCCESS ? CE_STATUS_SUCCESS : CE_STATUS_ERROR;
00032 }
00033 
00034 
00035 ce_status_e  ce_error_handler(kcm_status_e  kcm_status)
00036 {
00037     switch (kcm_status) {
00038         case KCM_STATUS_SUCCESS:
00039             return CE_STATUS_SUCCESS;
00040         case KCM_STATUS_INVALID_PARAMETER:
00041             return CE_STATUS_INVALID_PARAMETER;
00042         case KCM_STATUS_OUT_OF_MEMORY:
00043             return CE_STATUS_OUT_OF_MEMORY;
00044         case KCM_STATUS_INSUFFICIENT_BUFFER:
00045             return CE_STATUS_INSUFFICIENT_BUFFER;
00046         case KCM_STATUS_ITEM_NOT_FOUND:
00047             return CE_STATUS_ITEM_NOT_FOUND;
00048         case KCM_STATUS_ITEM_IS_EMPTY:
00049             return CE_STATUS_ITEM_IS_EMPTY;
00050         default:
00051             return CE_STATUS_ERROR;
00052     }
00053 }
00054 
00055 ce_status_e  ce_generate_keys_and_create_csr_from_certificate(
00056     const char *certificate_name, const cs_key_handle_t key_h,
00057     uint8_t **csr_out, size_t *csr_size_out)
00058 {
00059     bool success;
00060     ce_status_e  ce_status = CE_STATUS_SUCCESS;
00061     kcm_status_e  kcm_status = KCM_STATUS_SUCCESS;
00062     uint8_t *certificate_buff = NULL;
00063     size_t certificate_buff_max_size = 0, certificate_buff_size = 0, certificate_private_key_size = 0;
00064     uint8_t *csr_buff = NULL;
00065     size_t csr_buff_size = 0, csr_buff_max_size;
00066     char *kcm_crt_name = NULL, *kcm_priv_key_name = NULL;
00067     uint32_t kcm_crt_name_size = (uint32_t)strlen(certificate_name) + 1; // append null termination
00068 
00069 
00070     SA_PV_ERR_RECOVERABLE_RETURN_IF((certificate_name == NULL), CE_STATUS_INVALID_PARAMETER, "Invalid certificate_name");
00071     SA_PV_ERR_RECOVERABLE_RETURN_IF((key_h == 0), CE_STATUS_INVALID_PARAMETER, "Invalid key_h");
00072     SA_PV_LOG_INFO_FUNC_ENTER("certificate_name = %s key_h = %" PRIuPTR "", certificate_name, key_h);
00073     SA_PV_ERR_RECOVERABLE_RETURN_IF((csr_out == NULL), CE_STATUS_INVALID_PARAMETER, "Invalid csr_out");
00074     SA_PV_ERR_RECOVERABLE_RETURN_IF((csr_size_out == NULL), CE_STATUS_INVALID_PARAMETER, "Invalid csr_size_out");
00075 
00076     // assert NOT a bootstrap device certificate
00077     success = pv_str_equals(g_fcc_bootstrap_device_certificate_name, certificate_name, kcm_crt_name_size);
00078     SA_PV_ERR_RECOVERABLE_RETURN_IF((success), CE_STATUS_FORBIDDEN_REQUEST, "device bootstrap certificate renewal is not allowed");
00079 
00080     // assert NOT a bootstrap device key
00081     success = pv_str_equals(g_fcc_bootstrap_device_private_key_name, certificate_name, kcm_crt_name_size);
00082     SA_PV_ERR_RECOVERABLE_RETURN_IF((success), CE_STATUS_FORBIDDEN_REQUEST, "device bootstrap certificate renewal is not allowed");
00083 
00084     success = ce_set_item_names(certificate_name, &kcm_priv_key_name, NULL, &kcm_crt_name);
00085     SA_PV_ERR_RECOVERABLE_RETURN_IF((!success), CE_STATUS_ITEM_NOT_FOUND, "failed for ce_set_item_names()");
00086 
00087     // getting the private key size successfully signifies that the certificate's private key exist and we're okay to continue
00088     kcm_status = kcm_item_get_data_size((const uint8_t *)kcm_priv_key_name, strlen(kcm_priv_key_name), KCM_PRIVATE_KEY_ITEM, &certificate_private_key_size);
00089     SA_PV_ERR_RECOVERABLE_RETURN_IF((kcm_status != KCM_STATUS_SUCCESS), ce_error_handler(kcm_status), "failed to get the certificate private key length");
00090     SA_PV_ERR_RECOVERABLE_RETURN_IF((certificate_private_key_size == 0), CE_STATUS_ITEM_IS_EMPTY, "got empty private key for certificate %s", kcm_crt_name);
00091 
00092     // get the certificate octet length
00093     kcm_status = kcm_item_get_data_size((const uint8_t *)kcm_crt_name, strlen(kcm_crt_name), KCM_CERTIFICATE_ITEM, &certificate_buff_max_size);
00094     SA_PV_ERR_RECOVERABLE_RETURN_IF((kcm_status != KCM_STATUS_SUCCESS), CE_STATUS_ERROR, "failed to get certificate octet length");
00095     SA_PV_ERR_RECOVERABLE_RETURN_IF((certificate_buff_max_size == 0), CE_STATUS_ITEM_IS_EMPTY, "got 0 length for certificate");
00096 
00097     certificate_buff = (uint8_t *)malloc(certificate_buff_max_size);
00098     SA_PV_ERR_RECOVERABLE_RETURN_IF((certificate_buff == NULL), CE_STATUS_OUT_OF_MEMORY, "failed allocating certificate buffer");
00099 
00100     // get the certificate bytes
00101     kcm_status = kcm_item_get_data((const uint8_t *)kcm_crt_name, strlen(kcm_crt_name), KCM_CERTIFICATE_ITEM, certificate_buff, certificate_buff_max_size, &certificate_buff_size);
00102     SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS), (ce_status = ce_error_handler(kcm_status)), exit, "failed to get certificate buffer");
00103     SA_PV_ERR_RECOVERABLE_GOTO_IF((certificate_buff_size == 0), (ce_status = CE_STATUS_ITEM_IS_EMPTY), exit, "got 0 length for certificate");
00104 
00105     // we assume that the CSR size would not exceed the certificate size
00106     csr_buff_max_size = certificate_buff_size;
00107     
00108     csr_buff = (uint8_t *)malloc(csr_buff_max_size);
00109     SA_PV_ERR_RECOVERABLE_GOTO_IF((csr_buff == NULL), (ce_status = CE_STATUS_OUT_OF_MEMORY), exit, "Failed allocating CSR buffer");
00110 
00111     kcm_status = cs_generate_keys_and_create_csr_from_certificate(certificate_buff, certificate_buff_size, key_h, csr_buff, csr_buff_max_size, &csr_buff_size);
00112     SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS), (ce_status = ce_error_handler(kcm_status)), exit, "failed to generate keys and create CSR");
00113     SA_PV_ERR_RECOVERABLE_GOTO_IF((csr_buff == NULL), (ce_status = CE_STATUS_ERROR), exit, "failed creating CSR or generating keys for certificate (%s)", kcm_crt_name);
00114     
00115 
00116     // the calling user is responsible to free csr_out buffer
00117     *csr_out = csr_buff;
00118     *csr_size_out = csr_buff_size;
00119 
00120     SA_PV_LOG_INFO_FUNC_EXIT("csr_size_out = %" PRIu32 "", (uint32_t)(*csr_size_out));
00121 
00122 exit:
00123     if (certificate_buff != NULL) {
00124         free(certificate_buff);
00125     }
00126     if (ce_status != CE_STATUS_SUCCESS) {
00127         free(csr_buff);
00128     }
00129 
00130     return ce_status;
00131 }
00132 ce_status_e  ce_safe_renewal(const char *item_name, ce_renewal_params_s *renewal_data)
00133 {
00134     bool success;
00135     ce_status_e  ce_status = CE_STATUS_SUCCESS;
00136     kcm_status_e  kcm_status = KCM_STATUS_SUCCESS;
00137     char *priv_key_name = NULL, *pub_key_name = NULL, *certificate_name = NULL;
00138     size_t data_size_out;
00139     bool is_public_key = false;
00140     cs_ec_key_context_s *ec_key_ctx = NULL;
00141     struct cert_chain_context_s *certificate_chain_data = NULL;
00142 
00143     //Check parameters
00144     SA_PV_ERR_RECOVERABLE_RETURN_IF((item_name == NULL), CE_STATUS_INVALID_PARAMETER, "Invalid item_name");
00145     SA_PV_ERR_RECOVERABLE_RETURN_IF((renewal_data == NULL), CE_STATUS_INVALID_PARAMETER, "Invalid renewal_data");
00146     SA_PV_ERR_RECOVERABLE_RETURN_IF((renewal_data->crypto_handle ==(cs_key_handle_t) NULL), CE_STATUS_INVALID_PARAMETER, "Invalid crypto handle");
00147     SA_PV_ERR_RECOVERABLE_RETURN_IF((renewal_data->cert_data == NULL), CE_STATUS_INVALID_PARAMETER, "Invalid cert_data");
00148     certificate_chain_data = (struct cert_chain_context_s*)renewal_data->cert_data;
00149     SA_PV_ERR_RECOVERABLE_RETURN_IF((certificate_chain_data->certs == NULL || certificate_chain_data->chain_length == 0), CE_STATUS_INVALID_PARAMETER, "Invalid certificate data");
00150     SA_PV_LOG_INFO_FUNC_ENTER("item_name = %s ", item_name);
00151 
00152     //Set item names
00153     success = ce_set_item_names(item_name, &priv_key_name, &pub_key_name, &certificate_name);
00154     SA_PV_ERR_RECOVERABLE_RETURN_IF((!success), CE_STATUS_ITEM_NOT_FOUND, "failed for ce_set_item_names()");
00155 
00156     if (pub_key_name != NULL) { //If not lwm2m items
00157         //Check if public key is present
00158         kcm_status = kcm_item_get_data_size((const uint8_t *)pub_key_name, strlen(pub_key_name), KCM_PUBLIC_KEY_ITEM, &data_size_out);
00159         SA_PV_ERR_RECOVERABLE_RETURN_IF((kcm_status != KCM_STATUS_SUCCESS && kcm_status != KCM_STATUS_ITEM_NOT_FOUND), CE_STATUS_STORAGE_ERROR, "failed to get public key size");
00160 
00161         //Set public key flag
00162         if (kcm_status == KCM_STATUS_SUCCESS) {
00163             is_public_key = true;
00164         }
00165     } 
00166 
00167     //Verify items correlation
00168     kcm_status = cs_verify_items_correlation(renewal_data->crypto_handle, renewal_data->cert_data->certs->cert, renewal_data->cert_data->certs->cert_length);
00169     SA_PV_ERR_RECOVERABLE_RETURN_IF((kcm_status != KCM_STATUS_SUCCESS), CE_STATUS_RENEWAL_ITEM_VALIDATION_ERROR, "failed to validate renewal items");
00170 
00171     //Create backup items
00172     kcm_status = ce_create_backup_items(item_name, is_public_key);
00173     if (kcm_status == KCM_STATUS_ITEM_NOT_FOUND) {
00174         ce_status = CE_STATUS_ORIGINAL_ITEM_ERROR;
00175     } 
00176     if (kcm_status != KCM_STATUS_SUCCESS && kcm_status != KCM_STATUS_ITEM_NOT_FOUND) {
00177         ce_status = CE_STATUS_BACKUP_ITEM_ERROR;
00178     } 
00179     SA_PV_ERR_RECOVERABLE_GOTO_IF((ce_status != CE_STATUS_SUCCESS), ce_status = ce_status, exit_and_delete_renewal_data,"failed to create backup items");
00180 
00181     //Create renewal status file and write item_name to the file
00182     kcm_status = ce_create_renewal_status(item_name); 
00183     if (kcm_status == KCM_STATUS_FILE_EXIST) {
00184         //Assumption : in case of existing  active renewal process ->ce_safe_renewal api blocked by event loop.
00185         // So we assume that it is ok to delete renewal status file, as it is impossible that it used by another active renewal process.
00186         //try to delete existing renewal status file and create new one
00187         ce_delete_renewal_status();
00188         kcm_status = ce_create_renewal_status(item_name);
00189     }
00190     SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS), ce_status = CE_STATUS_RENEWAL_STATUS_ERROR, exit_and_delete_renewal_data, "failed to create renewal status file");
00191 
00192     //Clean original items
00193     kcm_status = ce_clean_items(item_name, KCM_ORIGINAL_ITEM, is_public_key );
00194     SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS), ce_status = CE_STATUS_STORAGE_ERROR, restore_backup_data, "Falid to clean original items");
00195 
00196     ec_key_ctx = (cs_ec_key_context_s*)renewal_data->crypto_handle;
00197 
00198     //Save new items
00199     kcm_status = kcm_item_store((const uint8_t*)priv_key_name, strlen(priv_key_name), KCM_PRIVATE_KEY_ITEM, false, ec_key_ctx->priv_key, ec_key_ctx->priv_key_size, NULL);
00200     SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS), ce_status = CE_STATUS_STORAGE_ERROR, restore_backup_data, "Falid to store new private key");
00201 
00202     if (is_public_key == true) {
00203         kcm_status = kcm_item_store((const uint8_t*)pub_key_name, strlen(pub_key_name), KCM_PUBLIC_KEY_ITEM, false, ec_key_ctx->pub_key, ec_key_ctx->pub_key_size, NULL);
00204         SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS), ce_status = CE_STATUS_STORAGE_ERROR, restore_backup_data, "Falid to store new public key");
00205     }
00206 
00207     //Save new certificate/certificate chain
00208     kcm_status = ce_store_new_certificate((const char*)certificate_name, certificate_chain_data);
00209     SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS), ce_status = CE_STATUS_STORAGE_ERROR, restore_backup_data, "Falid to store new certificate/certificate chain");
00210 
00211 
00212 restore_backup_data:
00213     if (ce_status != CE_STATUS_SUCCESS) {
00214         //the restore here done only in case of some error, and at this stage we are not still want to return an original error
00215         //this is the reason why we don't read the returned error of ce_restore_backup_items api
00216         ce_restore_backup_items(item_name);
00217     }
00218 
00219 exit_and_delete_renewal_data:
00220 
00221     //Delete renewal status file
00222     ce_delete_renewal_status();
00223 
00224     //Clean backup items
00225     ce_clean_items(item_name, KCM_BACKUP_ITEM, is_public_key);
00226 
00227     return ce_status;
00228 }
00229 
00230 /*! The API called during kcm_init() in case of error during renewal_certificate API. 
00231 * The functions checks status of the renewal process, restores original data and deletes redundant files.
00232 * The APIs checks the status based on renewal file and its data.
00233 *    @void
00234 */
00235 void ce_check_and_restore_backup_status(void)
00236 {
00237     kcm_status_e  kcm_status = KCM_STATUS_SUCCESS;
00238     size_t renewal_item_data_len = 0;
00239     size_t act_renewal_item_data_len = 0;
00240     uint8_t renewal_item_name[CE_MAX_SIZE_OF_KCM_ITEM_NAME] = { 0 };
00241 
00242 
00243     //Get renewal status file size
00244     kcm_status = storage_data_size_read((const uint8_t *)g_renewal_status_file, strlen(g_renewal_status_file), KCM_CONFIG_ITEM, KCM_BACKUP_ITEM, &renewal_item_data_len);
00245 
00246     //If renewal status file is not found or failed to get data size -> exit , no data to restore
00247     if (kcm_status != KCM_STATUS_SUCCESS) {
00248         if (kcm_status != KCM_STATUS_ITEM_NOT_FOUND) {
00249             SA_PV_LOG_ERR("Failed to read renewal status");//Add error print, as this case is exceptional
00250         }
00251         return;
00252     }
00253     if (renewal_item_data_len + 1 > sizeof(renewal_item_name)) {
00254         SA_PV_LOG_ERR("Renewal item name is too big");//Add error print, as this case is exceptional
00255         return;
00256     }
00257 
00258     //Read renewal status data
00259     kcm_status = storage_data_read((const uint8_t *)g_renewal_status_file, strlen(g_renewal_status_file), KCM_CONFIG_ITEM, KCM_BACKUP_ITEM, renewal_item_name, renewal_item_data_len, &act_renewal_item_data_len);
00260     SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS || act_renewal_item_data_len != renewal_item_data_len), kcm_status = kcm_status, exit, "Failed to read renewal status data");
00261 
00262     //Set null terminator
00263    // renewal_item_data[renewal_item_data_len] ='\0';
00264     renewal_item_name[renewal_item_data_len] = '\0';
00265 
00266     //Restore backup items - this will clean all unnecessary data
00267     kcm_status = ce_restore_backup_items((const char *)renewal_item_name);
00268     SA_PV_ERR_RECOVERABLE_GOTO_IF((kcm_status != KCM_STATUS_SUCCESS && kcm_status!= KCM_STATUS_ITEM_NOT_FOUND), kcm_status = kcm_status, exit, "Failed to restore backup items");
00269 
00270 
00271 exit:
00272     //Delete renewal status file
00273     kcm_status = storage_data_delete((const uint8_t *)g_renewal_status_file, (size_t)strlen(g_renewal_status_file), KCM_CONFIG_ITEM, KCM_BACKUP_ITEM);
00274     if (kcm_status != KCM_STATUS_SUCCESS) {
00275         SA_PV_LOG_ERR("Failed to delete renewal status");//Add error print, as this case is exceptional
00276     }
00277 
00278     SA_PV_LOG_INFO_FUNC_EXIT_NO_ARGS();
00279     return;
00280 }
00281