1 | /* Copyright (c) 2003, 2010, Oracle and/or its affiliates. |
2 | Copyright (c) 2013, MariaDB Foundation. |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
16 | |
17 | #include <my_global.h> |
18 | #include <m_string.h> /* strchr() */ |
19 | #include <m_ctype.h> /* my_isspace() */ |
20 | |
21 | #ifndef MAIN |
22 | |
23 | static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
24 | "abcdefghijklmnopqrstuvwxyz" |
25 | "0123456789+/" ; |
26 | |
27 | /** |
28 | * Maximum length my_base64_needed_encoded_length() |
29 | * can handle without signed integer overflow. |
30 | */ |
31 | int |
32 | my_base64_encode_max_arg_length() |
33 | { |
34 | /* |
35 | my_base64_needed_encoded_length(1589695686) -> 2147483646 (7FFFFFFE) |
36 | my_base64_needed_encoded_length(1589695687) -> -2147483645 |
37 | */ |
38 | return 0x5EC0D4C6; /* 1589695686 */ |
39 | } |
40 | |
41 | |
42 | int |
43 | my_base64_needed_encoded_length(int length_of_data) |
44 | { |
45 | int nb_base64_chars; |
46 | nb_base64_chars= (length_of_data + 2) / 3 * 4; |
47 | |
48 | return |
49 | nb_base64_chars + /* base64 char incl padding */ |
50 | (nb_base64_chars - 1)/ 76 + /* newlines */ |
51 | 1; /* NUL termination of string */ |
52 | } |
53 | |
54 | |
55 | /** |
56 | * Maximum length supported by my_base64_decode(). |
57 | */ |
58 | int |
59 | my_base64_decode_max_arg_length() |
60 | { |
61 | return 0x7FFFFFFF; |
62 | } |
63 | |
64 | |
65 | int |
66 | my_base64_needed_decoded_length(int length_of_encoded_data) |
67 | { |
68 | return (int) ((longlong) length_of_encoded_data + 3) / 4 * 3; |
69 | } |
70 | |
71 | |
72 | /* |
73 | Encode a data as base64. |
74 | |
75 | Note: We require that dst is pre-allocated to correct size. |
76 | See my_base64_needed_encoded_length(). |
77 | |
78 | Note: We add line separators every 76 characters. |
79 | |
80 | Note: The output string is properly padded with the '=' character, |
81 | so the length of the output string is always divisable by 4. |
82 | */ |
83 | |
84 | int |
85 | my_base64_encode(const void *src, size_t src_len, char *dst) |
86 | { |
87 | const unsigned char *s= (const unsigned char*)src; |
88 | size_t i= 0; |
89 | size_t len= 0; |
90 | |
91 | for (; i < src_len; len += 4) |
92 | { |
93 | unsigned c; |
94 | |
95 | if (len == 76) |
96 | { |
97 | len= 0; |
98 | *dst++= '\n'; |
99 | } |
100 | |
101 | c= s[i++]; |
102 | c <<= 8; |
103 | |
104 | if (i < src_len) |
105 | c += s[i]; |
106 | c <<= 8; |
107 | i++; |
108 | |
109 | if (i < src_len) |
110 | c += s[i]; |
111 | i++; |
112 | |
113 | *dst++= base64_table[(c >> 18) & 0x3f]; |
114 | *dst++= base64_table[(c >> 12) & 0x3f]; |
115 | |
116 | if (i > (src_len + 1)) |
117 | *dst++= '='; |
118 | else |
119 | *dst++= base64_table[(c >> 6) & 0x3f]; |
120 | |
121 | if (i > src_len) |
122 | *dst++= '='; |
123 | else |
124 | *dst++= base64_table[(c >> 0) & 0x3f]; |
125 | } |
126 | *dst= '\0'; |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | |
132 | /* |
133 | Base64 decoder stream |
134 | */ |
135 | typedef struct my_base64_decoder_t |
136 | { |
137 | const char *src; /* Pointer to the current input position */ |
138 | const char *end; /* Pointer to the end of input buffer */ |
139 | uint c; /* Collect bits into this number */ |
140 | int error; /* Error code */ |
141 | uchar state; /* Character number in the current group of 4 */ |
142 | uchar mark; /* Number of padding marks in the current group */ |
143 | } MY_BASE64_DECODER; |
144 | |
145 | |
146 | /* |
147 | Helper table for decoder. |
148 | -2 means "space character" |
149 | -1 means "bad character" |
150 | Non-negative values mean valid base64 encoding character. |
151 | */ |
152 | static int8 |
153 | from_base64_table[]= |
154 | { |
155 | /*00*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-1,-1, |
156 | /*10*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
157 | /*20*/ -2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* !"#$%&'()*+,-./ */ |
158 | /*30*/ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 0123456789:;<=>? */ |
159 | /*40*/ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* @ABCDEFGHIJKLMNO */ |
160 | /*50*/ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* PQRSTUVWXYZ[\]^_ */ |
161 | /*60*/ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* `abcdefghijklmno */ |
162 | /*70*/ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* pqrstuvwxyz{|}~ */ |
163 | /*80*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
164 | /*90*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
165 | /*A0*/ -2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
166 | /*B0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
167 | /*C0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
168 | /*D0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
169 | /*E0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
170 | /*F0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
171 | }; |
172 | |
173 | |
174 | /** |
175 | * Skip leading spaces in a base64 encoded stream |
176 | * and stop on the first non-space character. |
177 | * decoder->src will point to the first non-space character, |
178 | * or to the end of the input string. |
179 | * In case when end-of-input met on unexpected position, |
180 | * decoder->error is also set to 1. |
181 | * |
182 | * See http://en.wikipedia.org/wiki/Base64 for the base64 encoding details |
183 | * |
184 | * @param decoder Pointer to MY_BASE64_DECODER |
185 | * |
186 | * @return |
187 | * FALSE on success (there are some more non-space input characters) |
188 | * TRUE on error (end-of-input found) |
189 | */ |
190 | |
191 | static inline my_bool |
192 | my_base64_decoder_skip_spaces(MY_BASE64_DECODER *decoder) |
193 | { |
194 | for ( ; decoder->src < decoder->end; decoder->src++) |
195 | { |
196 | if (from_base64_table[(uchar) *decoder->src] != -2) |
197 | return FALSE; |
198 | } |
199 | if (decoder->state > 0) |
200 | decoder->error= 1; /* Unexpected end-of-input found */ |
201 | return TRUE; |
202 | } |
203 | |
204 | |
205 | /** |
206 | * Convert the next character in a base64 encoded stream |
207 | * to a number in the range [0..63] |
208 | * and mix it with the previously collected value in decoder->c. |
209 | * |
210 | * @param decode base64 decoding stream |
211 | * |
212 | * @return |
213 | * FALSE on success |
214 | * TRUE on error (invalid base64 character found) |
215 | */ |
216 | static inline my_bool |
217 | my_base64_add(MY_BASE64_DECODER *decoder) |
218 | { |
219 | int res; |
220 | decoder->c <<= 6; |
221 | if ((res= from_base64_table[(uchar) *decoder->src++]) < 0) |
222 | return (decoder->error= TRUE); |
223 | decoder->c+= (uint) res; |
224 | return FALSE; |
225 | } |
226 | |
227 | |
228 | /** |
229 | * Get the next character from a base64 encoded stream. |
230 | * Skip spaces, then scan the next base64 character or a pad character |
231 | * and collect bits into decoder->c. |
232 | * |
233 | * @param decoder Pointer to MY_BASE64_DECODER |
234 | * @return |
235 | * FALSE on success (a valid base64 encoding character found) |
236 | * TRUE on error (unexpected character or unexpected end-of-input found) |
237 | */ |
238 | static my_bool |
239 | my_base64_decoder_getch(MY_BASE64_DECODER *decoder) |
240 | { |
241 | if (my_base64_decoder_skip_spaces(decoder)) |
242 | return TRUE; /* End-of-input */ |
243 | |
244 | if (!my_base64_add(decoder)) /* Valid base64 character found */ |
245 | { |
246 | if (decoder->mark) |
247 | { |
248 | /* If we have scanned '=' already, then only '=' is valid */ |
249 | DBUG_ASSERT(decoder->state == 3); |
250 | decoder->error= 1; |
251 | decoder->src--; |
252 | return TRUE; /* expected '=', but encoding character found */ |
253 | } |
254 | decoder->state++; |
255 | return FALSE; |
256 | } |
257 | |
258 | /* Process error */ |
259 | switch (decoder->state) |
260 | { |
261 | case 0: |
262 | case 1: |
263 | decoder->src--; |
264 | return TRUE; /* base64 character expected */ |
265 | break; |
266 | |
267 | case 2: |
268 | case 3: |
269 | if (decoder->src[-1] == '=') |
270 | { |
271 | decoder->error= 0; /* Not an error - it's a pad character */ |
272 | decoder->mark++; |
273 | } |
274 | else |
275 | { |
276 | decoder->src--; |
277 | return TRUE; /* base64 character or '=' expected */ |
278 | } |
279 | break; |
280 | |
281 | default: |
282 | DBUG_ASSERT(0); |
283 | return TRUE; /* Wrong state, should not happen */ |
284 | } |
285 | |
286 | decoder->state++; |
287 | return FALSE; |
288 | } |
289 | |
290 | |
291 | /** |
292 | * Decode a base64 string |
293 | * The base64-encoded data in the range ['src','*end_ptr') will be |
294 | * decoded and stored starting at 'dst'. The decoding will stop |
295 | * after 'len' characters have been read from 'src', or when padding |
296 | * occurs in the base64-encoded data. In either case: if 'end_ptr' is |
297 | * non-null, '*end_ptr' will be set to point to the character after |
298 | * the last read character, even in the presence of error. |
299 | * |
300 | * Note: 'dst' must have sufficient space to store the decoded data. |
301 | * Use my_base64_needed_decoded_length() to calculate the correct space size. |
302 | * |
303 | * Note: we allow spaces and line separators at any position. |
304 | * |
305 | * @param src Pointer to base64-encoded string |
306 | * @param len Length of string at 'src' |
307 | * @param dst Pointer to location where decoded data will be stored |
308 | * @param end_ptr Pointer to variable that will refer to the character |
309 | * after the end of the encoded data that were decoded. |
310 | * Can be NULL. |
311 | * @flags flags e.g. allow multiple chunks |
312 | * @return Number of bytes written at 'dst', or -1 in case of failure |
313 | */ |
314 | int |
315 | my_base64_decode(const char *src_base, size_t len, |
316 | void *dst, const char **end_ptr, int flags) |
317 | { |
318 | char *d= (char*) dst; |
319 | MY_BASE64_DECODER decoder; |
320 | |
321 | decoder.src= src_base; |
322 | decoder.end= src_base + len; |
323 | decoder.error= 0; |
324 | decoder.mark= 0; |
325 | |
326 | for ( ; ; ) |
327 | { |
328 | decoder.c= 0; |
329 | decoder.state= 0; |
330 | |
331 | if (my_base64_decoder_getch(&decoder) || |
332 | my_base64_decoder_getch(&decoder) || |
333 | my_base64_decoder_getch(&decoder) || |
334 | my_base64_decoder_getch(&decoder)) |
335 | break; |
336 | |
337 | *d++= (decoder.c >> 16) & 0xff; |
338 | *d++= (decoder.c >> 8) & 0xff; |
339 | *d++= (decoder.c >> 0) & 0xff; |
340 | |
341 | if (decoder.mark) |
342 | { |
343 | d-= decoder.mark; |
344 | if (!(flags & MY_BASE64_DECODE_ALLOW_MULTIPLE_CHUNKS)) |
345 | break; |
346 | decoder.mark= 0; |
347 | } |
348 | } |
349 | |
350 | /* Return error if there are more non-space characters */ |
351 | decoder.state= 0; |
352 | if (!my_base64_decoder_skip_spaces(&decoder)) |
353 | decoder.error= 1; |
354 | |
355 | if (end_ptr != NULL) |
356 | *end_ptr= decoder.src; |
357 | |
358 | return decoder.error ? -1 : (int) (d - (char*) dst); |
359 | } |
360 | |
361 | |
362 | #else /* MAIN */ |
363 | |
364 | #define require(b) { \ |
365 | if (!(b)) { \ |
366 | printf("Require failed at %s:%d\n", __FILE__, __LINE__); \ |
367 | abort(); \ |
368 | } \ |
369 | } |
370 | |
371 | |
372 | int |
373 | main(void) |
374 | { |
375 | int i; |
376 | size_t j; |
377 | size_t k, l; |
378 | size_t dst_len; |
379 | size_t needed_length; |
380 | |
381 | for (i= 0; i < 500; i++) |
382 | { |
383 | /* Create source data */ |
384 | const size_t src_len= rand() % 1000 + 1; |
385 | |
386 | char * src= (char *) malloc(src_len); |
387 | char * s= src; |
388 | char * str; |
389 | char * dst; |
390 | |
391 | require(src); |
392 | for (j= 0; j<src_len; j++) |
393 | { |
394 | char c= rand(); |
395 | *s++= c; |
396 | } |
397 | |
398 | /* Encode */ |
399 | needed_length= my_base64_needed_encoded_length(src_len); |
400 | str= (char *) malloc(needed_length); |
401 | require(str); |
402 | for (k= 0; k < needed_length; k++) |
403 | str[k]= 0xff; /* Fill memory to check correct NUL termination */ |
404 | require(my_base64_encode(src, src_len, str) == 0); |
405 | require(needed_length == strlen(str) + 1); |
406 | |
407 | /* Decode */ |
408 | dst= (char *) malloc(my_base64_needed_decoded_length(strlen(str))); |
409 | require(dst); |
410 | dst_len= my_base64_decode(str, strlen(str), dst, NULL); |
411 | require(dst_len == src_len); |
412 | |
413 | if (memcmp(src, dst, src_len) != 0) |
414 | { |
415 | printf(" --------- src --------- --------- dst ---------\n" ); |
416 | for (k= 0; k<src_len; k+=8) |
417 | { |
418 | printf("%.4x " , (uint) k); |
419 | for (l=0; l<8 && k+l<src_len; l++) |
420 | { |
421 | unsigned char c= src[k+l]; |
422 | printf("%.2x " , (unsigned)c); |
423 | } |
424 | |
425 | printf(" " ); |
426 | |
427 | for (l=0; l<8 && k+l<dst_len; l++) |
428 | { |
429 | unsigned char c= dst[k+l]; |
430 | printf("%.2x " , (unsigned)c); |
431 | } |
432 | printf("\n" ); |
433 | } |
434 | printf("src length: %.8x, dst length: %.8x\n" , |
435 | (uint) src_len, (uint) dst_len); |
436 | require(0); |
437 | } |
438 | } |
439 | printf("Test succeeded.\n" ); |
440 | return 0; |
441 | } |
442 | |
443 | #endif |
444 | |