blob: 84980d070fa8073f70c388f1c74298a93eceb7c5 [file] [log] [blame]
Thu Nguyene42fb482024-10-15 14:43:11 +00001#include <libcper/base64.h>
2#include <libcper/BaseTypes.h>
Ed Tanousa7d2cdd2024-07-15 11:07:27 -07003
4#include <stdlib.h>
5#include <string.h>
6#include <stdio.h>
7#include <assert.h>
8
9static const UINT8 encode_table[65] =
10 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
11
12/**
13 *
14 * Caller is responsible for freeing the returned buffer.
15 */
16CHAR8 *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 Tanous8121f7e2025-03-06 14:39:07 -080023 if (len <= 0) {
24 return NULL;
25 }
26
Ed Tanousa7d2cdd2024-07-15 11:07:27 -070027 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.
Khang D Nguyenc21e3a12025-07-24 11:09:24 +070074UINT8 decode_table[256] = {
75 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
76 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
77 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
78 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f,
79 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80,
80 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
81 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
82 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,
83 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
84 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
85 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
86 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
87 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
88 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
89 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
90 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
91 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
92 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
93 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
94 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
95 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
96 0x80, 0x80, 0x80, 0x80,
97};
Ed Tanousa7d2cdd2024-07-15 11:07:27 -070098
99/**
100 *
101 * Caller is responsible for freeing the returned buffer.
102 */
103UINT8 *base64_decode(const CHAR8 *src, INT32 len, INT32 *out_len)
104{
Ed Tanous3e728062025-03-17 20:55:20 -0700105 UINT8 *out = NULL;
106 UINT8 *pos = NULL;
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700107 UINT8 block[4];
Ed Tanous3e728062025-03-17 20:55:20 -0700108 INT32 block_index = 0;
109 INT32 src_index = 0;
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700110
111 if (!out_len) {
Ed Tanous3e728062025-03-17 20:55:20 -0700112 goto error;
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700113 }
114
115 // Malloc might be up to 2 larger dependent on padding
Ed Tanous3e728062025-03-17 20:55:20 -0700116 *out_len = len / 4 * 3 + 2;
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700117 pos = out = malloc(*out_len);
118 if (out == NULL) {
Ed Tanous3e728062025-03-17 20:55:20 -0700119 goto error;
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700120 }
121
122 block_index = 0;
123 for (src_index = 0; src_index < len; src_index++) {
Ed Tanous3e728062025-03-17 20:55:20 -0700124 char current_char = src[src_index];
125 if (current_char == '=') {
126 break;
127 }
128 // If the final character is a newline, as can occur in many editors
129 // then ignore it.
130 if (src_index + 1 == len && current_char == '\n') {
131 printf("Ignoring trailing newline.\n");
132 break;
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700133 }
134
Ed Tanous3e728062025-03-17 20:55:20 -0700135 block[block_index] = decode_table[(UINT8)current_char];
136 if (block[block_index] == 0x80) {
137 printf("Invalid character \"%c\".\n", current_char);
138 goto error;
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700139 }
140
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700141 block_index++;
142 if (block_index == 4) {
143 *pos++ = (block[0] << 2) | (block[1] >> 4);
144 *pos++ = (block[1] << 4) | (block[2] >> 2);
145 *pos++ = (block[2] << 6) | block[3];
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700146 block_index = 0;
147 }
148 }
Ed Tanous3e728062025-03-17 20:55:20 -0700149 if (block_index == 0) {
150 // mod 4 Even number of characters, no padding.
151 } else if (block_index == 1) {
152 printf("Invalid base64 input length. Last character truncated.\n");
153 goto error;
154 } else if (block_index == 2) {
155 *pos++ = (block[0] << 2) | (block[1] >> 4);
156 } else if (block_index == 3) {
157 *pos++ = (block[0] << 2) | (block[1] >> 4);
158 *pos++ = (block[1] << 4) | (block[2] >> 2);
159 } else {
160 /* Invalid pad_counting */
161 printf("Invalid base64 input length %d.\n", block_index);
162 goto error;
163 }
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700164
165 *out_len = pos - out;
166 return out;
Ed Tanous3e728062025-03-17 20:55:20 -0700167
168error:
169 free(out);
170 return NULL;
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700171}