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