Simple interface for Mbed Cloud Client

Dependents:  

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_utilities.c Source File

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 }