The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
base32.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 (base32 format)
18  *
19  * @see RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
20  *
21  * @file src/lib/util/base32.c
22  *
23  * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24  */
25 RCSID("$Id: 480c5928eb239fdedda986c0ed5d2a4eb229c43d $")
26 
27 #include "base32.h"
28 
29 #include <freeradius-devel/util/value.h>
30 #define us(x) (uint8_t) x
31 
33  [26] = '2',
34  [27] = '3',
35  [28] = '4',
36  [29] = '5',
37  [30] = '6',
38  [31] = '7',
39  [0] = 'A',
40  [1] = 'B',
41  [2] = 'C',
42  [3] = 'D',
43  [4] = 'E',
44  [5] = 'F',
45  [6] = 'G',
46  [7] = 'H',
47  [8] = 'I',
48  [9] = 'J',
49  [10] = 'K',
50  [11] = 'L',
51  [12] = 'M',
52  [13] = 'N',
53  [14] = 'O',
54  [15] = 'P',
55  [16] = 'Q',
56  [17] = 'R',
57  [18] = 'S',
58  [19] = 'T',
59  [20] = 'U',
60  [21] = 'V',
61  [22] = 'W',
62  [23] = 'X',
63  [24] = 'Y',
64  [25] = 'Z',
65 };
66 
68  F32(0, UINT8_MAX), F16(32, UINT8_MAX), F2(48, UINT8_MAX),
69  ['2'] = 26,
70  ['3'] = 27,
71  ['4'] = 28,
72  ['5'] = 29,
73  ['6'] = 30,
74  ['7'] = 31,
75  F8(56, UINT8_MAX), F1(64, UINT8_MAX),
76  ['A'] = 0,
77  ['B'] = 1,
78  ['C'] = 2,
79  ['D'] = 3,
80  ['E'] = 4,
81  ['F'] = 5,
82  ['G'] = 6,
83  ['H'] = 7,
84  ['I'] = 8,
85  ['J'] = 9,
86  ['K'] = 10,
87  ['L'] = 11,
88  ['M'] = 12,
89  ['N'] = 13,
90  ['O'] = 14,
91  ['P'] = 15,
92  ['Q'] = 16,
93  ['R'] = 17,
94  ['S'] = 18,
95  ['T'] = 19,
96  ['U'] = 20,
97  ['V'] = 21,
98  ['W'] = 22,
99  ['X'] = 23,
100  ['Y'] = 24,
101  ['Z'] = 25,
102  F128(91, UINT8_MAX), F32(219, UINT8_MAX), F4(251, UINT8_MAX)
103 };
104 
106  [0] = '0',
107  [1] = '1',
108  [2] = '2',
109  [3] = '3',
110  [4] = '4',
111  [5] = '5',
112  [6] = '6',
113  [7] = '7',
114  [8] = '8',
115  [9] = '9',
116  [10] = 'A',
117  [11] = 'B',
118  [12] = 'C',
119  [13] = 'D',
120  [14] = 'E',
121  [15] = 'F',
122  [16] = 'G',
123  [17] = 'H',
124  [18] = 'I',
125  [19] = 'J',
126  [20] = 'K',
127  [21] = 'L',
128  [22] = 'M',
129  [23] = 'N',
130  [24] = 'O',
131  [25] = 'P',
132  [26] = 'Q',
133  [27] = 'R',
134  [28] = 'S',
135  [29] = 'T',
136  [30] = 'U',
137  [31] = 'V',
138 };
139 
141  F32(0, UINT8_MAX), F16(32, UINT8_MAX),
142  ['0'] = 0,
143  ['1'] = 1,
144  ['2'] = 2,
145  ['3'] = 3,
146  ['4'] = 4,
147  ['5'] = 5,
148  ['6'] = 6,
149  ['7'] = 7,
150  ['8'] = 8,
151  ['9'] = 9,
152  F4(58, UINT8_MAX), F2(62, UINT8_MAX), F1(64, UINT8_MAX),
153  ['A'] = 10,
154  ['B'] = 11,
155  ['C'] = 12,
156  ['D'] = 13,
157  ['E'] = 14,
158  ['F'] = 15,
159  ['G'] = 16,
160  ['H'] = 17,
161  ['I'] = 18,
162  ['J'] = 19,
163  ['K'] = 20,
164  ['L'] = 21,
165  ['M'] = 22,
166  ['N'] = 23,
167  ['O'] = 24,
168  ['P'] = 25,
169  ['Q'] = 26,
170  ['R'] = 27,
171  ['S'] = 28,
172  ['T'] = 29,
173  ['U'] = 30,
174  ['V'] = 31,
175  F128(87, UINT8_MAX), F32(215, UINT8_MAX), F8(247, UINT8_MAX)
176 };
177 
178 /** Base 64 encode binary data
179  *
180  * base32 encode in bytes to base32, writing to out.
181  *
182  * @param[out] out Where to write base32 string.
183  * @param[in] in Data to encode.
184  * @param[in] add_padding Add padding bytes.
185  * @param[in] alphabet to use for encoding.
186  * @return
187  * - Amount of data we wrote to the buffer.
188  * - <0 the number of bytes we would have needed in the ouput buffer.
189  */
191  bool add_padding, char const alphabet[static UINT8_MAX])
192 {
193  fr_sbuff_t our_out = FR_SBUFF(out);
194  fr_dbuff_t our_in = FR_DBUFF(in);
195 
196  fr_strerror_const("Insufficient buffer space");
197 
198  for (;;) {
199  uint8_t a, b, c, d, e;
200 
201  switch (fr_dbuff_extend_lowat(NULL, &our_in, 5)) {
202  /*
203  * Final quantum is 40 bits
204  */
205  default:
206  a = *fr_dbuff_current(&our_in);
207  b = *(fr_dbuff_current(&our_in) + 1);
208  c = *(fr_dbuff_current(&our_in) + 2);
209  d = *(fr_dbuff_current(&our_in) + 3);
210  e = *(fr_dbuff_current(&our_in) + 4);
211  FR_SBUFF_IN_CHAR_RETURN(&our_out,
212  alphabet[(a >> 3) & 0x1f], /* a - 5 bits */
213  alphabet[((a << 2) | (b >> 6)) & 0x1f], /* a - 3 bits, b - 2 bits */
214  alphabet[(b >> 1) & 0x1f], /* b - 5 bits */
215  alphabet[((b << 4) | (c >> 4)) & 0x1f], /* b - 1 bit, c - 4 bits */
216  alphabet[((c << 1) | (d >> 7)) & 0x1f], /* c - 4 bits, d - 1 bit */
217  alphabet[(d >> 2) & 0x1f], /* d - 5 bits */
218  alphabet[((d << 3) | (e >> 5)) & 0x1f], /* d - 2 bits, e - 3 bits */
219  alphabet[e & 0x1f]); /* e - 5 bits */
220  fr_dbuff_advance(&our_in, 5);
221  continue;
222 
223  /*
224  * Final quantum is 32 bits
225  */
226  case 4:
227  a = *fr_dbuff_current(&our_in);
228  b = *(fr_dbuff_current(&our_in) + 1);
229  c = *(fr_dbuff_current(&our_in) + 2);
230  d = *(fr_dbuff_current(&our_in) + 3);
231  FR_SBUFF_IN_CHAR_RETURN(&our_out,
232  alphabet[(a >> 3) & 0x1f], /* a - 5 bits */
233  alphabet[((a << 2) | (b >> 6)) & 0x1f], /* a - 3 bits, b - 2 bits */
234  alphabet[(b >> 1) & 0x1f], /* b - 5 bits */
235  alphabet[((b << 4) | (c >> 4)) & 0x1f], /* b - 1 bit, c - 4 bits */
236  alphabet[((c << 1) | (d >> 7)) & 0x1f], /* c - 4 bits, d - 1 bit */
237  alphabet[(d >> 2) & 0x1f], /* d - 5 bits */
238  alphabet[(d << 3) & 0x1f]); /* d - 2 bits */
239  fr_dbuff_advance(&our_in, 4);
240  if (add_padding) FR_SBUFF_IN_CHAR_RETURN(&our_out, '=');
241  break;
242 
243  /*
244  * Final quantum is 24 bits
245  */
246  case 3:
247  a = *fr_dbuff_current(&our_in);
248  b = *(fr_dbuff_current(&our_in) + 1);
249  c = *(fr_dbuff_current(&our_in) + 2);
250  FR_SBUFF_IN_CHAR_RETURN(&our_out,
251  alphabet[(a >> 3) & 0x1f], /* a - 5 bits */
252  alphabet[((a << 2) | (b >> 6)) & 0x1f], /* a - 3 bits, b - 2 bits */
253  alphabet[(b >> 1) & 0x1f], /* b - 5 bits */
254  alphabet[((b << 4) | (c >> 4)) & 0x1f], /* b - 1 bit, c - 4 bits */
255  alphabet[(c << 1) & 0x1f]); /* c - 4 bits */
256  fr_dbuff_advance(&our_in, 3);
257  if (add_padding) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "===");
258  break;
259 
260  /*
261  * Final quantum is 16 bits
262  */
263  case 2:
264  a = *fr_dbuff_current(&our_in);
265  b = *(fr_dbuff_current(&our_in) + 1);
266  FR_SBUFF_IN_CHAR_RETURN(&our_out,
267  alphabet[(a >> 3) & 0x1f], /* a - 5 bits */
268  alphabet[((a << 2) | (b >> 6)) & 0x1f], /* a - 3 bits, b - 2 bits */
269  alphabet[(b >> 1) & 0x1f], /* b - 5 bits */
270  alphabet[(b << 4) & 0x1f]); /* b - 1 bit, c - 4 bits */
271  fr_dbuff_advance(&our_in, 2);
272  if (add_padding) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "====");
273  break;
274 
275  /*
276  * Final quantum is 8 bits
277  */
278  case 1:
279  a = *fr_dbuff_current(&our_in);
280  FR_SBUFF_IN_CHAR_RETURN(&our_out,
281  alphabet[(a >> 3) & 0x1f], /* a - 5 bits */
282  alphabet[(a << 2) & 0x1f]); /* a - 3 bits, b - 2 bits */
283  fr_dbuff_advance(&our_in, 1);
284  if (add_padding) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "======");
285  break;
286 
287  case 0:
288  break;
289  }
290  break;
291  }
292 
294 
295  fr_sbuff_terminate(&our_out); /* Ensure this is terminated, even on zero length input */
296  fr_dbuff_set(in, &our_in);
297  FR_SBUFF_SET_RETURN(out, &our_out);
298 }
299 
300 /* Decode base32 encoded input
301  *
302  * @param[out] err If non-null contains any parse errors.
303  * @param[out] out Where to write the decoded binary data.
304  * @param[in] in String to decode.
305  * @param[in] expect_padding Expect, and advanced past, padding characters '=' at
306  * the end of the string. Produce an error if we find
307  * insufficient padding characters.
308  * @param[in] no_trailing Error out if we find non-base32 characters
309  * at the end of the string.
310  * @param[in] alphabet to use for decoding.
311  * @return
312  * - < 0 on failure. The offset where the decoding error occurred as a negative integer.
313  * - Length of decoded data.
314  */
316  bool expect_padding, bool no_trailing, uint8_t const alphabet[static UINT8_MAX])
317 {
318  fr_sbuff_t our_in = FR_SBUFF(in);
319  fr_dbuff_t our_out = FR_DBUFF(out);
320  fr_sbuff_marker_t m_final;
321  size_t len;
322  uint8_t pad;
323 
324  /*
325  * Process complete 40bit quanta
326  */
327  while (fr_sbuff_extend_lowat(NULL, &our_in, 8) >= 8) {
328  char *p = fr_sbuff_current(&our_in);
329 
330  if (!fr_is_base32_nstd(p[0], alphabet) ||
331  !fr_is_base32_nstd(p[1], alphabet) ||
332  !fr_is_base32_nstd(p[2], alphabet) ||
333  !fr_is_base32_nstd(p[3], alphabet) ||
334  !fr_is_base32_nstd(p[4], alphabet) ||
335  !fr_is_base32_nstd(p[5], alphabet) ||
336  !fr_is_base32_nstd(p[6], alphabet) ||
337  !fr_is_base32_nstd(p[7], alphabet)) break;
338 
339  if (fr_dbuff_in_bytes(&our_out,
340  (alphabet[us(p[0])] << 3) | (alphabet[us(p[1])] >> 2),
341  (alphabet[us(p[1])] << 6) | (alphabet[us(p[2])] << 1) | (alphabet[us(p[3])] >> 4),
342  (alphabet[us(p[3])] << 4) | (alphabet[us(p[4])] >> 1),
343  (alphabet[us(p[4])] << 7) | (alphabet[us(p[5])] << 2) | (alphabet[us(p[6])] >> 3),
344  (alphabet[us(p[6])] << 5) | alphabet[us(p[7])]) != 5) {
345  oob:
346  fr_strerror_printf("Output buffer too small, needed at least %zu bytes",
347  fr_dbuff_used(&our_out) + 1);
348 
350 
351  FR_SBUFF_ERROR_RETURN(&our_in);
352  }
353 
354  fr_sbuff_advance(&our_in, 8);
355  }
356 
357  fr_sbuff_marker(&m_final, &our_in);
358 
359  /*
360  * Find the first non-base32 char
361  */
362  while (fr_sbuff_extend(&our_in) && fr_is_base32_nstd(fr_sbuff_char(&our_in, '\0'), alphabet)) {
363  fr_sbuff_advance(&our_in, 1);
364  }
365 
366  len = fr_sbuff_behind(&m_final);
367  switch (len) {
368  case 0: /* Final quantum is 40 bits */
369  pad = 0;
370  break;
371 
372  case 2: /* Final quantum is 8 bits */
373  {
374  char *p = fr_sbuff_current(&m_final);
375 
376  if (fr_dbuff_in_bytes(&our_out,
377  (alphabet[us(p[0])] << 3) | (alphabet[us(p[1])] >> 2)) != 1) goto oob;
378  pad = 6;
379  }
380  break;
381 
382  case 4: /* Final quantum is 16 bits */
383  {
384  char *p = fr_sbuff_current(&m_final);
385 
386  if (fr_dbuff_in_bytes(&our_out,
387  (alphabet[us(p[0])] << 3) | (alphabet[us(p[1])] >> 2),
388  (alphabet[us(p[1])] << 6) | (alphabet[us(p[2])] << 1) | (alphabet[us(p[3])] >> 4))
389  != 2) goto oob;
390  pad = 4;
391  }
392  break;
393 
394  case 5: /* Final quantum is 24 bits */
395  {
396  char *p = fr_sbuff_current(&m_final);
397 
398  if (fr_dbuff_in_bytes(&our_out,
399  (alphabet[us(p[0])] << 3) | (alphabet[us(p[1])] >> 2),
400  (alphabet[us(p[1])] << 6) | (alphabet[us(p[2])] << 1) | (alphabet[us(p[3])] >> 4),
401  (alphabet[us(p[3])] << 4) | (alphabet[us(p[4])] >> 1)) != 3) goto oob;
402  pad = 3;
403  }
404  break;
405 
406  case 7: /* Final quantum is 32 bits */
407  {
408  char *p = fr_sbuff_current(&m_final);
409 
410  if (fr_dbuff_in_bytes(&our_out,
411  (alphabet[us(p[0])] << 3) | (alphabet[us(p[1])] >> 2),
412  (alphabet[us(p[1])] << 6) | (alphabet[us(p[2])] << 1) | (alphabet[us(p[3])] >> 4),
413  (alphabet[us(p[3])] << 4) | (alphabet[us(p[4])] >> 1),
414  (alphabet[us(p[4])] << 7) | (alphabet[us(p[5])] << 2) | (alphabet[us(p[6])] >> 3))
415  != 4) goto oob;
416  pad = 1;
417  }
418  break;
419 
420  default:
421  fr_strerror_printf("Invalid base32 final quantum length (%zu)", len);
422 
423  bad_format:
425 
426  FR_SBUFF_ERROR_RETURN(&our_in);
427  }
428 
429  if (expect_padding) {
430  uint8_t i;
431  for (i = 0; i < pad; i++) {
432  if (!fr_sbuff_extend(&our_in)) {
433  fr_strerror_printf("Missing padding '=' at end of base32 string. "
434  "Expected %u padding char(s)", pad);
435 
437 
438  goto bad_format;
439  }
440  if (!fr_sbuff_next_if_char(&our_in, '=')) {
441  fr_strerror_printf("Found non-padding char '%c' at end of base32 string",
442  fr_sbuff_char(&our_in, '\0'));
443 
445 
446  goto bad_format;
447  }
448  }
449  }
450 
451  if (no_trailing && fr_sbuff_extend(&our_in)) {
452  fr_strerror_printf("Found trailing garbage '%c' at end of base32 string",
453  fr_sbuff_char(&our_in, '\0'));
454 
456 
457  FR_SBUFF_ERROR_RETURN(&our_in);
458  }
459 
460  if (err) *err = FR_SBUFF_PARSE_OK;
461 
462  fr_sbuff_set(in, &our_in);
463  return fr_dbuff_set(out, &our_out);
464 }
char const fr_base32_alphabet_encode[UINT8_MAX]
Encode/decode binary data using printable characters (base32 format)
Definition: base32.c:32
ssize_t fr_base32_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: base32.c:190
uint8_t const fr_base32_alphabet_decode[UINT8_MAX]
Definition: base32.c:67
#define us(x)
Definition: base32.c:30
uint8_t const fr_base32_hex_alphabet_decode[UINT8_MAX]
Definition: base32.c:140
fr_slen_t fr_base32_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: base32.c:315
char const fr_base32_hex_alphabet_encode[UINT8_MAX]
Definition: base32.c:105
static bool fr_is_base32_nstd(char c, uint8_t const alphabet[static UINT8_MAX])
Check if char is in base32 alphabet.
Definition: base32.h:54
#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 F16(_idx, _val)
Definition: build.h:215
#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_OK
No error.
Definition: merged_model.c:46
@ 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