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.
pEpEngine/src/base64.c

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;
}