VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/string/base64.cpp@ 51792

Last change on this file since 51792 was 51792, checked in by vboxsync, 10 years ago

Better fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/* $Id: base64.cpp 51792 2014-07-01 20:34:29Z vboxsync $ */
2/** @file
3 * IPRT - Base64, MIME content transfer encoding.
4 */
5
6/*
7 * Copyright (C) 2009-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/base64.h>
32#include "internal/iprt.h"
33
34#include <iprt/assert.h>
35#include <iprt/err.h>
36#include <iprt/ctype.h>
37#include <iprt/string.h>
38#ifdef RT_STRICT
39# include <iprt/asm.h>
40#endif
41
42
43/*******************************************************************************
44* Defined Constants And Macros *
45*******************************************************************************/
46/** The line length used for encoding. */
47#define RTBASE64_LINE_LEN 64
48
49/** @name Special g_au8CharToVal values
50 * @{ */
51#define BASE64_SPACE 0xc0
52#define BASE64_PAD 0xe0
53#define BASE64_INVALID 0xff
54/** @} */
55
56
57/*******************************************************************************
58* Global Variables *
59*******************************************************************************/
60/** Base64 character to value. (RFC 2045)
61 * ASSUMES ASCII / UTF-8. */
62static const uint8_t g_au8CharToVal[256] =
63{
64 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, /* 0x00..0x0f */
65 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10..0x1f */
66 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63, /* 0x20..0x2f */
67 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, /* 0x30..0x3f */
68 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40..0x4f */
69 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50..0x5f */
70 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60..0x6f */
71 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70..0x7f */
72 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80..0x8f */
73 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90..0x9f */
74 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0..0xaf */
75 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0..0xbf */
76 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0..0xcf */
77 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0..0xdf */
78 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0..0xef */
79 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* 0xf0..0xff */
80};
81
82/** Value to Base64 character. (RFC 2045) */
83static const char g_szValToChar[64+1] =
84 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
85
86
87#ifdef RT_STRICT
88/**
89 * Perform table sanity checks on the first call.
90 */
91static void rtBase64Sanity(void)
92{
93 static bool s_fSane = false;
94 if (RT_UNLIKELY(!s_fSane))
95 {
96 for (unsigned i = 0; i < 64; i++)
97 {
98 unsigned ch = g_szValToChar[i];
99 Assert(ch);
100 Assert(g_au8CharToVal[ch] == i);
101 }
102
103 for (unsigned i = 0; i < 256; i++)
104 {
105 uint8_t u8 = g_au8CharToVal[i];
106 Assert( ( u8 == BASE64_INVALID
107 && !RT_C_IS_ALNUM(i)
108 && !RT_C_IS_SPACE(i))
109 || ( u8 == BASE64_PAD
110 && i == '=')
111 || ( u8 == BASE64_SPACE
112 && ( RT_C_IS_SPACE(i)
113 || (i == '\r'))) /* Carriage return is handled as a space character as well. */
114 || ( u8 < 64
115 && (unsigned)g_szValToChar[u8] == i));
116 }
117 ASMAtomicWriteBool(&s_fSane, true);
118 }
119}
120#endif /* RT_STRICT */
121
122
123RTDECL(ssize_t) RTBase64DecodedSizeEx(const char *pszString, size_t cchStringMax, char **ppszEnd)
124{
125#ifdef RT_STRICT
126 rtBase64Sanity();
127#endif
128
129 /*
130 * Walk the string until a non-encoded or non-space character is encountered.
131 */
132 uint32_t c6Bits = 0;
133 uint8_t u8 = BASE64_INVALID;
134 unsigned ch = 0;
135 AssertCompile(sizeof(char) == sizeof(uint8_t));
136
137 while (cchStringMax > 0 && (ch = *pszString))
138 {
139 u8 = g_au8CharToVal[ch];
140 if (u8 < 64)
141 c6Bits++;
142 else if (RT_UNLIKELY(u8 != BASE64_SPACE))
143 break;
144
145 /* advance */
146 pszString++;
147 cchStringMax--;
148 }
149
150 /*
151 * Padding can only be found at the end and there is
152 * only 1 or 2 padding chars. Deal with it first.
153 */
154 unsigned cbPad = 0;
155 if (u8 == BASE64_PAD)
156 {
157 cbPad = 1;
158 c6Bits++;
159 pszString++;
160 cchStringMax--;
161 while (cchStringMax > 0 && (ch = *pszString))
162 {
163 u8 = g_au8CharToVal[ch];
164 if (u8 != BASE64_SPACE)
165 {
166 if (u8 != BASE64_PAD)
167 break;
168 c6Bits++;
169 cbPad++;
170 }
171 pszString++;
172 cchStringMax--;
173 }
174 if (cbPad >= 3)
175 return -1;
176 }
177
178 /*
179 * Invalid char and no where to indicate where the
180 * Base64 text ends? Return failure.
181 */
182 if ( u8 == BASE64_INVALID
183 && !ppszEnd
184 && ch)
185 return -1;
186
187 /*
188 * Recalc 6-bit to 8-bit and adjust for padding.
189 */
190 size_t cb;
191 if (c6Bits * 3 / 3 == c6Bits)
192 {
193 if ((c6Bits * 3 % 4) != 0)
194 return -1;
195 cb = c6Bits * 3 / 4;
196 }
197 else
198 {
199 if ((c6Bits * (uint64_t)3 % 4) != 0)
200 return -1;
201 cb = c6Bits * (uint64_t)3 / 4;
202 }
203
204 if (cb < cbPad)
205 return -1;
206 cb -= cbPad;
207
208 if (ppszEnd)
209 *ppszEnd = (char *)pszString;
210 return cb;
211}
212RT_EXPORT_SYMBOL(RTBase64DecodedSizeEx);
213
214
215RTDECL(ssize_t) RTBase64DecodedSize(const char *pszString, char **ppszEnd)
216{
217 return RTBase64DecodedSizeEx(pszString, RTSTR_MAX, ppszEnd);
218}
219RT_EXPORT_SYMBOL(RTBase64DecodedSize);
220
221
222RTDECL(int) RTBase64DecodeEx(const char *pszString, size_t cchStringMax, void *pvData, size_t cbData,
223 size_t *pcbActual, char **ppszEnd)
224{
225#ifdef RT_STRICT
226 rtBase64Sanity();
227#endif
228
229 /*
230 * Process input in groups of 4 input / 3 output chars.
231 */
232 uint8_t u8Trio[3] = { 0, 0, 0 }; /* shuts up gcc */
233 uint8_t *pbData = (uint8_t *)pvData;
234 unsigned ch;
235 uint8_t u8;
236 unsigned c6Bits = 0;
237 AssertCompile(sizeof(char) == sizeof(uint8_t));
238
239 for (;;)
240 {
241 /* The first 6-bit group. */
242 while ((u8 = g_au8CharToVal[ch = cchStringMax > 0 ? (uint8_t)*pszString : 0]) == BASE64_SPACE)
243 pszString++, cchStringMax--;
244 if (u8 >= 64)
245 {
246 c6Bits = 0;
247 break;
248 }
249 u8Trio[0] = u8 << 2;
250 pszString++;
251 cchStringMax--;
252
253 /* The second 6-bit group. */
254 while ((u8 = g_au8CharToVal[ch = cchStringMax > 0 ? (uint8_t)*pszString : 0]) == BASE64_SPACE)
255 pszString++, cchStringMax--;
256 if (u8 >= 64)
257 {
258 c6Bits = 1;
259 break;
260 }
261 u8Trio[0] |= u8 >> 4;
262 u8Trio[1] = u8 << 4;
263 pszString++;
264 cchStringMax--;
265
266 /* The third 6-bit group. */
267 u8 = BASE64_INVALID;
268 while ((u8 = g_au8CharToVal[ch = cchStringMax > 0 ? (uint8_t)*pszString : 0]) == BASE64_SPACE)
269 pszString++, cchStringMax--;
270 if (u8 >= 64)
271 {
272 c6Bits = 2;
273 break;
274 }
275 u8Trio[1] |= u8 >> 2;
276 u8Trio[2] = u8 << 6;
277 pszString++;
278 cchStringMax--;
279
280 /* The fourth 6-bit group. */
281 u8 = BASE64_INVALID;
282 while ((u8 = g_au8CharToVal[ch = cchStringMax > 0 ? (uint8_t)*pszString : 0]) == BASE64_SPACE)
283 pszString++, cchStringMax--;
284 if (u8 >= 64)
285 {
286 c6Bits = 3;
287 break;
288 }
289 u8Trio[2] |= u8;
290 pszString++;
291 cchStringMax--;
292
293 /* flush the trio */
294 if (cbData < 3)
295 return VERR_BUFFER_OVERFLOW;
296 cbData -= 3;
297 pbData[0] = u8Trio[0];
298 pbData[1] = u8Trio[1];
299 pbData[2] = u8Trio[2];
300 pbData += 3;
301 }
302
303 /*
304 * Padding can only be found at the end and there is
305 * only 1 or 2 padding chars. Deal with it first.
306 */
307 unsigned cbPad = 0;
308 if (u8 == BASE64_PAD)
309 {
310 cbPad = 1;
311 pszString++;
312 cchStringMax--;
313 while (cchStringMax > 0 && (ch = (uint8_t)*pszString))
314 {
315 u8 = g_au8CharToVal[ch];
316 if (u8 != BASE64_SPACE)
317 {
318 if (u8 != BASE64_PAD)
319 break;
320 cbPad++;
321 }
322 pszString++;
323 cchStringMax--;
324 }
325 if (cbPad >= 3)
326 return VERR_INVALID_BASE64_ENCODING;
327 }
328
329 /*
330 * Invalid char and no where to indicate where the
331 * Base64 text ends? Return failure.
332 */
333 if ( u8 == BASE64_INVALID
334 && !ppszEnd
335 && ch != '\0')
336 return VERR_INVALID_BASE64_ENCODING;
337
338 /*
339 * Check padding vs. pending sextets, if anything left to do finish it off.
340 */
341 if (c6Bits || cbPad)
342 {
343 if (c6Bits + cbPad != 4)
344 return VERR_INVALID_BASE64_ENCODING;
345
346 switch (c6Bits)
347 {
348 case 1:
349 u8Trio[1] = u8Trio[2] = 0;
350 break;
351 case 2:
352 u8Trio[2] = 0;
353 break;
354 case 3:
355 default:
356 break;
357 }
358 switch (3 - cbPad)
359 {
360 case 1:
361 if (cbData < 1)
362 return VERR_BUFFER_OVERFLOW;
363 cbData--;
364 pbData[0] = u8Trio[0];
365 pbData++;
366 break;
367
368 case 2:
369 if (cbData < 2)
370 return VERR_BUFFER_OVERFLOW;
371 cbData -= 2;
372 pbData[0] = u8Trio[0];
373 pbData[1] = u8Trio[1];
374 pbData += 2;
375 break;
376
377 default:
378 break;
379 }
380 }
381
382 /*
383 * Set optional return values and return successfully.
384 */
385 if (ppszEnd)
386 *ppszEnd = (char *)pszString;
387 if (pcbActual)
388 *pcbActual = pbData - (uint8_t *)pvData;
389 return VINF_SUCCESS;
390}
391RT_EXPORT_SYMBOL(RTBase64DecodeEx);
392
393
394RTDECL(int) RTBase64Decode(const char *pszString, void *pvData, size_t cbData, size_t *pcbActual, char **ppszEnd)
395{
396 return RTBase64DecodeEx(pszString, RTSTR_MAX, pvData, cbData, pcbActual, ppszEnd);
397}
398RT_EXPORT_SYMBOL(RTBase64Decode);
399
400
401/**
402 * Calculates the length of the Base64 encoding of a given number of bytes of
403 * data.
404 *
405 * This will assume line breaks every 64 chars. A RTBase64EncodedLengthEx
406 * function can be added if closer control over the output is found to be
407 * required.
408 *
409 * @returns The Base64 string length.
410 * @param cbData The number of bytes to encode.
411 */
412RTDECL(size_t) RTBase64EncodedLength(size_t cbData)
413{
414 if (cbData * 8 / 8 != cbData)
415 {
416 AssertReturn(sizeof(size_t) == sizeof(uint64_t), ~(size_t)0);
417 uint64_t cch = cbData * (uint64_t)8;
418 while (cch % 24)
419 cch += 8;
420 cch /= 6;
421
422 cch += ((cch - 1) / RTBASE64_LINE_LEN) * RTBASE64_EOL_SIZE;
423 return cch;
424 }
425
426 size_t cch = cbData * 8;
427 while (cch % 24)
428 cch += 8;
429 cch /= 6;
430
431 cch += ((cch - 1) / RTBASE64_LINE_LEN) * RTBASE64_EOL_SIZE;
432 return cch;
433}
434RT_EXPORT_SYMBOL(RTBase64EncodedLength);
435
436
437/**
438 * Encodes the specifed data into a Base64 string, the caller supplies the
439 * output buffer.
440 *
441 * This will make the same assumptions about line breaks and EOL size as
442 * RTBase64EncodedLength() does. A RTBase64EncodeEx function can be added if
443 * more strict control over the output formatting is found necessary.
444 *
445 * @returns IRPT status code.
446 * @retval VERR_BUFFER_OVERFLOW if the output buffer is too small. The buffer
447 * may contain an invalid Base64 string.
448 *
449 * @param pvData The data to encode.
450 * @param cbData The number of bytes to encode.
451 * @param pszBuf Where to put the Base64 string.
452 * @param cbBuf The size of the output buffer, including the terminator.
453 * @param pcchActual The actual number of characters returned.
454 */
455RTDECL(int) RTBase64Encode(const void *pvData, size_t cbData, char *pszBuf, size_t cbBuf, size_t *pcchActual)
456{
457 /*
458 * Process whole "trios" of input data.
459 */
460 uint8_t u8A;
461 uint8_t u8B;
462 uint8_t u8C;
463 size_t cbLineFeed = cbBuf - RTBASE64_LINE_LEN;
464 const uint8_t *pbSrc = (const uint8_t *)pvData;
465 char *pchDst = pszBuf;
466 while (cbData >= 3)
467 {
468 if (cbBuf < 4 + 1)
469 return VERR_BUFFER_OVERFLOW;
470
471 /* encode */
472 u8A = pbSrc[0];
473 pchDst[0] = g_szValToChar[u8A >> 2];
474 u8B = pbSrc[1];
475 pchDst[1] = g_szValToChar[((u8A << 4) & 0x3f) | (u8B >> 4)];
476 u8C = pbSrc[2];
477 pchDst[2] = g_szValToChar[((u8B << 2) & 0x3f) | (u8C >> 6)];
478 pchDst[3] = g_szValToChar[u8C & 0x3f];
479
480 /* advance */
481 cbBuf -= 4;
482 pchDst += 4;
483 cbData -= 3;
484 pbSrc += 3;
485
486 /* deal out linefeeds */
487 if (cbBuf == cbLineFeed && cbData)
488 {
489 if (cbBuf < RTBASE64_EOL_SIZE + 1)
490 return VERR_BUFFER_OVERFLOW;
491 cbBuf -= RTBASE64_EOL_SIZE;
492 if (RTBASE64_EOL_SIZE == 2)
493 *pchDst++ = '\r';
494 *pchDst++ = '\n';
495 cbLineFeed = cbBuf - RTBASE64_LINE_LEN;
496 }
497 }
498
499 /*
500 * Deal with the odd bytes and string termination.
501 */
502 if (cbData)
503 {
504 if (cbBuf < 4 + 1)
505 return VERR_BUFFER_OVERFLOW;
506 switch (cbData)
507 {
508 case 1:
509 u8A = pbSrc[0];
510 pchDst[0] = g_szValToChar[u8A >> 2];
511 pchDst[1] = g_szValToChar[(u8A << 4) & 0x3f];
512 pchDst[2] = '=';
513 pchDst[3] = '=';
514 break;
515 case 2:
516 u8A = pbSrc[0];
517 pchDst[0] = g_szValToChar[u8A >> 2];
518 u8B = pbSrc[1];
519 pchDst[1] = g_szValToChar[((u8A << 4) & 0x3f) | (u8B >> 4)];
520 pchDst[2] = g_szValToChar[(u8B << 2) & 0x3f];
521 pchDst[3] = '=';
522 break;
523 }
524 pchDst += 4;
525 }
526
527 *pchDst = '\0';
528
529 if (pcchActual)
530 *pcchActual = pchDst - pszBuf;
531 return VINF_SUCCESS;
532}
533RT_EXPORT_SYMBOL(RTBase64Encode);
534
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette