|
| 1 | +#include "base64.h" |
| 2 | +#include <math.h> |
| 3 | +#include <stddef.h> |
| 4 | +#include <stdint.h> |
| 5 | +#include <stdio.h> |
| 6 | +#include <stdlib.h> |
| 7 | +#include <string.h> |
| 8 | + |
| 9 | +static const uint8_t base64_encode_map[] = { |
| 10 | + [0] = 'A', [1] = 'B', [2] = 'C', [3] = 'D', [4] = 'E', [5] = 'F', |
| 11 | + [6] = 'G', [7] = 'H', [8] = 'I', [9] = 'J', [10] = 'K', [11] = 'L', |
| 12 | + [12] = 'M', [13] = 'N', [14] = 'O', [15] = 'P', [16] = 'Q', [17] = 'R', |
| 13 | + [18] = 'S', [19] = 'T', [20] = 'U', [21] = 'V', [22] = 'W', [23] = 'X', |
| 14 | + [24] = 'Y', [25] = 'Z', [26] = 'a', [27] = 'b', [28] = 'c', [29] = 'd', |
| 15 | + [30] = 'e', [31] = 'f', [32] = 'g', [33] = 'h', [34] = 'i', [35] = 'j', |
| 16 | + [36] = 'k', [37] = 'l', [38] = 'm', [39] = 'n', [40] = 'o', [41] = 'p', |
| 17 | + [42] = 'q', [43] = 'r', [44] = 's', [45] = 't', [46] = 'u', [47] = 'v', |
| 18 | + [48] = 'w', [49] = 'x', [50] = 'y', [51] = 'z', [52] = '0', [53] = '1', |
| 19 | + [54] = '2', [55] = '3', [56] = '4', [57] = '5', [58] = '6', [59] = '7', |
| 20 | + [60] = '8', [61] = '9', [62] = '+', [63] = '/', |
| 21 | + // `=` -> padding |
| 22 | +}; |
| 23 | + |
| 24 | +static const uint8_t base64_decode_map[] = { |
| 25 | + ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, |
| 26 | + ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, |
| 27 | + ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17, |
| 28 | + ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, |
| 29 | + ['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, |
| 30 | + ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35, |
| 31 | + ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41, |
| 32 | + ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, |
| 33 | + ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53, |
| 34 | + ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, |
| 35 | + ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63, |
| 36 | + // `=` -> padding |
| 37 | +}; |
| 38 | + |
| 39 | +// todo: fix encoding for strings with length < 3 |
| 40 | +const char *const base64_encode(const uint8_t *const data, size_t data_size) { |
| 41 | + const size_t remaining = data_size % 3; |
| 42 | + const size_t base64_len = ceil(((double)data_size * 8) / 6) + 1; |
| 43 | + char *result = malloc(base64_len); |
| 44 | + if (!result) { |
| 45 | + return NULL; |
| 46 | + } |
| 47 | + memset(result, 0, base64_len); |
| 48 | + |
| 49 | + size_t base64_idx = 0; |
| 50 | + for (size_t i = 0; i < data_size - remaining; i += 3) { |
| 51 | + uint8_t byte1 = data[i], byte2 = data[i + 1], byte3 = data[i + 2]; |
| 52 | + |
| 53 | + uint8_t octet1 = (byte1 & 0b11111100) >> 2, |
| 54 | + octet2 = ((byte1 & 0b11) << 4) | ((byte2 & 0b11110000) >> 4), |
| 55 | + octet3 = ((byte2 & 0b1111) << 2) | ((byte3 & 0b11000000) >> 6), |
| 56 | + octet4 = (byte3 & 0b111111); |
| 57 | + |
| 58 | + result[base64_idx++] = base64_encode_map[octet1]; |
| 59 | + result[base64_idx++] = base64_encode_map[octet2]; |
| 60 | + result[base64_idx++] = base64_encode_map[octet3]; |
| 61 | + result[base64_idx++] = base64_encode_map[octet4]; |
| 62 | + } |
| 63 | + |
| 64 | + if (remaining == 1) { |
| 65 | + uint8_t byte1 = data[data_size - 1]; |
| 66 | + uint8_t octet1 = (byte1 & 0b11111100) >> 2; |
| 67 | + uint8_t octet2 = (byte1 & 0b11) << 4; |
| 68 | + |
| 69 | + result[base64_idx++] = base64_encode_map[octet1]; |
| 70 | + result[base64_idx++] = base64_encode_map[octet2]; |
| 71 | + result[base64_idx++] = '='; |
| 72 | + result[base64_idx++] = '='; |
| 73 | + } else if (remaining == 2) { |
| 74 | + uint8_t byte1 = data[data_size - 2]; |
| 75 | + uint8_t byte2 = data[data_size - 1]; |
| 76 | + |
| 77 | + uint8_t octet1 = (byte1 & 0b11111100) >> 2; |
| 78 | + uint8_t octet2 = ((byte1 & 0b11) << 4) | ((byte2 & 0b11110000) >> 4); |
| 79 | + uint8_t octet3 = ((byte2 & 0b1111) << 2); |
| 80 | + |
| 81 | + result[base64_idx++] = base64_encode_map[octet1]; |
| 82 | + result[base64_idx++] = base64_encode_map[octet2]; |
| 83 | + result[base64_idx++] = base64_encode_map[octet3]; |
| 84 | + result[base64_idx++] = '='; |
| 85 | + } |
| 86 | + |
| 87 | + return result; |
| 88 | +} |
| 89 | + |
| 90 | +const char *base64_decode(const char *base64_str, size_t base64_len) { |
| 91 | + size_t padding_len = 0; |
| 92 | + for (size_t i = base64_len - 1; base64_str[i] == '=' && i >= 0; i--) { |
| 93 | + ++padding_len; |
| 94 | + } |
| 95 | + const size_t result_len = ((base64_len - padding_len) * 6) / 8 + 1; |
| 96 | + char *result = malloc(result_len); |
| 97 | + if (!result) { |
| 98 | + return NULL; |
| 99 | + } |
| 100 | + memset(result, 0, result_len); |
| 101 | + |
| 102 | + size_t result_idx = 0; |
| 103 | + for (size_t i = 0; i < base64_len; i += 4) { |
| 104 | + uint8_t octet1 = base64_decode_map[base64_str[i]]; |
| 105 | + uint8_t octet2 = base64_decode_map[base64_str[i + 1]]; |
| 106 | + uint8_t octet3 = base64_decode_map[base64_str[i + 2]]; |
| 107 | + uint8_t octet4 = base64_decode_map[base64_str[i + 3]]; |
| 108 | + |
| 109 | + uint8_t byte1 = (octet1 << 2) | ((octet2 & 0b110000) >> 4), |
| 110 | + byte2 = (octet2 << 4) | ((octet3 & 0b111100) >> 2), |
| 111 | + byte3 = (octet3 << 6) | octet4; |
| 112 | + |
| 113 | + result[result_idx++] = byte1; |
| 114 | + result[result_idx++] = byte2; |
| 115 | + result[result_idx++] = byte3; |
| 116 | + } |
| 117 | + |
| 118 | + return result; |
| 119 | +} |
0 commit comments