forked from pEp.foundation/pEpEngine
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
6.0 KiB
228 lines
6.0 KiB
// This file is under GNU General Public License 3.0
|
|
// see LICENSE.txt
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include "platform.h"
|
|
#include "base64.h"
|
|
|
|
static char translate_char_to_bits(char input) {
|
|
if (input >= 65 && input <= 90)
|
|
return input - 65;
|
|
if (input >= 97 && input <= 122)
|
|
return input - 71; // 97 - 26
|
|
if (input >= 48 && input <= 57)
|
|
return input + 4; // 52 - 48
|
|
if (input == '+')
|
|
return 62;
|
|
if (input == '/')
|
|
return 63;
|
|
if (input == ' ' || input == '\r' || input == '\n')
|
|
return 127;
|
|
return -1;
|
|
}
|
|
|
|
static bool _is_whitespace(const char in) {
|
|
switch (in) {
|
|
case ' ':
|
|
case '\r':
|
|
case '\t':
|
|
case '\n':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static size_t subtract_whitespace(const char* input, int length) {
|
|
size_t actual_size = length;
|
|
int i;
|
|
const char* curr = input;
|
|
for (i = 0; i < length; i++, curr++) {
|
|
if (_is_whitespace(*curr))
|
|
actual_size--;
|
|
}
|
|
return actual_size;
|
|
}
|
|
|
|
static void trim_end(const char* input, int* length) {
|
|
const char* end = input + *length;
|
|
|
|
int start_length = *length;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < start_length; i++) {
|
|
if (!_is_whitespace(*(--end)))
|
|
break;
|
|
(*length) = (*length) - 1;
|
|
}
|
|
}
|
|
|
|
char next_char(const char** input_ptr, const char* end) {
|
|
const char* input = *input_ptr;
|
|
char this_ch = 0;
|
|
|
|
while (input < end) {
|
|
this_ch = *input++;
|
|
if (!this_ch)
|
|
return 0;
|
|
if (_is_whitespace(this_ch))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
*input_ptr = input;
|
|
return this_ch;
|
|
}
|
|
|
|
// 4 chars = 3 output bytes
|
|
bloblist_t* base64_str_to_binary_blob(const char* input, int length) {
|
|
if (length == 0)
|
|
return NULL;
|
|
|
|
trim_end(input, &length);
|
|
|
|
void* blobby = NULL;
|
|
|
|
const char* input_curr;
|
|
input_curr = input;
|
|
const char* input_end = input_curr + length;
|
|
length = subtract_whitespace(input, length);
|
|
size_t final_length = (length / 4) * 3;
|
|
|
|
// padded -- FIXME: whitespace in between ==!!!!
|
|
if (final_length && *(input_end - 1) == '=') {
|
|
final_length -= 1;
|
|
|
|
// if final length is now decreased by 1 and greater than 0,
|
|
// we know there's a char at (input_end - 2).
|
|
if (final_length && *(input_end - 2) == '=')
|
|
final_length -=1;
|
|
}
|
|
else {
|
|
// not padded
|
|
int leftover = length % 4;
|
|
switch (leftover) {
|
|
case 0:
|
|
break;
|
|
case 2:
|
|
final_length++;
|
|
break;
|
|
case 3:
|
|
final_length+=2;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!final_length)
|
|
goto pEp_error;
|
|
|
|
blobby = calloc(final_length, 1);
|
|
char* blobby_curr = (char*)blobby;
|
|
|
|
// if the last 1 or 2 bytes are padded, we do those after
|
|
size_t number_of_rounds = final_length / 3;
|
|
|
|
unsigned int cycle;
|
|
|
|
// full 3-byte rounds
|
|
for (cycle = 0; cycle < number_of_rounds; cycle++) {
|
|
char byte_array[] = {0,0,0};
|
|
char in_val = next_char(&input_curr, input_end);
|
|
if (in_val == 0)
|
|
goto pEp_error; // can ALSO happen when input_curr == input_end,
|
|
// which simply shouldn't happen, since we're
|
|
// interating based on expected OUTPUT, not
|
|
// input.
|
|
|
|
char out_val = translate_char_to_bits(in_val);
|
|
if (out_val > 63)
|
|
goto pEp_error;
|
|
|
|
byte_array[0] |= out_val << 2;
|
|
|
|
in_val = next_char(&input_curr, input_end);
|
|
if (in_val == 0)
|
|
goto pEp_error;
|
|
out_val = translate_char_to_bits(in_val);
|
|
if (out_val > 63)
|
|
goto pEp_error;
|
|
|
|
byte_array[0] |= out_val >> 4;
|
|
byte_array[1] = out_val << 4;
|
|
|
|
in_val = next_char(&input_curr, input_end);
|
|
if (in_val == 0)
|
|
goto pEp_error;
|
|
out_val = translate_char_to_bits(in_val);
|
|
if (out_val > 63)
|
|
goto pEp_error;
|
|
|
|
byte_array[1] |= out_val >> 2;
|
|
byte_array[2] = out_val << 6;
|
|
|
|
in_val = next_char(&input_curr, input_end);
|
|
if (in_val == 0)
|
|
goto pEp_error;
|
|
out_val = translate_char_to_bits(in_val);
|
|
if (out_val > 63)
|
|
goto pEp_error;
|
|
|
|
byte_array[2] |= out_val;
|
|
|
|
// Now write everything to the blob
|
|
*blobby_curr++ = byte_array[0];
|
|
*blobby_curr++ = byte_array[1];
|
|
*blobby_curr++ = byte_array[2];
|
|
}
|
|
|
|
int last_bytes = final_length % 3;
|
|
|
|
if (last_bytes != 0) {
|
|
char byte_1 = 0;
|
|
char byte_2 = 0;
|
|
|
|
char in_val = next_char(&input_curr, input_end);
|
|
if (in_val == 0)
|
|
goto pEp_error;
|
|
char out_val = translate_char_to_bits(in_val);
|
|
if (out_val > 63)
|
|
goto pEp_error;
|
|
byte_1 = out_val << 2;
|
|
in_val = next_char(&input_curr, input_end);
|
|
if (in_val == 0)
|
|
goto pEp_error;
|
|
out_val = translate_char_to_bits(in_val);
|
|
if (out_val > 63)
|
|
goto pEp_error;
|
|
byte_1 |= out_val >> 4;
|
|
*blobby_curr++ = byte_1;
|
|
|
|
if (last_bytes == 2) {
|
|
byte_2 = out_val << 4;
|
|
in_val = next_char(&input_curr, input_end);
|
|
if (in_val == 0)
|
|
goto pEp_error;
|
|
|
|
out_val = translate_char_to_bits(in_val);
|
|
if (out_val > 63)
|
|
goto pEp_error;
|
|
|
|
byte_2 |= out_val >> 2;
|
|
*blobby_curr++ = byte_2;
|
|
}
|
|
}
|
|
|
|
return new_bloblist((char*)blobby, final_length, NULL, NULL);
|
|
|
|
pEp_error:
|
|
free(blobby);
|
|
return NULL;
|
|
}
|