1817466cbSJens Wiklander /*
2817466cbSJens Wiklander * RFC 1521 base64 encoding/decoding
3817466cbSJens Wiklander *
47901324dSJerome Forissier * Copyright The Mbed TLS Contributors
5b0563631STom Van Eyck * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6817466cbSJens Wiklander */
7817466cbSJens Wiklander
8b0563631STom Van Eyck #include <limits.h>
9b0563631STom Van Eyck
107901324dSJerome Forissier #include "common.h"
11817466cbSJens Wiklander
12817466cbSJens Wiklander #if defined(MBEDTLS_BASE64_C)
13817466cbSJens Wiklander
14817466cbSJens Wiklander #include "mbedtls/base64.h"
15b0563631STom Van Eyck #include "base64_internal.h"
16039e02dfSJerome Forissier #include "constant_time_internal.h"
17*273a583eSThomas Bourgoin #include "mbedtls/error.h"
18817466cbSJens Wiklander
19817466cbSJens Wiklander #include <stdint.h>
20817466cbSJens Wiklander
21817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST)
22817466cbSJens Wiklander #include <string.h>
23817466cbSJens Wiklander #include "mbedtls/platform.h"
24817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */
25817466cbSJens Wiklander
26b0563631STom Van Eyck MBEDTLS_STATIC_TESTABLE
mbedtls_ct_base64_enc_char(unsigned char value)27b0563631STom Van Eyck unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
28b0563631STom Van Eyck {
29b0563631STom Van Eyck unsigned char digit = 0;
30b0563631STom Van Eyck /* For each range of values, if value is in that range, mask digit with
31b0563631STom Van Eyck * the corresponding value. Since value can only be in a single range,
32b0563631STom Van Eyck * only at most one masking will change digit. */
33b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
34b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
35b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
36b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
37b0563631STom Van Eyck digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
38b0563631STom Van Eyck return digit;
39b0563631STom Van Eyck }
40b0563631STom Van Eyck
41b0563631STom Van Eyck MBEDTLS_STATIC_TESTABLE
mbedtls_ct_base64_dec_value(unsigned char c)42b0563631STom Van Eyck signed char mbedtls_ct_base64_dec_value(unsigned char c)
43b0563631STom Van Eyck {
44b0563631STom Van Eyck unsigned char val = 0;
45b0563631STom Van Eyck /* For each range of digits, if c is in that range, mask val with
46b0563631STom Van Eyck * the corresponding value. Since c can only be in a single range,
47b0563631STom Van Eyck * only at most one masking will change val. Set val to one plus
48b0563631STom Van Eyck * the desired value so that it stays 0 if c is in none of the ranges. */
49b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' + 0 + 1);
50b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
51b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
52b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
53b0563631STom Van Eyck val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1);
54b0563631STom Van Eyck /* At this point, val is 0 if c is an invalid digit and v+1 if c is
55b0563631STom Van Eyck * a digit with the value v. */
56b0563631STom Van Eyck return val - 1;
57b0563631STom Van Eyck }
58817466cbSJens Wiklander
59817466cbSJens Wiklander /*
60817466cbSJens Wiklander * Encode a buffer into base64 format
61817466cbSJens Wiklander */
mbedtls_base64_encode(unsigned char * dst,size_t dlen,size_t * olen,const unsigned char * src,size_t slen)62817466cbSJens Wiklander int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
63817466cbSJens Wiklander const unsigned char *src, size_t slen)
64817466cbSJens Wiklander {
65817466cbSJens Wiklander size_t i, n;
66817466cbSJens Wiklander int C1, C2, C3;
67817466cbSJens Wiklander unsigned char *p;
68817466cbSJens Wiklander
6932b31808SJens Wiklander if (slen == 0) {
70817466cbSJens Wiklander *olen = 0;
7132b31808SJens Wiklander return 0;
72817466cbSJens Wiklander }
73817466cbSJens Wiklander
74817466cbSJens Wiklander n = slen / 3 + (slen % 3 != 0);
75817466cbSJens Wiklander
76b0563631STom Van Eyck if (n > (SIZE_MAX - 1) / 4) {
77b0563631STom Van Eyck *olen = SIZE_MAX;
7832b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
79817466cbSJens Wiklander }
80817466cbSJens Wiklander
81817466cbSJens Wiklander n *= 4;
82817466cbSJens Wiklander
8332b31808SJens Wiklander if ((dlen < n + 1) || (NULL == dst)) {
84817466cbSJens Wiklander *olen = n + 1;
8532b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
86817466cbSJens Wiklander }
87817466cbSJens Wiklander
88817466cbSJens Wiklander n = (slen / 3) * 3;
89817466cbSJens Wiklander
9032b31808SJens Wiklander for (i = 0, p = dst; i < n; i += 3) {
91817466cbSJens Wiklander C1 = *src++;
92817466cbSJens Wiklander C2 = *src++;
93817466cbSJens Wiklander C3 = *src++;
94817466cbSJens Wiklander
95039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
96039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
97039e02dfSJerome Forissier & 0x3F);
98039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6))
99039e02dfSJerome Forissier & 0x3F);
100039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F);
101817466cbSJens Wiklander }
102817466cbSJens Wiklander
10332b31808SJens Wiklander if (i < slen) {
104817466cbSJens Wiklander C1 = *src++;
105817466cbSJens Wiklander C2 = ((i + 1) < slen) ? *src++ : 0;
106817466cbSJens Wiklander
107039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
108039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
109039e02dfSJerome Forissier & 0x3F);
110817466cbSJens Wiklander
11132b31808SJens Wiklander if ((i + 1) < slen) {
112039e02dfSJerome Forissier *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
11332b31808SJens Wiklander } else {
11432b31808SJens Wiklander *p++ = '=';
11532b31808SJens Wiklander }
116817466cbSJens Wiklander
117817466cbSJens Wiklander *p++ = '=';
118817466cbSJens Wiklander }
119817466cbSJens Wiklander
120b0563631STom Van Eyck *olen = (size_t) (p - dst);
121817466cbSJens Wiklander *p = 0;
122817466cbSJens Wiklander
12332b31808SJens Wiklander return 0;
124817466cbSJens Wiklander }
125817466cbSJens Wiklander
126817466cbSJens Wiklander /*
127817466cbSJens Wiklander * Decode a base64-formatted buffer
128817466cbSJens Wiklander */
mbedtls_base64_decode(unsigned char * dst,size_t dlen,size_t * olen,const unsigned char * src,size_t slen)129817466cbSJens Wiklander int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
130817466cbSJens Wiklander const unsigned char *src, size_t slen)
131817466cbSJens Wiklander {
132039e02dfSJerome Forissier size_t i; /* index in source */
133039e02dfSJerome Forissier size_t n; /* number of digits or trailing = in source */
134039e02dfSJerome Forissier uint32_t x; /* value accumulator */
135039e02dfSJerome Forissier unsigned accumulated_digits = 0;
136039e02dfSJerome Forissier unsigned equals = 0;
137039e02dfSJerome Forissier int spaces_present = 0;
138817466cbSJens Wiklander unsigned char *p;
139817466cbSJens Wiklander
140817466cbSJens Wiklander /* First pass: check for validity and get output length */
14132b31808SJens Wiklander for (i = n = 0; i < slen; i++) {
142817466cbSJens Wiklander /* Skip spaces before checking for EOL */
143039e02dfSJerome Forissier spaces_present = 0;
14432b31808SJens Wiklander while (i < slen && src[i] == ' ') {
145817466cbSJens Wiklander ++i;
146039e02dfSJerome Forissier spaces_present = 1;
147817466cbSJens Wiklander }
148817466cbSJens Wiklander
149817466cbSJens Wiklander /* Spaces at end of buffer are OK */
15032b31808SJens Wiklander if (i == slen) {
151817466cbSJens Wiklander break;
15232b31808SJens Wiklander }
153817466cbSJens Wiklander
154817466cbSJens Wiklander if ((slen - i) >= 2 &&
15532b31808SJens Wiklander src[i] == '\r' && src[i + 1] == '\n') {
156817466cbSJens Wiklander continue;
15732b31808SJens Wiklander }
158817466cbSJens Wiklander
15932b31808SJens Wiklander if (src[i] == '\n') {
160817466cbSJens Wiklander continue;
16132b31808SJens Wiklander }
162817466cbSJens Wiklander
163817466cbSJens Wiklander /* Space inside a line is an error */
16432b31808SJens Wiklander if (spaces_present) {
16532b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
166039e02dfSJerome Forissier }
16732b31808SJens Wiklander
16832b31808SJens Wiklander if (src[i] > 127) {
16932b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
17032b31808SJens Wiklander }
17132b31808SJens Wiklander
17232b31808SJens Wiklander if (src[i] == '=') {
17332b31808SJens Wiklander if (++equals > 2) {
17432b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
17532b31808SJens Wiklander }
17632b31808SJens Wiklander } else {
17732b31808SJens Wiklander if (equals != 0) {
17832b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
17932b31808SJens Wiklander }
18032b31808SJens Wiklander if (mbedtls_ct_base64_dec_value(src[i]) < 0) {
18132b31808SJens Wiklander return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
18232b31808SJens Wiklander }
183039e02dfSJerome Forissier }
184817466cbSJens Wiklander n++;
185817466cbSJens Wiklander }
186817466cbSJens Wiklander
187*273a583eSThomas Bourgoin /* In valid base64, the number of digits (n-equals) is always of the form
188*273a583eSThomas Bourgoin * 4*k, 4*k+2 or *4k+3. Also, the number n of digits plus the number of
189*273a583eSThomas Bourgoin * equal signs at the end is always a multiple of 4. */
190*273a583eSThomas Bourgoin if ((n - equals) % 4 == 1) {
191*273a583eSThomas Bourgoin return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
192*273a583eSThomas Bourgoin }
193*273a583eSThomas Bourgoin if (n % 4 != 0) {
194*273a583eSThomas Bourgoin return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
195817466cbSJens Wiklander }
196817466cbSJens Wiklander
197*273a583eSThomas Bourgoin /* We've determined that the input is valid, and that it contains
198*273a583eSThomas Bourgoin * exactly k blocks of digits-or-equals, with n = 4 * k,
199*273a583eSThomas Bourgoin * and equals only present at the end of the last block if at all.
200*273a583eSThomas Bourgoin * Now we can calculate the length of the output.
201*273a583eSThomas Bourgoin *
202*273a583eSThomas Bourgoin * Each block of 4 digits in the input map to 3 bytes of output.
203*273a583eSThomas Bourgoin * For the last block:
204*273a583eSThomas Bourgoin * - abcd (where abcd are digits) is a full 3-byte block;
205*273a583eSThomas Bourgoin * - abc= means 1 byte less than a full 3-byte block of output;
206*273a583eSThomas Bourgoin * - ab== means 2 bytes less than a full 3-byte block of output;
207*273a583eSThomas Bourgoin * - a==== and ==== is rejected above.
208817466cbSJens Wiklander */
209*273a583eSThomas Bourgoin *olen = (n / 4) * 3 - equals;
210817466cbSJens Wiklander
211*273a583eSThomas Bourgoin /* If the output buffer is too small, signal this and stop here.
212*273a583eSThomas Bourgoin * Also, as documented, stop here if `dst` is null, independently of
213*273a583eSThomas Bourgoin * `dlen`.
214*273a583eSThomas Bourgoin *
215*273a583eSThomas Bourgoin * There is an edge case when the output is empty: in this case,
216*273a583eSThomas Bourgoin * `dlen == 0` with `dst == NULL` is valid (on some platforms,
217*273a583eSThomas Bourgoin * `malloc(0)` returns `NULL`). Since the call is valid, we return
218*273a583eSThomas Bourgoin * 0 in this case.
219*273a583eSThomas Bourgoin */
220*273a583eSThomas Bourgoin if ((*olen != 0 && dst == NULL) || dlen < *olen) {
22132b31808SJens Wiklander return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
222817466cbSJens Wiklander }
223817466cbSJens Wiklander
22432b31808SJens Wiklander for (x = 0, p = dst; i > 0; i--, src++) {
22532b31808SJens Wiklander if (*src == '\r' || *src == '\n' || *src == ' ') {
226817466cbSJens Wiklander continue;
22732b31808SJens Wiklander }
22832b31808SJens Wiklander if (*src == '=') {
229*273a583eSThomas Bourgoin /* We already know from the first loop that equal signs are
230*273a583eSThomas Bourgoin * only at the end. */
231*273a583eSThomas Bourgoin break;
23232b31808SJens Wiklander }
233*273a583eSThomas Bourgoin x = x << 6;
234*273a583eSThomas Bourgoin x |= mbedtls_ct_base64_dec_value(*src);
2357901324dSJerome Forissier
23632b31808SJens Wiklander if (++accumulated_digits == 4) {
237039e02dfSJerome Forissier accumulated_digits = 0;
238039e02dfSJerome Forissier *p++ = MBEDTLS_BYTE_2(x);
23932b31808SJens Wiklander *p++ = MBEDTLS_BYTE_1(x);
24032b31808SJens Wiklander *p++ = MBEDTLS_BYTE_0(x);
24132b31808SJens Wiklander }
242817466cbSJens Wiklander }
243*273a583eSThomas Bourgoin if (accumulated_digits == 3) {
244*273a583eSThomas Bourgoin *p++ = MBEDTLS_BYTE_2(x << 6);
245*273a583eSThomas Bourgoin *p++ = MBEDTLS_BYTE_1(x << 6);
246*273a583eSThomas Bourgoin } else if (accumulated_digits == 2) {
247*273a583eSThomas Bourgoin *p++ = MBEDTLS_BYTE_2(x << 12);
248817466cbSJens Wiklander }
249817466cbSJens Wiklander
250*273a583eSThomas Bourgoin if (*olen != (size_t) (p - dst)) {
251*273a583eSThomas Bourgoin return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
252*273a583eSThomas Bourgoin }
253817466cbSJens Wiklander
25432b31808SJens Wiklander return 0;
255817466cbSJens Wiklander }
256817466cbSJens Wiklander
257817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST)
258817466cbSJens Wiklander
259817466cbSJens Wiklander static const unsigned char base64_test_dec[64] =
260817466cbSJens Wiklander {
261817466cbSJens Wiklander 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
262817466cbSJens Wiklander 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
263817466cbSJens Wiklander 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
264817466cbSJens Wiklander 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
265817466cbSJens Wiklander 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
266817466cbSJens Wiklander 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
267817466cbSJens Wiklander 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
268817466cbSJens Wiklander 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
269817466cbSJens Wiklander };
270817466cbSJens Wiklander
271817466cbSJens Wiklander static const unsigned char base64_test_enc[] =
272817466cbSJens Wiklander "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
273817466cbSJens Wiklander "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
274817466cbSJens Wiklander
275817466cbSJens Wiklander /*
276817466cbSJens Wiklander * Checkup routine
277817466cbSJens Wiklander */
mbedtls_base64_self_test(int verbose)278817466cbSJens Wiklander int mbedtls_base64_self_test(int verbose)
279817466cbSJens Wiklander {
280817466cbSJens Wiklander size_t len;
281817466cbSJens Wiklander const unsigned char *src;
282817466cbSJens Wiklander unsigned char buffer[128];
283817466cbSJens Wiklander
28432b31808SJens Wiklander if (verbose != 0) {
285817466cbSJens Wiklander mbedtls_printf(" Base64 encoding test: ");
28632b31808SJens Wiklander }
287817466cbSJens Wiklander
288817466cbSJens Wiklander src = base64_test_dec;
289817466cbSJens Wiklander
290817466cbSJens Wiklander if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
29132b31808SJens Wiklander memcmp(base64_test_enc, buffer, 88) != 0) {
29232b31808SJens Wiklander if (verbose != 0) {
293817466cbSJens Wiklander mbedtls_printf("failed\n");
294817466cbSJens Wiklander }
295817466cbSJens Wiklander
29632b31808SJens Wiklander return 1;
29732b31808SJens Wiklander }
29832b31808SJens Wiklander
29932b31808SJens Wiklander if (verbose != 0) {
300817466cbSJens Wiklander mbedtls_printf("passed\n Base64 decoding test: ");
30132b31808SJens Wiklander }
302817466cbSJens Wiklander
303817466cbSJens Wiklander src = base64_test_enc;
304817466cbSJens Wiklander
305817466cbSJens Wiklander if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
30632b31808SJens Wiklander memcmp(base64_test_dec, buffer, 64) != 0) {
30732b31808SJens Wiklander if (verbose != 0) {
308817466cbSJens Wiklander mbedtls_printf("failed\n");
309817466cbSJens Wiklander }
310817466cbSJens Wiklander
31132b31808SJens Wiklander return 1;
31232b31808SJens Wiklander }
313817466cbSJens Wiklander
31432b31808SJens Wiklander if (verbose != 0) {
31532b31808SJens Wiklander mbedtls_printf("passed\n\n");
31632b31808SJens Wiklander }
31732b31808SJens Wiklander
31832b31808SJens Wiklander return 0;
319817466cbSJens Wiklander }
320817466cbSJens Wiklander
321817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */
322817466cbSJens Wiklander
323817466cbSJens Wiklander #endif /* MBEDTLS_BASE64_C */
324