Coverage Report

Created: 2026-03-31 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/util-base64.c
Line
Count
Source
1
/* Copyright (C) 2007-2012 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author David Abarbanel <david.abarbanel@baesystems.com>
22
 *
23
 */
24
25
#include "util-base64.h"
26
#include "util-debug.h"
27
#include "util-unittest.h"
28
/* Constants */
29
41.2M
#define BASE64_TABLE_MAX  122
30
31
/* Base64 character to index conversion table */
32
/* Characters are mapped as "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" */
33
static const int b64table[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
37
        -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
38
        54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
39
        -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
40
        5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
41
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
42
        25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
43
        29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
44
        39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
45
        49, 50, 51 };
46
47
/**
48
 * \brief Gets a base64-decoded value from an encoded character
49
 *
50
 * \param c The encoded character
51
 *
52
 * \return The decoded value (0 or above), or -1 if the parameter is invalid
53
 */
54
static inline int GetBase64Value(uint8_t c)
55
41.2M
{
56
41.2M
    int val = -1;
57
58
    /* Pull from conversion table */
59
41.2M
    if (c <= BASE64_TABLE_MAX) {
60
33.6M
        val = b64table[(int) c];
61
33.6M
    }
62
63
41.2M
    return val;
64
41.2M
}
65
66
/**
67
 * \brief Checks if the given char in a byte array is Base64 alphabet
68
 *
69
 * \param Char that needs to be checked
70
 *
71
 * \return True if the char was Base64 alphabet, False otherwise
72
 */
73
bool IsBase64Alphabet(uint8_t encoded_byte)
74
6.16M
{
75
6.16M
    if (GetBase64Value(encoded_byte) < 0 && encoded_byte != '=') {
76
4.30M
        return false;
77
4.30M
    }
78
1.85M
    return true;
79
6.16M
}
80
81
/**
82
 * \brief Decodes a 4-byte base64-encoded block into a 3-byte ascii-encoded block
83
 *
84
 * \param ascii the 3-byte ascii output block
85
 * \param b64 the 4-byte base64 input block
86
 *
87
 * \return none
88
 */
89
static inline void DecodeBase64Block(uint8_t ascii[ASCII_BLOCK], uint8_t b64[B64_BLOCK])
90
6.25M
{
91
6.25M
    ascii[0] = (uint8_t) (b64[0] << 2) | (b64[1] >> 4);
92
6.25M
    ascii[1] = (uint8_t) (b64[1] << 4) | (b64[2] >> 2);
93
6.25M
    ascii[2] = (uint8_t) (b64[2] << 6) | (b64[3]);
94
6.25M
}
95
96
/**
97
 * \brief Decodes a base64-encoded string buffer into an ascii-encoded byte buffer
98
 *
99
 * \param dest The destination byte buffer
100
 * \param dest_size The destination byte buffer size
101
 * \param src The source string
102
 * \param len The length of the source string
103
 * \param consumed_bytes The bytes that were actually processed/consumed
104
 * \param decoded_bytes The bytes that were decoded
105
 * \param mode The mode in which decoding should happen
106
 *
107
 * \return Error code indicating success or failures with parsing
108
 */
109
Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len,
110
        uint32_t *consumed_bytes, uint32_t *decoded_bytes, Base64Mode mode)
