krista@3032
|
1 |
// This file is under GNU General Public License 3.0
|
krista@3032
|
2 |
// see LICENSE.txt
|
krista@3032
|
3 |
|
krista@3032
|
4 |
#include <stdbool.h>
|
krista@3032
|
5 |
#include <stdlib.h>
|
krista@3032
|
6 |
#include <assert.h>
|
krista@3032
|
7 |
#include <string.h>
|
krista@3032
|
8 |
|
krista@3032
|
9 |
#include "platform.h"
|
krista@3032
|
10 |
#include "base64.h"
|
krista@3032
|
11 |
|
krista@3032
|
12 |
static char translate_char_to_bits(char input) {
|
krista@3032
|
13 |
if (input >= 65 && input <= 90)
|
krista@3032
|
14 |
return input - 65;
|
krista@3032
|
15 |
if (input >= 97 && input <= 122)
|
krista@3032
|
16 |
return input - 71; // 97 - 26
|
krista@3032
|
17 |
if (input >= 48 && input <= 57)
|
krista@3032
|
18 |
return input + 4; // 52 - 48
|
krista@3032
|
19 |
if (input == '+')
|
krista@3032
|
20 |
return 62;
|
krista@3032
|
21 |
if (input == '/')
|
krista@3032
|
22 |
return 63;
|
krista@3032
|
23 |
if (input == ' ' || input == '\r' || input == '\n')
|
krista@3032
|
24 |
return 127;
|
krista@3032
|
25 |
return -1;
|
krista@3032
|
26 |
}
|
krista@3032
|
27 |
|
krista@3032
|
28 |
static bool _is_whitespace(const char in) {
|
krista@3032
|
29 |
switch (in) {
|
krista@3032
|
30 |
case ' ':
|
krista@3032
|
31 |
case '\r':
|
krista@3032
|
32 |
case '\t':
|
krista@3032
|
33 |
case '\n':
|
krista@3032
|
34 |
return true;
|
krista@3032
|
35 |
default:
|
krista@3032
|
36 |
return false;
|
krista@3032
|
37 |
}
|
krista@3032
|
38 |
}
|
krista@3032
|
39 |
|
krista@3032
|
40 |
static size_t subtract_whitespace(const char* input, int length) {
|
krista@3032
|
41 |
size_t actual_size = length;
|
krista@3032
|
42 |
int i;
|
krista@3032
|
43 |
const char* curr = input;
|
krista@3032
|
44 |
for (i = 0; i < length; i++, curr++) {
|
krista@3032
|
45 |
if (_is_whitespace(*curr))
|
krista@3032
|
46 |
actual_size--;
|
krista@3032
|
47 |
}
|
krista@3032
|
48 |
return actual_size;
|
krista@3032
|
49 |
}
|
krista@3032
|
50 |
|
krista@3033
|
51 |
static void trim_end(const char* input, int* length) {
|
krista@3033
|
52 |
const char* end = input + *length;
|
krista@3033
|
53 |
|
krista@3033
|
54 |
int start_length = *length;
|
krista@3033
|
55 |
|
krista@3033
|
56 |
int i;
|
krista@3033
|
57 |
|
krista@3033
|
58 |
for (i = 0; i < start_length; i++) {
|
krista@3033
|
59 |
if (!_is_whitespace(*(--end)))
|
krista@3033
|
60 |
break;
|
krista@3033
|
61 |
(*length) = (*length) - 1;
|
krista@3033
|
62 |
}
|
krista@3033
|
63 |
}
|
krista@3033
|
64 |
|
krista@3032
|
65 |
char next_char(const char** input_ptr, const char* end) {
|
krista@3032
|
66 |
const char* input = *input_ptr;
|
krista@3032
|
67 |
char this_ch = 0;
|
krista@3032
|
68 |
|
krista@3032
|
69 |
while (input < end) {
|
krista@3032
|
70 |
this_ch = *input++;
|
krista@3032
|
71 |
if (!this_ch)
|
krista@3032
|
72 |
return 0;
|
krista@3032
|
73 |
if (_is_whitespace(this_ch))
|
krista@3032
|
74 |
continue;
|
krista@3032
|
75 |
break;
|
krista@3032
|
76 |
}
|
krista@3032
|
77 |
|
krista@3032
|
78 |
*input_ptr = input;
|
krista@3032
|
79 |
return this_ch;
|
krista@3032
|
80 |
}
|
krista@3032
|
81 |
|
krista@3032
|
82 |
// 4 chars = 3 output bytes
|
krista@3032
|
83 |
bloblist_t* base64_str_to_binary_blob(const char* input, int length) {
|
krista@3032
|
84 |
if (length == 0)
|
krista@3032
|
85 |
return NULL;
|
krista@3033
|
86 |
|
krista@3033
|
87 |
trim_end(input, &length);
|
krista@3033
|
88 |
|
krista@3032
|
89 |
const char* input_curr;
|
krista@3032
|
90 |
input_curr = input;
|
krista@3032
|
91 |
const char* input_end = input_curr + length;
|
krista@3032
|
92 |
length = subtract_whitespace(input, length);
|
krista@3032
|
93 |
size_t final_length = (length / 4) * 3;
|
krista@3032
|
94 |
|
krista@3033
|
95 |
// padded -- FIXME: whitespace in between ==!!!!
|
krista@3032
|
96 |
if (*(input_end - 1) == '=') {
|
krista@3032
|
97 |
final_length -= 1;
|
krista@3033
|
98 |
|
krista@3032
|
99 |
if (*(input_end - 2) == '=')
|
krista@3032
|
100 |
final_length -=1;
|
krista@3032
|
101 |
}
|
krista@3032
|
102 |
else {
|
krista@3032
|
103 |
// not padded
|
krista@3032
|
104 |
int leftover = length % 4;
|
krista@3032
|
105 |
switch (leftover) {
|
krista@3032
|
106 |
case 0:
|
krista@3032
|
107 |
break;
|
krista@3032
|
108 |
case 2:
|
krista@3032
|
109 |
final_length++;
|
krista@3032
|
110 |
break;
|
krista@3032
|
111 |
case 3:
|
krista@3032
|
112 |
final_length+=2;
|
krista@3033
|
113 |
break;
|
krista@3032
|
114 |
default:
|
krista@3032
|
115 |
return NULL;
|
krista@3032
|
116 |
}
|
krista@3032
|
117 |
}
|
krista@3032
|
118 |
void* blobby = calloc(final_length, 1);
|
krista@3032
|
119 |
char* blobby_curr = (char*)blobby;
|
krista@3032
|
120 |
|
krista@3032
|
121 |
// if the last 1 or 2 bytes are padded, we do those after
|
krista@3032
|
122 |
size_t number_of_rounds = final_length / 3;
|
krista@3032
|
123 |
|
krista@3032
|
124 |
unsigned int cycle;
|
krista@3032
|
125 |
|
krista@3032
|
126 |
// full 3-byte rounds
|
krista@3032
|
127 |
for (cycle = 0; cycle < number_of_rounds; cycle++) {
|
krista@3032
|
128 |
char byte_array[] = {0,0,0};
|
krista@3032
|
129 |
char in_val = next_char(&input_curr, input_end);
|
krista@3032
|
130 |
if (in_val == 0)
|
krista@3032
|
131 |
goto pEp_error; // can ALSO happen when input_curr == input_end,
|
krista@3032
|
132 |
// which simply shouldn't happen, since we're
|
krista@3032
|
133 |
// interating based on expected OUTPUT, not
|
krista@3032
|
134 |
// input.
|
krista@3032
|
135 |
|
krista@3032
|
136 |
char out_val = translate_char_to_bits(in_val);
|
krista@3032
|
137 |
if (out_val > 63)
|
krista@3032
|
138 |
goto pEp_error;
|
krista@3032
|
139 |
|
krista@3032
|
140 |
byte_array[0] |= out_val << 2;
|
krista@3032
|
141 |
|
krista@3032
|
142 |
in_val = next_char(&input_curr, input_end);
|
krista@3032
|
143 |
if (in_val == 0)
|
krista@3032
|
144 |
goto pEp_error;
|
krista@3032
|
145 |
out_val = translate_char_to_bits(in_val);
|
krista@3032
|
146 |
if (out_val > 63)
|
krista@3032
|
147 |
goto pEp_error;
|
krista@3032
|
148 |
|
krista@3032
|
149 |
byte_array[0] |= out_val >> 4;
|
krista@3032
|
150 |
byte_array[1] = out_val << 4;
|
krista@3032
|
151 |
|
krista@3032
|
152 |
in_val = next_char(&input_curr, input_end);
|
krista@3032
|
153 |
if (in_val == 0)
|
krista@3032
|
154 |
goto pEp_error;
|
krista@3032
|
155 |
out_val = translate_char_to_bits(in_val);
|
krista@3032
|
156 |
if (out_val > 63)
|
krista@3032
|
157 |
goto pEp_error;
|
krista@3032
|
158 |
|
krista@3032
|
159 |
byte_array[1] |= out_val >> 2;
|
krista@3032
|
160 |
byte_array[2] = out_val << 6;
|
krista@3032
|
161 |
|
krista@3032
|
162 |
in_val = next_char(&input_curr, input_end);
|
krista@3032
|
163 |
if (in_val == 0)
|
krista@3032
|
164 |
goto pEp_error;
|
krista@3032
|
165 |
out_val = translate_char_to_bits(in_val);
|
krista@3032
|
166 |
if (out_val > 63)
|
krista@3032
|
167 |
goto pEp_error;
|
krista@3032
|
168 |
|
krista@3032
|
169 |
byte_array[2] |= out_val;
|
krista@3032
|
170 |
|
krista@3032
|
171 |
// Now write everything to the blob
|
krista@3032
|
172 |
*blobby_curr++ = byte_array[0];
|
krista@3032
|
173 |
*blobby_curr++ = byte_array[1];
|
krista@3032
|
174 |
*blobby_curr++ = byte_array[2];
|
krista@3032
|
175 |
}
|
krista@3032
|
176 |
|
krista@3032
|
177 |
int last_bytes = final_length % 3;
|
krista@3032
|
178 |
|
krista@3032
|
179 |
if (last_bytes != 0) {
|
krista@3032
|
180 |
char byte_1 = 0;
|
krista@3032
|
181 |
char byte_2 = 0;
|
krista@3032
|
182 |
|
krista@3032
|
183 |
char in_val = next_char(&input_curr, input_end);
|
krista@3032
|
184 |
if (in_val == 0)
|
krista@3032
|
185 |
goto pEp_error;
|
krista@3032
|
186 |
char out_val = translate_char_to_bits(in_val);
|
krista@3032
|
187 |
if (out_val > 63)
|
krista@3032
|
188 |
goto pEp_error;
|
krista@3032
|
189 |
byte_1 = out_val << 2;
|
krista@3032
|
190 |
in_val = next_char(&input_curr, input_end);
|
krista@3032
|
191 |
if (in_val == 0)
|
krista@3032
|
192 |
goto pEp_error;
|
krista@3032
|
193 |
out_val = translate_char_to_bits(in_val);
|
krista@3032
|
194 |
if (out_val > 63)
|
krista@3032
|
195 |
goto pEp_error;
|
krista@3032
|
196 |
byte_1 |= out_val >> 4;
|
krista@3032
|
197 |
*blobby_curr++ = byte_1;
|
krista@3032
|
198 |
|
krista@3032
|
199 |
if (last_bytes == 2) {
|
krista@3032
|
200 |
byte_2 = out_val << 4;
|
krista@3032
|
201 |
in_val = next_char(&input_curr, input_end);
|
krista@3032
|
202 |
if (in_val == 0)
|
krista@3032
|
203 |
goto pEp_error;
|
krista@3032
|
204 |
|
krista@3032
|
205 |
out_val = translate_char_to_bits(in_val);
|
krista@3032
|
206 |
if (out_val > 63)
|
krista@3032
|
207 |
goto pEp_error;
|
krista@3032
|
208 |
|
krista@3032
|
209 |
byte_2 |= out_val >> 2;
|
krista@3032
|
210 |
*blobby_curr++ = byte_2;
|
krista@3032
|
211 |
}
|
krista@3032
|
212 |
}
|
krista@3032
|
213 |
|
krista@3032
|
214 |
return new_bloblist((char*)blobby, final_length, NULL, NULL);
|
krista@3032
|
215 |
|
krista@3032
|
216 |
pEp_error:
|
krista@3032
|
217 |
free(blobby);
|
krista@3032
|
218 |
return NULL;
|
vb@3045
|
219 |
}
|
vb@3045
|
220 |
|