Nigel Rantor / azure_c_shared_utility

Fork of azure_c_shared_utility by Azure IoT

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers base64.c Source File

base64.c

00001 // Copyright (c) Microsoft. All rights reserved.
00002 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
00003 
00004 #include <stdlib.h>
00005 #include "azure_c_shared_utility/gballoc.h"
00006 #include <stddef.h>
00007 #include <stdint.h>
00008 #include "azure_c_shared_utility/base64.h"
00009 #include "azure_c_shared_utility/xlogging.h"
00010 
00011 
00012 #define splitInt(intVal, bytePos)   (char)((intVal >> (bytePos << 3)) & 0xFF)
00013 #define joinChars(a, b, c, d) (uint32_t)((uint32_t)a + ((uint32_t)b << 8) + ((uint32_t)c << 16) + ((uint32_t)d << 24))
00014 
00015 static char base64char(unsigned char val)
00016 {
00017     char result;
00018 
00019     if (val < 26)
00020     {
00021         result = 'A' + (char)val;
00022     }
00023     else if (val < 52)
00024     {
00025         result = 'a' + ((char)val - 26);
00026     }
00027     else if (val < 62)
00028     {
00029         result = '0' + ((char)val - 52);
00030     }
00031     else if (val == 62)
00032     {
00033         result = '+';
00034     }
00035     else
00036     {
00037         result = '/';
00038     }
00039 
00040     return result;
00041 }
00042 
00043 static char base64b16(unsigned char val)
00044 {
00045     const uint32_t base64b16values[4] = {
00046         joinChars('A', 'E', 'I', 'M'),
00047         joinChars('Q', 'U', 'Y', 'c'),
00048         joinChars('g', 'k', 'o', 's'),
00049         joinChars('w', '0', '4', '8')
00050     };
00051     return splitInt(base64b16values[val >> 2], (val & 0x03));
00052 }
00053 
00054 static char base64b8(unsigned char val)
00055 {
00056     const uint32_t base64b8values = joinChars('A', 'Q', 'g', 'w');
00057     return splitInt(base64b8values, val);
00058 }
00059 
00060 static int base64toValue(char base64character, unsigned char* value)
00061 {
00062     int result = 0;
00063     if (('A' <= base64character) && (base64character <= 'Z'))
00064     {
00065         *value = base64character - 'A';
00066     }
00067     else if (('a' <= base64character) && (base64character <= 'z'))
00068     {
00069         *value = ('Z' - 'A') + 1 + (base64character - 'a');
00070     }
00071     else if (('0' <= base64character) && (base64character <= '9'))
00072     {
00073         *value = ('Z' - 'A') + 1 + ('z' - 'a') + 1 + (base64character - '0');
00074     }
00075     else if ('+' == base64character)
00076     {
00077         *value = 62;
00078     }
00079     else if ('/' == base64character)
00080     {
00081         *value = 63;
00082     }
00083     else
00084     {
00085         *value = 0;
00086         result = -1;
00087     }
00088     return result;
00089 }
00090 
00091 static size_t numberOfBase64Characters(const char* encodedString)
00092 {
00093     size_t length = 0;
00094     unsigned char junkChar;
00095     while (base64toValue(encodedString[length],&junkChar) != -1)
00096     {
00097         length++;
00098     }
00099     return length;
00100 }
00101 
00102 /*returns the count of original bytes before being base64 encoded*/
00103 /*notice NO validation of the content of encodedString. Its length is validated to be a multiple of 4.*/
00104 static size_t Base64decode_len(const char *encodedString)
00105 {
00106     size_t result;
00107     size_t sourceLength = strlen(encodedString);
00108     
00109     if (sourceLength == 0)
00110     {
00111         result = 0;
00112     }
00113     else
00114     {
00115         result = sourceLength / 4 * 3;
00116         if (encodedString[sourceLength - 1] == '=')
00117         {
00118             if (encodedString[sourceLength - 2] == '=')
00119             {
00120                 result --;
00121             }
00122             result--;
00123         }
00124     }
00125     return result;
00126 }
00127 
00128 static void Base64decode(unsigned char *decodedString, const char *base64String)
00129 {
00130 
00131     size_t numberOfEncodedChars;
00132     size_t indexOfFirstEncodedChar;
00133     size_t decodedIndex;
00134 
00135     //
00136     // We can only operate on individual bytes.  If we attempt to work
00137     // on anything larger we could get an alignment fault on some
00138     // architectures
00139     //
00140 
00141     numberOfEncodedChars = numberOfBase64Characters(base64String);
00142     indexOfFirstEncodedChar = 0;
00143     decodedIndex = 0;
00144     while (numberOfEncodedChars >= 4)
00145     {
00146         unsigned char c1;
00147         unsigned char c2;
00148         unsigned char c3;
00149         unsigned char c4;
00150         (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
00151         (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
00152         (void)base64toValue(base64String[indexOfFirstEncodedChar + 2], &c3);
00153         (void)base64toValue(base64String[indexOfFirstEncodedChar + 3], &c4);
00154         decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
00155         decodedIndex++;
00156         decodedString[decodedIndex] = ((c2 & 0x0f) << 4) | (c3 >> 2);
00157         decodedIndex++;
00158         decodedString[decodedIndex] = ((c3 & 0x03) << 6) | c4;
00159         decodedIndex++;
00160         numberOfEncodedChars -= 4;
00161         indexOfFirstEncodedChar += 4;
00162 
00163     }
00164 
00165     if (numberOfEncodedChars == 2)
00166     {
00167         unsigned char c1;
00168         unsigned char c2;
00169         (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
00170         (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
00171         decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
00172     }
00173     else if (numberOfEncodedChars == 3)
00174     {
00175         unsigned char c1;
00176         unsigned char c2;
00177         unsigned char c3;
00178         (void)base64toValue(base64String[indexOfFirstEncodedChar], &c1);
00179         (void)base64toValue(base64String[indexOfFirstEncodedChar + 1], &c2);
00180         (void)base64toValue(base64String[indexOfFirstEncodedChar + 2], &c3);
00181         decodedString[decodedIndex] = (c1 << 2) | (c2 >> 4);
00182         decodedIndex++;
00183         decodedString[decodedIndex] = ((c2 & 0x0f) << 4) | (c3 >> 2);
00184     }
00185 }
00186 
00187 BUFFER_HANDLE Base64_Decoder(const char* source)
00188 {
00189     BUFFER_HANDLE result;
00190     /*Codes_SRS_BASE64_06_008: [If source is NULL then Base64_Decode shall return NULL.]*/
00191     if (source == NULL)
00192     {
00193         LogError("invalid parameter const char* source=%p", source);
00194         result = NULL;
00195     }
00196     else
00197     {
00198         if ((strlen(source) % 4) != 0)
00199         {
00200             /*Codes_SRS_BASE64_06_011: [If the source string has an invalid length for a base 64 encoded string then Base64_Decode shall return NULL.]*/
00201             LogError("Invalid length Base64 string!");
00202             result = NULL;
00203         }
00204         else
00205         {
00206             if ((result = BUFFER_new()) == NULL)
00207             {
00208                 /*Codes_SRS_BASE64_06_010: [If there is any memory allocation failure during the decode then Base64_Decode shall return NULL.]*/
00209                 LogError("Could not create a buffer to decoding.");
00210             }
00211             else
00212             {
00213                 size_t sizeOfOutputBuffer = Base64decode_len(source);
00214                 /*Codes_SRS_BASE64_06_009: [If the string pointed to by source is zero length then the handle returned shall refer to a zero length buffer.]*/
00215                 if (sizeOfOutputBuffer > 0)
00216                 {
00217                     if (BUFFER_pre_build(result, sizeOfOutputBuffer) != 0)
00218                     {
00219                         /*Codes_SRS_BASE64_06_010: [If there is any memory allocation failure during the decode then Base64_Decode shall return NULL.]*/
00220                         LogError("Could not prebuild a buffer for base 64 decoding.");
00221                         BUFFER_delete(result);
00222                         result = NULL;
00223                     }
00224                     else
00225                     {
00226                         Base64decode(BUFFER_u_char(result), source);
00227                     }
00228                 }
00229             }
00230         }
00231     }
00232     return result;
00233 }
00234 
00235 
00236 static STRING_HANDLE Base64_Encode_Internal(const unsigned char* source, size_t size)
00237 {
00238     STRING_HANDLE result;
00239     size_t neededSize = 0;
00240     char* encoded;
00241     size_t currentPosition = 0;
00242     neededSize += (size == 0) ? (0) : ((((size - 1) / 3) + 1) * 4);
00243     neededSize += 1; /*+1 because \0 at the end of the string*/
00244     /*Codes_SRS_BASE64_06_006: [If when allocating memory to produce the encoding a failure occurs then Base64_Encoder shall return NULL.]*/
00245     encoded = (char*)malloc(neededSize);
00246     if (encoded == NULL)
00247     {
00248         result = NULL;
00249         LogError("Base64_Encoder:: Allocation failed.");
00250     }
00251     else
00252     {
00253         /*b0            b1(+1)          b2(+2)
00254         7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
00255         |----c1---| |----c2---| |----c3---| |----c4---|
00256         */
00257 
00258         size_t destinationPosition = 0;
00259         while (size - currentPosition >= 3)
00260         {
00261             char c1 = base64char(source[currentPosition] >> 2);
00262             char c2 = base64char(
00263                 ((source[currentPosition] & 3) << 4) |
00264                     (source[currentPosition + 1] >> 4)
00265             );
00266             char c3 = base64char(
00267                 ((source[currentPosition + 1] & 0x0F) << 2) |
00268                     ((source[currentPosition + 2] >> 6) & 3)
00269             );
00270             char c4 = base64char(
00271                 source[currentPosition + 2] & 0x3F
00272             );
00273             currentPosition += 3;
00274             encoded[destinationPosition++] = c1;
00275             encoded[destinationPosition++] = c2;
00276             encoded[destinationPosition++] = c3;
00277             encoded[destinationPosition++] = c4;
00278 
00279         }
00280         if (size - currentPosition == 2)
00281         {
00282             char c1 = base64char(source[currentPosition] >> 2);
00283             char c2 = base64char(
00284                 ((source[currentPosition] & 0x03) << 4) |
00285                     (source[currentPosition + 1] >> 4)
00286             );
00287             char c3 = base64b16(source[currentPosition + 1] & 0x0F);
00288             encoded[destinationPosition++] = c1;
00289             encoded[destinationPosition++] = c2;
00290             encoded[destinationPosition++] = c3;
00291             encoded[destinationPosition++] = '=';
00292         }
00293         else if (size - currentPosition == 1)
00294         {
00295             char c1 = base64char(source[currentPosition] >> 2);
00296             char c2 = base64b8(source[currentPosition] & 0x03);
00297             encoded[destinationPosition++] = c1;
00298             encoded[destinationPosition++] = c2;
00299             encoded[destinationPosition++] = '=';
00300             encoded[destinationPosition++] = '=';
00301         }
00302 
00303         /*null terminating the string*/
00304         encoded[destinationPosition] = '\0';
00305         /*Codes_SRS_BASE64_06_007: [Otherwise Base64_Encoder shall return a pointer to STRING, that string contains the base 64 encoding of input.]*/
00306         result = STRING_new_with_memory(encoded);
00307         if (result == NULL)
00308         {
00309             free(encoded);
00310             LogError("Base64_Encoder:: Allocation failed for return value.");
00311         }
00312     }
00313     return result;
00314 }
00315 
00316 STRING_HANDLE Base64_Encode_Bytes(const unsigned char* source, size_t size)
00317 {
00318     STRING_HANDLE result;
00319     /*Codes_SRS_BASE64_02_001: [If source is NULL then Base64_Encode_Bytes shall return NULL.] */
00320     if (source == NULL)
00321     {
00322         result = NULL;
00323     }
00324     /*Codes_SRS_BASE64_02_002: [If source is not NULL and size is zero, then Base64_Encode_Bytes shall produce an empty STRING_HANDLE.] */
00325     else if (size == 0)
00326     {
00327         result = STRING_new(); /*empty string*/
00328     }
00329     else
00330     {
00331         result = Base64_Encode_Internal(source, size);
00332     }
00333     return result;
00334 }
00335 
00336 STRING_HANDLE Base64_Encoder(BUFFER_HANDLE input)
00337 {
00338     STRING_HANDLE result;
00339     /*the following will happen*/
00340     /*1. the "data" of the binary shall be "eaten" 3 characters at a time and produce 4 base64 encoded characters for as long as there are more than 3 characters still to process*/
00341     /*2. the remaining characters (1 or 2) shall be encoded.*/
00342     /*there's a level of assumption that 'a' corresponds to 0b000000 and that '_' corresponds to 0b111111*/
00343     /*the encoding will use the optional [=] or [==] at the end of the encoded string, so that other less standard aware libraries can do their work*/
00344     /*these are the bits of the 3 normal bytes to be encoded*/
00345 
00346     /*Codes_SRS_BASE64_06_001: [If input is NULL then Base64_Encoder shall return NULL.]*/
00347     if (input == NULL)
00348     {
00349         result = NULL;
00350         LogError("Base64_Encoder:: NULL input");
00351     }
00352     else
00353     {
00354         size_t inputSize;
00355         const unsigned char* inputBinary;
00356         if ((BUFFER_content(input, &inputBinary) != 0) ||
00357             (BUFFER_size(input, &inputSize) != 0))
00358         {
00359             result = NULL;
00360             LogError("Base64_Encoder:: BUFFER_routines failure.");
00361         }
00362         else
00363         {
00364             result = Base64_Encode_Internal(inputBinary, inputSize);
00365         }
00366     }
00367     return result;
00368 }