1 /* /////////////////////////////////////////////////////////////////////////////
\r
4 * Purpose: Implementation file for the b64 library
\r
6 * Created: 18th October 2004
\r
7 * Updated: 3rd May 2008
\r
9 * Home: http://synesis.com.au/software/
\r
11 * Copyright (c) 2004-2008, Matthew Wilson and Synesis Software
\r
12 * All rights reserved.
\r
14 * Redistribution and use in source and binary forms, with or without
\r
15 * modification, are permitted provided that the following conditions are met:
\r
17 * - Redistributions of source code must retain the above copyright notice, this
\r
18 * list of conditions and the following disclaimer.
\r
19 * - Redistributions in binary form must reproduce the above copyright notice,
\r
20 * this list of conditions and the following disclaimer in the documentation
\r
21 * and/or other materials provided with the distribution.
\r
22 * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
\r
23 * any contributors may be used to endorse or promote products derived from
\r
24 * this software without specific prior written permission.
\r
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
\r
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
\r
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
\r
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
\r
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
\r
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
\r
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
\r
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\r
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
\r
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
\r
36 * POSSIBILITY OF SUCH DAMAGE.
\r
38 * ////////////////////////////////////////////////////////////////////////// */
\r
41 /** \file b64.c Implementation file for the b64 library
\r
44 /* /////////////////////////////////////////////////////////////////////////////
\r
45 * Version information
\r
48 #ifndef B64_DOCUMENTATION_SKIP_SECTION
\r
49 # define B64_VER_C_B64_MAJOR 1
\r
50 # define B64_VER_C_B64_MINOR 2
\r
51 # define B64_VER_C_B64_REVISION 3
\r
52 # define B64_VER_C_B64_EDIT 17
\r
53 #endif /* !B64_DOCUMENTATION_SKIP_SECTION */
\r
55 /* /////////////////////////////////////////////////////////////////////////////
\r
59 #include "ortp/b64.h"
\r
63 #include "ortp/port.h"
\r
64 /* /////////////////////////////////////////////////////////////////////////////
\r
65 * Constants and definitions
\r
68 #ifndef B64_DOCUMENTATION_SKIP_SECTION
\r
69 # define NUM_PLAIN_DATA_BYTES (3)
\r
70 # define NUM_ENCODED_DATA_BYTES (4)
\r
71 #endif /* !B64_DOCUMENTATION_SKIP_SECTION */
\r
73 /* /////////////////////////////////////////////////////////////////////////////
\r
77 #ifndef NUM_ELEMENTS
\r
78 # define NUM_ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
\r
79 #endif /* !NUM_ELEMENTS */
\r
81 /* /////////////////////////////////////////////////////////////////////////////
\r
85 #if defined(_MSC_VER) && \
\r
87 # pragma warning(disable : 4705)
\r
88 #endif /* _MSC_VER < 1000 */
\r
90 /* /////////////////////////////////////////////////////////////////////////////
\r
94 static const char b64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
\r
96 static const signed char b64_indexes[] =
\r
98 /* 0 - 31 / 0x00 - 0x1f */
\r
99 -1, -1, -1, -1, -1, -1, -1, -1
\r
100 , -1, -1, -1, -1, -1, -1, -1, -1
\r
101 , -1, -1, -1, -1, -1, -1, -1, -1
\r
102 , -1, -1, -1, -1, -1, -1, -1, -1
\r
103 /* 32 - 63 / 0x20 - 0x3f */
\r
104 , -1, -1, -1, -1, -1, -1, -1, -1
\r
105 , -1, -1, -1, 62, -1, -1, -1, 63 /* ... , '+', ... '/' */
\r
106 , 52, 53, 54, 55, 56, 57, 58, 59 /* '0' - '7' */
\r
107 , 60, 61, -1, -1, -1, -1, -1, -1 /* '8', '9', ... */
\r
108 /* 64 - 95 / 0x40 - 0x5f */
\r
109 , -1, 0, 1, 2, 3, 4, 5, 6 /* ..., 'A' - 'G' */
\r
110 , 7, 8, 9, 10, 11, 12, 13, 14 /* 'H' - 'O' */
\r
111 , 15, 16, 17, 18, 19, 20, 21, 22 /* 'P' - 'W' */
\r
112 , 23, 24, 25, -1, -1, -1, -1, -1 /* 'X', 'Y', 'Z', ... */
\r
113 /* 96 - 127 / 0x60 - 0x7f */
\r
114 , -1, 26, 27, 28, 29, 30, 31, 32 /* ..., 'a' - 'g' */
\r
115 , 33, 34, 35, 36, 37, 38, 39, 40 /* 'h' - 'o' */
\r
116 , 41, 42, 43, 44, 45, 46, 47, 48 /* 'p' - 'w' */
\r
117 , 49, 50, 51, -1, -1, -1, -1, -1 /* 'x', 'y', 'z', ... */
\r
119 , -1, -1, -1, -1, -1, -1, -1, -1
\r
120 , -1, -1, -1, -1, -1, -1, -1, -1
\r
121 , -1, -1, -1, -1, -1, -1, -1, -1
\r
122 , -1, -1, -1, -1, -1, -1, -1, -1
\r
124 , -1, -1, -1, -1, -1, -1, -1, -1
\r
125 , -1, -1, -1, -1, -1, -1, -1, -1
\r
126 , -1, -1, -1, -1, -1, -1, -1, -1
\r
127 , -1, -1, -1, -1, -1, -1, -1, -1
\r
129 , -1, -1, -1, -1, -1, -1, -1, -1
\r
130 , -1, -1, -1, -1, -1, -1, -1, -1
\r
131 , -1, -1, -1, -1, -1, -1, -1, -1
\r
132 , -1, -1, -1, -1, -1, -1, -1, -1
\r
134 , -1, -1, -1, -1, -1, -1, -1, -1
\r
135 , -1, -1, -1, -1, -1, -1, -1, -1
\r
136 , -1, -1, -1, -1, -1, -1, -1, -1
\r
137 , -1, -1, -1, -1, -1, -1, -1, -1
\r
140 /* /////////////////////////////////////////////////////////////////////////////
\r
144 /** This function reads in 3 bytes at a time, and translates them into 4
\r
147 static size_t b64_encode_( unsigned char const *src
\r
154 size_t total = ((srcSize + (NUM_PLAIN_DATA_BYTES - 1)) / NUM_PLAIN_DATA_BYTES) * NUM_ENCODED_DATA_BYTES;
\r
156 assert(NULL != rc);
\r
161 size_t numLines = (total + (lineLen - 1)) / lineLen;
\r
163 total += 2 * (numLines - 1);
\r
170 else if(destLen < total)
\r
172 *rc = B64_RC_INSUFFICIENT_BUFFER;
\r
179 char *end = dest + destLen;
\r
182 for(; NUM_PLAIN_DATA_BYTES <= srcSize; srcSize -= NUM_PLAIN_DATA_BYTES)
\r
184 char characters[NUM_ENCODED_DATA_BYTES];
\r
192 * | | | | | | | | | | | | |
\r
193 * | | | | | | | | | | | | | | | | | | | | | | | | |
\r
195 * | 0 | 1 | 2 | 3 |
\r
199 /* characters[0] is the 6 left-most bits of src[0] */
\r
200 characters[0] = (char)((src[0] & 0xfc) >> 2);
\r
201 /* characters[0] is the right-most 2 bits of src[0] and the left-most 4 bits of src[1] */
\r
202 characters[1] = (char)(((src[0] & 0x03) << 4) + ((src[1] & 0xf0) >> 4));
\r
203 /* characters[0] is the right-most 4 bits of src[1] and the 2 left-most bits of src[2] */
\r
204 characters[2] = (char)(((src[1] & 0x0f) << 2) + ((src[2] & 0xc0) >> 6));
\r
205 /* characters[3] is the right-most 6 bits of src[2] */
\r
206 characters[3] = (char)(src[2] & 0x3f);
\r
208 #ifndef __WATCOMC__
\r
209 assert(characters[0] >= 0 && characters[0] < 64);
\r
210 assert(characters[1] >= 0 && characters[1] < 64);
\r
211 assert(characters[2] >= 0 && characters[2] < 64);
\r
212 assert(characters[3] >= 0 && characters[3] < 64);
\r
213 #endif /* __WATCOMC__ */
\r
215 src += NUM_PLAIN_DATA_BYTES;
\r
216 *p++ = b64_chars[(unsigned char)characters[0]];
\r
217 assert(NULL != strchr(b64_chars, *(p-1)));
\r
219 assert(len != lineLen);
\r
221 *p++ = b64_chars[(unsigned char)characters[1]];
\r
222 assert(NULL != strchr(b64_chars, *(p-1)));
\r
224 assert(len != lineLen);
\r
226 *p++ = b64_chars[(unsigned char)characters[2]];
\r
227 assert(NULL != strchr(b64_chars, *(p-1)));
\r
229 assert(len != lineLen);
\r
231 *p++ = b64_chars[(unsigned char)characters[3]];
\r
232 assert(NULL != strchr(b64_chars, *(p-1)));
\r
234 if( ++len == lineLen &&
\r
245 /* Deal with the overspill, by boosting it up to three bytes (using 0s)
\r
246 * and then appending '=' for any missing characters.
\r
248 * This is done into a temporary buffer, so we can call ourselves and
\r
249 * have the output continue to be written direct to the destination.
\r
252 unsigned char dummy[NUM_PLAIN_DATA_BYTES];
\r
255 for(i = 0; i < srcSize; ++i)
\r
260 for(; i < NUM_PLAIN_DATA_BYTES; ++i)
\r
265 b64_encode_(&dummy[0], NUM_PLAIN_DATA_BYTES, p, NUM_ENCODED_DATA_BYTES * (1 + 2), 0, rc);
\r
267 for(p += 1 + srcSize; srcSize++ < NUM_PLAIN_DATA_BYTES; )
\r
277 /** This function reads in a character string in 4-character chunks, and writes
\r
278 * out the converted form in 3-byte chunks to the destination.
\r
280 static size_t b64_decode_( char const *src
\r
282 , unsigned char *dest
\r
285 , char const **badChar
\r
288 const size_t wholeChunks = (srcLen / NUM_ENCODED_DATA_BYTES);
\r
289 const size_t remainderBytes = (srcLen % NUM_ENCODED_DATA_BYTES);
\r
290 size_t maxTotal = (wholeChunks + (0 != remainderBytes)) * NUM_PLAIN_DATA_BYTES;
\r
291 unsigned char *dest_ = dest;
\r
293 ((void)remainderBytes);
\r
295 assert(NULL != badChar);
\r
296 assert(NULL != rc);
\r
305 else if(destSize < maxTotal)
\r
307 *rc = B64_RC_INSUFFICIENT_BUFFER;
\r
313 /* Now we iterate through the src, collecting together four characters
\r
314 * at a time from the Base-64 alphabet, until the end-point is reached.
\r
319 char const *begin = src;
\r
320 char const *const end = begin + srcLen;
\r
321 size_t currIndex = 0;
\r
322 size_t numPads = 0;
\r
323 signed char indexes[NUM_ENCODED_DATA_BYTES]; /* 4 */
\r
325 for(; begin != end; ++begin)
\r
327 const char ch = *begin;
\r
331 assert(currIndex < NUM_ENCODED_DATA_BYTES);
\r
333 indexes[currIndex++] = '\0';
\r
339 /* NOTE: Had to rename 'index' to 'ix', due to name clash with GCC on 64-bit Linux. */
\r
340 signed char ix = b64_indexes[(unsigned char)ch];
\r
350 if(B64_F_STOP_ON_UNEXPECTED_WS & flags)
\r
352 *rc = B64_RC_DATA_ERROR;
\r
364 if(B64_F_STOP_ON_UNKNOWN_CHAR & flags)
\r
366 *rc = B64_RC_DATA_ERROR;
\r
380 assert(currIndex < NUM_ENCODED_DATA_BYTES);
\r
382 indexes[currIndex++] = ix;
\r
386 if(NUM_ENCODED_DATA_BYTES == currIndex)
\r
388 unsigned char bytes[NUM_PLAIN_DATA_BYTES]; /* 3 */
\r
390 bytes[0] = (unsigned char)((indexes[0] << 2) + ((indexes[1] & 0x30) >> 4));
\r
394 *dest++ = bytes[0];
\r
397 bytes[1] = (unsigned char)(((indexes[1] & 0xf) << 4) + ((indexes[2] & 0x3c) >> 2));
\r
399 *dest++ = bytes[1];
\r
403 bytes[2] = (unsigned char)(((indexes[2] & 0x3) << 6) + indexes[3]);
\r
405 *dest++ = bytes[2];
\r
415 return (size_t)(dest - dest_);
\r
419 /* /////////////////////////////////////////////////////////////////////////////
\r
423 size_t b64_encode(void const *src, size_t srcSize, char *dest, size_t destLen)
\r
425 /* Use Null Object (Variable) here for rc, so do not need to check
\r
430 return b64_encode_((unsigned char const*)src, srcSize, dest, destLen, 0, &rc_);
\r
433 size_t b64_encode2( void const *src
\r
438 , int lineLen /* = -1 */
\r
439 , B64_RC *rc /* = NULL */)
\r
441 /* Use Null Object (Variable) here for rc, so do not need to check
\r
450 switch(B64_F_LINE_LEN_MASK & flags)
\r
452 case B64_F_LINE_LEN_USE_PARAM:
\r
457 /* Fall through to 64 */
\r
458 case B64_F_LINE_LEN_64:
\r
461 case B64_F_LINE_LEN_76:
\r
465 /*the following assert makes a compiler error with icc*/
\r
466 /*assert(!"Bad line length flag specified to b64_encode2()");*/
\r
467 case B64_F_LINE_LEN_INFINITE:
\r
472 assert(0 == (lineLen % 4));
\r
474 return b64_encode_((unsigned char const*)src, srcSize, dest, destLen, (unsigned)lineLen, rc);
\r
477 size_t b64_decode(char const *src, size_t srcLen, void *dest, size_t destSize)
\r
479 /* Use Null Object (Variable) here for rc and badChar, so do not need to
\r
482 char const *badChar_;
\r
485 return b64_decode_(src, srcLen, (unsigned char*)dest, destSize, B64_F_STOP_ON_NOTHING, &badChar_, &rc_);
\r
488 size_t b64_decode2( char const *src
\r
493 , char const **badChar /* = NULL */
\r
494 , B64_RC *rc /* = NULL */)
\r
496 char const *badChar_;
\r
499 /* Use Null Object (Variable) here for rc and badChar, so do not need to
\r
502 if(NULL == badChar)
\r
504 badChar = &badChar_;
\r
511 return b64_decode_(src, srcLen, (unsigned char*)dest, destSize, flags, badChar, rc);
\r
514 /* ////////////////////////////////////////////////////////////////////////// */
\r
516 #ifdef B64_DOCUMENTATION_SKIP_SECTION
\r
517 struct b64ErrorString_t_
\r
518 #else /* !B64_DOCUMENTATION_SKIP_SECTION */
\r
519 typedef struct b64ErrorString_t_ b64ErrorString_t_;
\r
520 struct b64ErrorString_t_
\r
521 #endif /* !B64_DOCUMENTATION_SKIP_SECTION */
\r
523 int code; /*!< The error code. */
\r
524 char const *str; /*!< The string. */
\r
525 size_t len; /*!< The string length. */
\r
530 #define SEVERITY_STR_DECL(rc, desc) \
\r
532 static const char s_str##rc[] = desc; \
\r
533 static const b64ErrorString_t_ s_rct##rc = { rc, s_str##rc, NUM_ELEMENTS(s_str##rc) - 1 }
\r
536 #define SEVERITY_STR_ENTRY(rc) \
\r
541 static char const *b64_LookupCodeA_(int code, b64ErrorString_t_ const **mappings, size_t cMappings, size_t *len)
\r
543 /* Use Null Object (Variable) here for len, so do not need to check
\r
553 /* Checked, indexed search. */
\r
555 code < B64_max_RC_value)
\r
557 if(code == mappings[code]->code)
\r
559 return (*len = mappings[code]->len, mappings[code]->str);
\r
563 /* Linear search. Should only be needed if order in
\r
564 * b64_LookupErrorStringA_() messed up.
\r
566 { size_t i; for(i = 0; i < cMappings; ++i)
\r
568 if(code == mappings[i]->code)
\r
570 return (*len = mappings[i]->len, mappings[i]->str);
\r
574 return (*len = 0, "");
\r
577 static char const *b64_LookupErrorStringA_(int error, size_t *len)
\r
579 SEVERITY_STR_DECL(B64_RC_OK , "Operation was successful" );
\r
580 SEVERITY_STR_DECL(B64_RC_INSUFFICIENT_BUFFER , "The given translation buffer was not of sufficient size" );
\r
581 SEVERITY_STR_DECL(B64_RC_TRUNCATED_INPUT , "The input did not represent a fully formed stream of octet couplings" );
\r
582 SEVERITY_STR_DECL(B64_RC_DATA_ERROR , "Invalid data" );
\r
584 static const b64ErrorString_t_ *s_strings[] =
\r
586 SEVERITY_STR_ENTRY(B64_RC_OK),
\r
587 SEVERITY_STR_ENTRY(B64_RC_INSUFFICIENT_BUFFER),
\r
588 SEVERITY_STR_ENTRY(B64_RC_TRUNCATED_INPUT),
\r
589 SEVERITY_STR_ENTRY(B64_RC_DATA_ERROR),
\r
592 return b64_LookupCodeA_(error, s_strings, NUM_ELEMENTS(s_strings), len);
\r
595 char const *b64_getErrorString(B64_RC code)
\r
597 return b64_LookupErrorStringA_((int)code, NULL);
\r
600 size_t b64_getErrorStringLength(B64_RC code)
\r
604 return (b64_LookupErrorStringA_((int)code, &len), len);
\r
607 /* ////////////////////////////////////////////////////////////////////////// */
\r