1 /*
2 * RFC 1521 base64 encoding/decoding
3 *
4 * Copyright The Mbed TLS Contributors
5 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6 */
7
8 #include <limits.h>
9
10 #include "common.h"
11
12 #if defined(MBEDTLS_BASE64_C)
13
14 #include "mbedtls/base64.h"
15 #include "base64_internal.h"
16 #include "constant_time_internal.h"
17
18 #include <stdint.h>
19
20 #if defined(MBEDTLS_SELF_TEST)
21 #include <string.h>
22 #include "mbedtls/platform.h"
23 #endif /* MBEDTLS_SELF_TEST */
24
25 MBEDTLS_STATIC_TESTABLE
mbedtls_ct_base64_enc_char(unsigned char value)26 unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
27 {
28 unsigned char digit = 0;
29 /* For each range of values, if value is in that range, mask digit with
30 * the corresponding value. Since value can only be in a single range,
31 * only at most one masking will change digit. */
32 digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
33 digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
34 digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
35 digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
36 digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
37 return digit;
38 }
39
40 MBEDTLS_STATIC_TESTABLE
mbedtls_ct_base64_dec_value(unsigned char c)41 signed char mbedtls_ct_base64_dec_value(unsigned char c)
42 {
43 unsigned char val = 0;
44 /* For each range of digits, if c is in that range, mask val with
45 * the corresponding value. Since c can only be in a single range,
46 * only at most one masking will change val. Set val to one plus
47 * the desired value so that it stays 0 if c is in none of the ranges. */
48 val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' + 0 + 1);
49 val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
50 val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
51 val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
52 val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1);
53 /* At this point, val is 0 if c is an invalid digit and v+1 if c is
54 * a digit with the value v. */
55 return val - 1;
56 }
57
58 /*
59 * Encode a buffer into base64 format
60 */
mbedtls_base64_encode(unsigned char * dst,size_t dlen,size_t * olen,const unsigned char * src,size_t slen)61 int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
62 const unsigned char *src, size_t slen)
63 {
64 size_t i, n;
65 int C1, C2, C3;
66 unsigned char *p;
67
68 if (slen == 0) {
69 *olen = 0;
70 return 0;
71 }
72
73 n = slen / 3 + (slen % 3 != 0);
74
75 if (n > (SIZE_MAX - 1) / 4) {
76 *olen = SIZE_MAX;
77 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
78 }
79
80 n *= 4;
81
82 if ((dlen < n + 1) || (NULL == dst)) {
83 *olen = n + 1;
84 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
85 }
86
87 n = (slen / 3) * 3;
88
89 for (i = 0, p = dst; i < n; i += 3) {
90 C1 = *src++;
91 C2 = *src++;
92 C3 = *src++;
93
94 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
95 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
96 & 0x3F);
97 *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6))
98 & 0x3F);
99 *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F);
100 }
101
102 if (i < slen) {
103 C1 = *src++;
104 C2 = ((i + 1) < slen) ? *src++ : 0;
105
106 *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
107 *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
108 & 0x3F);
109
110 if ((i + 1) < slen) {
111 *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
112 } else {
113 *p++ = '=';
114 }
115
116 *p++ = '=';
117 }
118
119 *olen = (size_t) (p - dst);
120 *p = 0;
121
122 return 0;
123 }
124
125 /*
126 * Decode a base64-formatted buffer
127 */
mbedtls_base64_decode(unsigned char * dst,size_t dlen,size_t * olen,const unsigned char * src,size_t slen)128 int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
129 const unsigned char *src, size_t slen)
130 {
131 size_t i; /* index in source */
132 size_t n; /* number of digits or trailing = in source */
133 uint32_t x; /* value accumulator */
134 unsigned accumulated_digits = 0;
135 unsigned equals = 0;
136 int spaces_present = 0;
137 unsigned char *p;
138
139 /* First pass: check for validity and get output length */
140 for (i = n = 0; i < slen; i++) {
141 /* Skip spaces before checking for EOL */
142 spaces_present = 0;
143 while (i < slen && src[i] == ' ') {
144 ++i;
145 spaces_present = 1;
146 }
147
148 /* Spaces at end of buffer are OK */
149 if (i == slen) {
150 break;
151 }
152
153 if ((slen - i) >= 2 &&
154 src[i] == '\r' && src[i + 1] == '\n') {
155 continue;
156 }
157
158 if (src[i] == '\n') {
159 continue;
160 }
161
162 /* Space inside a line is an error */
163 if (spaces_present) {
164 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
165 }
166
167 if (src[i] > 127) {
168 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
169 }
170
171 if (src[i] == '=') {
172 if (++equals > 2) {
173 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
174 }
175 } else {
176 if (equals != 0) {
177 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
178 }
179 if (mbedtls_ct_base64_dec_value(src[i]) < 0) {
180 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
181 }
182 }
183 n++;
184 }
185
186 if (n == 0) {
187 *olen = 0;
188 return 0;
189 }
190
191 /* The following expression is to calculate the following formula without
192 * risk of integer overflow in n:
193 * n = ( ( n * 6 ) + 7 ) >> 3;
194 */
195 n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
196 n -= equals;
197
198 if (dst == NULL || dlen < n) {
199 *olen = n;
200 return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
201 }
202
203 equals = 0;
204 for (x = 0, p = dst; i > 0; i--, src++) {
205 if (*src == '\r' || *src == '\n' || *src == ' ') {
206 continue;
207 }
208
209 x = x << 6;
210 if (*src == '=') {
211 ++equals;
212 } else {
213 x |= mbedtls_ct_base64_dec_value(*src);
214 }
215
216 if (++accumulated_digits == 4) {
217 accumulated_digits = 0;
218 *p++ = MBEDTLS_BYTE_2(x);
219 if (equals <= 1) {
220 *p++ = MBEDTLS_BYTE_1(x);
221 }
222 if (equals <= 0) {
223 *p++ = MBEDTLS_BYTE_0(x);
224 }
225 }
226 }
227
228 *olen = (size_t) (p - dst);
229
230 return 0;
231 }
232
233 #if defined(MBEDTLS_SELF_TEST)
234
235 static const unsigned char base64_test_dec[64] =
236 {
237 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
238 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
239 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
240 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
241 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
242 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
243 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
244 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
245 };
246
247 static const unsigned char base64_test_enc[] =
248 "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
249 "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
250
251 /*
252 * Checkup routine
253 */
mbedtls_base64_self_test(int verbose)254 int mbedtls_base64_self_test(int verbose)
255 {
256 size_t len;
257 const unsigned char *src;
258 unsigned char buffer[128];
259
260 if (verbose != 0) {
261 mbedtls_printf(" Base64 encoding test: ");
262 }
263
264 src = base64_test_dec;
265
266 if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
267 memcmp(base64_test_enc, buffer, 88) != 0) {
268 if (verbose != 0) {
269 mbedtls_printf("failed\n");
270 }
271
272 return 1;
273 }
274
275 if (verbose != 0) {
276 mbedtls_printf("passed\n Base64 decoding test: ");
277 }
278
279 src = base64_test_enc;
280
281 if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
282 memcmp(base64_test_dec, buffer, 64) != 0) {
283 if (verbose != 0) {
284 mbedtls_printf("failed\n");
285 }
286
287 return 1;
288 }
289
290 if (verbose != 0) {
291 mbedtls_printf("passed\n\n");
292 }
293
294 return 0;
295 }
296
297 #endif /* MBEDTLS_SELF_TEST */
298
299 #endif /* MBEDTLS_BASE64_C */
300