Coverage Report

Created: 2026-04-01 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mbedtls/library/base64.c
Line
Count
Source
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 "base64_internal.h"
14
#include "constant_time_internal.h"
15
#include "mbedtls/error.h"
16
17
#include <limits.h>
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
25
MBEDTLS_STATIC_TESTABLE
26
unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
27
0
{
28
0
    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
0
    digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
33
0
    digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
34
0
    digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
35
0
    digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
36
0
    digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
37
0
    return digit;
38
0
}
39
40
MBEDTLS_STATIC_TESTABLE
41
signed char mbedtls_ct_base64_dec_value(unsigned char c)
42
51.1M
{
43
51.1M
    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
51.1M
    val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' +  0 + 1);
49
51.1M
    val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
50
51.1M
    val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
51
51.1M
    val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
52
51.1M
    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
51.1M
    return val - 1;
56
51.1M
}
57
58
/*
59
 * Encode a buffer into base64 format
60
 */
61
int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
62
                          const unsigned char *src, size_t slen)
63
0
{
64
0
    size_t i, n;
65
0
    int C1, C2, C3;
66
0
    unsigned char *p;
67
68
0
    if (slen == 0) {
69
0
        *olen = 0;
70
0
        return 0;
71
0
    }
72
73
0
    n = slen / 3 + (slen % 3 != 0);
74
75
0
    if (n > (SIZE_MAX - 1) / 4) {
76
0
        *olen = SIZE_MAX;
77
0
        return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
78
0
    }
79
80
0
    n *= 4;
81
82
0
    if ((dlen < n + 1) || (NULL == dst)) {
83
0
        *olen = n + 1;
84
0
        return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
85
0
    }
86
87
0
    n = (slen / 3) * 3;
88
89
0
    for (i = 0, p = dst; i < n; i += 3) {
90
0
        C1 = *src++;
91
0
        C2 = *src++;
92
0
        C3 = *src++;
93
94
0
        *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
95
0
        *p++ = mbedtls_ct_base64_enc_char((((C1 &  3) << 4) + (C2 >> 4))
96
0
                                          & 0x3F);
97
0
        *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6))
98
0
                                          & 0x3F);
99
0
        *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F);
100
0
    }
101
102
0
    if (i < slen) {
103
0
        C1 = *src++;
104
0
        C2 = ((i + 1) < slen) ? *src++ : 0;
105
106
0
        *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F);
107
0
        *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4))
108
0
                                          & 0x3F);
109
110
0
        if ((i + 1) < slen) {
111
0
            *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F);
112
0
        } else {
113
0
            *p++ = '=';
114
0
        }
115
116
0
        *p++ = '=';
117
0
    }
118
119
0
    *olen = (size_t) (p - dst);
120
0
    *p = 0;
121
122
0
    return 0;
123
0
}
124
125
/*
126
 * Decode a base64-formatted buffer
127
 */
128
int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
129
                          const unsigned char *src, size_t slen)
