1 #include <assert.h>
2 #include <errno.h>
3 #include <limits.h>
4 #include <stddef.h>
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "core.h"
10 #include "utils.h"
11
12 /* Derived from original code by CodesInChaos */
13 char *
sodium_bin2hex(char * const hex,const size_t hex_maxlen,const unsigned char * const bin,const size_t bin_len)14 sodium_bin2hex(char *const hex, const size_t hex_maxlen,
15 const unsigned char *const bin, const size_t bin_len)
16 {
17 size_t i = (size_t) 0U;
18 unsigned int x;
19 int b;
20 int c;
21
22 if (bin_len >= SIZE_MAX / 2 || hex_maxlen <= bin_len * 2U) {
23 sodium_misuse(); /* LCOV_EXCL_LINE */
24 }
25 while (i < bin_len) {
26 c = bin[i] & 0xf;
27 b = bin[i] >> 4;
28 x = (unsigned char) (87U + c + (((c - 10U) >> 8) & ~38U)) << 8 |
29 (unsigned char) (87U + b + (((b - 10U) >> 8) & ~38U));
30 hex[i * 2U] = (char) x;
31 x >>= 8;
32 hex[i * 2U + 1U] = (char) x;
33 i++;
34 }
35 hex[i * 2U] = 0U;
36
37 return hex;
38 }
39
40 int
sodium_hex2bin(unsigned char * const bin,const size_t bin_maxlen,const char * const hex,const size_t hex_len,const char * const ignore,size_t * const bin_len,const char ** const hex_end)41 sodium_hex2bin(unsigned char *const bin, const size_t bin_maxlen,
42 const char *const hex, const size_t hex_len,
43 const char *const ignore, size_t *const bin_len,
44 const char **const hex_end)
45 {
46 size_t bin_pos = (size_t) 0U;
47 size_t hex_pos = (size_t) 0U;
48 int ret = 0;
49 unsigned char c;
50 unsigned char c_acc = 0U;
51 unsigned char c_alpha0, c_alpha;
52 unsigned char c_num0, c_num;
53 unsigned char c_val;
54 unsigned char state = 0U;
55
56 while (hex_pos < hex_len) {
57 c = (unsigned char) hex[hex_pos];
58 c_num = c ^ 48U;
59 c_num0 = (c_num - 10U) >> 8;
60 c_alpha = (c & ~32U) - 55U;
61 c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
62 if ((c_num0 | c_alpha0) == 0U) {
63 if (ignore != NULL && state == 0U && strchr(ignore, c) != NULL) {
64 hex_pos++;
65 continue;
66 }
67 break;
68 }
69 c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
70 if (bin_pos >= bin_maxlen) {
71 ret = -1;
72 errno = ERANGE;
73 break;
74 }
75 if (state == 0U) {
76 c_acc = c_val * 16U;
77 } else {
78 bin[bin_pos++] = c_acc | c_val;
79 }
80 state = ~state;
81 hex_pos++;
82 }
83 if (state != 0U) {
84 hex_pos--;
85 errno = EINVAL;
86 ret = -1;
87 }
88 if (ret != 0) {
89 bin_pos = (size_t) 0U;
90 }
91 if (hex_end != NULL) {
92 *hex_end = &hex[hex_pos];
93 } else if (hex_pos != hex_len) {
94 errno = EINVAL;
95 ret = -1;
96 }
97 if (bin_len != NULL) {
98 *bin_len = bin_pos;
99 }
100 return ret;
101 }
102
103 /*
104 * Some macros for constant-time comparisons. These work over values in
105 * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
106 *
107 * Original code by Thomas Pornin.
108 */
109 #define EQ(x, y) \
110 ((((0U - ((unsigned int) (x) ^ (unsigned int) (y))) >> 8) & 0xFF) ^ 0xFF)
111 #define GT(x, y) ((((unsigned int) (y) - (unsigned int) (x)) >> 8) & 0xFF)
112 #define GE(x, y) (GT(y, x) ^ 0xFF)
113 #define LT(x, y) GT(y, x)
114 #define LE(x, y) GE(y, x)
115
116 static int
b64_byte_to_char(unsigned int x)117 b64_byte_to_char(unsigned int x)
118 {
119 return (LT(x, 26) & (x + 'A')) |
120 (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) |
121 (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') |
122 (EQ(x, 63) & '/');
123 }
124
125 static unsigned int
b64_char_to_byte(int c)126 b64_char_to_byte(int c)
127 {
128 const unsigned int x =
129 (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
130 (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
131 (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
132 (EQ(c, '/') & 63);
133
134 return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
135 }
136
137 static int
b64_byte_to_urlsafe_char(unsigned int x)138 b64_byte_to_urlsafe_char(unsigned int x)
139 {
140 return (LT(x, 26) & (x + 'A')) |
141 (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) |
142 (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '-') |
143 (EQ(x, 63) & '_');
144 }
145
146 static unsigned int
b64_urlsafe_char_to_byte(int c)147 b64_urlsafe_char_to_byte(int c)
148 {
149 const unsigned x =
150 (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
151 (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
152 (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '-') & 62) |
153 (EQ(c, '_') & 63);
154
155 return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
156 }
157
158
159 #define VARIANT_NO_PADDING_MASK 0x2U
160 #define VARIANT_URLSAFE_MASK 0x4U
161
162 static void
sodium_base64_check_variant(const int variant)163 sodium_base64_check_variant(const int variant)
164 {
165 if ((((unsigned int) variant) & ~ 0x6U) != 0x1U) {
166 sodium_misuse();
167 }
168 }
169
170 size_t
sodium_base64_encoded_len(const size_t bin_len,const int variant)171 sodium_base64_encoded_len(const size_t bin_len, const int variant)
172 {
173 sodium_base64_check_variant(variant);
174
175 return sodium_base64_ENCODED_LEN(bin_len, variant);
176 }
177
178 char *
sodium_bin2base64(char * const b64,const size_t b64_maxlen,const unsigned char * const bin,const size_t bin_len,const int variant)179 sodium_bin2base64(char * const b64, const size_t b64_maxlen,
180 const unsigned char * const bin, const size_t bin_len,
181 const int variant)
182 {
183 size_t acc_len = (size_t) 0;
184 size_t b64_len;
185 size_t b64_pos = (size_t) 0;
186 size_t bin_pos = (size_t) 0;
187 size_t nibbles;
188 size_t remainder;
189 unsigned int acc = 0U;
190
191 sodium_base64_check_variant(variant);
192 nibbles = bin_len / 3;
193 remainder = bin_len - 3 * nibbles;
194 b64_len = nibbles * 4;
195 if (remainder != 0) {
196 if ((((unsigned int) variant) & VARIANT_NO_PADDING_MASK) == 0U) {
197 b64_len += 4;
198 } else {
199 b64_len += 2 + (remainder >> 1);
200 }
201 }
202 if (b64_maxlen <= b64_len) {
203 sodium_misuse();
204 }
205 if ((((unsigned int) variant) & VARIANT_URLSAFE_MASK) != 0U) {
206 while (bin_pos < bin_len) {
207 acc = (acc << 8) + bin[bin_pos++];
208 acc_len += 8;
209 while (acc_len >= 6) {
210 acc_len -= 6;
211 b64[b64_pos++] = (char) b64_byte_to_urlsafe_char((acc >> acc_len) & 0x3F);
212 }
213 }
214 if (acc_len > 0) {
215 b64[b64_pos++] = (char) b64_byte_to_urlsafe_char((acc << (6 - acc_len)) & 0x3F);
216 }
217 } else {
218 while (bin_pos < bin_len) {
219 acc = (acc << 8) + bin[bin_pos++];
220 acc_len += 8;
221 while (acc_len >= 6) {
222 acc_len -= 6;
223 b64[b64_pos++] = (char) b64_byte_to_char((acc >> acc_len) & 0x3F);
224 }
225 }
226 if (acc_len > 0) {
227 b64[b64_pos++] = (char) b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
228 }
229 }
230 assert(b64_pos <= b64_len);
231 while (b64_pos < b64_len) {
232 b64[b64_pos++] = '=';
233 }
234 do {
235 b64[b64_pos++] = 0U;
236 } while (b64_pos < b64_maxlen);
237
238 return b64;
239 }
240
241 static int
_sodium_base642bin_skip_padding(const char * const b64,const size_t b64_len,size_t * const b64_pos_p,const char * const ignore,size_t padding_len)242 _sodium_base642bin_skip_padding(const char * const b64, const size_t b64_len,
243 size_t * const b64_pos_p,
244 const char * const ignore, size_t padding_len)
245 {
246 int c;
247
248 while (padding_len > 0) {
249 if (*b64_pos_p >= b64_len) {
250 errno = ERANGE;
251 return -1;
252 }
253 c = b64[*b64_pos_p];
254 if (c == '=') {
255 padding_len--;
256 } else if (ignore == NULL || strchr(ignore, c) == NULL) {
257 errno = EINVAL;
258 return -1;
259 }
260 (*b64_pos_p)++;
261 }
262 return 0;
263 }
264
265 int
sodium_base642bin(unsigned char * const bin,const size_t bin_maxlen,const char * const b64,const size_t b64_len,const char * const ignore,size_t * const bin_len,const char ** const b64_end,const int variant)266 sodium_base642bin(unsigned char * const bin, const size_t bin_maxlen,
267 const char * const b64, const size_t b64_len,
268 const char * const ignore, size_t * const bin_len,
269 const char ** const b64_end, const int variant)
270 {
271 size_t acc_len = (size_t) 0;
272 size_t b64_pos = (size_t) 0;
273 size_t bin_pos = (size_t) 0;
274 int is_urlsafe;
275 int ret = 0;
276 unsigned int acc = 0U;
277 unsigned int d;
278 char c;
279
280 sodium_base64_check_variant(variant);
281 is_urlsafe = ((unsigned int) variant) & VARIANT_URLSAFE_MASK;
282 while (b64_pos < b64_len) {
283 c = b64[b64_pos];
284 if (is_urlsafe) {
285 d = b64_urlsafe_char_to_byte(c);
286 } else {
287 d = b64_char_to_byte(c);
288 }
289 if (d == 0xFF) {
290 if (ignore != NULL && strchr(ignore, c) != NULL) {
291 b64_pos++;
292 continue;
293 }
294 break;
295 }
296 acc = (acc << 6) + d;
297 acc_len += 6;
298 if (acc_len >= 8) {
299 acc_len -= 8;
300 if (bin_pos >= bin_maxlen) {
301 errno = ERANGE;
302 ret = -1;
303 break;
304 }
305 bin[bin_pos++] = (acc >> acc_len) & 0xFF;
306 }
307 b64_pos++;
308 }
309 if (acc_len > 4U || (acc & ((1U << acc_len) - 1U)) != 0U) {
310 ret = -1;
311 } else if (ret == 0 &&
312 (((unsigned int) variant) & VARIANT_NO_PADDING_MASK) == 0U) {
313 ret = _sodium_base642bin_skip_padding(b64, b64_len, &b64_pos, ignore,
314 acc_len / 2);
315 }
316 if (ret != 0) {
317 bin_pos = (size_t) 0U;
318 } else if (ignore != NULL) {
319 while (b64_pos < b64_len && strchr(ignore, b64[b64_pos]) != NULL) {
320 b64_pos++;
321 }
322 }
323 if (b64_end != NULL) {
324 *b64_end = &b64[b64_pos];
325 } else if (b64_pos != b64_len) {
326 errno = EINVAL;
327 ret = -1;
328 }
329 if (bin_len != NULL) {
330 *bin_len = bin_pos;
331 }
332 return ret;
333 }
334