111
507k
{
112
507k
    int val;
113
507k
    uint32_t padding = 0, bbidx = 0, sp = 0, leading_sp = 0;
114
507k
    uint8_t *dptr = dest;
115
507k
    uint8_t b64[B64_BLOCK] = { 0,0,0,0 };
116
507k
    bool valid = true;
117
507k
    Base64Ecode ecode = BASE64_ECODE_OK;
118
507k
    *decoded_bytes = 0;
119
120
    /* Traverse through each alpha-numeric letter in the source array */
121
35.6M
    for (uint32_t i = 0; i < len; i++) {
122
        /* Get decimal representation */
123
35.1M
        val = GetBase64Value(src[i]);
124
35.1M
        if (val < 0) {
125
10.1M
            if (mode == BASE64_MODE_RFC2045 && src[i] != '=') {
126
9.86M
                if (bbidx == 0) {
127
                    /* Special case where last block of data has a leading space or invalid char */
128
1.73M
                    leading_sp++;
129
1.73M
                }
130
9.86M
                sp++;
131
9.86M
                continue;
132
9.86M
            }
133
            /* Invalid character found, so decoding fails */
134
239k
            if (src[i] != '=') {
135
6.59k
                valid = false;
136
6.59k
                ecode = BASE64_ECODE_ERR;
137
6.59k
                if (mode == BASE64_MODE_STRICT) {
138
0
                    *decoded_bytes = 0;
139
0
                }
140
6.59k
                break;
141
6.59k
            }
142
233k
            padding++;
143
233k
        }
144
145
        /* For each alpha-numeric letter in the source array, find the numeric
146
         * value */
147
25.2M
        b64[bbidx++] = (val > 0 ? (uint8_t)val : 0);
148
149
        /* Decode every 4 base64 bytes into 3 ascii bytes */
150
25.2M
        if (bbidx == B64_BLOCK) {
151
152
            /* For every 4 bytes, add 3 bytes but deduct the '=' padded blocks */
153
6.25M
            uint32_t numDecoded_blk = ASCII_BLOCK - (padding < B64_BLOCK ? padding : ASCII_BLOCK);
154
6.25M
            if (dest_size < *decoded_bytes + numDecoded_blk) {
155
2.24k
                SCLogDebug("Destination buffer full");
156
2.24k
                ecode = BASE64_ECODE_BUF;
157
2.24k
                break;
158
2.24k
            }
159
6.25M
            if (dest_size - *decoded_bytes < ASCII_BLOCK)
160
194
                return BASE64_ECODE_BUF;
161
162
            /* Decode base-64 block into ascii block and move pointer */
163
6.25M
            DecodeBase64Block(dptr, b64);
164
6.25M
            dptr += numDecoded_blk;
165
6.25M
            *decoded_bytes += numDecoded_blk;
166
            /* Reset base-64 block and index */
167
6.25M
            bbidx = 0;
168
6.25M
            padding = 0;
169
6.25M
            *consumed_bytes += B64_BLOCK + sp;
170
6.25M
            sp = 0;
171
6.25M
            leading_sp = 0;
172
6.25M
            memset(&b64, 0, sizeof(b64));
173
6.25M
        }
174
25.2M
    }
175
176
507k
    if (bbidx > 0 && bbidx < 4 && ((!valid && mode == BASE64_MODE_RFC4648))) {
177
        /* Decoded bytes for 1 or 2 base64 encoded bytes is 1 */
178
1.58k
        padding = bbidx > 1 ? B64_BLOCK - bbidx : 2;
179
1.58k
        uint32_t numDecoded_blk = ASCII_BLOCK - (padding < B64_BLOCK ? padding : ASCII_BLOCK);
180
1.58k
        if (dest_size < *decoded_bytes + numDecoded_blk) {
181
0
            SCLogDebug("Destination buffer full");
182
0
            ecode = BASE64_ECODE_BUF;
183
0
            return ecode;
184
0
        }
185
        /* if the destination size is not at least 3 Bytes long, it'll give a dynamic
186
         * buffer overflow while decoding, so, return and let the caller take care of the
187
         * remaining bytes to be decoded which should always be < 4 at this stage */
188
1.58k
        if (dest_size - *decoded_bytes < ASCII_BLOCK)
189
0
            return BASE64_ECODE_BUF;
190
1.58k
        *decoded_bytes += numDecoded_blk;
191
1.58k
        DecodeBase64Block(dptr, b64);
192
1.58k
        *consumed_bytes += bbidx;
193
1.58k
    }
194
195
    /* Finish remaining b64 bytes by padding */
196
507k
    if (valid && bbidx > 0 && (mode != BASE64_MODE_RFC2045)) {
197
        /* Decode remaining */
198
749
        if (dest_size - *decoded_bytes < ASCII_BLOCK)
199
0
            return BASE64_ECODE_BUF;
200
749
        *decoded_bytes += ASCII_BLOCK - (B64_BLOCK - bbidx);
201
749
        DecodeBase64Block(dptr, b64);
202
749
    }
203
204
507k
    if (*decoded_bytes == 0) {
205
15.1k
        SCLogDebug("base64 decoding failed");
206
15.1k
    }
207
208
507k
    *consumed_bytes += leading_sp;
209
507k
    return ecode;
210
507k
}
211
212
#ifdef UNITTESTS
213
214
#define TEST_RFC2045(src, fin_str, dest_size, exp_decoded, exp_consumed, ecode)                    \
215
    {                                                                                              \
216
        uint32_t consumed_bytes = 0, num_decoded = 0;                                              \
217
        uint8_t dst[dest_size];                                                                    \
218
        Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src),         \
