VirtualBox

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

Last change on this file since 73587 was 73587, checked in by vboxsync, 6 years ago

iptr/base64: introduce RTBase64EncodedLengthEx() and RTBase64EncodeEx(),
and RTBASE64_FLAGS_NO_LINE_BREAKS flag for them.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.8 KB
Line 
1/* $Id: base64.cpp 73587 2018-08-09 13:37:52Z vboxsync $ */
2/** @file
3 * IPRT - Base64, MIME content transfer encoding.
4 */
5
6/*
7 * Copyright (C) 2009-2017 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 || ( u8 < 64
114 && (unsigned)g_szValToChar[u8] == i));
115 }
116 ASMAtomicWriteBool(&s_fSane, true);
117 }
118}
119#endif /* RT_STRICT */
120
121
122RTDECL(ssize_t) RTBase64DecodedSizeEx(const char *pszString, size_t cchStringMax, char **ppszEnd)
123{
124#ifdef RT_STRICT
125 rtBase64Sanity();
126#endif
127
128 /*
129 * Walk the string until a non-encoded or non-space character is encountered.
130 */
131 uint32_t c6Bits = 0;
132 uint8_t u8 = BASE64_INVALID;
133 unsigned ch = 0;
134 AssertCompile(sizeof(char) == sizeof(uint8_t));
135
136 while (cchStringMax > 0 && (ch = *pszString))
137 {
138 u8 = g_au8CharToVal[ch];
139 if (u8 < 64)
140 c6Bits++;
141 else if (RT_UNLIKELY(u8 != BASE64_SPACE))
142 break;
143
144 /* advance */
145 pszString++;
146 cchStringMax--;
147 }
148
149 /*
150 * Padding can only be found at the end and there is
151 * only 1 or 2 padding chars. Deal with it first.
152 */
153 unsigned cbPad = 0;
154 if (u8 == BASE64_PAD)
155 {
156 cbPad = 1;
157 c6Bits++;
158 pszString++;
159 cchStringMax--;
160 while (cchStringMax > 0 && (ch = *pszString))
161 {
162 u8 = g_au8CharToVal[ch];
163 if (u8 != BASE64_SPACE)
164 {
165 if (u8 != BASE64_PAD)
166 break;
167 c6Bits++;
168 cbPad++;
169 }
170 pszString++;
171 cchStringMax--;
172 }
173 if (cbPad >= 3)
174 return -1;
175 }
176
177 /*
178 * Invalid char and no where to indicate where the
179 * Base64 text ends? Return failure.
180 */
181 if ( u8 == BASE64_INVALID
182 && !ppszEnd
183 && ch)
184 return -1;
185
186 /*
187 * Recalc 6-bit to 8-bit and adjust for padding.
188 */
189 size_t cb;
190 if (c6Bits * 3 / 3 == c6Bits)
191 {
192 if ((c6Bits * 3 % 4) != 0)
193 return -1;
194 cb = c6Bits * 3 / 4;
195 }
196 else
197 {
198 if ((c6Bits * (uint64_t)3 % 4) != 0)
199 return -1;
200 cb = c6Bits * (uint64_t)3 / 4;
201 }
202
203 if (cb < cbPad)
204 return -1;
205 cb -= cbPad;
206
207 if (ppszEnd)
208 *ppszEnd = (char *)pszString;
209 return cb;
210}
211RT_EXPORT_SYMBOL(RTBase64DecodedSizeEx);
212
213
214RTDECL(ssize_t) RTBase64DecodedSize(const char *pszString, char **ppszEnd)
215{
216 return RTBase64DecodedSizeEx(pszString, RTSTR_MAX, ppszEnd);
217}
218RT_EXPORT_SYMBOL(RTBase64DecodedSize);
219
220
221RTDECL(int) RTBase64DecodeEx(const char *pszString, size_t cchStringMax, void *pvData, size_t cbData,
222 size_t *pcbActual, char **ppszEnd)
223{
224#ifdef RT_STRICT
225 rtBase64Sanity();
226#endif
227
228 /*
229 * Process input in groups of 4 input / 3 output chars.
230 */
231 uint8_t u8Trio[3] = { 0, 0, 0 }; /* shuts up gcc */
232 uint8_t *pbData = (uint8_t *)pvData;
233 unsigned ch;
234 uint8_t u8;
235 unsigned c6Bits = 0;
236 AssertCompile(sizeof(char) == sizeof(uint8_t));
237
238 for (;;)
239 {
240 /* The first 6-bit group. */
241 while ((u8 = g_au8CharToVal[ch = cchStringMax > 0 ? (uint8_t)*pszString : 0]) == BASE64_SPACE)
242 pszString++, cchStringMax--;
243 if (u8 >= 64)
244 {
245 c6Bits = 0;
246 break;
247 }
248 u8Trio[0] = u8 << 2;
249 pszString++;
250 cchStringMax--;
251
252 /* The second 6-bit group. */
253 while ((u8 = g_au8CharToVal[ch = cchStringMax > 0 ? (uint8_t)*pszString : 0]) == BASE64_SPACE)
254 pszString++, cchStringMax--;
255 if (u8 >= 64)
256 {
257 c6Bits = 1;
258 break;
259 }
260 u8Trio[0] |= u8 >> 4;
261 u8Trio[1] = u8 << 4;
262 pszString++;
263 cchStringMax--;
264
265 /* The third 6-bit group. */
266 u8 = BASE64_INVALID;
267 while ((u8 = g_au8CharToVal[ch = cchStringMax > 0 ? (uint8_t)*pszString : 0]) == BASE64_SPACE)
268 pszString++, cchStringMax--;
269 if (u8 >= 64)
270 {
271 c6Bits = 2;
272 break;
273 }
274 u8Trio[1] |= u8 >> 2;
275 u8Trio[2] = u8 << 6;
276 pszString++;
277 cchStringMax--;
278
279 /* The fourth 6-bit group. */
280 u8 = BASE64_INVALID;
281 while ((u8 = g_au8CharToVal[ch = cchStringMax > 0 ? (uint8_t)*pszString : 0]) == BASE64_SPACE)
282 pszString++, cchStringMax--;
283 if (u8 >= 64)
284 {
285 c6Bits = 3;
286 break;
287 }
288 u8Trio[2] |= u8;
289 pszString++;
290 cchStringMax--;
291
292 /* flush the trio */
293 if (cbData < 3)
294 return VERR_BUFFER_OVERFLOW;
295 cbData -= 3;
296 pbData[0] = u8Trio[0];
297 pbData[1] = u8Trio[1];
298 pbData[2] = u8Trio[2];
299 pbData += 3;
300 }
301
302 /*
303 * Padding can only be found at the end and there is
304 * only 1 or 2 padding chars. Deal with it first.
305 */
306 unsigned cbPad = 0;
307 if (u8 == BASE64_PAD)
308 {
309 cbPad = 1;
310 pszString++;
311 cchStringMax--;
312 while (cchStringMax > 0 && (ch = (uint8_t)*pszString))
313 {
314 u8 = g_au8CharToVal[ch];
315 if (u8 != BASE64_SPACE)
316 {
317 if (u8 != BASE64_PAD)
318 break;
319 cbPad++;
320 }
321 pszString++;
322 cchStringMax--;
323 }
324 if (cbPad >= 3)
325 return VERR_INVALID_BASE64_ENCODING;
326 }
327
328 /*
329 * Invalid char and no where to indicate where the
330 * Base64 text ends? Return failure.
331 */
332 if ( u8 == BASE64_INVALID
333 && !ppszEnd
334 && ch != '\0')
335 return VERR_INVALID_BASE64_ENCODING;
336
337 /*
338 * Check padding vs. pending sextets, if anything left to do finish it off.
339 */
340 if (c6Bits || cbPad)
341 {
342 if (c6Bits + cbPad != 4)
343 return VERR_INVALID_BASE64_ENCODING;
344
345 switch (c6Bits)
346 {
347 case 1:
348 u8Trio[1] = u8Trio[2] = 0;
349 break;
350 case 2:
351 u8Trio[2] = 0;
352 break;
353 case 3:
354 default:
355 break;
356 }
357 switch (3 - cbPad)
358 {
359 case 1:
360 if (cbData < 1)
361 return VERR_BUFFER_OVERFLOW;
362 cbData--;
363 pbData[0] = u8Trio[0];
364 pbData++;
365 break;
366
367 case 2:
368 if (cbData < 2)
369 return VERR_BUFFER_OVERFLOW;
370 cbData -= 2;
371 pbData[0] = u8Trio[0];
372 pbData[1] = u8Trio[1];
373 pbData += 2;
374 break;
375
376 default:
377 break;
378 }
379 }
380
381 /*
382 * Set optional return values and return successfully.
383 */
384 if (ppszEnd)
385 *ppszEnd = (char *)pszString;
386 if (pcbActual)
387 *pcbActual = pbData - (uint8_t *)pvData;
388 return VINF_SUCCESS;
389}
390RT_EXPORT_SYMBOL(RTBase64DecodeEx);
391
392
393RTDECL(int) RTBase64Decode(const char *pszString, void *pvData, size_t cbData, size_t *pcbActual, char **ppszEnd)
394{
395 return RTBase64DecodeEx(pszString, RTSTR_MAX, pvData, cbData, pcbActual, ppszEnd);
396}
397RT_EXPORT_SYMBOL(RTBase64Decode);
398
399
400/**
401 * Calculates the length of the Base64 encoding of a given number of bytes of
402 * data produced by RTBase64Encode().
403 *
404 * @returns The Base64 string length.
405 * @param cbData The number of bytes to encode.
406 */
407RTDECL(size_t) RTBase64EncodedLength(size_t cbData)
408{
409 return RTBase64EncodedLengthEx(cbData, 0);
410}
411RT_EXPORT_SYMBOL(RTBase64EncodedLength);
412
413
414/**
415 * Calculates the length of the Base64 encoding of a given number of bytes of
416 * data produced by RTBase64EncodeEx() with the same @a fFlags.
417 *
418 * @returns The Base64 string length.
419 * @param cbData The number of bytes to encode.
420 * @param fFlags Flags, any combination of the RTBASE64_FLAGS \#defines.
421 */
422RTDECL(size_t) RTBase64EncodedLengthEx(size_t cbData, uint32_t fFlags)
423{
424 if (cbData * 8 / 8 != cbData)
425 {
426 AssertReturn(sizeof(size_t) == sizeof(uint64_t), ~(size_t)0);
427 uint64_t cch = cbData * (uint64_t)8;
428 while (cch % 24)
429 cch += 8;
430 cch /= 6;
431
432 if ((fFlags & RTBASE64_FLAGS_NO_LINE_BREAKS) == 0) /* add EOLs? */
433 cch += ((cch - 1) / RTBASE64_LINE_LEN) * RTBASE64_EOL_SIZE;
434 return cch;
435 }
436
437 size_t cch = cbData * 8;
438 while (cch % 24)
439 cch += 8;
440 cch /= 6;
441
442 if ((fFlags & RTBASE64_FLAGS_NO_LINE_BREAKS) == 0) /* add EOLs? */
443 cch += ((cch - 1) / RTBASE64_LINE_LEN) * RTBASE64_EOL_SIZE;
444 return cch;
445}
446RT_EXPORT_SYMBOL(RTBase64EncodedLengthEx);
447
448
449/**
450 * Encodes the specifed data into a Base64 string, the caller supplies the
451 * output buffer.
452 *
453 * This is equivalent to calling RTBase64EncodeEx() with no flags.
454 *
455 * @returns IRPT status code.
456 * @retval VERR_BUFFER_OVERFLOW if the output buffer is too small. The buffer
457 * may contain an invalid Base64 string.
458 *
459 * @param pvData The data to encode.
460 * @param cbData The number of bytes to encode.
461 * @param pszBuf Where to put the Base64 string.
462 * @param cbBuf The size of the output buffer, including the terminator.
463 * @param pcchActual The actual number of characters returned.
464 */
465RTDECL(int) RTBase64Encode(const void *pvData, size_t cbData, char *pszBuf, size_t cbBuf, size_t *pcchActual)
466{
467 return RTBase64EncodeEx(pvData, cbData, 0, pszBuf, cbBuf, pcchActual);
468}
469RT_EXPORT_SYMBOL(RTBase64Encode);
470
471
472/**
473 * Encodes the specifed data into a Base64 string, the caller supplies the
474 * output buffer.
475 *
476 * @returns IRPT status code.
477 * @retval VERR_BUFFER_OVERFLOW if the output buffer is too small. The buffer
478 * may contain an invalid Base64 string.
479 *
480 * @param pvData The data to encode.
481 * @param cbData The number of bytes to encode.
482 * @param pszBuf Where to put the Base64 string.
483 * @param cbBuf The size of the output buffer, including the terminator.
484 * @param pcchActual The actual number of characters returned.
485 */
486RTDECL(int) RTBase64EncodeEx(const void *pvData, size_t cbData, uint32_t fFlags,
487 char *pszBuf, size_t cbBuf, size_t *pcchActual)
488{
489 /*
490 * Process whole "trios" of input data.
491 */
492 uint8_t u8A;
493 uint8_t u8B;
494 uint8_t u8C;
495 size_t cbLineFeed = cbBuf - RTBASE64_LINE_LEN;
496 const uint8_t *pbSrc = (const uint8_t *)pvData;
497 char *pchDst = pszBuf;
498 while (cbData >= 3)
499 {
500 if (cbBuf < 4 + 1)
501 return VERR_BUFFER_OVERFLOW;
502
503 /* encode */
504 u8A = pbSrc[0];
505 pchDst[0] = g_szValToChar[u8A >> 2];
506 u8B = pbSrc[1];
507 pchDst[1] = g_szValToChar[((u8A << 4) & 0x3f) | (u8B >> 4)];
508 u8C = pbSrc[2];
509 pchDst[2] = g_szValToChar[((u8B << 2) & 0x3f) | (u8C >> 6)];
510 pchDst[3] = g_szValToChar[u8C & 0x3f];
511
512 /* advance */
513 cbBuf -= 4;
514 pchDst += 4;
515 cbData -= 3;
516 pbSrc += 3;
517
518 if ((fFlags & RTBASE64_FLAGS_NO_LINE_BREAKS) == 0) /* add EOLs? */
519 {
520 /* deal out end-of-line */
521 if (cbBuf == cbLineFeed && cbData)
522 {
523 if (cbBuf < RTBASE64_EOL_SIZE + 1)
524 return VERR_BUFFER_OVERFLOW;
525 cbBuf -= RTBASE64_EOL_SIZE;
526 if (RTBASE64_EOL_SIZE == 2)
527 *pchDst++ = '\r';
528 *pchDst++ = '\n';
529 cbLineFeed = cbBuf - RTBASE64_LINE_LEN;
530 }
531 }
532 }
533
534 /*
535 * Deal with the odd bytes and string termination.
536 */
537 if (cbData)
538 {
539 if (cbBuf < 4 + 1)
540 return VERR_BUFFER_OVERFLOW;
541 switch (cbData)
542 {
543 case 1:
544 u8A = pbSrc[0];
545 pchDst[0] = g_szValToChar[u8A >> 2];
546 pchDst[1] = g_szValToChar[(u8A << 4) & 0x3f];
547 pchDst[2] = '=';
548 pchDst[3] = '=';
549 break;
550 case 2:
551 u8A = pbSrc[0];
552 pchDst[0] = g_szValToChar[u8A >> 2];
553 u8B = pbSrc[1];
554 pchDst[1] = g_szValToChar[((u8A << 4) & 0x3f) | (u8B >> 4)];
555 pchDst[2] = g_szValToChar[(u8B << 2) & 0x3f];
556 pchDst[3] = '=';
557 break;
558 }
559 pchDst += 4;
560 }
561
562 *pchDst = '\0';
563
564 if (pcchActual)
565 *pcchActual = pchDst - pszBuf;
566 return VINF_SUCCESS;
567}
568RT_EXPORT_SYMBOL(RTBase64EncodeEx);
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