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 */
13char *
14sodium_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
40int
41sodium_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
116static int
117b64_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
125static unsigned int
126b64_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
137static int
138b64_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
146static unsigned int
147b64_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
162static void
163sodium_base64_check_variant(const int variant)
164{
165    if ((((unsigned int) variant) & ~ 0x6U) != 0x1U) {
166        sodium_misuse();
167    }
168}
169
170size_t
171sodium_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
178char *
179sodium_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
241static int
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
265int
266sodium_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