xref: /optee_os/lib/libmbedtls/mbedtls/library/base64.c (revision 273a583ea99627ff3b8ccbbaedbdacecd0909b2e)
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