Coverage Report

Created: 2025-11-16 06:46

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