VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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