]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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 "SecBase64P.h" | |
45 | ||
46 | #include <assert.h> | |
47 | #include <string.h> | |
48 | ||
49 | /* ///////////////////////////////////////////////////////////////////////////// | |
50 | * Constants and definitions | |
51 | */ | |
52 | ||
53 | #ifndef B64_DOCUMENTATION_SKIP_SECTION | |
54 | # define NUM_PLAIN_DATA_BYTES (3) | |
55 | # define NUM_ENCODED_DATA_BYTES (4) | |
56 | #endif /* !B64_DOCUMENTATION_SKIP_SECTION */ | |
57 | ||
58 | /* ///////////////////////////////////////////////////////////////////////////// | |
59 | * Warnings | |
60 | */ | |
61 | ||
62 | #if defined(_MSC_VER) && \ | |
63 | _MSC_VER < 1000 | |
64 | # pragma warning(disable : 4705) | |
65 | #endif /* _MSC_VER < 1000 */ | |
66 | ||
67 | /* ///////////////////////////////////////////////////////////////////////////// | |
68 | * Data | |
69 | */ | |
70 | ||
71 | static const char b64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
72 | ||
73 | static const signed char b64_indexes[] = | |
74 | { | |
75 | /* 0 - 31 / 0x00 - 0x1f */ | |
76 | -1, -1, -1, -1, -1, -1, -1, -1 | |
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 | /* 32 - 63 / 0x20 - 0x3f */ | |
81 | , -1, -1, -1, -1, -1, -1, -1, -1 | |
82 | , -1, -1, -1, 62, -1, -1, -1, 63 /* ... , '+', ... '/' */ | |
83 | , 52, 53, 54, 55, 56, 57, 58, 59 /* '0' - '7' */ | |
84 | , 60, 61, -1, -1, -1, -1, -1, -1 /* '8', '9', ... */ | |
85 | /* 64 - 95 / 0x40 - 0x5f */ | |
86 | , -1, 0, 1, 2, 3, 4, 5, 6 /* ..., 'A' - 'G' */ | |
87 | , 7, 8, 9, 10, 11, 12, 13, 14 /* 'H' - 'O' */ | |
88 | , 15, 16, 17, 18, 19, 20, 21, 22 /* 'P' - 'W' */ | |
89 | , 23, 24, 25, -1, -1, -1, -1, -1 /* 'X', 'Y', 'Z', ... */ | |
90 | /* 96 - 127 / 0x60 - 0x7f */ | |
91 | , -1, 26, 27, 28, 29, 30, 31, 32 /* ..., 'a' - 'g' */ | |
92 | , 33, 34, 35, 36, 37, 38, 39, 40 /* 'h' - 'o' */ | |
93 | , 41, 42, 43, 44, 45, 46, 47, 48 /* 'p' - 'w' */ | |
94 | , 49, 50, 51, -1, -1, -1, -1, -1 /* 'x', 'y', 'z', ... */ | |
95 | ||
96 | , -1, -1, -1, -1, -1, -1, -1, -1 | |
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 | ||
101 | , -1, -1, -1, -1, -1, -1, -1, -1 | |
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 | ||
106 | , -1, -1, -1, -1, -1, -1, -1, -1 | |
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 | ||
111 | , -1, -1, -1, -1, -1, -1, -1, -1 | |
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 | }; | |
116 | ||
117 | /* ///////////////////////////////////////////////////////////////////////////// | |
118 | * Helper functions | |
119 | */ | |
120 | ||
121 | /** This function reads in 3 bytes at a time, and translates them into 4 | |
122 | * characters. | |
123 | */ | |
124 | static size_t SecBase64Encode_( unsigned char const *src | |
125 | , size_t srcSize | |
126 | , char *const dest | |
127 | , size_t destLen | |
128 | , unsigned lineLen | |
129 | , SecBase64Result *rc) | |
130 | { | |
131 | size_t total = ((srcSize + (NUM_PLAIN_DATA_BYTES - 1)) / NUM_PLAIN_DATA_BYTES) * NUM_ENCODED_DATA_BYTES; | |
132 | ||
133 | assert(NULL != rc); | |
134 | *rc = kSecB64_R_OK; | |
135 | ||
136 | if(lineLen > 0) | |
137 | { | |
427c49bc | 138 | size_t numLines = (total + (lineLen - 1)) / lineLen; |
b1ab9ed8 A |
139 | |
140 | total += 2 * (numLines - 1); | |
141 | } | |
142 | ||
143 | if(NULL == dest) | |
144 | { | |
145 | return total; | |
146 | } | |
147 | else if(destLen < total) | |
148 | { | |
149 | *rc = kSecB64_R_INSUFFICIENT_BUFFER; | |
150 | ||
151 | return 0; | |
152 | } | |
153 | else | |
154 | { | |
155 | char *p = dest; | |
156 | char *end = dest + destLen; | |
157 | size_t len = 0; | |
158 | ||
159 | for(; NUM_PLAIN_DATA_BYTES <= srcSize; srcSize -= NUM_PLAIN_DATA_BYTES) | |
160 | { | |
161 | char characters[NUM_ENCODED_DATA_BYTES]; | |
162 | ||
163 | /* | |
164 | * | |
165 | * | 0 | 1 | 2 | | |
166 | * | |
167 | * | | | | | |
168 | * | | | | | | | | |
169 | * | | | | | | | | | | | | | | |
170 | * | | | | | | | | | | | | | | | | | | | | | | | | | | |
171 | * | |
172 | * | 0 | 1 | 2 | 3 | | |
173 | * | |
174 | */ | |
175 | ||
176 | /* characters[0] is the 6 left-most bits of src[0] */ | |
177 | characters[0] = (char)((src[0] & 0xfc) >> 2); | |
178 | /* characters[0] is the right-most 2 bits of src[0] and the left-most 4 bits of src[1] */ | |
179 | characters[1] = (char)(((src[0] & 0x03) << 4) + ((src[1] & 0xf0) >> 4)); | |
180 | /* characters[0] is the right-most 4 bits of src[1] and the 2 left-most bits of src[2] */ | |
181 | characters[2] = (char)(((src[1] & 0x0f) << 2) + ((src[2] & 0xc0) >> 6)); | |
182 | /* characters[3] is the right-most 6 bits of src[2] */ | |
183 | characters[3] = (char)(src[2] & 0x3f); | |
184 | ||
185 | #ifndef __WATCOMC__ | |
186 | assert(characters[0] >= 0 && characters[0] < 64); | |
187 | assert(characters[1] >= 0 && characters[1] < 64); | |
188 | assert(characters[2] >= 0 && characters[2] < 64); | |
189 | assert(characters[3] >= 0 && characters[3] < 64); | |
190 | #endif /* __WATCOMC__ */ | |
191 | ||
192 | src += NUM_PLAIN_DATA_BYTES; | |
193 | *p++ = b64_chars[(unsigned char)characters[0]]; | |
194 | assert(NULL != strchr(b64_chars, *(p-1))); | |
195 | ++len; | |
196 | assert(len != lineLen); | |
197 | ||
198 | *p++ = b64_chars[(unsigned char)characters[1]]; | |
199 | assert(NULL != strchr(b64_chars, *(p-1))); | |
200 | ++len; | |
201 | assert(len != lineLen); | |
202 | ||
203 | *p++ = b64_chars[(unsigned char)characters[2]]; | |
204 | assert(NULL != strchr(b64_chars, *(p-1))); | |
205 | ++len; | |
206 | assert(len != lineLen); | |
207 | ||
208 | *p++ = b64_chars[(unsigned char)characters[3]]; | |
209 | assert(NULL != strchr(b64_chars, *(p-1))); | |
210 | ||
211 | if( ++len == lineLen && | |
212 | p != end) | |
213 | { | |
214 | *p++ = '\r'; | |
215 | *p++ = '\n'; | |
216 | len = 0; | |
217 | } | |
218 | } | |
219 | ||
220 | if(0 != srcSize) | |
221 | { | |
222 | /* Deal with the overspill, by boosting it up to three bytes (using 0s) | |
223 | * and then appending '=' for any missing characters. | |
224 | * | |
225 | * This is done into a temporary buffer, so we can call ourselves and | |
226 | * have the output continue to be written direct to the destination. | |
227 | */ | |
228 | ||
229 | unsigned char dummy[NUM_PLAIN_DATA_BYTES]; | |
230 | size_t i; | |
231 | ||
232 | for(i = 0; i < srcSize; ++i) | |
233 | { | |
234 | dummy[i] = *src++; | |
235 | } | |
236 | ||
237 | for(; i < NUM_PLAIN_DATA_BYTES; ++i) | |
238 | { | |
239 | dummy[i] = '\0'; | |
240 | } | |
241 | ||
242 | SecBase64Encode_(&dummy[0], NUM_PLAIN_DATA_BYTES, p, NUM_ENCODED_DATA_BYTES * (1 + 2), 0, rc); | |
243 | ||
244 | for(p += 1 + srcSize; srcSize++ < NUM_PLAIN_DATA_BYTES; ) | |
245 | { | |
246 | *p++ = '='; | |
247 | } | |
248 | } | |
249 | ||
250 | return total; | |
251 | } | |
252 | } | |
253 | ||
254 | /** This function reads in a character string in 4-character chunks, and writes | |
255 | * out the converted form in 3-byte chunks to the destination. | |
256 | */ | |
257 | static size_t SecBase64Decode_( char const *src | |
258 | , size_t srcLen | |
259 | , unsigned char *dest | |
260 | , size_t destSize | |
261 | , unsigned flags | |
262 | , char const **badChar | |
263 | , SecBase64Result *rc) | |
264 | { | |
265 | const size_t wholeChunks = (srcLen / NUM_ENCODED_DATA_BYTES); | |
266 | const size_t remainderBytes = (srcLen % NUM_ENCODED_DATA_BYTES); | |
267 | size_t maxTotal = (wholeChunks + (0 != remainderBytes)) * NUM_PLAIN_DATA_BYTES; | |
268 | unsigned char *dest_ = dest; | |
269 | ||
270 | ((void)remainderBytes); | |
271 | ||
272 | assert(NULL != badChar); | |
273 | assert(NULL != rc); | |
274 | ||
275 | *badChar = NULL; | |
276 | *rc = kSecB64_R_OK; | |
277 | ||
278 | if(NULL == dest) | |
279 | { | |
280 | return maxTotal; | |
281 | } | |
282 | else if(destSize < maxTotal) | |
283 | { | |
284 | *rc = kSecB64_R_INSUFFICIENT_BUFFER; | |
285 | ||
286 | return 0; | |
287 | } | |
288 | else | |
289 | { | |
290 | /* Now we iterate through the src, collecting together four characters | |
291 | * at a time from the Base-64 alphabet, until the end-point is reached. | |
292 | * | |
293 | * | |
294 | */ | |
295 | ||
296 | char const *begin = src; | |
297 | char const *const end = begin + srcLen; | |
298 | size_t currIndex = 0; | |
299 | size_t numPads = 0; | |
300 | signed char indexes[NUM_ENCODED_DATA_BYTES]; /* 4 */ | |
301 | ||
302 | for(; begin != end; ++begin) | |
303 | { | |
304 | const char ch = *begin; | |
305 | ||
306 | if('=' == ch) | |
307 | { | |
308 | assert(currIndex < NUM_ENCODED_DATA_BYTES); | |
309 | ||
310 | indexes[currIndex++] = '\0'; | |
311 | ||
312 | ++numPads; | |
313 | } | |
314 | else | |
315 | { | |
316 | signed char ix = b64_indexes[(unsigned char)ch]; | |
317 | ||
318 | if(-1 == ix) | |
319 | { | |
320 | switch(ch) | |
321 | { | |
322 | case ' ': | |
323 | case '\t': | |
324 | case '\b': | |
325 | case '\v': | |
326 | if(kSecB64_F_STOP_ON_UNEXPECTED_WS & flags) | |
327 | { | |
328 | *rc = kSecB64_R_DATA_ERROR; | |
329 | *badChar = begin; | |
330 | return 0; | |
331 | } | |
332 | else | |
333 | { | |
334 | /* Fall through */ | |
335 | } | |
336 | case '\r': | |
337 | case '\n': | |
338 | continue; | |
339 | default: | |
340 | if(kSecB64_F_STOP_ON_UNKNOWN_CHAR & flags) | |
341 | { | |
342 | *rc = kSecB64_R_DATA_ERROR; | |
343 | *badChar = begin; | |
344 | return 0; | |
345 | } | |
346 | else | |
347 | { | |
348 | continue; | |
349 | } | |
350 | } | |
351 | } | |
352 | else | |
353 | { | |
354 | numPads = 0; | |
355 | ||
356 | assert(currIndex < NUM_ENCODED_DATA_BYTES); | |
357 | ||
358 | indexes[currIndex++] = ix; | |
359 | } | |
360 | } | |
361 | ||
362 | if(NUM_ENCODED_DATA_BYTES == currIndex) | |
363 | { | |
364 | unsigned char bytes[NUM_PLAIN_DATA_BYTES]; /* 3 */ | |
365 | ||
366 | bytes[0] = (unsigned char)((indexes[0] << 2) + ((indexes[1] & 0x30) >> 4)); | |
367 | ||
368 | currIndex = 0; | |
369 | ||
370 | *dest++ = bytes[0]; | |
371 | if(2 != numPads) | |
372 | { | |
373 | bytes[1] = (unsigned char)(((indexes[1] & 0xf) << 4) + ((indexes[2] & 0x3c) >> 2)); | |
374 | ||
375 | *dest++ = bytes[1]; | |
376 | ||
377 | if(1 != numPads) | |
378 | { | |
379 | bytes[2] = (unsigned char)(((indexes[2] & 0x3) << 6) + indexes[3]); | |
380 | ||
381 | *dest++ = bytes[2]; | |
382 | } | |
383 | } | |
384 | if(0 != numPads) | |
385 | { | |
386 | break; | |
387 | } | |
388 | } | |
389 | } | |
390 | ||
391 | return (size_t)(dest - dest_); | |
392 | } | |
393 | } | |
394 | ||
395 | /* ///////////////////////////////////////////////////////////////////////////// | |
396 | * API functions | |
397 | */ | |
398 | ||
399 | size_t SecBase64Encode(void const *src, size_t srcSize, char *dest, size_t destLen) | |
400 | { | |
401 | /* Use Null Object (Variable) here for rc, so do not need to check | |
402 | * elsewhere. | |
403 | */ | |
404 | SecBase64Result rc_; | |
405 | ||
406 | return SecBase64Encode_((unsigned char const*)src, srcSize, dest, destLen, 0, &rc_); | |
407 | } | |
408 | ||
409 | size_t SecBase64Encode2( void const *src | |
410 | , size_t srcSize | |
411 | , char *dest | |
412 | , size_t destLen | |
413 | , unsigned flags | |
414 | , int lineLen /* = -1 */ | |
415 | , SecBase64Result *rc /* = NULL */) | |
416 | { | |
417 | /* Use Null Object (Variable) here for rc, so do not need to check | |
418 | * elsewhere | |
419 | */ | |
420 | SecBase64Result rc_; | |
421 | if(NULL == rc) | |
422 | { | |
423 | rc = &rc_; | |
424 | } | |
425 | ||
426 | switch(kSecB64_F_LINE_LEN_MASK & flags) | |
427 | { | |
428 | case kSecB64_F_LINE_LEN_USE_PARAM: | |
429 | if(lineLen >= 0) | |
430 | { | |
431 | break; | |
432 | } | |
433 | /* Fall through to 64 */ | |
434 | case kSecB64_F_LINE_LEN_64: | |
435 | lineLen = 64; | |
436 | break; | |
437 | case kSecB64_F_LINE_LEN_76: | |
438 | lineLen = 76; | |
439 | break; | |
440 | default: | |
441 | assert(!"Bad line length flag specified to SecBase64Encode2()"); | |
442 | case kSecB64_F_LINE_LEN_INFINITE: | |
443 | lineLen = 0; | |
444 | break; | |
445 | } | |
446 | ||
447 | assert(0 == (lineLen % 4)); | |
448 | ||
449 | return SecBase64Encode_((unsigned char const*)src, srcSize, dest, destLen, (unsigned)lineLen, rc); | |
450 | } | |
451 | ||
452 | size_t SecBase64Decode(char const *src, size_t srcLen, void *dest, size_t destSize) | |
453 | { | |
454 | /* Use Null Object (Variable) here for rc and badChar, so do not need to | |
455 | * check elsewhere. | |
456 | */ | |
457 | char const *badChar_; | |
458 | SecBase64Result rc_; | |
459 | ||
460 | return SecBase64Decode_(src, srcLen, (unsigned char*)dest, destSize, kSecB64_F_STOP_ON_NOTHING, &badChar_, &rc_); | |
461 | } | |
462 | ||
463 | size_t SecBase64Decode2( char const *src | |
464 | , size_t srcLen | |
465 | , void *dest | |
466 | , size_t destSize | |
467 | , unsigned flags | |
468 | , char const **badChar /* = NULL */ | |
469 | , SecBase64Result *rc /* = NULL */) | |
470 | { | |
471 | char const *badChar_; | |
472 | SecBase64Result rc_; | |
473 | ||
474 | /* Use Null Object (Variable) here for rc and badChar, so do not need to | |
475 | * check elsewhere. | |
476 | */ | |
477 | if(NULL == badChar) | |
478 | { | |
479 | badChar = &badChar_; | |
480 | } | |
481 | if(NULL == rc) | |
482 | { | |
483 | rc = &rc_; | |
484 | } | |
485 | ||
486 | return SecBase64Decode_(src, srcLen, (unsigned char*)dest, destSize, flags, badChar, rc); | |
487 | } | |
488 | ||
489 | /* ////////////////////////////////////////////////////////////////////////// */ |