/ OSX / sec / Security / SecBase64.c
SecBase64.c
  1  /* /////////////////////////////////////////////////////////////////////////////
  2   * File:        b64.c
  3   *
  4   * Purpose:     Implementation file for the b64 library
  5   *
  6   * Created:     18th October 2004
  7   * Updated:     2nd August 2006
  8   *
  9   * Home:        http://synesis.com.au/software/
 10   *
 11   * Copyright (c) 2004-2006, Matthew Wilson and Synesis Software
 12   * All rights reserved.
 13   *
 14   * Redistribution and use in source and binary forms, with or without 
 15   * modification, are permitted provided that the following conditions are met:
 16   *
 17   * - Redistributions of source code must retain the above copyright notice, this
 18   *   list of conditions and the following disclaimer. 
 19   * - Redistributions in binary form must reproduce the above copyright notice,
 20   *   this list of conditions and the following disclaimer in the documentation
 21   *   and/or other materials provided with the distribution.
 22   * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
 23   *   any contributors may be used to endorse or promote products derived from
 24   *   this software without specific prior written permission.
 25   *
 26   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 27   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 28   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 29   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 30   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 31   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 32   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 33   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 34   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 35   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 36   * POSSIBILITY OF SUCH DAMAGE.
 37   *
 38   * ////////////////////////////////////////////////////////////////////////// */
 39  
 40  
 41  /** \file b64.c Implementation file for the b64 library
 42   */
 43  
 44  #include "SecBase64.h"
 45  
 46  #include <security_utilities/simulatecrash_assert.h>
 47  #include <string.h>
 48  #include <stdbool.h>
 49  
 50  /* /////////////////////////////////////////////////////////////////////////////
 51   * Constants and definitions
 52   */
 53  
 54  #ifndef B64_DOCUMENTATION_SKIP_SECTION
 55  # define NUM_PLAIN_DATA_BYTES        (3)
 56  # define NUM_ENCODED_DATA_BYTES      (4)
 57  #endif /* !B64_DOCUMENTATION_SKIP_SECTION */
 58  
 59  /* /////////////////////////////////////////////////////////////////////////////
 60   * Warnings
 61   */
 62  
 63  #if defined(_MSC_VER) && \
 64      _MSC_VER < 1000
 65  # pragma warning(disable : 4705)
 66  #endif /* _MSC_VER < 1000 */
 67  
 68  /* /////////////////////////////////////////////////////////////////////////////
 69   * Data
 70   */
 71  
 72  static const char           b64_chars[] =   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 73  
 74  static const signed char    b64_indexes[]   =   
 75  {
 76      /* 0 - 31 / 0x00 - 0x1f */
 77          -1, -1, -1, -1, -1, -1, -1, -1  
 78      ,   -1, -1, -1, -1, -1, -1, -1, -1  
 79      ,   -1, -1, -1, -1, -1, -1, -1, -1  
 80      ,   -1, -1, -1, -1, -1, -1, -1, -1
 81      /* 32 - 63 / 0x20 - 0x3f */
 82      ,   -1, -1, -1, -1, -1, -1, -1, -1  
 83      ,   -1, -1, -1, 62, -1, -1, -1, 63  /* ... , '+', ... '/' */
 84      ,   52, 53, 54, 55, 56, 57, 58, 59  /* '0' - '7' */
 85      ,   60, 61, -1, -1, -1, -1, -1, -1  /* '8', '9', ... */
 86      /* 64 - 95 / 0x40 - 0x5f */
 87      ,   -1, 0,  1,  2,  3,  4,  5,  6   /* ..., 'A' - 'G' */
 88      ,   7,  8,  9,  10, 11, 12, 13, 14  /* 'H' - 'O' */
 89      ,   15, 16, 17, 18, 19, 20, 21, 22  /* 'P' - 'W' */
 90      ,   23, 24, 25, -1, -1, -1, -1, -1  /* 'X', 'Y', 'Z', ... */
 91      /* 96 - 127 / 0x60 - 0x7f */
 92      ,   -1, 26, 27, 28, 29, 30, 31, 32  /* ..., 'a' - 'g' */
 93      ,   33, 34, 35, 36, 37, 38, 39, 40  /* 'h' - 'o' */
 94      ,   41, 42, 43, 44, 45, 46, 47, 48  /* 'p' - 'w' */
 95      ,   49, 50, 51, -1, -1, -1, -1, -1  /* 'x', 'y', 'z', ... */
 96  
 97      ,   -1, -1, -1, -1, -1, -1, -1, -1  
 98      ,   -1, -1, -1, -1, -1, -1, -1, -1  
 99      ,   -1, -1, -1, -1, -1, -1, -1, -1  
100      ,   -1, -1, -1, -1, -1, -1, -1, -1  
101  
102      ,   -1, -1, -1, -1, -1, -1, -1, -1  
103      ,   -1, -1, -1, -1, -1, -1, -1, -1  
104      ,   -1, -1, -1, -1, -1, -1, -1, -1  
105      ,   -1, -1, -1, -1, -1, -1, -1, -1  
106  
107      ,   -1, -1, -1, -1, -1, -1, -1, -1  
108      ,   -1, -1, -1, -1, -1, -1, -1, -1  
109      ,   -1, -1, -1, -1, -1, -1, -1, -1  
110      ,   -1, -1, -1, -1, -1, -1, -1, -1  
111  
112      ,   -1, -1, -1, -1, -1, -1, -1, -1  
113      ,   -1, -1, -1, -1, -1, -1, -1, -1  
114      ,   -1, -1, -1, -1, -1, -1, -1, -1  
115      ,   -1, -1, -1, -1, -1, -1, -1, -1  
116  };
117  
118  /* /////////////////////////////////////////////////////////////////////////////
119   * Helper functions
120   */
121  
122  /** This function reads in 3 bytes at a time, and translates them into 4
123   * characters.
124   */
125  static size_t SecBase64Encode_(  unsigned char const *src
126                          ,   size_t              srcSize
127                          ,   char *const         dest
128                          ,   size_t              destLen
129                          ,   unsigned            lineLen
130                          ,   SecBase64Result     *rc)
131  {
132      size_t  total   =   ((srcSize + (NUM_PLAIN_DATA_BYTES - 1)) / NUM_PLAIN_DATA_BYTES) * NUM_ENCODED_DATA_BYTES;
133  
134      assert(NULL != rc);
135      *rc = kSecB64_R_OK;
136  
137      if(lineLen > 0)
138      {
139          size_t    numLines    =   (total + (lineLen - 1)) / lineLen;
140  
141          total += 2 * (numLines - 1);
142      }
143  
144      if(NULL == dest)
145      {
146          return total;
147      }
148      else if(destLen < total)
149      {
150          *rc = kSecB64_R_INSUFFICIENT_BUFFER;
151  
152          return 0;
153      }
154      else
155      {
156          char    *p      =   dest;
157          char    *end    =   dest + destLen;
158          size_t  len     =   0;
159  
160          for(; NUM_PLAIN_DATA_BYTES <= srcSize; srcSize -= NUM_PLAIN_DATA_BYTES)
161          {
162              char    characters[NUM_ENCODED_DATA_BYTES];
163  
164              /* 
165               * 
166               * |       0       |       1       |       2       |
167               *
168               * |               |               |               |
169               * |       |       |       |       |       |       |
170               * |   |   |   |   |   |   |   |   |   |   |   |   |
171               * | | | | | | | | | | | | | | | | | | | | | | | | |
172               * 
173               * |     0     |     1     |     2     |     3     |
174               * 
175               */
176  
177              /* characters[0] is the 6 left-most bits of src[0] */
178              characters[0] = (char)((src[0] & 0xfc) >> 2);
179              /* characters[0] is the right-most 2 bits of src[0] and the left-most 4 bits of src[1] */
180              characters[1] = (char)(((src[0] & 0x03) << 4) + ((src[1] & 0xf0) >> 4));
181              /* characters[0] is the right-most 4 bits of src[1] and the 2 left-most bits of src[2] */
182              characters[2] = (char)(((src[1] & 0x0f) << 2) + ((src[2] & 0xc0) >> 6));
183              /* characters[3] is the right-most 6 bits of src[2] */
184              characters[3] = (char)(src[2] & 0x3f);
185  
186  #ifndef __WATCOMC__
187              assert(characters[0] >= 0 && characters[0] < 64);
188              assert(characters[1] >= 0 && characters[1] < 64);
189              assert(characters[2] >= 0 && characters[2] < 64);
190              assert(characters[3] >= 0 && characters[3] < 64);
191  #endif /* __WATCOMC__ */
192  
193              src += NUM_PLAIN_DATA_BYTES;
194              *p++ = b64_chars[(unsigned char)characters[0]];
195              assert(NULL != strchr(b64_chars, *(p-1)));
196              ++len;
197              assert(len != lineLen);
198  
199              *p++ = b64_chars[(unsigned char)characters[1]];
200              assert(NULL != strchr(b64_chars, *(p-1)));
201              ++len;
202              assert(len != lineLen);
203  
204              *p++ = b64_chars[(unsigned char)characters[2]];
205              assert(NULL != strchr(b64_chars, *(p-1)));
206              ++len;
207              assert(len != lineLen);
208  
209              *p++ = b64_chars[(unsigned char)characters[3]];
210              assert(NULL != strchr(b64_chars, *(p-1)));
211  
212              if( ++len == lineLen &&
213                  p != end)
214              {
215                  *p++ = '\r';
216                  *p++ = '\n';
217                  len = 0;
218              }
219          }
220  
221          if(0 != srcSize)
222          {
223              /* Deal with the overspill, by boosting it up to three bytes (using 0s)
224               * and then appending '=' for any missing characters.
225               *
226               * This is done into a temporary buffer, so we can call ourselves and
227               * have the output continue to be written direct to the destination.
228               */
229  
230              unsigned char   dummy[NUM_PLAIN_DATA_BYTES];
231              size_t          i;
232  
233              for(i = 0; i < srcSize; ++i)
234              {
235                  dummy[i] = *src++;
236              }
237  
238              for(; i < NUM_PLAIN_DATA_BYTES; ++i)
239              {
240                  dummy[i] = '\0';
241              }
242  
243              SecBase64Encode_(&dummy[0], NUM_PLAIN_DATA_BYTES, p, NUM_ENCODED_DATA_BYTES * (1 + 2), 0, rc);
244  
245              for(p += 1 + srcSize; srcSize++ < NUM_PLAIN_DATA_BYTES; )
246              {
247                  *p++ = '=';
248              }
249          }
250  
251          return total;
252      }
253  }
254  
255  /** This function reads in a character string in 4-character chunks, and writes 
256   * out the converted form in 3-byte chunks to the destination.
257   */
258  static size_t SecBase64Decode_(  char const      *src
259                          ,   size_t          srcLen
260                          ,   unsigned char   *dest
261                          ,   size_t          destSize
262                          ,   unsigned        flags
263                          ,   char const      **badChar
264                          ,   SecBase64Result *rc)
265  {
266      const size_t    wholeChunks     =   (srcLen / NUM_ENCODED_DATA_BYTES);
267      const size_t    remainderBytes  =   (srcLen % NUM_ENCODED_DATA_BYTES);
268      size_t          maxTotal        =   (wholeChunks + (0 != remainderBytes)) * NUM_PLAIN_DATA_BYTES;
269      unsigned char   *dest_          =   dest;
270  
271      ((void)remainderBytes);
272  
273      assert(NULL != badChar);
274      assert(NULL != rc);
275  
276      *badChar    =   NULL;
277      *rc         =   kSecB64_R_OK;
278  
279      if(NULL == dest)
280      {
281          return maxTotal;
282      }
283      else if(destSize < maxTotal)
284      {
285          *rc = kSecB64_R_INSUFFICIENT_BUFFER;
286  
287          return 0;
288      }
289      else
290      {
291          /* Now we iterate through the src, collecting together four characters
292           * at a time from the Base-64 alphabet, until the end-point is reached.
293           *
294           * 
295           */
296  
297          char const          *begin      =   src;
298          char const  *const  end         =   begin + srcLen;
299          size_t              currIndex   =   0;
300          size_t              numPads     =   0;
301          signed char         indexes[NUM_ENCODED_DATA_BYTES];    /* 4 */
302  
303          for(; begin != end; ++begin)
304          {
305              const char  ch  =   *begin;
306  
307              if('=' == ch)
308              {
309                  assert(currIndex < NUM_ENCODED_DATA_BYTES);
310  
311                  indexes[currIndex++] = '\0';
312  
313                  ++numPads;
314              }
315              else
316              {
317                  signed char ix   =   b64_indexes[(unsigned char)ch];
318  
319                  if(-1 == ix)
320                  {
321                      switch(ch)
322                      {
323                          case    ' ':
324                          case    '\t':
325                          case    '\b':
326                          case    '\v':
327                              if(kSecB64_F_STOP_ON_UNEXPECTED_WS & flags)
328                              {
329                                  *rc         =   kSecB64_R_DATA_ERROR;
330                                  *badChar    =   begin;
331                                  return 0;
332                              }
333                              else
334                              {
335                                  /* Fall through */
336                              }
337                          case    '\r':
338                          case    '\n':
339                              continue;
340                          default:
341                              if(kSecB64_F_STOP_ON_UNKNOWN_CHAR & flags)
342                              {
343                                  *rc         =   kSecB64_R_DATA_ERROR;
344                                  *badChar    =   begin;
345                                  return 0;
346                              }
347                              else
348                              {
349                                  continue;
350                              }
351                      }
352                  }
353                  else
354                  {
355                      numPads = 0;
356  
357                      assert(currIndex < NUM_ENCODED_DATA_BYTES);
358  
359                      indexes[currIndex++] = ix;
360                  }
361              }
362  
363              if(NUM_ENCODED_DATA_BYTES == currIndex)
364              {
365                  unsigned char   bytes[NUM_PLAIN_DATA_BYTES];        /* 3 */
366  
367                  bytes[0] = (unsigned char)((indexes[0] << 2) + ((indexes[1] & 0x30) >> 4));
368  
369                  currIndex = 0;
370  
371                  *dest++ = bytes[0];
372                  if(2 != numPads)
373                  {
374                      bytes[1] = (unsigned char)(((indexes[1] & 0xf) << 4) + ((indexes[2] & 0x3c) >> 2));
375  
376                      *dest++ = bytes[1];
377  
378                      if(1 != numPads)
379                      {
380                          bytes[2] = (unsigned char)(((indexes[2] & 0x3) << 6) + indexes[3]);
381  
382                          *dest++ = bytes[2];
383                      }
384                  }
385                  if(0 != numPads)
386                  {
387                      break;
388                  }
389              }
390          }
391  
392          return (size_t)(dest - dest_);
393      }
394  }
395  
396  /* /////////////////////////////////////////////////////////////////////////////
397   * API functions
398   */
399  
400  size_t SecBase64Encode(void const *src, size_t srcSize, char *dest, size_t destLen)
401  {
402      /* Use Null Object (Variable) here for rc, so do not need to check
403       * elsewhere.
404       */
405      SecBase64Result  rc_;
406  
407      return SecBase64Encode_((unsigned char const*)src, srcSize, dest, destLen, 0, &rc_);
408  }
409  
410  size_t SecBase64Encode2( void const *src
411                  ,   size_t          srcSize
412                  ,   char            *dest
413                  ,   size_t          destLen
414                  ,   unsigned        flags
415                  ,   int             lineLen /* = -1 */
416                  ,   SecBase64Result *rc     /* = NULL */)
417  {
418      /* Use Null Object (Variable) here for rc, so do not need to check
419       * elsewhere
420       */
421      SecBase64Result  rc_;
422      if(NULL == rc)
423      {
424          rc = &rc_;
425      }
426  
427      switch(kSecB64_F_LINE_LEN_MASK & flags)
428      {
429          case    kSecB64_F_LINE_LEN_USE_PARAM:
430              if(lineLen >= 0)
431              {
432                  break;
433              }
434              /* Fall through to 64 */
435          case    kSecB64_F_LINE_LEN_64:
436              lineLen = 64;
437              break;
438          case    kSecB64_F_LINE_LEN_76:
439              lineLen = 76;
440              break;
441          default:
442              assert(false); // "Bad line length flag specified to SecBase64Encode2()"
443          case    kSecB64_F_LINE_LEN_INFINITE:
444              lineLen = 0;
445              break;
446      }
447  
448      assert(0 == (lineLen % 4));
449  
450      return SecBase64Encode_((unsigned char const*)src, srcSize, dest, destLen, (unsigned)lineLen, rc);
451  }
452  
453  size_t SecBase64Decode(char const *src, size_t srcLen, void *dest, size_t destSize)
454  {
455      /* Use Null Object (Variable) here for rc and badChar, so do not need to
456       * check elsewhere.
457       */
458      char const  *badChar_;
459      SecBase64Result      rc_;
460  
461      return SecBase64Decode_(src, srcLen, (unsigned char*)dest, destSize, kSecB64_F_STOP_ON_NOTHING, &badChar_, &rc_);
462  }
463  
464  size_t SecBase64Decode2( char const  *src
465                  ,   size_t      srcLen
466                  ,   void        *dest
467                  ,   size_t      destSize
468                  ,   unsigned    flags
469                  ,   char const  **badChar   /* = NULL */
470                  ,   SecBase64Result      *rc         /* = NULL */)
471  {
472      char const      *badChar_;
473      SecBase64Result          rc_;
474  
475      /* Use Null Object (Variable) here for rc and badChar, so do not need to
476       * check elsewhere.
477       */
478      if(NULL == badChar)
479      {
480          badChar = &badChar_;
481      }
482      if(NULL == rc)
483      {
484          rc = &rc_;
485      }
486  
487      return SecBase64Decode_(src, srcLen, (unsigned char*)dest, destSize, flags, badChar, rc);
488  }
489  
490  /* ////////////////////////////////////////////////////////////////////////// */