CUBRID Engine  latest
base64.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * base64.c - Base64 encoding and decoding
21  *
22  */
23 
24 #ident "$Id$"
25 
26 #include "config.h"
27 
28 #include <stdio.h>
29 #include <string.h>
30 #include <math.h>
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <stddef.h>
34 #include <assert.h>
35 
36 #include "porting.h"
37 #include "error_code.h"
38 #include "memory_alloc.h"
39 #include "base64.h"
40 
41 #define MAX_BASE64_LINE_LENGTH 76
42 #define CH_INVALID -1
43 #define CH_SPACE -2
44 
45 /* core structure for decoding */
46 typedef struct base64_chunk BASE64_CHUNK;
48 {
49  char base64_bytes[4]; /* position in base64 table for encoded bytes */
50  int padding_len; /* number of padding */
51 };
52 
53 /*
54  * Helper table for encoding
55  */
56 const char *base64_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/";
57 
58 /*
59  * Helper table for decoding.
60  * -2 means space character
61  * -1 means invalid character
62  * positive values mean valid character
63  */
64 const char from_base64_table[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1, -1, -1,
65  -1,
66  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
67  /* !"#$%&'()*+,-./ */
68  -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
69  /* 0123456789:;<=>? */
70  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
71  /* @ABCDEFGHIJKLMNO */
72  -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
73  /* PQRSTUVWXYZ[\]^_ */
74  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
75  /* `abcdefghijklmno */
76  -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
77  /* pqrstuvwxyz{|}~ */
78  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
79  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
80  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1,
81  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
82  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
83  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
84  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
85  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
86 };
87 
88 static bool char_is_invalid (unsigned char ch);
89 static bool char_is_padding (unsigned char ch);
90 static int get_base64_encode_len (int src_len);
91 static int base64_encode_local (const unsigned char *src, int src_len, unsigned char *dest);
92 static int base64_remove_space (const unsigned char *src, int src_len, unsigned char *dest, int *dst_len);
93 static int base64_partition_into_chunk (const unsigned char *src, int src_len, int *chunk_num, int *dst_len,
94  BASE64_CHUNK *** pppchunk);
95 static int base64_decode_chunk (unsigned char *dest, int chunk_num, BASE64_CHUNK ** ppchunk);
96 static void free_base64_chunk (BASE64_CHUNK ** ppchunk, int chunk_num);
97 
98 /*
99  * char_is_invalid () -
100  * return: true if input char is invalid character, otherwise false
101  * ch(in): input character
102  */
103 static bool
104 char_is_invalid (unsigned char ch)
105 {
106  return (ch != '=' && from_base64_table[ch] == CH_INVALID);
107 }
108 
109 /*
110  * char_is_padding () -
111  * return: true if input char is padding('='), otherwise false
112  * ch(in): input character
113  */
114 static bool
115 char_is_padding (unsigned char ch)
116 {
117  return (ch == '=');
118 }
119 
120 /*
121  * find_base64 () -
122  * return: offset in base64 table for input character
123  * ch(in): input character
124  */
125 static int
126 find_base64 (unsigned char ch)
127 {
128  int offset;
129  const char *pos = NULL;
130 
131  pos = strchr (base64_map, ch);
132  assert (pos != NULL);
133 
134  offset = CAST_STRLEN (pos - base64_map);
135  return offset;
136 }
137 
138 /*
139  * base64_partition_into_chunk () - Partition base64 encoded string into
140  * multiple chunks. Partition stops when
141  * meeting the first chunk ending in
142  * padding(s).
143  *
144  * return: int(NO_ERROR if successful, otherwise other base64 error code)
145  * src(in): source string(base64 encoded)
146  * src_len(in): source string length
147  * chunk_num_out(in/out): actual number of chunks calculated
148  * dst_len_out(in/out): actual length of buffer to store decoded bytes
149  * pppchunk(in/out): array pointer of chunks to store encoded bytes
150  */
151 static int
152 base64_partition_into_chunk (const unsigned char *src, int src_len, int *chunk_num_out, int *dst_len_out,
153  BASE64_CHUNK *** pppchunk)
154 {
155  int err = NO_ERROR;
156  int i, j, chunk_count, chunk_num, dst_len;
157  unsigned char d1, d2, d3, d4;
158  bool tail_chunk_flag;
159  BASE64_CHUNK *chk = NULL;
160  BASE64_CHUNK **ppchunk = NULL;
161 
162  assert (src != NULL && src_len > 0 && chunk_num_out != NULL && dst_len_out != NULL && pppchunk != NULL);
163 
164  *chunk_num_out = 0;
165  *dst_len_out = 0;
166  *pppchunk = NULL;
167 
168  /* require 4-byte alignment for chunk */
169  if ((src_len & 0x3) != 0)
170  {
171  err = BASE64_INVALID_INPUT;
172  goto end;
173  }
174 
175  /* chunk array pointer to accommodate maximum chunk_num base64 chunks */
176  chunk_num = src_len / 4;
177  ppchunk = (BASE64_CHUNK **) db_private_alloc (NULL, chunk_num * sizeof (BASE64_CHUNK *));
178 
179  if (ppchunk == NULL)
180  {
182  goto end;
183  }
184 
185  memset (ppchunk, 0, chunk_num * sizeof (BASE64_CHUNK *));
186 
187  tail_chunk_flag = false;
188  i = chunk_count = dst_len = 0;
189  while (chunk_count < chunk_num)
190  {
191  d1 = src[i];
192  d2 = src[i + 1];
193  d3 = src[i + 2];
194  d4 = src[i + 3];
195 
196  /* check all invalid conditions */
197  if (char_is_invalid (d1) || char_is_invalid (d2) || char_is_invalid (d3) || char_is_invalid (d4)
198  || char_is_padding (d1) || char_is_padding (d2))
199  {
200  err = BASE64_INVALID_INPUT;
201  goto end;
202  }
203 
204  if (char_is_padding (d3) && !char_is_padding (d4))
205  {
206  err = BASE64_INVALID_INPUT;
207  goto end;
208  }
209 
210  chk = (BASE64_CHUNK *) db_private_alloc (NULL, sizeof (BASE64_CHUNK));
211  if (chk == NULL)
212  {
214  goto end;
215  }
216 
217  memset (chk, 0, sizeof (BASE64_CHUNK));
218 
219  chk->padding_len = char_is_padding (d4) ? 1 : 0;
220  chk->padding_len += char_is_padding (d3) ? 1 : 0;
221 
222  for (j = 0; j <= (3 - chk->padding_len); j++)
223  {
224  chk->base64_bytes[j] = (char) find_base64 (src[i + j]);
225  }
226 
227  ppchunk[chunk_count] = chk;
228 
229  if (char_is_padding (d4) == true)
230  {
231  /* middle chunk that has padding is invalid */
232  if ((chunk_count + 1) != chunk_num)
233  {
234  err = BASE64_INVALID_INPUT;
235  goto end;
236  }
237  else /* tail chunk that has padding */
238  {
239  /* how many decoded bytes to add in this chunk, e.g, from_base64(YWE=)='aa',from_base64(YW==)='a' */
240  dst_len += char_is_padding (d3) ? 1 : 2;
241  tail_chunk_flag = true;
242  }
243  }
244 
245  chunk_count++;
246 
247  if (tail_chunk_flag == true)
248  {
249  break;
250  }
251 
252  dst_len += 3;
253  i += 4;
254  }
255 
256 end:
257 
258  if (err == NO_ERROR)
259  {
260  *chunk_num_out = chunk_count;
261  *dst_len_out = dst_len;
262  *pppchunk = ppchunk;
263  }
264  else
265  {
266  /* free resource if error */
267  if (ppchunk != NULL)
268  {
269  free_base64_chunk (ppchunk, chunk_count);
270  }
271 
272  *chunk_num_out = 0;
273  *dst_len_out = 0;
274  *pppchunk = NULL;
275  }
276 
277  return err;
278 }
279 
280 /*
281  * base64_remove_space () - remove space characters to facilitate base64
282  * chunk partition
283  * return: NO_ERROR
284  * src(in): input string
285  * size(in):length of input string
286  * dest(in/out): buffer to store the string that has no space characters
287  * dst_len(in/out): length of dest
288  *
289  * Note: dest is allocated in this function but freed in caller. dst_len may
290  * differ from size in case of space characters.
291  */
292 static int
293 base64_remove_space (const unsigned char *src, int size, unsigned char *dest, int *dst_len)
294 {
295  int i;
296  unsigned char *q;
297 
298  assert (src != NULL && dest != NULL && dst_len != NULL);
299 
300  *dst_len = 0;
301  q = dest;
302 
303  i = 0;
304  while (i < size)
305  {
306  /* search for the next char that is non-space character */
307  while (from_base64_table[src[i]] == CH_SPACE && i < size)
308  {
309  i++;
310  }
311 
312  if (i >= size)
313  {
314  break;
315  }
316 
317  /* be careful of memory bounds! */
318  *q++ = src[i++];
319  (*dst_len)++;
320  }
321 
322  return NO_ERROR;
323 }
324 
325 /*
326  * base64_decode_chunk () - convert encoded bytes stored in chunks to
327  * plain-text bytes
328  * return: NO_ERROR
329  * dest(in/out): buffer to store decoded bytes
330  * chunk_num(in): number of chunks
331  * ppchunk(in): buffer to store chunks
332  *
333  * Note: each chunk has four encoded bytes and can be converted to [1,2,3]
334  * decoded bytes depending on padding.
335  */
336 static int
337 base64_decode_chunk (unsigned char *dest, int chunk_num, BASE64_CHUNK ** ppchunk)
338 {
339  int i, d, copy_num;
340  char decode_bytes[3];
341  BASE64_CHUNK *chk;
342 
343  assert (dest != NULL && ppchunk != NULL);
344 
345  i = 0;
346  d = 0;
347  while (i < chunk_num)
348  {
349  chk = ppchunk[i];
350  d += (chk->base64_bytes[0]) << 18;
351  d += (chk->base64_bytes[1]) << 12;
352  d += (chk->base64_bytes[2]) << 6;
353  d += (chk->base64_bytes[3]) << 0;
354 
355  decode_bytes[0] = (d >> 16) & 0xff;
356  decode_bytes[1] = (d >> 8) & 0xff;
357  decode_bytes[2] = (d >> 0) & 0xff;
358 
359  /* only allow for 0,1,2 */
360  assert (chk->padding_len < 3);
361  copy_num = 3 - chk->padding_len;
362 
363  memcpy (dest, decode_bytes, copy_num);
364 
365  dest += copy_num;
366  d = 0;
367  i++;
368  }
369 
370  return NO_ERROR;
371 }
372 
373 /*
374  * base64_decode() -
375  * return: int(NO_ERROR if successful,otherwise failure error code)
376  * src(in): source plain-text string
377  * src_len(in): length of source string
378  * dest(in/out): buffer to store decoded bytes, allocated in this func
379  * dest_len(in/out): required length to store decoded buffer
380  *
381  * Note: there are some error handlings to handle invalid characters.
382  * some buffers are allocated in this function or its callees,
383  * and freed in the end.
384  *
385  */
386 int
387 base64_decode (const unsigned char *src, int src_len, unsigned char **dest, int *dest_len)
388 {
389  int error_status = NO_ERROR;
390  int len_no_space, real_dest_len, chunk_num = 0;
391  unsigned char *str_no_space = NULL;
392  unsigned char *real_dest = NULL;
393  BASE64_CHUNK **ppchunk = NULL;
394 
395  assert (src != NULL && dest != NULL && dest_len != NULL);
396 
397  *dest = NULL;
398  *dest_len = 0;
399 
400  len_no_space = 0;
401 
402  /* assume there are no space characters in source string */
403  str_no_space = (unsigned char *) db_private_alloc (NULL, src_len + 1);
404  if (str_no_space == NULL)
405  {
406  error_status = ER_OUT_OF_VIRTUAL_MEMORY;
407  goto buf_clean;
408  }
409 
410  /* remove space characters */
411  base64_remove_space (src, src_len, str_no_space, &len_no_space);
412 
413  /* ' ' has spaces. after space removal, it is empty string */
414  if (len_no_space == 0)
415  {
416  error_status = BASE64_EMPTY_INPUT;
417  goto buf_clean;
418  }
419 
420  /* Partition encoded bytes with no space characters into multiple chunks for decoding, stop when there is a middle
421  * chunk ending in padding(s), which is considered as invalid input */
422  error_status = base64_partition_into_chunk (str_no_space, len_no_space, &chunk_num, &real_dest_len, &ppchunk);
423 
424  if (error_status != NO_ERROR)
425  {
426  goto buf_clean;
427  }
428 
429  real_dest = (unsigned char *) db_private_alloc (NULL, real_dest_len + 1);
430  if (real_dest == NULL)
431  {
432  error_status = ER_OUT_OF_VIRTUAL_MEMORY;
433  goto buf_clean;
434  }
435 
436  real_dest[real_dest_len] = '\0';
437 
438  assert (chunk_num > 0);
439  base64_decode_chunk (real_dest, chunk_num, ppchunk);
440 
441  *dest = real_dest;
442  *dest_len = real_dest_len;
443 
444  error_status = NO_ERROR;
445 
446 buf_clean:
447 
448  if (str_no_space != NULL)
449  {
450  db_private_free_and_init (NULL, str_no_space);
451  }
452 
453  if (error_status != NO_ERROR && real_dest != NULL)
454  {
455  db_private_free_and_init (NULL, real_dest);
456  }
457 
458  if (ppchunk != NULL)
459  {
460  free_base64_chunk (ppchunk, chunk_num);
461  }
462 
463  return error_status;
464 }
465 
466 /*
467  * get_base64_encode_len () -
468  * return: the string length with base64 encoding
469  * src_len(in): source string length
470  */
471 static int
473 {
474  int nbytes, backspace_bytes_need, total_bytes;
475 
476  assert (src_len >= 0);
477 
478  nbytes = (src_len + 2) / 3 * 4;
479 
480  /* need to account for backspaces in the encoded string */
481  backspace_bytes_need = nbytes / MAX_BASE64_LINE_LENGTH;
482  if (nbytes % MAX_BASE64_LINE_LENGTH == 0)
483  {
484  --backspace_bytes_need;
485  }
486 
487  total_bytes = nbytes + backspace_bytes_need;
488 
489  return total_bytes;
490 }
491 
492 /*
493  * base64_encode () -
494  * return: NO_ERROR(NO_ERROR if successful,otherwise failure error code)
495  * src(in): source plain-text string
496  * src_len(in): length of source string
497  * dest(in/out): base64 encoded string
498  * dest_len(in/out): required length for encoded string
499  *
500  */
501 int
502 base64_encode (const unsigned char *src, int src_len, unsigned char **dest, int *dest_len)
503 {
504  int encode_len;
505  int error_status;
506  unsigned char *dest_p = NULL;
507 
508  assert (src != NULL && src_len >= 0 && dest != NULL && dest_len != NULL);
509 
510  *dest = NULL;
511  *dest_len = 0;
512 
513  encode_len = get_base64_encode_len (src_len);
514 
515  dest_p = (unsigned char *) db_private_alloc (NULL, encode_len + 1);
516  if (dest_p == NULL)
517  {
518  error_status = ER_OUT_OF_VIRTUAL_MEMORY;
519  goto end;
520  }
521 
522  error_status = base64_encode_local (src, src_len, dest_p);
523 
524  if (error_status == NO_ERROR)
525  {
526  dest_p[encode_len] = '\0';
527 
528  *dest = dest_p;
529  *dest_len = encode_len;
530  }
531  else
532  {
533  if (dest_p != NULL)
534  {
535  db_private_free_and_init (NULL, dest_p);
536  }
537  }
538 
539 end:
540 
541  return error_status;
542 }
543 
544 /*
545  * base64_encode_local () - convert a plain-text string with base64,
546  * invoked by base64_encode
547  * return: NO_ERROR
548  * src(in): source plain-text string
549  * src_len(in): length of source string
550  * dest(in): base64 encoded string
551  *
552  * Note: it's the caller's responsibility to claim and reclaim(if needed)
553  * memory space for dest
554  */
555 static int
556 base64_encode_local (const unsigned char *src, int src_len, unsigned char *dest)
557 {
558  const unsigned char *p;
559  unsigned int d;
560  int i, encoded_len, fill, line_break_count;
561 
562  assert (src != NULL && src_len >= 0 && dest != NULL);
563 
564  encoded_len = 0;
565  line_break_count = 0;
566  p = src;
567 
568  i = 0;
569  while (i < src_len)
570  {
571  /* require maximum char length per line to be 76 */
572  if (line_break_count == MAX_BASE64_LINE_LENGTH)
573  {
574  dest[encoded_len++] = '\n';
575  line_break_count = 0;
576  }
577 
578  /* move forward the source string every three bytes and translate it into four 6-bit bytes */
579  d = (p[i++] << 16); /* the most significant(3rd) bytes */
580  fill = 2; /* assuming 2 paddings needed */
581 
582  if (src_len >= (i + 1))
583  {
584  d += p[i++] << 8; /* the second byte, one padding needed */
585  fill = 1;
586  }
587 
588  if (src_len >= (i + 1)) /* least significant byte, no padding needed */
589  {
590  d += p[i++] << 0;
591  fill = 0;
592  }
593 
594  /* convert 24bit stream into four 6-bit bytes */
595  dest[encoded_len++] = base64_map[(d >> 18) & 0x3f];
596  dest[encoded_len++] = base64_map[(d >> 12) & 0x3f];
597  dest[encoded_len++] = (fill > 1) ? '=' : base64_map[(d >> 6) & 0x3f];
598  dest[encoded_len++] = (fill > 0) ? '=' : base64_map[(d >> 0) & 0x3f];
599 
600  line_break_count += 4;
601  }
602 
603  return NO_ERROR;
604 }
605 
606 /*
607  * free_base64_chunk () - free allocated base64_chunk
608  * return:
609  * pchunk(in): source plain-text string
610  *
611  */
612 static void
613 free_base64_chunk (BASE64_CHUNK ** ppchunk, int chunk_num)
614 {
615  int i = 0;
616 
617  if (ppchunk != NULL)
618  {
619  for (i = 0; i < chunk_num; ++i)
620  {
621  if (ppchunk[i] != NULL)
622  {
623  db_private_free (NULL, ppchunk[i]);
624  }
625  }
626 
627  db_private_free (NULL, ppchunk);
628  }
629 
630  return;
631 }
char base64_bytes[4]
Definition: base64.c:49
#define NO_ERROR
Definition: error_code.h:46
static int base64_remove_space(const unsigned char *src, int src_len, unsigned char *dest, int *dst_len)
Definition: base64.c:293
#define CH_SPACE
Definition: base64.c:43
const char * base64_map
Definition: base64.c:56
#define CAST_STRLEN
Definition: porting.h:470
#define MAX_BASE64_LINE_LENGTH
Definition: base64.c:41
int base64_decode(const unsigned char *src, int src_len, unsigned char **dest, int *dest_len)
Definition: base64.c:387
int padding_len
Definition: base64.c:50
const char from_base64_table[]
Definition: base64.c:64
static bool char_is_invalid(unsigned char ch)
Definition: base64.c:104
static int base64_decode_chunk(unsigned char *dest, int chunk_num, BASE64_CHUNK **ppchunk)
Definition: base64.c:337
#define assert(x)
static int find_base64(unsigned char ch)
Definition: base64.c:126
#define ER_OUT_OF_VIRTUAL_MEMORY
Definition: error_code.h:50
static bool char_is_padding(unsigned char ch)
Definition: base64.c:115
#define NULL
Definition: freelistheap.h:34
static void free_base64_chunk(BASE64_CHUNK **ppchunk, int chunk_num)
Definition: base64.c:613
#define err(fd,...)
Definition: porting.h:431
#define db_private_free_and_init(thrd, ptr)
Definition: memory_alloc.h:141
#define db_private_free(thrd, ptr)
Definition: memory_alloc.h:229
#define db_private_alloc(thrd, size)
Definition: memory_alloc.h:227
#define d1
static int base64_encode_local(const unsigned char *src, int src_len, unsigned char *dest)
Definition: base64.c:556
int i
Definition: dynamic_load.c:954
int base64_encode(const unsigned char *src, int src_len, unsigned char **dest, int *dest_len)
Definition: base64.c:502
#define CH_INVALID
Definition: base64.c:42
static int get_base64_encode_len(int src_len)
Definition: base64.c:472
const char ** p
Definition: dynamic_load.c:945
static int base64_partition_into_chunk(const unsigned char *src, int src_len, int *chunk_num, int *dst_len, BASE64_CHUNK ***pppchunk)
Definition: base64.c:152