The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
25RCSID("$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);
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);
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);
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);
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);
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: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 F16(_idx, _val)
Definition build.h:217
#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_OK
No error.
@ 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