Jim Carver
/
mbed-cloud-workshop-connect-HTS221
bug fix
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Jul 12 2022 21:04:55 by 1.7.2