xref: /optee_os/lib/libmbedtls/mbedtls/library/base64.c (revision b0563631928755fe864b97785160fb3088e9efdc)
1817466cbSJens Wiklander /*
2817466cbSJens Wiklander  *  RFC 1521 base64 encoding/decoding
3817466cbSJens Wiklander  *
47901324dSJerome Forissier  *  Copyright The Mbed TLS Contributors
5*b0563631STom Van Eyck  *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6817466cbSJens Wiklander  */
7817466cbSJens Wiklander 
8*b0563631STom Van Eyck #include <limits.h>
9*b0563631STom Van Eyck 
107901324dSJerome Forissier #include "common.h"
11817466cbSJens Wiklander 
12817466cbSJens Wiklander #if defined(MBEDTLS_BASE64_C)
13817466cbSJens Wiklander 
14817466cbSJens Wiklander #include "mbedtls/base64.h"
15*b0563631STom Van Eyck #include "base64_internal.h"
16039e02dfSJerome Forissier #include "constant_time_internal.h"
17817466cbSJens Wiklander 
18817466cbSJens Wiklander #include <stdint.h>
19817466cbSJens Wiklander 
20817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST)
21817466cbSJens Wiklander #include <string.h>
22817466cbSJens Wiklander #include "mbedtls/platform.h"
23817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */
24817466cbSJens Wiklander 
25*b0563631STom Van Eyck MBEDTLS_STATIC_TESTABLE
26*b0563631STom Van Eyck unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
27*b0563631STom Van Eyck {
28*b0563631STom Van Eyck     unsigned char digit = 0;
29*b0563631STom Van Eyck     /* For each range of values, if value is in that range, mask digit with
30*b0563631STom Van Eyck      * the corresponding value. Since value can only be in a single range,
31*b0563631STom Van Eyck      * only at most one masking will change digit. */
32*b0563631STom Van Eyck     digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
33*b0563631STom Van Eyck     digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
34*b0563631STom Van Eyck     digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
35*b0563631STom Van Eyck     digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
36*b0563631STom Van Eyck     digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
37*b0563631STom Van Eyck     return digit;
38*b0563631STom Van Eyck }
39*b0563631STom Van Eyck 
40*b0563631STom Van Eyck MBEDTLS_STATIC_TESTABLE
41*b0563631STom Van Eyck signed char mbedtls_ct_base64_dec_value(unsigned char c)
42*b0563631STom Van Eyck {
43*b0563631STom Van Eyck     unsigned char val = 0;
44*b0563631STom Van Eyck     /* For each range of digits, if c is in that range, mask val with
45*b0563631STom Van Eyck      * the corresponding value. Since c can only be in a single range,
46*b0563631STom Van Eyck      * only at most one masking will change val. Set val to one plus
47*b0563631STom Van Eyck      * the desired value so that it stays 0 if c is in none of the ranges. */
48*b0563631STom Van Eyck     val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' +  0 + 1);
49*b0563631STom Van Eyck     val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
50*b0563631STom Van Eyck     val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
51*b0563631STom Van Eyck     val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
52*b0563631STom Van Eyck     val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1);
53*b0563631STom Van Eyck     /* At this point, val is 0 if c is an invalid digit and v+1 if c is
54*b0563631STom Van Eyck      * a digit with the value v. */
55*b0563631STom Van Eyck     return val - 1;
56*b0563631STom Van Eyck }
57817466cbSJens Wiklander 
58817466cbSJens Wiklander /*
59817466cbSJens Wiklander  * Encode a buffer into base64 format
60817466cbSJens Wiklander  */
61817466cbSJens Wiklander int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
62817466cbSJens Wiklander                           const unsigned char *src, size_t slen)
63817466cbSJens Wiklander {
64817466cbSJens Wiklander     size_t i, n;
65817466cbSJens Wiklander     int C1, C2, C3;
66817466cbSJens Wiklander     unsigned char *p;
67817466cbSJens Wiklander 
6832b31808SJens Wiklander     if (slen == 0) {
69817466cbSJens Wiklander         *olen = 0;
7032b31808SJens Wiklander         return 0;
71817466cbSJens Wiklander     }
72817466cbSJens Wiklander 
73817466cbSJens Wiklander     n = slen / 3 + (slen % 3 != 0);
74817466cbSJens Wiklander 
75*b0563631STom Van Eyck     if (n > (SIZE_MAX - 1) / 4) {
76*b0563631STom Van Eyck         *olen = SIZE_MAX;
7732b31808SJens Wiklander         return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
78817466cbSJens Wiklander     }
79817466cbSJens Wiklander 
80817466cbSJens Wiklander     n *= 4;
81817466cbSJens Wiklander 
8232b31808SJens Wiklander     if ((dlen < n + 1) || (NULL == dst)) {
83817466cbSJens Wiklander         *olen = n + 1;
8432b31808SJens Wiklander         return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
85817466cbSJens Wiklander     }
86817466cbSJens Wiklander 
87817466cbSJens Wiklander     n = (slen / 3) * 3;
88817466cbSJens Wiklander 
8932b31808SJens Wiklander     for (i = 0, p = dst; i < n; i += 3) {
90817466cbSJens Wiklander         C1 = *src++;
91817466cbSJens Wiklander         C2 = *src++;
92817466cbSJens Wiklander         C3 = *src++;
93817466cbSJens Wiklander 
94039e02dfSJerome Forissier         *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
95039e02dfSJerome Forissier         *p++ = mbedtls_ct_base64_enc_char((((C1 &  3) << 4) + (C2 >> 4))
96039e02dfSJerome Forissier                                           & 0x3F);
97039e02dfSJerome Forissier         *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6))
98039e02dfSJerome Forissier                                           & 0x3F);
99039e02dfSJerome Forissier         *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F);
100817466cbSJens Wiklander     }
101817466cbSJens Wiklander 
10232b31808SJens Wiklander     if (i < slen) {
103817466cbSJens Wiklander         C1 = *src++;
104817466cbSJens Wiklander         C2 = ((i + 1) < slen) ? *src++ : 0;
105817466cbSJens Wiklander 
106039e02dfSJerome Forissier         *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
107039e02dfSJerome Forissier         *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
108039e02dfSJerome Forissier                                           & 0x3F);
109817466cbSJens Wiklander 
11032b31808SJens Wiklander         if ((i + 1) < slen) {
111039e02dfSJerome Forissier             *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
11232b31808SJens Wiklander         } else {
11332b31808SJens Wiklander             *p++ = '=';
11432b31808SJens Wiklander         }
115817466cbSJens Wiklander 
116817466cbSJens Wiklander         *p++ = '=';
117817466cbSJens Wiklander     }
118817466cbSJens Wiklander 
119*b0563631STom Van Eyck     *olen = (size_t) (p - dst);
120817466cbSJens Wiklander     *p = 0;
121817466cbSJens Wiklander 
12232b31808SJens Wiklander     return 0;
123817466cbSJens Wiklander }
124817466cbSJens Wiklander 
125817466cbSJens Wiklander /*
126817466cbSJens Wiklander  * Decode a base64-formatted buffer
127817466cbSJens Wiklander  */
128817466cbSJens Wiklander int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
129817466cbSJens Wiklander                           const unsigned char *src, size_t slen)
130817466cbSJens Wiklander {
131039e02dfSJerome Forissier     size_t i; /* index in source */
132039e02dfSJerome Forissier     size_t n; /* number of digits or trailing = in source */
133039e02dfSJerome Forissier     uint32_t x; /* value accumulator */
134039e02dfSJerome Forissier     unsigned accumulated_digits = 0;
135039e02dfSJerome Forissier     unsigned equals = 0;
136039e02dfSJerome Forissier     int spaces_present = 0;
137817466cbSJens Wiklander     unsigned char *p;
138817466cbSJens Wiklander 
139817466cbSJens Wiklander     /* First pass: check for validity and get output length */
14032b31808SJens Wiklander     for (i = n = 0; i < slen; i++) {
141817466cbSJens Wiklander         /* Skip spaces before checking for EOL */
142039e02dfSJerome Forissier         spaces_present = 0;
14332b31808SJens Wiklander         while (i < slen && src[i] == ' ') {
144817466cbSJens Wiklander             ++i;
145039e02dfSJerome Forissier             spaces_present = 1;
146817466cbSJens Wiklander         }
147817466cbSJens Wiklander 
148817466cbSJens Wiklander         /* Spaces at end of buffer are OK */
14932b31808SJens Wiklander         if (i == slen) {
150817466cbSJens Wiklander             break;
15132b31808SJens Wiklander         }
152817466cbSJens Wiklander 
153817466cbSJens Wiklander         if ((slen - i) >= 2 &&
15432b31808SJens Wiklander             src[i] == '\r' && src[i + 1] == '\n') {
155817466cbSJens Wiklander             continue;
15632b31808SJens Wiklander         }
157817466cbSJens Wiklander 
15832b31808SJens Wiklander         if (src[i] == '\n') {
159817466cbSJens Wiklander             continue;
16032b31808SJens Wiklander         }
161817466cbSJens Wiklander 
162817466cbSJens Wiklander         /* Space inside a line is an error */
16332b31808SJens Wiklander         if (spaces_present) {
16432b31808SJens Wiklander             return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
165039e02dfSJerome Forissier         }
16632b31808SJens Wiklander 
16732b31808SJens Wiklander         if (src[i] > 127) {
16832b31808SJens Wiklander             return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
16932b31808SJens Wiklander         }
17032b31808SJens Wiklander 
17132b31808SJens Wiklander         if (src[i] == '=') {
17232b31808SJens Wiklander             if (++equals > 2) {
17332b31808SJens Wiklander                 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
17432b31808SJens Wiklander             }
17532b31808SJens Wiklander         } else {
17632b31808SJens Wiklander             if (equals != 0) {
17732b31808SJens Wiklander                 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
17832b31808SJens Wiklander             }
17932b31808SJens Wiklander             if (mbedtls_ct_base64_dec_value(src[i]) < 0) {
18032b31808SJens Wiklander                 return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
18132b31808SJens Wiklander             }
182039e02dfSJerome Forissier         }
183817466cbSJens Wiklander         n++;
184817466cbSJens Wiklander     }
185817466cbSJens Wiklander 
18632b31808SJens Wiklander     if (n == 0) {
187817466cbSJens Wiklander         *olen = 0;
18832b31808SJens Wiklander         return 0;
189817466cbSJens Wiklander     }
190817466cbSJens Wiklander 
191817466cbSJens Wiklander     /* The following expression is to calculate the following formula without
192817466cbSJens Wiklander      * risk of integer overflow in n:
193817466cbSJens Wiklander      *     n = ( ( n * 6 ) + 7 ) >> 3;
194817466cbSJens Wiklander      */
195817466cbSJens Wiklander     n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
196039e02dfSJerome Forissier     n -= equals;
197817466cbSJens Wiklander 
19832b31808SJens Wiklander     if (dst == NULL || dlen < n) {
199817466cbSJens Wiklander         *olen = n;
20032b31808SJens Wiklander         return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
201817466cbSJens Wiklander     }
202817466cbSJens Wiklander 
203039e02dfSJerome Forissier     equals = 0;
20432b31808SJens Wiklander     for (x = 0, p = dst; i > 0; i--, src++) {
20532b31808SJens Wiklander         if (*src == '\r' || *src == '\n' || *src == ' ') {
206817466cbSJens Wiklander             continue;
20732b31808SJens Wiklander         }
208817466cbSJens Wiklander 
209039e02dfSJerome Forissier         x = x << 6;
21032b31808SJens Wiklander         if (*src == '=') {
211039e02dfSJerome Forissier             ++equals;
21232b31808SJens Wiklander         } else {
213039e02dfSJerome Forissier             x |= mbedtls_ct_base64_dec_value(*src);
21432b31808SJens Wiklander         }
2157901324dSJerome Forissier 
21632b31808SJens Wiklander         if (++accumulated_digits == 4) {
217039e02dfSJerome Forissier             accumulated_digits = 0;
218039e02dfSJerome Forissier             *p++ = MBEDTLS_BYTE_2(x);
21932b31808SJens Wiklander             if (equals <= 1) {
22032b31808SJens Wiklander                 *p++ = MBEDTLS_BYTE_1(x);
22132b31808SJens Wiklander             }
22232b31808SJens Wiklander             if (equals <= 0) {
22332b31808SJens Wiklander                 *p++ = MBEDTLS_BYTE_0(x);
22432b31808SJens Wiklander             }
225817466cbSJens Wiklander         }
226817466cbSJens Wiklander     }
227817466cbSJens Wiklander 
228*b0563631STom Van Eyck     *olen = (size_t) (p - dst);
229817466cbSJens Wiklander 
23032b31808SJens Wiklander     return 0;
231817466cbSJens Wiklander }
232817466cbSJens Wiklander 
233817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST)
234817466cbSJens Wiklander 
235817466cbSJens Wiklander static const unsigned char base64_test_dec[64] =
236817466cbSJens Wiklander {
237817466cbSJens Wiklander     0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
238817466cbSJens Wiklander     0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
239817466cbSJens Wiklander     0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
240817466cbSJens Wiklander     0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
241817466cbSJens Wiklander     0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
242817466cbSJens Wiklander     0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
243817466cbSJens Wiklander     0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
244817466cbSJens Wiklander     0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
245817466cbSJens Wiklander };
246817466cbSJens Wiklander 
247817466cbSJens Wiklander static const unsigned char base64_test_enc[] =
248817466cbSJens Wiklander     "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
249817466cbSJens Wiklander     "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
250817466cbSJens Wiklander 
251817466cbSJens Wiklander /*
252817466cbSJens Wiklander  * Checkup routine
253817466cbSJens Wiklander  */
254817466cbSJens Wiklander int mbedtls_base64_self_test(int verbose)
255817466cbSJens Wiklander {
256817466cbSJens Wiklander     size_t len;
257817466cbSJens Wiklander     const unsigned char *src;
258817466cbSJens Wiklander     unsigned char buffer[128];
259817466cbSJens Wiklander 
26032b31808SJens Wiklander     if (verbose != 0) {
261817466cbSJens Wiklander         mbedtls_printf("  Base64 encoding test: ");
26232b31808SJens Wiklander     }
263817466cbSJens Wiklander 
264817466cbSJens Wiklander     src = base64_test_dec;
265817466cbSJens Wiklander 
266817466cbSJens Wiklander     if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
26732b31808SJens Wiklander         memcmp(base64_test_enc, buffer, 88) != 0) {
26832b31808SJens Wiklander         if (verbose != 0) {
269817466cbSJens Wiklander             mbedtls_printf("failed\n");
270817466cbSJens Wiklander         }
271817466cbSJens Wiklander 
27232b31808SJens Wiklander         return 1;
27332b31808SJens Wiklander     }
27432b31808SJens Wiklander 
27532b31808SJens Wiklander     if (verbose != 0) {
276817466cbSJens Wiklander         mbedtls_printf("passed\n  Base64 decoding test: ");
27732b31808SJens Wiklander     }
278817466cbSJens Wiklander 
279817466cbSJens Wiklander     src = base64_test_enc;
280817466cbSJens Wiklander 
281817466cbSJens Wiklander     if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
28232b31808SJens Wiklander         memcmp(base64_test_dec, buffer, 64) != 0) {
28332b31808SJens Wiklander         if (verbose != 0) {
284817466cbSJens Wiklander             mbedtls_printf("failed\n");
285817466cbSJens Wiklander         }
286817466cbSJens Wiklander 
28732b31808SJens Wiklander         return 1;
28832b31808SJens Wiklander     }
289817466cbSJens Wiklander 
29032b31808SJens Wiklander     if (verbose != 0) {
29132b31808SJens Wiklander         mbedtls_printf("passed\n\n");
29232b31808SJens Wiklander     }
29332b31808SJens Wiklander 
29432b31808SJens Wiklander     return 0;
295817466cbSJens Wiklander }
296817466cbSJens Wiklander 
297817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */
298817466cbSJens Wiklander 
299817466cbSJens Wiklander #endif /* MBEDTLS_BASE64_C */
300