Line data Source code
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 : #include "mbedtls/error.h"
18 :
19 : #include <stdint.h>
20 :
21 : #if defined(MBEDTLS_SELF_TEST)
22 : #include <string.h>
23 : #include "mbedtls/platform.h"
24 : #endif /* MBEDTLS_SELF_TEST */
25 :
26 : MBEDTLS_STATIC_TESTABLE
27 0 : unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
28 : {
29 0 : unsigned char digit = 0;
30 : /* For each range of values, if value is in that range, mask digit with
31 : * the corresponding value. Since value can only be in a single range,
32 : * only at most one masking will change digit. */
33 0 : digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
34 0 : digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
35 0 : digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
36 0 : digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
37 0 : digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
38 0 : return digit;
39 : }
40 :
41 : MBEDTLS_STATIC_TESTABLE
42 255984 : signed char mbedtls_ct_base64_dec_value(unsigned char c)
43 : {
44 255984 : unsigned char val = 0;
45 : /* For each range of digits, if c is in that range, mask val with
46 : * the corresponding value. Since c can only be in a single range,
47 : * only at most one masking will change val. Set val to one plus
48 : * the desired value so that it stays 0 if c is in none of the ranges. */
49 255984 : val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' + 0 + 1);
50 255984 : val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
51 255984 : val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
52 255984 : val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
53 255984 : val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1);
54 : /* At this point, val is 0 if c is an invalid digit and v+1 if c is
55 : * a digit with the value v. */
56 255984 : return val - 1;
57 : }
58 :
59 : /*
60 : * Encode a buffer into base64 format
61 : */
62 0 : int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
63 : const unsigned char *src, size_t slen)
64 : {
65 : size_t i, n;
66 : int C1, C2, C3;
67 : unsigned char *p;
68 :
69 0 : if (slen == 0) {
70 0 : *olen = 0;
71 0 : return 0;
72 : }
73 :
74 0 : n = slen / 3 + (slen % 3 != 0);
75 :
76 0 : if (n > (SIZE_MAX - 1) / 4) {
77 0 : *olen = SIZE_MAX;
78 0 : return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
79 : }
80 :
81 0 : n *= 4;
82 :
83 0 : if ((dlen < n + 1) || (NULL == dst)) {
84 0 : *olen = n + 1;
85 0 : return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
86 : }
87 :
88 0 : n = (slen / 3) * 3;
89 :
90 0 : for (i = 0, p = dst; i < n; i += 3) {
91 0 : C1 = *src++;
92 0 : C2 = *src++;
93 0 : C3 = *src++;
94 :
95 0 : *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
96 0 : *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
97 0 : & 0x3F);
98 0 : *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6))
99 0 : & 0x3F);
100 0 : *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F);
101 : }
102 :
103 0 : if (i < slen) {
104 0 : C1 = *src++;
105 0 : C2 = ((i + 1) < slen) ? *src++ : 0;
106 :
107 0 : *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
108 0 : *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
109 0 : & 0x3F);
110 :
111 0 : if ((i + 1) < slen) {
112 0 : *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
113 : } else {
114 0 : *p++ = '=';
115 : }
116 :
117 0 : *p++ = '=';
118 : }
119 :
120 0 : *olen = (size_t) (p - dst);
121 0 : *p = 0;
122 :
123 0 : return 0;
124 : }
125 :
126 : /*
127 : * Decode a base64-formatted buffer
128 : */
129 364 : int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
130 : const unsigned char *src, size_t slen)
131 : {
132 : size_t i; /* index in source */
133 : size_t n; /* number of digits or trailing = in source */
134 : uint32_t x; /* value accumulator */
135 364 : unsigned accumulated_digits = 0;
136 364 : unsigned equals = 0;
137 364 : int spaces_present = 0;
138 : unsigned char *p;
139 :
140 : /* First pass: check for validity and get output length */
141 176516 : for (i = n = 0; i < slen; i++) {
142 : /* Skip spaces before checking for EOL */
143 176152 : spaces_present = 0;
144 176152 : while (i < slen && src[i] == ' ') {
145 0 : ++i;
146 0 : spaces_present = 1;
147 : }
148 :
149 : /* Spaces at end of buffer are OK */
150 176152 : if (i == slen) {
151 0 : break;
152 : }
153 :
154 176152 : if ((slen - i) >= 2 &&
155 175788 : src[i] == '\r' && src[i + 1] == '\n') {
156 2748 : continue;
157 : }
158 :
159 173404 : if (src[i] == '\n') {
160 2748 : continue;
161 : }
162 :
163 : /* Space inside a line is an error */
164 170656 : if (spaces_present) {
165 0 : return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
166 : }
167 :
168 170656 : if (src[i] > 127) {
169 0 : return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
170 : }
171 :
172 170656 : if (src[i] == '=') {
173 0 : if (++equals > 2) {
174 0 : return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
175 : }
176 : } else {
177 170656 : if (equals != 0) {
178 0 : return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
179 : }
180 170656 : if (mbedtls_ct_base64_dec_value(src[i]) < 0) {
181 0 : return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
182 : }
183 : }
184 170656 : n++;
185 : }
186 :
187 : /* In valid base64, the number of digits (n-equals) is always of the form
188 : * 4*k, 4*k+2 or *4k+3. Also, the number n of digits plus the number of
189 : * equal signs at the end is always a multiple of 4. */
190 364 : if ((n - equals) % 4 == 1) {
191 0 : return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
192 : }
193 364 : if (n % 4 != 0) {
194 0 : return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
195 : }
196 :
197 : /* We've determined that the input is valid, and that it contains
198 : * exactly k blocks of digits-or-equals, with n = 4 * k,
199 : * and equals only present at the end of the last block if at all.
200 : * Now we can calculate the length of the output.
201 : *
202 : * Each block of 4 digits in the input map to 3 bytes of output.
203 : * For the last block:
204 : * - abcd (where abcd are digits) is a full 3-byte block;
205 : * - abc= means 1 byte less than a full 3-byte block of output;
206 : * - ab== means 2 bytes less than a full 3-byte block of output;
207 : * - a==== and ==== is rejected above.
208 : */
209 364 : *olen = (n / 4) * 3 - equals;
210 :
211 : /* If the output buffer is too small, signal this and stop here.
212 : * Also, as documented, stop here if `dst` is null, independently of
213 : * `dlen`.
214 : *
215 : * There is an edge case when the output is empty: in this case,
216 : * `dlen == 0` with `dst == NULL` is valid (on some platforms,
217 : * `malloc(0)` returns `NULL`). Since the call is valid, we return
218 : * 0 in this case.
219 : */
220 364 : if ((*olen != 0 && dst == NULL) || dlen < *olen) {
221 182 : return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
222 : }
223 :
224 88258 : for (x = 0, p = dst; i > 0; i--, src++) {
225 88076 : if (*src == '\r' || *src == '\n' || *src == ' ') {
226 2748 : continue;
227 : }
228 85328 : if (*src == '=') {
229 : /* We already know from the first loop that equal signs are
230 : * only at the end. */
231 0 : break;
232 : }
233 85328 : x = x << 6;
234 85328 : x |= mbedtls_ct_base64_dec_value(*src);
235 :
236 85328 : if (++accumulated_digits == 4) {
237 21332 : accumulated_digits = 0;
238 21332 : *p++ = MBEDTLS_BYTE_2(x);
239 21332 : *p++ = MBEDTLS_BYTE_1(x);
240 21332 : *p++ = MBEDTLS_BYTE_0(x);
241 : }
242 : }
243 182 : if (accumulated_digits == 3) {
244 0 : *p++ = MBEDTLS_BYTE_2(x << 6);
245 0 : *p++ = MBEDTLS_BYTE_1(x << 6);
246 182 : } else if (accumulated_digits == 2) {
247 0 : *p++ = MBEDTLS_BYTE_2(x << 12);
248 : }
249 :
250 182 : if (*olen != (size_t) (p - dst)) {
251 0 : return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
252 : }
253 :
254 182 : return 0;
255 : }
256 :
257 : #if defined(MBEDTLS_SELF_TEST)
258 :
259 : static const unsigned char base64_test_dec[64] =
260 : {
261 : 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
262 : 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
263 : 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
264 : 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
265 : 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
266 : 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
267 : 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
268 : 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
269 : };
270 :
271 : static const unsigned char base64_test_enc[] =
272 : "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
273 : "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
274 :
275 : /*
276 : * Checkup routine
277 : */
278 0 : int mbedtls_base64_self_test(int verbose)
279 : {
280 : size_t len;
281 : const unsigned char *src;
282 : unsigned char buffer[128];
283 :
284 0 : if (verbose != 0) {
285 0 : mbedtls_printf(" Base64 encoding test: ");
286 : }
287 :
288 0 : src = base64_test_dec;
289 :
290 0 : if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
291 0 : memcmp(base64_test_enc, buffer, 88) != 0) {
292 0 : if (verbose != 0) {
293 0 : mbedtls_printf("failed\n");
294 : }
295 :
296 0 : return 1;
297 : }
298 :
299 0 : if (verbose != 0) {
300 0 : mbedtls_printf("passed\n Base64 decoding test: ");
301 : }
302 :
303 0 : src = base64_test_enc;
304 :
305 0 : if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
306 0 : memcmp(base64_test_dec, buffer, 64) != 0) {
307 0 : if (verbose != 0) {
308 0 : mbedtls_printf("failed\n");
309 : }
310 :
311 0 : return 1;
312 : }
313 :
314 0 : if (verbose != 0) {
315 0 : mbedtls_printf("passed\n\n");
316 : }
317 :
318 0 : return 0;
319 : }
320 :
321 : #endif /* MBEDTLS_SELF_TEST */
322 :
323 : #endif /* MBEDTLS_BASE64_C */
|