130
110k
{
131
110k
    size_t i; /* index in source */
132
110k
    size_t n; /* number of digits or trailing = in source */
133
110k
    uint32_t x; /* value accumulator */
134
110k
    unsigned accumulated_digits = 0;
135
110k
    unsigned equals = 0;
136
110k
    int spaces_present = 0;
137
110k
    unsigned char *p;
138
139
    /* First pass: check for validity and get output length */
140
34.7M
    for (i = n = 0; i < slen; i++) {
141
        /* Skip spaces before checking for EOL */
142
34.6M
        spaces_present = 0;
143
34.6M
        while (i < slen && src[i] == ' ') {
144
20.5k
            ++i;
145
20.5k
            spaces_present = 1;
146
20.5k
        }
147
148
        /* Spaces at end of buffer are OK */
149
34.6M
        if (i == slen) {
150
708
            break;
151
708
        }
152
153
34.6M
        if ((slen - i) >= 2 &&
154
34.5M
            src[i] == '\r' && src[i + 1] == '\n') {
155
7.73k
            continue;
156
7.73k
        }
157
158
34.6M
        if (src[i] == '\n') {
159
355k
            continue;
160
355k
        }
161
162
        /* Space inside a line is an error */
163
34.3M
        if (spaces_present) {
164
279
            return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
165
279
        }
166
167
34.3M
        if (src[i] > 127) {
168
630
            return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
169
630
        }
170
171
34.3M
        if (src[i] == '=') {
172
21.3k
            if (++equals > 2) {
173
202
                return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
174
202
            }
175
34.2M
        } else {
176
34.2M
            if (equals != 0) {
177
225
                return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
178
225
            }
179
34.2M
            if (mbedtls_ct_base64_dec_value(src[i]) < 0) {
180
1.79k
                return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
181
1.79k
            }
182
34.2M
        }
183
34.3M
        n++;
184
34.3M
    }
185
186
    /* In valid base64, the number of digits (n-equals) is always of the form
187
     * 4*k, 4*k+2 or *4k+3. Also, the number n of digits plus the number of
188
     * equal signs at the end is always a multiple of 4. */
189
107k
    if ((n - equals) % 4 == 1) {
190
1.11k
        return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
191
1.11k
    }
192
106k
    if (n % 4 != 0) {
193
2.01k
        return MBEDTLS_ERR_BASE64_INVALID_CHARACTER;
194
2.01k
    }
195
196
    /* We've determined that the input is valid, and that it contains
197
     * exactly k blocks of digits-or-equals, with n = 4 * k,
198
     * and equals only present at the end of the last block if at all.
199
     * Now we can calculate the length of the output.
200
     *
201
     * Each block of 4 digits in the input map to 3 bytes of output.
202
     * For the last block:
203
     * - abcd (where abcd are digits) is a full 3-byte block;
204
     * - abc= means 1 byte less than a full 3-byte block of output;
205
     * - ab== means 2 bytes less than a full 3-byte block of output;
206
     * - a==== and ==== is rejected above.
207
     */
208
104k
    *olen = (n / 4) * 3 - equals;
209
210
    /* If the output buffer is too small, signal this and stop here.
211
     * Also, as documented, stop here if `dst` is null, independently of
212
     * `dlen`.
213
     *
214
     * There is an edge case when the output is empty: in this case,
215
     * `dlen == 0` with `dst == NULL` is valid (on some platforms,
216
     * `malloc(0)` returns `NULL`). Since the call is valid, we return
217
     * 0 in this case.
218
     */
219
104k
    if ((*olen != 0 && dst == NULL) || dlen < *olen) {
220
52.2k
        return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
221
52.2k
    }
222
223
17.0M
    for (x = 0, p = dst; i > 0; i--, src++) {
224
17.0M
        if (*src == '\r' || *src == '\n' || *src == ' ') {
225
185k
            continue;
226
185k
        }
227
16.8M
        if (*src == '=') {
228
            /* We already know from the first loop that equal signs are
229
             * only at the end. */
230
8.56k
            break;
231
8.56k
        }
232
16.8M
        x = x << 6;
233
16.8M
        x |= mbedtls_ct_base64_dec_value(*src);
234
235
16.8M
        if (++accumulated_digits == 4) {
236
4.20M
            accumulated_digits = 0;
237
4.20M
            *p++ = MBEDTLS_BYTE_2(x);
238
4.20M
            *p++ = MBEDTLS_BYTE_1(x);
239
4.20M
            *p++ = MBEDTLS_BYTE_0(x);
240
4.20M
        }
241
16.8M
    }
242
52.3k
    if (accumulated_digits == 3) {
243
7.01k
        *p++ = MBEDTLS_BYTE_2(x << 6);
244
7.01k
        *p++ = MBEDTLS_BYTE_1(x << 6);
245
45.3k
    } else if (accumulated_digits == 2) {
246
1.55k
        *p++ = MBEDTLS_BYTE_2(x << 12);
247
1.55k
    }
248
249
52.3k
    if (*olen != (size_t) (p - dst)) {
250
0
        return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
251
0
    }
252
253
52.3k
    return 0;
254
52.3k
}
255
256
#if defined(MBEDTLS_SELF_TEST)
257
258
static const unsigned char base64_test_dec[64] =
259
{
260
    0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
261
    0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
262
    0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
263
    0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
264
    0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
265
    0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
266
    0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
267
    0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
268
};
269
270
static const unsigned char base64_test_enc[] =
271
    "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
272
    "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
273
274
/*
275
 * Checkup routine
276
 */
277
int mbedtls_base64_self_test(int verbose)
278
0
{
279
0
    size_t len;
280
0
    const unsigned char *src;
281
0
    unsigned char buffer[128];
282
283
0
    if (verbose != 0) {
284
0
        mbedtls_printf("  Base64 encoding test: ");
285
0
    }
286
287
0
    src = base64_test_dec;
288
289
0
    if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
290
0
        memcmp(base64_test_enc, buffer, 88) != 0) {
291
0
        if (verbose != 0) {
292
0
            mbedtls_printf("failed\n");
293
0
        }
294
295
0
        return 1;
296
0
    }
297
298
0
    if (verbose != 0) {
299
0
        mbedtls_printf("passed\n  Base64 decoding test: ");
300
0
    }
301
302
0
    src = base64_test_enc;
303
304
0
    if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
305
0
        memcmp(base64_test_dec, buffer, 64) != 0) {
306
0
        if (verbose != 0) {
307
0
            mbedtls_printf("failed\n");
308
0
        }
309
310
0
        return 1;
311
0
    }
312
313
0
    if (verbose != 0) {
314
0
        mbedtls_printf("passed\n\n");
315
0
    }
316
317
0
    return 0;
318
0
}
319
320
#endif /* MBEDTLS_SELF_TEST */
321
322
#endif /* MBEDTLS_BASE64_C */