The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
base16.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 (base16 format - hex)
18  *
19  * @see RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
20  *
21  * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22  */
23 RCSID("$Id: 691c9659b38d8cfd2ebcdb7356625cd751f620a4 $")
24 
25 #include <freeradius-devel/util/base16.h>
26 #define us(x) (uint8_t) x
27 
28 /** lower case encode alphabet for base16
29  *
30  */
32  [0] = '0',
33  [1] = '1',
34  [2] = '2',
35  [3] = '3',
36  [4] = '4',
37  [5] = '5',
38  [6] = '6',
39  [7] = '7',
40  [8] = '8',
41  [9] = '9',
42  [10] = 'a',
43  [11] = 'b',
44  [12] = 'c',
45  [13] = 'd',
46  [14] = 'e',
47  [15] = 'f'
48 };
49 
50 /** lower case encode alphabet for base16
51  *
52  */
54  [0] = '0',
55  [1] = '1',
56  [2] = '2',
57  [3] = '3',
58  [4] = '4',
59  [5] = '5',
60  [6] = '6',
61  [7] = '7',
62  [8] = '8',
63  [9] = '9',
64  [10] = 'A',
65  [11] = 'B',
66  [12] = 'C',
67  [13] = 'D',
68  [14] = 'E',
69  [15] = 'F'
70 };
71 
72 /** Mixed case decode alphabet for base16
73  *
74  */
76  F32(0, UINT8_MAX), F16(32, UINT8_MAX),
77  ['0'] = 0,
78  ['1'] = 1,
79  ['2'] = 2,
80  ['3'] = 3,
81  ['4'] = 4,
82  ['5'] = 5,
83  ['6'] = 6,
84  ['7'] = 7,
85  ['8'] = 8,
86  ['9'] = 9,
87  F4(58, UINT8_MAX), F2(62, UINT8_MAX), F1(64, UINT8_MAX),
88  ['A'] = 10, /* Uppercase */
89  ['B'] = 11,
90  ['C'] = 12,
91  ['D'] = 13,
92  ['E'] = 14,
93  ['F'] = 15,
94  F16(71, UINT8_MAX), F8(87, UINT8_MAX), F2(95, UINT8_MAX),
95  ['a'] = 10, /* Lowercase */
96  ['b'] = 11,
97  ['c'] = 12,
98  ['d'] = 13,
99  ['e'] = 14,
100  ['f'] = 15,
101  F128(103, UINT8_MAX), F16(231, UINT8_MAX), F8(247, UINT8_MAX), F1(255, UINT8_MAX)
102 };
103 
104 /** Convert binary data to a hex string
105  *
106  * Ascii encoded hex string will not be prefixed with '0x'
107  *
108  * @param[out] out Output buffer to write to.
109  * @param[in] in input.
110  * @param[in] alphabet to use for encode.
111  * @return
112  * - >=0 the number of bytes written to out.
113  * - <0 number of bytes we would have needed to print the next hexit.
114  */
116 {
117  fr_sbuff_t our_out = FR_SBUFF(out);
118  fr_dbuff_t our_in = FR_DBUFF(in);
119 
120  while (fr_dbuff_extend(&our_in)) {
121  uint8_t a = *fr_dbuff_current(&our_in);
122 
123  FR_SBUFF_IN_CHAR_RETURN(&our_out, alphabet[us(a >> 4)], (alphabet[us(a & 0x0f)]));
124  fr_dbuff_advance(&our_in, 1);
125  }
126 
127  fr_sbuff_terminate(&our_out); /* Ensure this is terminated, even on zero length input */
128  fr_dbuff_set(in, &our_in);
129  FR_SBUFF_SET_RETURN(out, &our_out);
130 }
131 
132 /** Decode base16 encoded input
133  *
134  * @param[out] err If non-null contains any parse errors.
135  * @param[out] out Where to write the decoded binary data.
136  * @param[in] in String to decode.
137  * @param[in] no_trailing Error out if we find non-base16 characters
138  * at the end of the string.
139  * @param[in] alphabet to use for decoding.
140  * @return
141  * - < 0 on failure. The offset where the decoding error occurred as a negative integer.
142  * - Length of decoded data.
143  */
145  bool no_trailing, uint8_t const alphabet[static UINT8_MAX + 1])
146 {
147  fr_sbuff_t our_in = FR_SBUFF(in);
148  fr_dbuff_t our_out = FR_DBUFF(out);
149 
150  while (fr_sbuff_extend_lowat(NULL, &our_in, 2) >= 2) {
151  char *p = fr_sbuff_current(&our_in);
152  bool a, b;
153 
154  a = fr_is_base16_nstd(p[0], alphabet);
155  b = fr_is_base16_nstd(p[1], alphabet);
156  if (!a || !b) {
157  if (a && !b && no_trailing) {
159  FR_SBUFF_ERROR_RETURN(&our_in);
160  }
161  break;
162  }
163 
164  FR_DBUFF_IN_BYTES_RETURN(&our_out, (alphabet[us(p[0])] << 4) | alphabet[us(p[1])]);
165 
166  fr_sbuff_advance(&our_in, 2);
167  }
168 
169  if (err) *err = FR_SBUFF_PARSE_OK;
170 
171  fr_sbuff_set(in, &our_in);
172  return fr_dbuff_set(out, &our_out);
173 }
char const fr_base16_alphabet_encode_lc[UINT8_MAX+1]
lower case encode alphabet for base16
Definition: base16.c:31
fr_slen_t fr_base16_encode_nstd(fr_sbuff_t *out, fr_dbuff_t *in, char const alphabet[static UINT8_MAX+1])
Convert binary data to a hex string.
Definition: base16.c:115
#define us(x)
Encode/decode binary data using printable characters (base16 format - hex)
Definition: base16.c:26
char const fr_base16_alphabet_encode_uc[UINT8_MAX+1]
lower case encode alphabet for base16
Definition: base16.c:53
fr_slen_t fr_base16_decode_nstd(fr_sbuff_parse_error_t *err, fr_dbuff_t *out, fr_sbuff_t *in, bool no_trailing, uint8_t const alphabet[static UINT8_MAX+1])
Decode base16 encoded input.
Definition: base16.c:144
uint8_t const fr_base16_alphabet_decode_mc[UINT8_MAX+1]
Mixed case decode alphabet for base16.
Definition: base16.c:75
static bool fr_is_base16_nstd(char c, uint8_t const alphabet[static UINT8_MAX+1])
Check if char is in base16 alphabet.
Definition: base16.h:51
#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_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition: dbuff.h:906
#define fr_dbuff_extend(_dbuff)
Extend if no space remains.
Definition: dbuff.h:700
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition: dbuff.h:222
#define FR_DBUFF_IN_BYTES_RETURN(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1467
static fr_slen_t err
Definition: dict.h:645
static fr_slen_t in
Definition: dict.h:645
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_OK
No error.
Definition: merged_model.c:46
@ FR_SBUFF_PARSE_ERROR_TRAILING
Trailing characters found.
Definition: merged_model.c:49
#define FR_SBUFF_IN_CHAR_RETURN(_sbuff,...)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_current(_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_extend_lowat(_status, _sbuff_or_marker, _lowat)
return fr_dbuff_set(dbuff, &our_dbuff)
FR_SBUFF_SET_RETURN(sbuff, &our_sbuff)
static size_t char ** out
Definition: value.h:984