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