Simulated product dispenser

Dependencies:   HTS221

Fork of mbed-cloud-workshop-connect-HTS221 by Jim Carver

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