xref: /freebsd/sys/contrib/libsodium/src/libsodium/sodium/codecs.c (revision 3611ec604864a7d4dcc9a3ea898c80eb35eef8a0)
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