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