219
                &consumed_bytes, &num_decoded, BASE64_MODE_RFC2045);                               \
220
        FAIL_IF(code != ecode);                                                                    \
221
        FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0);                                       \
222
        FAIL_IF(num_decoded != exp_decoded);                                                       \
223
        FAIL_IF(consumed_bytes != exp_consumed);                                                   \
224
    }
225
226
#define TEST_RFC4648(src, fin_str, dest_size, exp_decoded, exp_consumed, ecode)                    \
227
    {                                                                                              \
228
        uint32_t consumed_bytes = 0, num_decoded = 0;                                              \
229
        uint8_t dst[dest_size];                                                                    \
230
        Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src),         \
231
                &consumed_bytes, &num_decoded, BASE64_MODE_RFC4648);                               \
232
        FAIL_IF(code != ecode);                                                                    \
233
        FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0);                                       \
234
        FAIL_IF(num_decoded != exp_decoded);                                                       \
235
        FAIL_IF(consumed_bytes != exp_consumed);                                                   \
236
    }
237
238
static int B64DecodeCompleteString(void)
239
{
240
    /*
241
     * SGVsbG8gV29ybGR6 : Hello Worldz
242
     * */
243
    const char *src = "SGVsbG8gV29ybGR6";
244
    const char *fin_str = "Hello Worldz";
245
    TEST_RFC2045(src, fin_str, strlen(fin_str), strlen(fin_str), strlen(src), BASE64_ECODE_OK);
246
    PASS;
247
}
248
249
static int B64DecodeInCompleteString(void)
250
{
251
    /*
252
     * SGVsbG8gV29ybGR6 : Hello Worldz
253
     * */
254
    const char *src = "SGVsbG8gV29ybGR";
255
    const char *fin_str = "Hello Wor";
256
    TEST_RFC2045(src, fin_str, strlen(fin_str), strlen(fin_str), strlen(src) - 3, BASE64_ECODE_OK);
257
    PASS;
258
}
259
260
static int B64DecodeCompleteStringWSp(void)
261
{
262
    /*
263
     * SGVsbG8gV29ybGQ= : Hello World
264
     * */
265
266
    const char *src = "SGVs bG8 gV29y bGQ=";
267
    const char *fin_str = "Hello World";
268
    TEST_RFC2045(src, fin_str, strlen(fin_str) + 3, strlen(fin_str), strlen(src), BASE64_ECODE_OK);
269
    PASS;
270
}
271
272
static int B64DecodeInCompleteStringWSp(void)
273
{
274
    /*
275
     * SGVsbG8gV29ybGQ= : Hello World
276
     * Special handling for this case (sp in remainder) done in ProcessBase64Remainder
277
     * */
278
279
    const char *src = "SGVs bG8 gV29y bGQ";
280
    const char *fin_str = "Hello Wor";
281
    TEST_RFC2045(src, fin_str, strlen(fin_str) + 1 /* 12 B in dest_size */, strlen(fin_str),
282
            strlen(src) - 3, BASE64_ECODE_OK);
283
    PASS;
284
}
285
286
static int B64DecodeStringBiggerThanBuffer(void)
287
{
288
    /*
289
     * SGVsbG8gV29ybGQ= : Hello World
290
     * */
291
292
    const char *src = "SGVs bG8 gV29y bGQ=";
293
    const char *fin_str = "Hello Wor";
294
    TEST_RFC2045(
295
            src, fin_str, strlen(fin_str) + 1, strlen(fin_str), strlen(src) - 4, BASE64_ECODE_BUF);
296
    PASS;
297
}
298
299
static int B64DecodeStringEndingSpaces(void)
300
{
301
    const char *src = "0YPhA d H";
302
    uint32_t consumed_bytes = 0, num_decoded = 0;
303
    uint8_t dst[10];
304
    Base64Ecode code = DecodeBase64(dst, sizeof(dst), (const uint8_t *)src, strlen(src),
305
            &consumed_bytes, &num_decoded, BASE64_MODE_RFC2045);
306
    FAIL_IF(code != BASE64_ECODE_OK);
307
    FAIL_IF(num_decoded != 3);
308
    FAIL_IF(consumed_bytes != 4);
309
    PASS;
310
}
311
312
static int B64TestVectorsRFC2045(void)
313
{
314
    const char *src1 = "";
315
    const char *fin_str1 = "";
316
317
    const char *src2 = "Zg==";
318
    const char *fin_str2 = "f";
319
320
    const char *src3 = "Zm8=";
321
    const char *fin_str3 = "fo";
322
323
    const char *src4 = "Zm9v";
324
    const char *fin_str4 = "foo";
325
326
    const char *src5 = "Zm9vYg==";
327
    const char *fin_str5 = "foob";
328
329
    const char *src6 = "Zm9vYmE=";
330
    const char *fin_str6 = "fooba";
331
332
    const char *src7 = "Zm9vYmFy";
333
    const char *fin_str7 = "foobar";
334
335
    const char *src8 = "Zm 9v Ym Fy";
336
    const char *fin_str8 = "foobar";
337
338
    const char *src9 = "Zm$9vYm.Fy";
339
    const char *fin_str9 = "foobar";
340
341
    const char *src10 = "Y21Wd2IzSjBaVzFoYVd4bWNtRjFaRUJoZEc4dVoyOTJMbUYxOmpqcHh4b3Rhb2w%5";
342
    const char *fin_str10 = "cmVwb3J0ZW1haWxmcmF1ZEBhdG8uZ292LmF1:jjpxxotaol9";
343
344
    TEST_RFC2045(src1, fin_str1, ASCII_BLOCK * 2, strlen(fin_str1), strlen(src1), BASE64_ECODE_OK);
345
    TEST_RFC2045(src2, fin_str2, ASCII_BLOCK * 2, strlen(fin_str2), strlen(src2), BASE64_ECODE_OK);
346
    TEST_RFC2045(src3, fin_str3, ASCII_BLOCK * 2, strlen(fin_str3), strlen(src3), BASE64_ECODE_OK);
347
    TEST_RFC2045(src4, fin_str4, ASCII_BLOCK * 2, strlen(fin_str4), strlen(src4), BASE64_ECODE_OK);
348
    TEST_RFC2045(src5, fin_str5, ASCII_BLOCK * 2, strlen(fin_str5), strlen(src5), BASE64_ECODE_OK);
349
    TEST_RFC2045(src6, fin_str6, ASCII_BLOCK * 2, strlen(fin_str6), strlen(src6), BASE64_ECODE_OK);
350
    TEST_RFC2045(src7, fin_str7, ASCII_BLOCK * 2, strlen(fin_str7), strlen(src7), BASE64_ECODE_OK);
351
    TEST_RFC2045(src8, fin_str8, ASCII_BLOCK * 2, strlen(fin_str8), strlen(src8), BASE64_ECODE_OK);
352
    TEST_RFC2045(src9, fin_str9, ASCII_BLOCK * 2, strlen(fin_str9), strlen(src9), BASE64_ECODE_OK);
353
    TEST_RFC2045(src10, fin_str10, strlen(fin_str10) + 2, strlen(fin_str10), strlen(src10),
354
            BASE64_ECODE_OK);
355
    PASS;
356
}
357
358
static int B64TestVectorsRFC4648(void)
359
{
360
    const char *src1 = "";
361
    const char *fin_str1 = "";
362
363
    const char *src2 = "Zg==";
364
    const char *fin_str2 = "f";
365
366
    const char *src3 = "Zm8=";
367
    const char *fin_str3 = "fo";
368
369
    const char *src4 = "Zm9v";
370
    const char *fin_str4 = "foo";
371
372
    const char *src5 = "Zm9vYg==";
373
    const char *fin_str5 = "foob";
374
375
    const char *src6 = "Zm9vYmE=";
376
    const char *fin_str6 = "fooba";
377
378
    const char *src7 = "Zm9vYmFy";
379
    const char *fin_str7 = "foobar";
380
381
    const char *src8 = "Zm 9v Ym Fy";
382
    const char *fin_str8 = "f";
383
384
    const char *src9 = "Zm$9vYm.Fy";
385
    const char *fin_str9 = "f";
386
387
    const char *src10 = "Y21Wd2IzSjBaVzFoYVd4bWNtRjFaRUJoZEc4dVoyOTJMbUYxOmpqcHh4b3Rhb2w%3D";
388
    const char *fin_str10 = "cmVwb3J0ZW1haWxmcmF1ZEBhdG8uZ292LmF1:jjpxxotaol";
389
390
    TEST_RFC4648(src1, fin_str1, ASCII_BLOCK * 2, strlen(fin_str1), strlen(src1), BASE64_ECODE_OK);
391
    TEST_RFC4648(src2, fin_str2, ASCII_BLOCK * 2, strlen(fin_str2), strlen(src2), BASE64_ECODE_OK);
392
    TEST_RFC4648(src3, fin_str3, ASCII_BLOCK * 2, strlen(fin_str3), strlen(src3), BASE64_ECODE_OK);
393
    TEST_RFC4648(src4, fin_str4, ASCII_BLOCK * 2, strlen(fin_str4), strlen(src4), BASE64_ECODE_OK);
394
    TEST_RFC4648(src5, fin_str5, ASCII_BLOCK * 2, strlen(fin_str5), strlen(src5), BASE64_ECODE_OK);
395
    TEST_RFC4648(src6, fin_str6, ASCII_BLOCK * 2, strlen(fin_str6), strlen(src6), BASE64_ECODE_OK);
396
    TEST_RFC4648(src7, fin_str7, ASCII_BLOCK * 2, strlen(fin_str7), strlen(src7), BASE64_ECODE_OK);
397
    TEST_RFC4648(src8, fin_str8, ASCII_BLOCK * 2, 1 /* f */, 2 /* Zm */, BASE64_ECODE_ERR);
398
    TEST_RFC4648(src9, fin_str9, ASCII_BLOCK * 2, 1 /* f */, 2 /* Zm */, BASE64_ECODE_ERR);
399
    TEST_RFC4648(src10, fin_str10, strlen(fin_str10) + 1, strlen(fin_str10), strlen(src10) - 3,
400
            BASE64_ECODE_ERR);
401
    PASS;
402
}
403
404
void Base64RegisterTests(void)
405
{
406
    UtRegisterTest("B64DecodeCompleteStringWSp", B64DecodeCompleteStringWSp);
407
    UtRegisterTest("B64DecodeInCompleteStringWSp", B64DecodeInCompleteStringWSp);
408
    UtRegisterTest("B64DecodeCompleteString", B64DecodeCompleteString);
409
    UtRegisterTest("B64DecodeInCompleteString", B64DecodeInCompleteString);
410
    UtRegisterTest("B64DecodeStringBiggerThanBuffer", B64DecodeStringBiggerThanBuffer);
411
    UtRegisterTest("B64DecodeStringEndingSpaces", B64DecodeStringEndingSpaces);
412
    UtRegisterTest("B64TestVectorsRFC2045", B64TestVectorsRFC2045);
413
    UtRegisterTest("B64TestVectorsRFC4648", B64TestVectorsRFC4648);
414
}
415
#endif