src/base64.c
author Volker Birk <vb@pep-project.org>
Fri, 17 May 2019 17:59:06 +0200
branchsync
changeset 3720 9ed76a79d784
parent 3045 cb5f4c863d4f
child 4762 3fcdca1ce989
permissions -rw-r--r--
...
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #include <stdbool.h>
     5 #include <stdlib.h>
     6 #include <assert.h>
     7 #include <string.h>
     8 
     9 #include "platform.h"
    10 #include "base64.h"
    11 
    12 static char translate_char_to_bits(char input) {
    13     if (input >= 65 && input <= 90)
    14         return input - 65;
    15     if (input >= 97 && input <= 122)
    16         return input - 71; // 97 - 26
    17     if (input >= 48 && input <= 57)
    18         return input + 4; // 52 - 48
    19     if (input == '+')
    20         return 62;
    21     if (input == '/')
    22         return 63;
    23     if (input == ' ' || input == '\r' || input == '\n')
    24         return 127;
    25     return -1;    
    26 }
    27 
    28 static bool _is_whitespace(const char in) {
    29     switch (in) {
    30         case ' ':
    31         case '\r':
    32         case '\t':
    33         case '\n':
    34             return true;
    35         default:
    36             return false;
    37     }        
    38 }
    39 
    40 static size_t subtract_whitespace(const char* input, int length) {
    41     size_t actual_size = length;
    42     int i;
    43     const char* curr = input;
    44     for (i = 0; i < length; i++, curr++) {
    45         if (_is_whitespace(*curr))
    46             actual_size--;
    47     }
    48     return actual_size;
    49 }
    50     
    51 static void trim_end(const char* input, int* length) {
    52     const char* end = input + *length;
    53     
    54     int start_length = *length;
    55     
    56     int i;
    57     
    58     for (i = 0; i < start_length; i++) {
    59         if (!_is_whitespace(*(--end)))
    60             break;
    61         (*length) = (*length) - 1;        
    62     }
    63 }    
    64     
    65 char next_char(const char** input_ptr, const char* end) {
    66     const char* input = *input_ptr;
    67     char this_ch = 0;
    68     
    69     while (input < end) {
    70         this_ch = *input++;
    71         if (!this_ch)
    72             return 0;
    73         if (_is_whitespace(this_ch))
    74             continue;
    75         break;    
    76     }
    77     
    78     *input_ptr = input;
    79     return this_ch;
    80 }
    81 
    82 // 4 chars = 3 output bytes
    83 bloblist_t* base64_str_to_binary_blob(const char* input, int length) {
    84     if (length == 0)
    85         return NULL;
    86     
    87     trim_end(input, &length);
    88         
    89     const char* input_curr;
    90     input_curr = input;
    91     const char* input_end = input_curr + length;
    92     length = subtract_whitespace(input, length);
    93     size_t final_length = (length / 4) * 3;
    94 
    95     // padded -- FIXME: whitespace in between ==!!!! 
    96     if (*(input_end - 1) == '=') {
    97         final_length -= 1;
    98         
    99         if (*(input_end - 2) == '=')
   100             final_length -=1;
   101     }
   102     else {
   103         // not padded
   104         int leftover = length % 4;
   105         switch (leftover) {
   106             case 0:
   107                 break;
   108             case 2:
   109                 final_length++;
   110                 break;
   111             case 3:
   112                 final_length+=2;
   113                 break;
   114             default:
   115                 return NULL;
   116         }
   117     }
   118     void* blobby = calloc(final_length, 1);
   119     char* blobby_curr = (char*)blobby;
   120 
   121     // if the last 1 or 2 bytes are padded, we do those after
   122     size_t number_of_rounds = final_length / 3;
   123     
   124     unsigned int cycle;
   125     
   126     // full 3-byte rounds
   127     for (cycle = 0; cycle < number_of_rounds; cycle++) {
   128         char byte_array[] = {0,0,0};
   129         char in_val = next_char(&input_curr, input_end);
   130         if (in_val == 0)
   131             goto pEp_error; // can ALSO happen when input_curr == input_end,
   132                             // which simply shouldn't happen, since we're
   133                             // interating based on expected OUTPUT, not
   134                             // input.
   135                             
   136         char out_val = translate_char_to_bits(in_val);
   137         if (out_val > 63)
   138             goto pEp_error;
   139         
   140         byte_array[0] |= out_val << 2;
   141 
   142         in_val = next_char(&input_curr, input_end);
   143         if (in_val == 0)
   144             goto pEp_error;         
   145         out_val = translate_char_to_bits(in_val);
   146         if (out_val > 63)
   147             goto pEp_error;
   148 
   149         byte_array[0] |= out_val >> 4;
   150         byte_array[1] = out_val << 4;
   151         
   152         in_val = next_char(&input_curr, input_end);
   153         if (in_val == 0)
   154             goto pEp_error;         
   155         out_val = translate_char_to_bits(in_val);
   156         if (out_val > 63)
   157             goto pEp_error;
   158     
   159         byte_array[1] |= out_val >> 2;
   160         byte_array[2] = out_val << 6;
   161 
   162         in_val = next_char(&input_curr, input_end);
   163         if (in_val == 0)
   164             goto pEp_error;         
   165         out_val = translate_char_to_bits(in_val);
   166         if (out_val > 63)
   167             goto pEp_error;
   168         
   169         byte_array[2] |= out_val;
   170         
   171         // Now write everything to the blob
   172         *blobby_curr++ = byte_array[0];
   173         *blobby_curr++ = byte_array[1];
   174         *blobby_curr++ = byte_array[2];        
   175     }
   176 
   177     int last_bytes = final_length % 3;
   178 
   179     if (last_bytes != 0) {
   180         char byte_1 = 0;
   181         char byte_2 = 0;
   182 
   183         char in_val = next_char(&input_curr, input_end);
   184         if (in_val == 0)
   185             goto pEp_error;         
   186         char out_val = translate_char_to_bits(in_val);
   187         if (out_val > 63)
   188             goto pEp_error;
   189         byte_1 = out_val << 2;
   190         in_val = next_char(&input_curr, input_end);
   191         if (in_val == 0)
   192             goto pEp_error;         
   193         out_val = translate_char_to_bits(in_val);
   194         if (out_val > 63)
   195             goto pEp_error;
   196         byte_1 |= out_val >> 4;
   197         *blobby_curr++ = byte_1;                   
   198             
   199         if (last_bytes == 2) {
   200             byte_2 = out_val << 4;
   201             in_val = next_char(&input_curr, input_end);
   202             if (in_val == 0)
   203                 goto pEp_error;         
   204             
   205             out_val = translate_char_to_bits(in_val);
   206             if (out_val > 63)
   207                 goto pEp_error;
   208         
   209             byte_2 |= out_val >> 2;
   210             *blobby_curr++ = byte_2;
   211         }
   212     }
   213     
   214     return new_bloblist((char*)blobby, final_length, NULL, NULL);
   215             
   216 pEp_error:
   217     free(blobby);
   218     return NULL;
   219 }    
   220