The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
25 RCSID("$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);
345  FR_SBUFF_IN_CHAR_RETURN(&our_out,
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);
356  FR_SBUFF_IN_CHAR_RETURN(&our_out,
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);
366  FR_SBUFF_IN_CHAR_RETURN(&our_out,
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:213
#define RCSID(id)
Definition: build.h:444
#define F128(_idx, _val)
Definition: build.h:218
#define F8(_idx, _val)
Definition: build.h:214
#define F2(_idx, _val)
Definition: build.h:212
#define F32(_idx, _val)
Definition: build.h:216
#define F1(_idx, _val)
Fill macros for array initialisation.
Definition: build.h:211
#define fr_dbuff_advance(_dbuff_or_marker, _len)
Advance 'current' position in dbuff or marker by _len bytes.
Definition: dbuff.h:1067
#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:762
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition: dbuff.h:906
#define fr_dbuff_extend_lowat(_status, _dbuff_or_marker, _lowat)
Extend if we're below _lowat.
Definition: dbuff.h:655
#define fr_dbuff_in_bytes(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker.
Definition: dbuff.h:1460
#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:645
static fr_slen_t in
Definition: dict.h:645
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
ssize_t fr_slen_t
Definition: merged_model.c:35
#define UINT8_MAX
Definition: merged_model.c:32
fr_sbuff_parse_error_t
Definition: merged_model.c:45
@ FR_SBUFF_PARSE_ERROR_FORMAT
Format of data was invalid.
Definition: merged_model.c:50
@ FR_SBUFF_PARSE_ERROR_OUT_OF_SPACE
No space available in output buffer.
Definition: merged_model.c:51
@ FR_SBUFF_PARSE_ERROR_TRAILING
Trailing characters found.
Definition: merged_model.c:49
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:2047
#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(_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
return fr_dbuff_set(dbuff, &our_dbuff)
FR_SBUFF_SET_RETURN(sbuff, &our_sbuff)
static size_t char ** out
Definition: value.h:984