Simple interface for Mbed Cloud Client
Embed:
(wiki syntax)
Show/hide line numbers
arm_uc_utilities.c
00001 // ---------------------------------------------------------------------------- 00002 // Copyright 2016-2017 ARM Ltd. 00003 // 00004 // SPDX-License-Identifier: Apache-2.0 00005 // 00006 // Licensed under the Apache License, Version 2.0 (the "License"); 00007 // you may not use this file except in compliance with the License. 00008 // You may obtain a copy of the License at 00009 // 00010 // http://www.apache.org/licenses/LICENSE-2.0 00011 // 00012 // Unless required by applicable law or agreed to in writing, software 00013 // distributed under the License is distributed on an "AS IS" BASIS, 00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 // See the License for the specific language governing permissions and 00016 // limitations under the License. 00017 // ---------------------------------------------------------------------------- 00018 00019 #include "update-client-common/arm_uc_utilities.h" 00020 #include "update-client-common/arm_uc_error.h" 00021 #include "update-client-common/arm_uc_config.h" 00022 00023 #include <string.h> 00024 #include <stdlib.h> 00025 #include <stdio.h> 00026 #include "sotp.h" 00027 00028 /* lookup table for printing hexadecimal values */ 00029 const uint8_t arm_uc_hex_table[16] = {'0', '1', '2', '3', '4', '5', '6', '7', 00030 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 00031 00032 /** 00033 * @brief Parse a uri string to populate a arm_uc_uri_t struct 00034 * @detail Format of uri scheme:[//]host[:port]/path 00035 * [] means optional, path will always start with a '/' 00036 * 00037 * @param str Pointer to string containing URI. 00038 * @param size String length. 00039 * @param uri The arm_uc_uri_t struct to be populated 00040 * @return Error code. 00041 */ 00042 arm_uc_error_t arm_uc_str2uri(const uint8_t* buffer, 00043 uint32_t buffer_size, 00044 arm_uc_uri_t* uri) 00045 { 00046 arm_uc_error_t result = (arm_uc_error_t){ ERR_INVALID_PARAMETER }; 00047 00048 if (buffer && 00049 uri && 00050 uri->ptr && 00051 (buffer_size < uri->size_max)) 00052 { 00053 const uint8_t* str = buffer; 00054 uint8_t* colon = NULL; 00055 uint8_t* slash = NULL; 00056 uint32_t len = 0; 00057 00058 /* find scheme by searching for first colon */ 00059 colon = memchr(str, ':', buffer_size); 00060 len = colon - str; 00061 00062 if (len < uri->size_max) 00063 { 00064 /* copy scheme to temporary uri buffer and convert to lower case. 00065 */ 00066 for (uint32_t index = 0; index < len; index++) 00067 { 00068 /* lower case characters have higher ASCII value */ 00069 if (str[index] < 'a') 00070 { 00071 uri->ptr[index] = str[index] + ('a' - 'A'); 00072 } 00073 else 00074 { 00075 uri->ptr[index] = str[index]; 00076 } 00077 } 00078 00079 /* copy ':' */ 00080 uri->ptr[len] = str[len]; 00081 00082 /* convert scheme string to scheme type */ 00083 if (memcmp(uri->ptr, "http:", 5) == 0) 00084 { 00085 uri->scheme = URI_SCHEME_HTTP; 00086 00087 /* set default port based on scheme - can be overwritten */ 00088 uri->port = 80; 00089 } 00090 else 00091 { 00092 uri->scheme = URI_SCHEME_NONE; 00093 } 00094 00095 /* only continue if scheme is supported */ 00096 if (uri->scheme != URI_SCHEME_NONE) 00097 { 00098 /* strip any leading '/' */ 00099 str = colon + 1; 00100 for (str += 1; 00101 (str[0] == '/') && (str < (buffer + buffer_size)); 00102 ++str); 00103 00104 /* find separation between host and path */ 00105 slash = memchr(str, '/', buffer_size - (str - buffer)); 00106 00107 if (slash != NULL) 00108 { 00109 bool parsed = true; 00110 00111 /* find optional port */ 00112 colon = memchr(str, ':', buffer_size - (slash - buffer)); 00113 00114 if (colon != NULL) 00115 { 00116 uri->port = arm_uc_str2uint32(colon + 1, 00117 buffer_size - (colon - buffer), 00118 &parsed); 00119 len = colon - str; 00120 } 00121 else 00122 { 00123 len = slash - str; 00124 } 00125 00126 /* check */ 00127 if ((parsed == 1) && (len < uri->size_max)) 00128 { 00129 /* copy host name to URI buffer */ 00130 memcpy(uri->ptr, str, len); 00131 00132 /* \0 terminate string */ 00133 uri->ptr[len] = '\0'; 00134 00135 /* update length */ 00136 uri->size = len + 1; 00137 00138 /* set host pointer */ 00139 uri->host = (char*) uri->ptr; 00140 00141 /* find remaining path length */ 00142 str = slash; 00143 len = arm_uc_strnlen(str, buffer_size - (str - buffer)); 00144 00145 /* check */ 00146 if ((len > 0) && (len < (uri->size_max - uri->size))) 00147 { 00148 /* copy path to URI buffer */ 00149 memcpy(&uri->ptr[uri->size], str, len); 00150 00151 /* set path pointer */ 00152 uri->path = (char*) &uri->ptr[uri->size]; 00153 00154 /* \0 terminate string */ 00155 uri->ptr[uri->size + len] = '\0'; 00156 00157 /* update length after path pointer is set */ 00158 uri->size += len + 1; 00159 00160 /* parsing passed all checks */ 00161 result = (arm_uc_error_t){ ERR_NONE }; 00162 } 00163 } 00164 } 00165 } 00166 } 00167 } 00168 00169 return result; 00170 } 00171 00172 /** 00173 * @brief Find substring inside string. 00174 * @details The size of both string and substring is explicitly passed so no 00175 * assumptions are made about NULL termination. 00176 * 00177 * @param big_buffer Pointer to the string to be searched. 00178 * @param big_length Length of the string to be searched. 00179 * @param little_buffer Pointer to the substring being searched for. 00180 * @param little_length Length of the substring being searched for. 00181 * @return Index to where the substring was found inside the string. If the 00182 * string doesn't contain the subtring, UINT32_MAX is returned. 00183 */ 00184 uint32_t arm_uc_strnstrn(const uint8_t* big_buffer, 00185 uint32_t big_length, 00186 const uint8_t* little_buffer, 00187 uint32_t little_length) 00188 { 00189 uint32_t result = UINT32_MAX; 00190 00191 /* Sanity check. Pointers are not NULL. The little buffer is smaller than 00192 the big buffer. The little buffer is not empty. 00193 */ 00194 if (big_buffer && 00195 little_buffer && 00196 (big_length >= little_length) && 00197 (little_length > 0)) 00198 { 00199 uint8_t little_hash = 0; 00200 uint8_t big_hash = 0; 00201 uint32_t little_length_m1 = little_length - 1; 00202 00203 /* Prepare hashes. The last byte for the big hash is added in the 00204 comparison loop. 00205 */ 00206 for (uint32_t index = 0; index < little_length_m1; index++) 00207 { 00208 little_hash ^= little_buffer[index]; 00209 big_hash ^= big_buffer[index]; 00210 } 00211 00212 /* Add the last byte for the little hash. */ 00213 little_hash ^= little_buffer[little_length_m1]; 00214 00215 /* Comparison loop. In each loop the big hash is updated and compared 00216 to the little hash. If the hash matches, a more thorough byte-wise 00217 comparison is performed. The complexity of the hash determines how 00218 often a collision occures and how often a full comparison is done. 00219 */ 00220 for (uint32_t index = 0; 00221 index < (big_length - (little_length_m1)); 00222 index++) 00223 { 00224 /* update hash */ 00225 big_hash ^= big_buffer[index + (little_length_m1)]; 00226 00227 /* cursory check */ 00228 if (little_hash == big_hash) 00229 { 00230 /* hash checks out do comprehensive check */ 00231 uint32_t checks = 0; 00232 00233 for ( ; checks < little_length; checks++) 00234 { 00235 /* stop counting if bytes differ */ 00236 if (big_buffer[index + checks] != little_buffer[checks]) 00237 { 00238 break; 00239 } 00240 } 00241 00242 /* check if all bytes matched */ 00243 if (checks == little_length) 00244 { 00245 /* save pointer and break loop */ 00246 result = index; 00247 break; 00248 } 00249 } 00250 00251 /* update hash - remove tail */ 00252 big_hash ^= big_buffer[index]; 00253 } 00254 } 00255 00256 return result; 00257 } 00258 00259 /** 00260 * @brief Find string length. 00261 * @details Custom implementation of strnlen which is a GNU extension. 00262 * Returns either the string length or max_length. 00263 * 00264 * @param buffer Pointer to string. 00265 * @param max_length Maximum buffer length. 00266 * 00267 * @return String length or max_length. 00268 */ 00269 uint32_t arm_uc_strnlen(const uint8_t* buffer, uint32_t max_length) 00270 { 00271 uint32_t length = 0; 00272 00273 for ( ; length < max_length; length++) 00274 { 00275 if (buffer[length] == '\0') 00276 { 00277 break; 00278 } 00279 } 00280 00281 return length; 00282 } 00283 00284 /** 00285 * @brief Convert string to unsigned 32 bit integer. 00286 * @details Function tries to parse string as an unsigned 32 bit integer 00287 * and return the value. The function expects the first byte to be an 00288 * integer and will continue until: 00289 * 1. the buffer is empty 00290 * 2. the intermediate result is larger then UINT32_MAX 00291 * 3. the next byte is not a number 00292 * 00293 * If a valid 32 bit unsigned integer is found the third parameter is 00294 * set to true and the return value holds the parsed number. Otherwise, 00295 * the third parameter will be false and the return value will be 0. 00296 * 00297 * @param buffer Pointer to string. 00298 * @param max_length Maximum buffer length. 00299 * @param success Pointer to boolean indicating whether the parsing was successful. 00300 * @return Parsed value. Only valid if success it true. 00301 */ 00302 uint32_t arm_uc_str2uint32(const uint8_t* buffer, 00303 uint32_t max_length, 00304 bool* success) 00305 { 00306 uint64_t result = 0; 00307 bool found = false; 00308 00309 /* default output and return status is 0 and false */ 00310 uint32_t output = 0; 00311 00312 if (success) 00313 { 00314 *success = false; 00315 } 00316 00317 /* null pointer and length check */ 00318 if (buffer && (max_length > 0)) 00319 { 00320 /* loop through string */ 00321 for (uint32_t index = 0; index < max_length; index++) 00322 { 00323 /* check if character is a number */ 00324 if (('0' <= buffer[index]) && 00325 (buffer[index] <= '9') && 00326 (result < UINT64_MAX)) 00327 { 00328 /* shift one decimal position and append next digit */ 00329 result *= 10; 00330 result += buffer[index] - '0'; 00331 00332 /* found at least one integer, mark as found */ 00333 found = true; 00334 } 00335 else 00336 { 00337 /* character is not a number, stop loop */ 00338 break; 00339 } 00340 } 00341 00342 /* set output and return value only if a valid number was found */ 00343 if (found && (result <= UINT64_MAX)) 00344 { 00345 output = result; 00346 00347 if (success) 00348 { 00349 *success = true; 00350 } 00351 } 00352 } 00353 00354 return output; 00355 } 00356 00357 uint32_t arm_uc_crc32(const uint8_t* buffer, uint32_t length) 00358 { 00359 const uint8_t* current = buffer; 00360 uint32_t crc = 0xFFFFFFFF; 00361 00362 while (length--) 00363 { 00364 crc ^= *current++; 00365 00366 for (uint32_t counter = 0; counter < 8; counter++) 00367 { 00368 if (crc & 1) 00369 { 00370 crc = (crc >> 1) ^ 0xEDB88320; 00371 } 00372 else 00373 { 00374 crc = crc >> 1; 00375 } 00376 } 00377 } 00378 00379 return (crc ^ 0xFFFFFFFF); 00380 } 00381 00382 uint32_t arm_uc_parse_uint32(const uint8_t* input) 00383 { 00384 uint32_t result = 0; 00385 00386 if (input) 00387 { 00388 result = input[0]; 00389 result = (result << 8) | input[1]; 00390 result = (result << 8) | input[2]; 00391 result = (result << 8) | input[3]; 00392 } 00393 00394 return result; 00395 } 00396 00397 uint64_t arm_uc_parse_uint64(const uint8_t* input) 00398 { 00399 uint64_t result = 0; 00400 00401 if (input) 00402 { 00403 result = input[0]; 00404 result = (result << 8) | input[1]; 00405 result = (result << 8) | input[2]; 00406 result = (result << 8) | input[3]; 00407 result = (result << 8) | input[4]; 00408 result = (result << 8) | input[5]; 00409 result = (result << 8) | input[6]; 00410 result = (result << 8) | input[7]; 00411 } 00412 00413 return result; 00414 } 00415 00416 void arm_uc_write_uint32(uint8_t* buffer, uint32_t value) 00417 { 00418 if (buffer) 00419 { 00420 buffer[3] = value; 00421 buffer[2] = (value >> 8); 00422 buffer[1] = (value >> 16); 00423 buffer[0] = (value >> 24); 00424 } 00425 } 00426 00427 void arm_uc_write_uint64(uint8_t* buffer, uint64_t value) 00428 { 00429 if (buffer) 00430 { 00431 buffer[7] = value; 00432 buffer[6] = (value >> 8); 00433 buffer[5] = (value >> 16); 00434 buffer[4] = (value >> 24); 00435 buffer[3] = (value >> 32); 00436 buffer[2] = (value >> 40); 00437 buffer[1] = (value >> 48); 00438 buffer[0] = (value >> 56); 00439 } 00440 } 00441 00442 // Constant time binary comparison 00443 uint32_t ARM_UC_BinCompareCT(const arm_uc_buffer_t* a, const arm_uc_buffer_t* b) 00444 { 00445 uint32_t result; 00446 uint32_t i; 00447 const uint32_t *wa = (uint32_t *)a->ptr; 00448 const uint32_t *wb = (uint32_t *)b->ptr; 00449 const uint32_t bytes_aligned = a->size & ~((1 << sizeof(uint32_t)) - 1); 00450 const uint32_t bytes = a->size; 00451 00452 // Check sizes 00453 if (a->size != b->size) 00454 { 00455 return 1; 00456 } 00457 result = 0; 00458 for (i = 0; i < bytes_aligned; i += sizeof(uint32_t)) 00459 { 00460 const uint32_t idx = i/sizeof(uint32_t); 00461 result = result | (wa[idx] ^ wb[idx]); 00462 } 00463 for (; i < bytes; i++) 00464 { 00465 result = result | (a->ptr[i] ^ b->ptr[i]); 00466 } 00467 // Reduce to 0 or 1 in constant time 00468 return (result | -result) >> 31; 00469 } 00470 00471 static const uint8_t base64EncodeArray[65] = {MBED_CLOUD_UPDATE_BASE64_CHARSET}; 00472 00473 uint8_t * ARM_UC_Base64Enc(uint8_t* buf, const uint32_t size, const arm_uc_buffer_t* bin) 00474 { 00475 uint32_t partial = 0; 00476 const uint8_t * lastPos = buf + size; 00477 uint32_t i; 00478 uint32_t pad2 = (bin->size - bin->size % 3); 00479 uint32_t pad1 = (bin->size - bin->size % 3) + 1; 00480 for (i = 0; i < bin->size && buf <= lastPos - 4; i+=3) { 00481 partial = (bin->ptr[i] << 16); 00482 if ( i < pad1 ) 00483 { 00484 partial = partial | (bin->ptr[i+1] << 8); 00485 } 00486 if ( i < pad2) 00487 { 00488 partial = partial | (bin->ptr[i+2] << 0); 00489 } 00490 buf[0] = base64EncodeArray[(partial >> 18) & 0x3f]; 00491 buf[1] = base64EncodeArray[(partial >> 12) & 0x3f]; 00492 buf[2] = (i < pad1) ? base64EncodeArray[(partial >> 6) & 0x3f] : base64EncodeArray[64]; 00493 buf[3] = (i < pad2) ? base64EncodeArray[(partial >> 0) & 0x3f] : base64EncodeArray[64]; 00494 buf += 4; 00495 } 00496 buf[0] = 0; 00497 return buf; 00498 } 00499 00500 uint32_t ARM_UC_Base64DecodeChar(uint8_t c) 00501 { 00502 if (c == MBED_CLOUD_UPDATE_BASE64_CHARSET[64] || c == MBED_CLOUD_UPDATE_BASE64_CHARSET[0]) 00503 { 00504 return 0; 00505 } 00506 uint32_t idx = 0; 00507 int32_t i; 00508 for (i = 5; i >= 0; i--) 00509 { 00510 uint32_t tmpidx = idx | 1 << i; 00511 uint8_t ct = MBED_CLOUD_UPDATE_BASE64_CHARSET[tmpidx]; 00512 if (c == ct) 00513 { 00514 return tmpidx; 00515 } 00516 else if (c > ct) 00517 { 00518 idx = tmpidx; 00519 } 00520 } 00521 return (uint32_t) -1; 00522 } 00523 00524 void ARM_UC_Base64Dec(arm_uc_buffer_t* bin, const uint32_t size, const uint8_t* buf) 00525 { 00526 uintptr_t optr = (uintptr_t)bin->ptr; 00527 const uint8_t* iptr = buf; 00528 while ((uintptr_t)iptr + 4 < (uintptr_t) buf + size && optr + 1 < (uintptr_t)bin->ptr + bin->size_max) { 00529 uint8_t partial[3]; 00530 uint8_t a = (ARM_UC_Base64DecodeChar(iptr[0])); 00531 uint8_t b = (ARM_UC_Base64DecodeChar(iptr[1])); 00532 uint8_t c = (ARM_UC_Base64DecodeChar(iptr[2])); 00533 uint8_t d = (ARM_UC_Base64DecodeChar(iptr[3])); 00534 uint8_t l = 3; 00535 if (d == MBED_CLOUD_UPDATE_BASE64_CHARSET[64]) 00536 { 00537 l--; 00538 } 00539 if (c == MBED_CLOUD_UPDATE_BASE64_CHARSET[64]) 00540 { 00541 l--; 00542 } 00543 partial[0] = ((a << 2) & 0xfc) | ((b >> 4) & 0x3); 00544 partial[1] = ((b << 4) & 0xf0) | ((c >> 2) & 0xf); 00545 partial[2] = ((c << 6) & 0xc0) | ((d >> 0) & 0x3f); 00546 memcpy((void*)optr, partial, l); 00547 iptr += 4; 00548 optr += l; 00549 if (d == MBED_CLOUD_UPDATE_BASE64_CHARSET[64]) 00550 { 00551 break; 00552 } 00553 } 00554 bin->size = optr - (uintptr_t)bin->ptr; 00555 } 00556 00557 00558 /*****************************************************************************/ 00559 /* SOTP access functions */ 00560 /*****************************************************************************/ 00561 00562 /** 00563 * Reads an integer up to 8 bytes in size from SOTP. 00564 * @param type SOTP item type. 00565 * @param size SOTP item size in bytes (1, 2, 4 or 8). 00566 * @param out Location where the integer value is written. 00567 * @return ERR_NONE if the read succeeded, ERR_INVALID_PARAMETER otherwise. 00568 */ 00569 arm_uc_error_t arm_uc_read_sotp_uint(uint32_t type, uint16_t size, void *out) 00570 { 00571 arm_uc_error_t result = (arm_uc_error_t){ ERR_INVALID_PARAMETER }; 00572 00573 if (out != NULL && 00574 (size == sizeof(uint8_t) || size == sizeof(uint16_t) || 00575 size == sizeof(uint32_t) || size == sizeof(uint64_t))) 00576 { 00577 uint64_t temp_sotp = 0; 00578 uint16_t actual_len_bytes = 0; 00579 sotp_result_e status = sotp_get(type, size, (uint32_t*)&temp_sotp, 00580 &actual_len_bytes); 00581 if (status == SOTP_SUCCESS && actual_len_bytes == size) 00582 { 00583 memcpy(out, &temp_sotp, size); 00584 result = (arm_uc_error_t){ ERR_NONE }; 00585 } 00586 } 00587 00588 return result; 00589 } 00590 00591 /** 00592 * Writes an integer up to 8 bytes in size to SOTP. 00593 * @param type SOTP item type. 00594 * @param size SOTP item size in bytes (1, 2, 4 or 8). 00595 * @param in Location where the integer value is read. 00596 * @return ERR_NONE if the read succeeded, ERR_INVALID_PARAMETER otherwise. 00597 */ 00598 arm_uc_error_t arm_uc_write_sotp_uint(uint32_t type, uint16_t size, void *in) 00599 { 00600 arm_uc_error_t result = (arm_uc_error_t){ ERR_INVALID_PARAMETER }; 00601 00602 if (in != NULL && 00603 (size == sizeof(uint8_t) || size == sizeof(uint16_t) || 00604 size == sizeof(uint32_t) || size == sizeof(uint64_t))) 00605 { 00606 uint64_t temp_sotp = 0; 00607 /* ensure 32-bit alignment is enforced */ 00608 memcpy(&temp_sotp, in, size); 00609 sotp_result_e status = sotp_set(type, size, (uint32_t*)&temp_sotp); 00610 if (status == SOTP_SUCCESS) 00611 { 00612 result = (arm_uc_error_t){ ERR_NONE }; 00613 } 00614 } 00615 00616 return result; 00617 }
Generated on Tue Jul 12 2022 19:01:33 by 1.7.2