The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
base64.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Encode/decode binary data using printable characters (base64 format)
18 *
19 * @see RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
20 *
21 * @file src/lib/util/base64.c
22 *
23 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25RCSID("$Id: b40713d51eb16ce6fb5c2eb3f08c9e69e708f623 $")
26
27#include "base64.h"
28
29#include <freeradius-devel/util/value.h>
30#define us(x) (uint8_t) x
31
33 [62] = '+',
34 [63] = '/',
35 [52] = '0',
36 [53] = '1',
37 [54] = '2',
38 [55] = '3',
39 [56] = '4',
40 [57] = '5',
41 [58] = '6',
42 [59] = '7',
43 [60] = '8',
44 [61] = '9',
45 [0] = 'A',
46 [1] = 'B',
47 [2] = 'C',
48 [3] = 'D',
49 [4] = 'E',
50 [5] = 'F',
51 [6] = 'G',
52 [7] = 'H',
53 [8] = 'I',
54 [9] = 'J',
55 [10] = 'K',
56 [11] = 'L',
57 [12] = 'M',
58 [13] = 'N',
59 [14] = 'O',
60 [15] = 'P',
61 [16] = 'Q',
62 [17] = 'R',
63 [18] = 'S',
64 [19] = 'T',
65 [20] = 'U',
66 [21] = 'V',
67 [22] = 'W',
68 [23] = 'X',
69 [24] = 'Y',
70 [25] = 'Z',
71 [26] = 'a',
72 [27] = 'b',
73 [28] = 'c',
74 [29] = 'd',
75 [30] = 'e',
76 [31] = 'f',
77 [32] = 'g',
78 [33] = 'h',
79 [34] = 'i',
80 [35] = 'j',
81 [36] = 'k',
82 [37] = 'l',
83 [38] = 'm',
84 [39] = 'n',
85 [40] = 'o',
86 [41] = 'p',
87 [42] = 'q',
88 [43] = 'r',
89 [44] = 's',
90 [45] = 't',
91 [46] = 'u',
92 [47] = 'v',
93 [48] = 'w',
94 [49] = 'x',
95 [50] = 'y',
96 [51] = 'z'
97};
98
100 F32(0, UINT8_MAX), F8(32, UINT8_MAX), F2(40, UINT8_MAX),
101 ['+'] = 62,
102 F2(44, UINT8_MAX),
103 F1(46, UINT8_MAX),
104 ['/'] = 63,
105 ['0'] = 52,
106 ['1'] = 53,
107 ['2'] = 54,
108 ['3'] = 55,
109 ['4'] = 56,
110 ['5'] = 57,
111 ['6'] = 58,
112 ['7'] = 59,
113 ['8'] = 60,
114 ['9'] = 61,
115 F4(58, UINT8_MAX), F2(62, UINT8_MAX), F1(64, UINT8_MAX),
116 ['A'] = 0,
117 ['B'] = 1,
118 ['C'] = 2,
119 ['D'] = 3,
120 ['E'] = 4,
121 ['F'] = 5,
122 ['G'] = 6,
123 ['H'] = 7,
124 ['I'] = 8,
125 ['J'] = 9,
126 ['K'] = 10,
127 ['L'] = 11,
128 ['M'] = 12,
129 ['N'] = 13,
130 ['O'] = 14,
131 ['P'] = 15,
132 ['Q'] = 16,
133 ['R'] = 17,
134 ['S'] = 18,
135 ['T'] = 19,
136 ['U'] = 20,
137 ['V'] = 21,
138 ['W'] = 22,
139 ['X'] = 23,
140 ['Y'] = 24,
141 ['Z'] = 25,
142 F4(91, UINT8_MAX), F2(95, UINT8_MAX),
143 ['a'] = 26,
144 ['b'] = 27,
145 ['c'] = 28,
146 ['d'] = 29,
147 ['e'] = 30,
148 ['f'] = 31,
149 ['g'] = 32,
150 ['h'] = 33,
151 ['i'] = 34,
152 ['j'] = 35,
153 ['k'] = 36,
154 ['l'] = 37,
155 ['m'] = 38,
156 ['n'] = 39,
157 ['o'] = 40,
158 ['p'] = 41,
159 ['q'] = 42,
160 ['r'] = 43,
161 ['s'] = 44,
162 ['t'] = 45,
163 ['u'] = 46,
164 ['v'] = 47,
165 ['w'] = 48,
166 ['x'] = 49,
167 ['y'] = 50,
168 ['z'] = 51,
169 F128(123, UINT8_MAX),
170 F4(251, UINT8_MAX)
171};
172
174 [62] = '-',
175 [52] = '0',
176 [53] = '1',
177 [54] = '2',
178 [55] = '3',
179 [56] = '4',
180 [57] = '5',
181 [58] = '6',
182 [59] = '7',
183 [60] = '8',
184 [61] = '9',
185 [63] = '_',
186 [0] = 'A',
187 [1] = 'B',
188 [2] = 'C',
189 [3] = 'D',
190 [4] = 'E',
191 [5] = 'F',
192 [6] = 'G',
193 [7] = 'H',
194 [8] = 'I',
195 [9] = 'J',
196 [10] = 'K',
197 [11] = 'L',
198 [12] = 'M',
199 [13] = 'N',
200 [14] = 'O',
201 [15] = 'P',
202 [16] = 'Q',
203 [17] = 'R',
204 [18] = 'S',
205 [19] = 'T',
206 [20] = 'U',
207 [21] = 'V',
208 [22] = 'W',
209 [23] = 'X',
210 [24] = 'Y',
211 [25] = 'Z',
212 [26] = 'a',
213 [27] = 'b',
214 [28] = 'c',
215 [29] = 'd',
216 [30] = 'e',
217 [31] = 'f',
218 [32] = 'g',
219 [33] = 'h',
220 [34] = 'i',
221 [35] = 'j',
222 [36] = 'k',
223 [37] = 'l',
224 [38] = 'm',
225 [39] = 'n',
226 [40] = 'o',
227 [41] = 'p',
228 [42] = 'q',
229 [43] = 'r',
230 [44] = 's',
231 [45] = 't',
232 [46] = 'u',
233 [47] = 'v',
234 [48] = 'w',
235 [49] = 'x',
236 [50] = 'y',
237 [51] = 'z'
238};
239
241 F32(0, UINT8_MAX), F8(32, UINT8_MAX), F4(40, UINT8_MAX),
242 ['-'] = 62,
243 F2(46, UINT8_MAX),
244 ['0'] = 52,
245 ['1'] = 53,
246 ['2'] = 54,
247 ['3'] = 55,
248 ['4'] = 56,
249 ['5'] = 57,
250 ['6'] = 58,
251 ['7'] = 59,
252 ['8'] = 60,
253 ['9'] = 61,
254 F4(58, UINT8_MAX), F2(62, UINT8_MAX), F1(64, UINT8_MAX),
255 ['A'] = 0,
256 ['B'] = 1,
257 ['C'] = 2,
258 ['D'] = 3,
259 ['E'] = 4,
260 ['F'] = 5,
261 ['G'] = 6,
262 ['H'] = 7,
263 ['I'] = 8,
264 ['J'] = 9,
265 ['K'] = 10,
266 ['L'] = 11,
267 ['M'] = 12,
268 ['N'] = 13,
269 ['O'] = 14,
270 ['P'] = 15,
271 ['Q'] = 16,
272 ['R'] = 17,
273 ['S'] = 18,
274 ['T'] = 19,
275 ['U'] = 20,
276 ['V'] = 21,
277 ['W'] = 22,
278 ['X'] = 23,
279 ['Y'] = 24,
280 ['Z'] = 25,
281 F4(91, UINT8_MAX),
282 ['_'] = 63,
283 F1(96, UINT8_MAX),
284 ['a'] = 26,
285 ['b'] = 27,
286 ['c'] = 28,
287 ['d'] = 29,
288 ['e'] = 30,
289 ['f'] = 31,
290 ['g'] = 32,
291 ['h'] = 33,
292 ['i'] = 34,
293 ['j'] = 35,
294 ['k'] = 36,
295 ['l'] = 37,
296 ['m'] = 38,
297 ['n'] = 39,
298 ['o'] = 40,
299 ['p'] = 41,
300 ['q'] = 42,
301 ['r'] = 43,
302 ['s'] = 44,
303 ['t'] = 45,
304 ['u'] = 46,
305 ['v'] = 47,
306 ['w'] = 48,
307 ['x'] = 49,
308 ['y'] = 50,
309 ['z'] = 51,
310 F128(123, UINT8_MAX),
311 F4(251, UINT8_MAX)
312};
313
314/** Base 64 encode binary data
315 *
316 * Base64 encode in bytes to base64, writing to out.
317 *
318 * @param[out] out Where to write Base64 string.
319 * @param[in] in Data to encode.
320 * @param[in] add_padding Add padding bytes.
321 * @param[in] alphabet to use for encoding.
322 * @return
323 * - Amount of data we wrote to the buffer.
324 * - <0 the number of bytes we would have needed in the ouput buffer.
325 */
327 bool add_padding, char const alphabet[static UINT8_MAX])
328{
329 fr_sbuff_t our_out = FR_SBUFF(out);
330 fr_dbuff_t our_in = FR_DBUFF(in);
331
332 fr_strerror_const("Insufficient buffer space");
333
334 for (;;) {
335 uint8_t a, b, c;
336
337 switch (fr_dbuff_extend_lowat(NULL, &our_in, 3)) {
338 /*
339 * Enough bytes for a 24bit quanta
340 */
341 default:
342 a = *fr_dbuff_current(&our_in);
343 b = *(fr_dbuff_current(&our_in) + 1);
344 c = *(fr_dbuff_current(&our_in) + 2);
346 alphabet[(a >> 2) & 0x3f],
347 alphabet[((a << 4) | (b >> 4)) & 0x3f],
348 alphabet[((b << 2) | (c >> 6)) & 0x3f],
349 alphabet[c & 0x3f]);
350 fr_dbuff_advance(&our_in, 3);
351 continue;
352
353 case 2:
354 a = *fr_dbuff_current(&our_in);
355 b = *(fr_dbuff_current(&our_in) + 1);
357 alphabet[(a >> 2) & 0x3f],
358 alphabet[((a << 4) | (b >> 4)) & 0x3f],
359 alphabet[(b << 2) & 0x3f]);
360 fr_dbuff_advance(&our_in, 2); /* Place at the end */
361 if (add_padding) FR_SBUFF_IN_CHAR_RETURN(&our_out, '=');
362 break;
363
364 case 1:
365 a = *fr_dbuff_current(&our_in);
367 alphabet[(a >> 2) & 0x3f],
368 alphabet[(a << 4) & 0x3f]);
369 fr_dbuff_advance(&our_in, 1); /* Place at the end */
370 if (add_padding) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "==");
371 break;
372
373 case 0:
374 break;
375 }
376 break;
377 }
378
380
381 fr_sbuff_terminate(&our_out); /* Ensure this is terminated, even on zero length input */
382 fr_dbuff_set(in, &our_in);
383 FR_SBUFF_SET_RETURN(out, &our_out);
384}
385
386/* Decode base64 encoded input array.
387 *
388 * @param[out] err If non-null contains any parse errors.
389 * @param[out] out Where to write the decoded binary data.
390 * @param[in] in String to decode.
391 * @param[in] expect_padding Expect, and advanced past, padding characters '=' at
392 * the end of the string. Produce an error if we find
393 * insufficient padding characters.
394 * @param[in] no_trailing Error out if we find non-base64 characters
395 * at the end of the string.
396 * @param[in] alphabet to use for decoding.
397 * @return
398 * - < 0 on failure. The offset where the decoding error occurred as a negative integer.
399 * - Length of decoded data.
400 */
402 bool expect_padding, bool no_trailing, uint8_t const alphabet[static UINT8_MAX])
403{
404 fr_sbuff_t our_in = FR_SBUFF(in);
405 fr_dbuff_t our_out = FR_DBUFF(out);
406 fr_sbuff_marker_t m_final;
407 uint8_t pad;
408
409 /*
410 * Process complete 24bit quanta
411 */
412 while (fr_sbuff_extend_lowat(NULL, &our_in, 4) >= 4) {
413 char *p = fr_sbuff_current(&our_in);
414
415 if (!fr_is_base64_nstd(p[0], alphabet) ||
416 !fr_is_base64_nstd(p[1], alphabet) ||
417 !fr_is_base64_nstd(p[2], alphabet) ||
418 !fr_is_base64_nstd(p[3], alphabet)) break;
419
420 if (fr_dbuff_in_bytes(&our_out,
421 ((alphabet[us(p[0])] << 2) | (alphabet[us(p[1])] >> 4)),
422 ((alphabet[us(p[1])] << 4) & 0xf0) | (alphabet[us(p[2])] >> 2),
423 ((alphabet[us(p[2])] << 6) & 0xc0) | alphabet[us(p[3])]) != 3) {
424 oob:
425 fr_strerror_printf("Output buffer too small, needed at least %zu bytes",
426 fr_dbuff_used(&our_out) + 1);
427
429
430 FR_SBUFF_ERROR_RETURN(&our_in);
431 }
432
433 fr_sbuff_advance(&our_in, 4);
434 }
435
436 fr_sbuff_marker(&m_final, &our_in);
437
438 /*
439 * Find the first non-base64 char
440 */
441 while (fr_sbuff_extend(&our_in) && fr_is_base64_nstd(fr_sbuff_char(&our_in, '\0'), alphabet)) {
442 fr_sbuff_advance(&our_in, 1);
443 }
444
445 switch (fr_sbuff_behind(&m_final)) {
446 case 0: /* Final quantum is 24 bits */
447 pad = 0;
448 break;
449
450 case 2: /* Final quantum is 8 bits */
451 {
452 char *p = fr_sbuff_current(&m_final);
453
454 if (fr_dbuff_in_bytes(&our_out,
455 (alphabet[us(p[0])] << 2) | (alphabet[us(p[1])] >> 4)) != 1) goto oob;
456 pad = 2;
457 }
458 break;
459
460 case 3: /* Final quantum is 16 bits */
461 {
462 char *p = fr_sbuff_current(&m_final);
463
464 if (fr_dbuff_in_bytes(&our_out,
465 ((alphabet[us(p[0])] << 2) | (alphabet[us(p[1])] >> 4)),
466 ((alphabet[us(p[1])] << 4) & 0xf0) | (alphabet[us(p[2])] >> 2)) != 2) goto oob;
467 pad = 1;
468 }
469 break;
470
471 default:
472 fr_strerror_const("Invalid base64 padding data");
473
474 bad_format:
476
477 FR_SBUFF_ERROR_RETURN(&our_in);
478 }
479
480 if (expect_padding) {
481 uint8_t i;
482 for (i = 0; i < pad; i++) {
483 if (!fr_sbuff_extend(&our_in)) {
484 fr_strerror_printf("Missing padding '=' at end of base64 string. "
485 "Expected %u padding char(s)", pad);
486 goto bad_format;
487 }
488 if (!fr_sbuff_next_if_char(&our_in, '=')) {
489 fr_strerror_printf("Found non-padding char '%c' at end of base64 string",
490 fr_sbuff_char(&our_in, '\0'));
491 goto bad_format;
492 }
493 }
494 }
495
496 if (no_trailing && fr_sbuff_extend(&our_in)) {
497 fr_strerror_printf("Found trailing garbage '%c' at end of base64 string",
498 fr_sbuff_char(&our_in, '\0'));
499
501
502 FR_SBUFF_ERROR_RETURN(&our_in);
503 }
504
505 fr_sbuff_set(in, &our_in);
506 return fr_dbuff_set(out, &our_out);
507}
char const fr_base64_alphabet_encode[UINT8_MAX]
Definition base64.c:32
uint8_t const fr_base64_alphabet_decode[UINT8_MAX]
Definition base64.c:99
ssize_t fr_base64_encode_nstd(fr_sbuff_t *out, fr_dbuff_t *in, bool add_padding, char const alphabet[static UINT8_MAX])
Base 64 encode binary data.
Definition base64.c:326
#define us(x)
Definition base64.c:30
fr_slen_t fr_base64_decode_nstd(fr_sbuff_parse_error_t *err, fr_dbuff_t *out, fr_sbuff_t *in, bool expect_padding, bool no_trailing, uint8_t const alphabet[static UINT8_MAX])
Definition base64.c:401
uint8_t const fr_base64_url_alphabet_decode[UINT8_MAX]
Definition base64.c:240
char const fr_base64_url_alphabet_encode[UINT8_MAX]
Definition base64.c:173
static bool fr_is_base64_nstd(char c, uint8_t const alphabet[static UINT8_MAX])
Check if char is in Base64 alphabet.
Definition base64.h:61
#define F4(_idx, _val)
Definition build.h:215
#define RCSID(id)
Definition build.h:483
#define F128(_idx, _val)
Definition build.h:220
#define F8(_idx, _val)
Definition build.h:216
#define F2(_idx, _val)
Definition build.h:214
#define F32(_idx, _val)
Definition build.h:218
#define F1(_idx, _val)
Fill macros for array initialisation.
Definition build.h:213
#define fr_dbuff_advance(_dbuff_or_marker, _len)
Advance 'current' position in dbuff or marker by _len bytes.
Definition dbuff.h:1072
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition dbuff.h:767
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition dbuff.h:911
#define fr_dbuff_set(_dst, _src)
Set the 'current' position in a dbuff or marker using another dbuff or marker, a char pointer,...
Definition dbuff.h:1004
#define fr_dbuff_extend_lowat(_status, _dbuff_or_marker, _lowat)
Extend if we're below _lowat.
Definition dbuff.h:660
#define fr_dbuff_in_bytes(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker.
Definition dbuff.h:1465
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition dbuff.h:222
static fr_slen_t err
Definition dict.h:824
static fr_slen_t in
Definition dict.h:824
long int ssize_t
unsigned char uint8_t
ssize_t fr_slen_t
#define UINT8_MAX
fr_sbuff_parse_error_t
@ FR_SBUFF_PARSE_ERROR_FORMAT
Format of data was invalid.
@ FR_SBUFF_PARSE_ERROR_OUT_OF_SPACE
No space available in output buffer.
@ FR_SBUFF_PARSE_ERROR_TRAILING
Trailing characters found.
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2088
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_char(_sbuff_or_marker, _eob)
#define FR_SBUFF_IN_STRCPY_LITERAL_RETURN(_sbuff, _str)
#define fr_sbuff_extend(_sbuff_or_marker)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_behind(_sbuff_or_marker)
#define fr_sbuff_extend_lowat(_status, _sbuff_or_marker, _lowat)
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static size_t char ** out
Definition value.h:997