| Thu Nguyen | e42fb48 | 2024-10-15 14:43:11 +0000 | [diff] [blame] | 1 | #include <libcper/base64.h> | 
|  | 2 | #include <libcper/BaseTypes.h> | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 3 |  | 
|  | 4 | #include <stdlib.h> | 
|  | 5 | #include <string.h> | 
|  | 6 | #include <stdio.h> | 
|  | 7 | #include <assert.h> | 
|  | 8 |  | 
|  | 9 | static const UINT8 encode_table[65] = | 
|  | 10 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | 
|  | 11 |  | 
|  | 12 | /** | 
|  | 13 | * | 
|  | 14 | * Caller is responsible for freeing the returned buffer. | 
|  | 15 | */ | 
|  | 16 | CHAR8 *base64_encode(const UINT8 *src, INT32 len, INT32 *out_len) | 
|  | 17 | { | 
|  | 18 | CHAR8 *out; | 
|  | 19 | CHAR8 *out_pos; | 
|  | 20 | const UINT8 *src_end; | 
|  | 21 | const UINT8 *in_pos; | 
|  | 22 |  | 
| Ed Tanous | 8121f7e | 2025-03-06 14:39:07 -0800 | [diff] [blame] | 23 | if (len <= 0) { | 
|  | 24 | return NULL; | 
|  | 25 | } | 
|  | 26 |  | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 27 | if (!out_len) { | 
|  | 28 | return NULL; | 
|  | 29 | } | 
|  | 30 |  | 
|  | 31 | // 3 byte blocks to 4 byte blocks plus up to 2 bytes of padding | 
|  | 32 | *out_len = 4 * ((len + 2) / 3); | 
|  | 33 |  | 
|  | 34 | // Handle overflows | 
|  | 35 | if (*out_len < len) { | 
|  | 36 | return NULL; | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | out = malloc(*out_len); | 
|  | 40 | if (out == NULL) { | 
|  | 41 | return NULL; | 
|  | 42 | } | 
|  | 43 |  | 
|  | 44 | src_end = src + len; | 
|  | 45 | in_pos = src; | 
|  | 46 | out_pos = out; | 
|  | 47 | while (src_end - in_pos >= 3) { | 
|  | 48 | *out_pos++ = encode_table[in_pos[0] >> 2]; | 
|  | 49 | *out_pos++ = encode_table[((in_pos[0] & 0x03) << 4) | | 
|  | 50 | (in_pos[1] >> 4)]; | 
|  | 51 | *out_pos++ = encode_table[((in_pos[1] & 0x0f) << 2) | | 
|  | 52 | (in_pos[2] >> 6)]; | 
|  | 53 | *out_pos++ = encode_table[in_pos[2] & 0x3f]; | 
|  | 54 | in_pos += 3; | 
|  | 55 | } | 
|  | 56 |  | 
|  | 57 | if (src_end - in_pos) { | 
|  | 58 | *out_pos++ = encode_table[in_pos[0] >> 2]; | 
|  | 59 | if (src_end - in_pos == 1) { | 
|  | 60 | *out_pos++ = encode_table[(in_pos[0] & 0x03) << 4]; | 
|  | 61 | *out_pos++ = '='; | 
|  | 62 | } else { | 
|  | 63 | *out_pos++ = encode_table[((in_pos[0] & 0x03) << 4) | | 
|  | 64 | (in_pos[1] >> 4)]; | 
|  | 65 | *out_pos++ = encode_table[(in_pos[1] & 0x0f) << 2]; | 
|  | 66 | } | 
|  | 67 | *out_pos++ = '='; | 
|  | 68 | } | 
|  | 69 |  | 
|  | 70 | return out; | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | // Base64 decode table.  Invalid values are specified with 0x80. | 
|  | 74 | UINT8 decode_table[256] = | 
|  | 75 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 76 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 77 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x3e\x80\x80\x80\x3f" | 
|  | 78 | "\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x80\x80\x80\x00\x80\x80" | 
|  | 79 | "\x80\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" | 
|  | 80 | "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x80\x80\x80\x80\x80" | 
|  | 81 | "\x80\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28" | 
|  | 82 | "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x80\x80\x80\x80\x80" | 
|  | 83 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 84 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 85 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 86 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 87 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 88 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 89 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" | 
|  | 90 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"; | 
|  | 91 |  | 
|  | 92 | /** | 
|  | 93 | * | 
|  | 94 | * Caller is responsible for freeing the returned buffer. | 
|  | 95 | */ | 
|  | 96 | UINT8 *base64_decode(const CHAR8 *src, INT32 len, INT32 *out_len) | 
|  | 97 | { | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 98 | UINT8 *out = NULL; | 
|  | 99 | UINT8 *pos = NULL; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 100 | UINT8 block[4]; | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 101 | INT32 block_index = 0; | 
|  | 102 | INT32 src_index = 0; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 103 |  | 
|  | 104 | if (!out_len) { | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 105 | goto error; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 106 | } | 
|  | 107 |  | 
|  | 108 | // Malloc might be up to 2 larger dependent on padding | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 109 | *out_len = len / 4 * 3 + 2; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 110 | pos = out = malloc(*out_len); | 
|  | 111 | if (out == NULL) { | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 112 | goto error; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 113 | } | 
|  | 114 |  | 
|  | 115 | block_index = 0; | 
|  | 116 | for (src_index = 0; src_index < len; src_index++) { | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 117 | char current_char = src[src_index]; | 
|  | 118 | if (current_char == '=') { | 
|  | 119 | break; | 
|  | 120 | } | 
|  | 121 | // If the final character is a newline, as can occur in many editors | 
|  | 122 | // then ignore it. | 
|  | 123 | if (src_index + 1 == len && current_char == '\n') { | 
|  | 124 | printf("Ignoring trailing newline.\n"); | 
|  | 125 | break; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 126 | } | 
|  | 127 |  | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 128 | block[block_index] = decode_table[(UINT8)current_char]; | 
|  | 129 | if (block[block_index] == 0x80) { | 
|  | 130 | printf("Invalid character \"%c\".\n", current_char); | 
|  | 131 | goto error; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 132 | } | 
|  | 133 |  | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 134 | block_index++; | 
|  | 135 | if (block_index == 4) { | 
|  | 136 | *pos++ = (block[0] << 2) | (block[1] >> 4); | 
|  | 137 | *pos++ = (block[1] << 4) | (block[2] >> 2); | 
|  | 138 | *pos++ = (block[2] << 6) | block[3]; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 139 | block_index = 0; | 
|  | 140 | } | 
|  | 141 | } | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 142 | if (block_index == 0) { | 
|  | 143 | // mod 4 Even number of characters, no padding. | 
|  | 144 | } else if (block_index == 1) { | 
|  | 145 | printf("Invalid base64 input length.  Last character truncated.\n"); | 
|  | 146 | goto error; | 
|  | 147 | } else if (block_index == 2) { | 
|  | 148 | *pos++ = (block[0] << 2) | (block[1] >> 4); | 
|  | 149 | } else if (block_index == 3) { | 
|  | 150 | *pos++ = (block[0] << 2) | (block[1] >> 4); | 
|  | 151 | *pos++ = (block[1] << 4) | (block[2] >> 2); | 
|  | 152 | } else { | 
|  | 153 | /* Invalid pad_counting */ | 
|  | 154 | printf("Invalid base64 input length %d.\n", block_index); | 
|  | 155 | goto error; | 
|  | 156 | } | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 157 |  | 
|  | 158 | *out_len = pos - out; | 
|  | 159 | return out; | 
| Ed Tanous | 3e72806 | 2025-03-17 20:55:20 -0700 | [diff] [blame] | 160 |  | 
|  | 161 | error: | 
|  | 162 | free(out); | 
|  | 163 | return NULL; | 
| Ed Tanous | a7d2cdd | 2024-07-15 11:07:27 -0700 | [diff] [blame] | 164 | } |