Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/crypto/evp/encode.c
Line
Count
Source
1
/*
2
 * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <stdio.h>
11
#include <limits.h>
12
#include <assert.h>
13
#include "internal/cryptlib.h"
14
#include <openssl/evp.h>
15
#include "crypto/evp.h"
16
#include "evp_local.h"
17
18
#include "enc_b64_avx2.h"
19
#include "dec_b64_avx2.h"
20
#include "enc_b64_scalar.h"
21
22
static unsigned char conv_ascii2bin(unsigned char a,
23
    const unsigned char *table);
24
size_t evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
25
    const unsigned char *f, int dlen, int *wrap_cnt);
26
static int evp_decodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
27
    const unsigned char *f, int n, int eof);
28
/*-
29
 * 64 char lines
30
 * pad input with 0
31
 * left over chars are set to =
32
 * 1 byte  => xx==
33
 * 2 bytes => xxx=
34
 * 3 bytes => xxxx
35
 */
36
#define BIN_PER_LINE (64 / 4 * 3)
37
#define CHUNKS_PER_LINE (64 / 4)
38
#define CHAR_PER_LINE (64 + 1)
39
40
/*-
41
 * 0xF0 is a EOLN
42
 * 0xF1 is ignore but next needs to be 0xF0 (for \r\n processing).
43
 * 0xF2 is EOF
44
 * 0xE0 is ignore at start of line.
45
 * 0xFF is error
46
 */
47
48
#define B64_EOLN 0xF0
49
#define B64_CR 0xF1
50
0
#define B64_EOF 0xF2
51
0
#define B64_WS 0xE0
52
0
#define B64_ERROR 0xFF
53
0
#define B64_NOT_BASE64(a) (((a) | 0x13) == 0xF3)
54
0
#define B64_BASE64(a) (!B64_NOT_BASE64(a))
55
56
static const unsigned char data_ascii2bin[128] = {
57
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
58
    0xF0, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
59
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
60
    0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
61
    0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xF2, 0xFF, 0x3F, 0x34, 0x35,
62
    0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF,
63
    0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04,
64
    0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
65
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
66
    0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x1B, 0x1C,
67
    0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
68
    0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
69
    0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
70
};
71
72
static const unsigned char srpdata_ascii2bin[128] = {
73
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
74
    0xF0, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
75
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
76
    0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
77
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0x3E, 0x3F, 0x00, 0x01,
78
    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF,
79
    0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
80
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
81
    0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
82
    0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x24, 0x25, 0x26,
83
    0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
84
    0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
85
    0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
86
};
87
88
#ifndef CHARSET_EBCDIC
89
static unsigned char conv_ascii2bin(unsigned char a, const unsigned char *table)
90
0
{
91
0
    if (a & 0x80)
92
0
        return B64_ERROR;
93
0
    return table[a];
94
0
}
95
#else
96
static unsigned char conv_ascii2bin(unsigned char a, const unsigned char *table)
97
{
98
    a = os_toascii[a];
99
    if (a & 0x80)
100
        return B64_ERROR;
101
    return table[a];
102
}
103
#endif
104
105
EVP_ENCODE_CTX *EVP_ENCODE_CTX_new(void)
106
0
{
107
0
    return OPENSSL_zalloc(sizeof(EVP_ENCODE_CTX));
108
0
}
109
110
void EVP_ENCODE_CTX_free(EVP_ENCODE_CTX *ctx)
111
0
{
112
0
    OPENSSL_free(ctx);
113
0
}
114
115
int EVP_ENCODE_CTX_copy(EVP_ENCODE_CTX *dctx, const EVP_ENCODE_CTX *sctx)
116
0
{
117
0
    memcpy(dctx, sctx, sizeof(EVP_ENCODE_CTX));
118
119
0
    return 1;
120
0
}
121
122
int EVP_ENCODE_CTX_num(EVP_ENCODE_CTX *ctx)
123
0
{
124
0
    return ctx->num;
125
0
}
126
127
void evp_encode_ctx_set_flags(EVP_ENCODE_CTX *ctx, unsigned int flags)
128
0
{
129
0
    ctx->flags = flags;
130
0
}
131
132
void EVP_EncodeInit(EVP_ENCODE_CTX *ctx)
133
0
{
134
0
    ctx->num = 0;
135
0
    ctx->line_num = 0;
136
0
    ctx->flags = 0;
137
0
}
138
139
int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
140
    const unsigned char *in, int inl)
141
0
{
142
0
    int i;
143
0
    size_t j;
144
0
    size_t total = 0;
145
146
0
    *outl = 0;
147
0
    if (inl <= 0)
148
0
        return 0;
149
0
    assert(EVP_ENCODE_B64_LENGTH <= (int)sizeof(ctx->enc_data));
150
0
    if (EVP_ENCODE_B64_LENGTH - ctx->num > inl) {
151
0
        memcpy(&(ctx->enc_data[ctx->num]), in, inl);
152
0
        ctx->num += inl;
153
0
        return 1;
154
0
    }
155
0
    if (ctx->num != 0) {
156
0
        i = EVP_ENCODE_B64_LENGTH - ctx->num;
157
0
        memcpy(&(ctx->enc_data[ctx->num]), in, i);
158
0
        in += i;
159
0
        inl -= i;
160
0
        int wrap_cnt = 0;
161
0
        j = evp_encodeblock_int(ctx, out, ctx->enc_data, EVP_ENCODE_B64_LENGTH,
162
0
            &wrap_cnt);
163
0
        ctx->num = 0;
164
0
        out += j;
165
0
        total = j;
166
0
        *out = '\0';
167
0
    }
168
0
    int wrap_cnt = 0;
169
0
    if (EVP_ENCODE_B64_LENGTH % 3 != 0) {
170
0
        j = evp_encodeblock_int(ctx, out, in, inl - (inl % EVP_ENCODE_B64_LENGTH),
171
0
            &wrap_cnt);
172
0
    } else {
173
#ifdef HAVE_AVX2
174
        if (HAVE_AVX2()) {
175
            const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? EVP_ENCODE_B64_LENGTH : 0;
176
177
            j = encode_base64_avx2(ctx,
178
                (unsigned char *)out,
179
                (const unsigned char *)in,
180
                inl - (inl % EVP_ENCODE_B64_LENGTH), newlines, &wrap_cnt);
181
        } else
182
#endif
183
0
        {
184
0
            j = evp_encodeblock_int(ctx, out, in, inl - (inl % EVP_ENCODE_B64_LENGTH),
185
0
                &wrap_cnt);
186
0
        }
187
0
    }
188
0
    in += inl - (inl % EVP_ENCODE_B64_LENGTH);
189
0
    inl -= inl - (inl % EVP_ENCODE_B64_LENGTH);
190
0
    out += j;
191
0
    total += j;
192
0
    if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0 && EVP_ENCODE_B64_LENGTH % 3 != 0) {
193
0
        *(out++) = '\n';
194
0
        total++;
195
0
    }
196
0
    *out = '\0';
197
0
    if (total > INT_MAX) {
198
        /* Too much output data! */
199
0
        *outl = 0;
200
0
        return 0;
201
0
    }
202
0
    if (inl != 0)
203
0
        memcpy(&(ctx->enc_data[0]), in, inl);
204
0
    ctx->num = inl;
205
0
    *outl = (int)total;
206
207
0
    return 1;
208
0
}
209
210
void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)
211
0
{
212
0
    size_t ret = 0;
213
0
    int wrap_cnt = 0;
214
215
0
    if (ctx->num != 0) {
216
0
        ret = evp_encodeblock_int(ctx, out, ctx->enc_data, ctx->num,
217
0
            &wrap_cnt);
218
0
        if (ret > 0) {
219
0
            if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
220
0
                out[ret++] = '\n';
221
0
            out[ret] = '\0';
222
0
            ctx->num = 0;
223
0
        }
224
0
    }
225
0
    *outl = (int)ret;
226
0
}
227
228
int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen)
229
0
{
230
0
    int wrap_cnt = 0;
231
232
#ifdef HAVE_AVX2
233
    if (HAVE_AVX2())
234
        return (int)encode_base64_avx2(NULL, t, f, dlen, 0, &wrap_cnt);
235
#endif
236
0
    return (int)evp_encodeblock_int(NULL, t, f, dlen, &wrap_cnt);
237
0
}
238
239
void EVP_DecodeInit(EVP_ENCODE_CTX *ctx)
240
0
{
241
    /* Only ctx->num and ctx->flags are used during decoding. */
242
0
    ctx->num = 0;
243
0
    ctx->line_num = 0;
244
0
    ctx->flags = 0;
245
0
}
246
247
/*-
248
 * -1 for error
249
 *  0 for last line
250
 *  1 for full line
251
 *
252
 * Note: even though EVP_DecodeUpdate attempts to detect and report end of
253
 * content, the context doesn't currently remember it and will accept more data
254
 * in the next call. Therefore, the caller is responsible for checking and
255
 * rejecting a 0 return value in the middle of content.
256
 *
257
 * Note: even though EVP_DecodeUpdate has historically tried to detect end of
258
 * content based on line length, this has never worked properly. Therefore,
259
 * we now return 0 when one of the following is true:
260
 *   - Padding or B64_EOF was detected and the last block is complete.
261
 *   - Input has zero-length.
262
 * -1 is returned if:
263
 *   - Invalid characters are detected.
264
 *   - There is extra trailing padding, or data after padding.
265
 *   - B64_EOF is detected after an incomplete base64 block.
266
 */
267
int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
268
    const unsigned char *in, int inl)
269
0
{
270
0
    int seof = 0, eof = 0, rv = -1, ret = 0, i, v, tmp, n, decoded_len;
271
0
    unsigned char *d;
272
0
    const unsigned char *table;
273
274
0
    n = ctx->num;
275
0
    d = ctx->enc_data;
276
277
0
    if (n > 0 && d[n - 1] == '=') {
278
0
        eof++;
279
0
        if (n > 1 && d[n - 2] == '=')
280
0
            eof++;
281
0
    }
282
283
    /* Legacy behaviour: an empty input chunk signals end of input. */
284
0
    if (inl == 0) {
285
0
        rv = 0;
286
0
        goto end;
287
0
    }
288
289
0
    if ((ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0)
290
0
        table = srpdata_ascii2bin;
291
0
    else
292
0
        table = data_ascii2bin;
293
294
#ifdef HAVE_AVX2
295
    if (HAVE_AVX2() && eof == 0 && n == 0 && inl >= 64) {
296
        int fast_consumed = 0;
297
        int decoded = decode_base64_avx2(ctx, out, in, inl,
298
            1 /* swallow_ws */, &fast_consumed);
299
300
        /* unconsumed input is left for the scalar loop */
301
        if (!ossl_assert(decoded >= 0)) {
302
            rv = -1;
303
            goto end;
304
        }
305
        ret += decoded;
306
        out += decoded;
307
        in += fast_consumed;
308
        inl -= fast_consumed;
309
        n = ctx->num;
310
        d = ctx->enc_data;
311
    }
312
#endif
313
314
0
    for (i = 0; i < inl; i++) {
315
0
        tmp = *(in++);
316
0
        v = conv_ascii2bin(tmp, table);
317
0
        if (v == B64_ERROR) {
318
0
            rv = -1;
319
0
            goto end;
320
0
        }
321
322
0
        if (tmp == '=') {
323
0
            eof++;
324
0
        } else if (eof > 0 && B64_BASE64(v)) {
325
            /* More data after padding. */
326
0
            rv = -1;
327
0
            goto end;
328
0
        }
329
330
0
        if (eof > 2) {
331
0
            rv = -1;
332
0
            goto end;
333
0
        }
334
335
0
        if (v == B64_EOF) {
336
0
            seof = 1;
337
0
            goto tail;
338
0
        }
339
340
        /* Only save valid base64 characters. */
341
0
        if (B64_BASE64(v)) {
342
0
            if (n >= 64) {
343
                /*
344
                 * We increment n once per loop, and empty the buffer as soon as
345
                 * we reach 64 characters, so this can only happen if someone's
346
                 * manually messed with the ctx. Refuse to write any more data.
347
                 */
348
0
                rv = -1;
349
0
                goto end;
350
0
            }
351
0
            OPENSSL_assert(n < (int)sizeof(ctx->enc_data));
352
0
            d[n++] = tmp;
353
0
        }
354
355
0
        if (n == 64) {
356
0
            decoded_len = evp_decodeblock_int(ctx, out, d, n, eof);
357
0
            n = 0;
358
0
            if (decoded_len < 0 || (decoded_len == 0 && eof > 0)) {
359
0
                rv = -1;
360
0
                goto end;
361
0
            }
362
0
            ret += decoded_len;
363
0
            out += decoded_len;
364
0
        }
365
0
    }
366
367
    /*
368
     * Legacy behaviour: if the current line is a full base64-block (i.e., has
369
     * 0 mod 4 base64 characters), it is processed immediately. We keep this
370
     * behaviour as applications may not be calling EVP_DecodeFinal properly.
371
     */
372
0
tail:
373
0
    if (n > 0) {
374
0
        if ((n & 3) == 0) {
375
0
            decoded_len = evp_decodeblock_int(ctx, out, d, n, eof);
376
0
            n = 0;
377
0
            if (decoded_len < 0 || (decoded_len == 0 && eof > 0)) {
378
0
                rv = -1;
379
0
                goto end;
380
0
            }
381
0
            ret += decoded_len;
382
0
        } else if (seof) {
383
            /* EOF in the middle of a base64 block. */
384
0
            rv = -1;
385
0
            goto end;
386
0
        }
387
0
    }
388
389
0
    rv = seof || (n == 0 && eof) ? 0 : 1;
390
0
end:
391
    /* Legacy behaviour. This should probably rather be zeroed on error. */
392
0
    *outl = ret;
393
0
    ctx->num = n;
394
0
    return rv;
395
0
}
396
397
static ossl_inline int evp_decodeblock_trim(const unsigned char **f, int n,
398
    const unsigned char *table)
399
0
{
400
    /* trim whitespace from the start of the line. */
401
0
    while ((n > 0) && (conv_ascii2bin(**f, table) == B64_WS)) {
402
0
        (*f)++;
403
0
        n--;
404
0
    }
405
406
    /*
407
     * strip off stuff at the end of the line ascii2bin values B64_WS,
408
     * B64_EOLN, B64_EOLN and B64_EOF
409
     */
410
0
    while ((n > 3) && (B64_NOT_BASE64(conv_ascii2bin((*f)[n - 1], table))))
411
0
        n--;
412
413
0
    return n;
414
0
}
415
416
static int evp_decodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
417
    const unsigned char *f, int n,
418
    int eof)
419
0
{
420
0
    int i, ret = 0, a, b, c, d;
421
0
    unsigned long l;
422
0
    const unsigned char *table;
423
424
0
    if (eof < -1 || eof > 2)
425
0
        return -1;
426
427
0
    if (ctx != NULL && (ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0)
428
0
        table = srpdata_ascii2bin;
429
0
    else
430
0
        table = data_ascii2bin;
431
432
0
    n = evp_decodeblock_trim(&f, n, table);
433
434
0
    if (n % 4 != 0)
435
0
        return -1;
436
0
    if (n == 0)
437
0
        return 0;
438
439
    /* all 4-byte blocks except the last one do not have padding. */
440
0
    for (i = 0; i < n - 4; i += 4) {
441
0
        a = conv_ascii2bin(*(f++), table);
442
0
        b = conv_ascii2bin(*(f++), table);
443
0
        c = conv_ascii2bin(*(f++), table);
444
0
        d = conv_ascii2bin(*(f++), table);
445
0
        if ((a | b | c | d) & 0x80)
446
0
            return -1;
447
0
        l = ((((unsigned long)a) << 18L) | (((unsigned long)b) << 12L) | (((unsigned long)c) << 6L) | (((unsigned long)d)));
448
0
        *(t++) = (unsigned char)(l >> 16L) & 0xff;
449
0
        *(t++) = (unsigned char)(l >> 8L) & 0xff;
450
0
        *(t++) = (unsigned char)(l) & 0xff;
451
0
        ret += 3;
452
0
    }
453
454
    /* process the last block that may have padding. */
455
0
    a = conv_ascii2bin(*(f++), table);
456
0
    b = conv_ascii2bin(*(f++), table);
457
0
    c = conv_ascii2bin(*(f++), table);
458
0
    d = conv_ascii2bin(*(f++), table);
459
0
    if ((a | b | c | d) & 0x80)
460
0
        return -1;
461
0
    l = ((((unsigned long)a) << 18L) | (((unsigned long)b) << 12L) | (((unsigned long)c) << 6L) | (((unsigned long)d)));
462
463
0
    if (eof == -1)
464
0
        eof = (c == '=') + (d == '=');
465
466
0
    switch (eof) {
467
0
    case 2:
468
0
        *t = (unsigned char)(l >> 16L) & 0xff;
469
0
        break;
470
0
    case 1:
471
0
        *(t++) = (unsigned char)(l >> 16L) & 0xff;
472
0
        *t = (unsigned char)(l >> 8L) & 0xff;
473
0
        break;
474
0
    case 0:
475
0
        *(t++) = (unsigned char)(l >> 16L) & 0xff;
476
0
        *(t++) = (unsigned char)(l >> 8L) & 0xff;
477
0
        *t = (unsigned char)(l) & 0xff;
478
0
        break;
479
0
    }
480
0
    ret += 3 - eof;
481
482
0
    return ret;
483
0
}
484
485
int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n)
486
0
{
487
#ifdef HAVE_AVX2
488
    if (HAVE_AVX2() && n >= 64) {
489
        const unsigned char *fx = f;
490
        int nx = evp_decodeblock_trim(&fx, n, data_ascii2bin);
491
492
        if (nx % 4 != 0)
493
            return -1;
494
495
        if (nx >= 64) {
496
            EVP_ENCODE_CTX tmpctx;
497
            int consumed = 0, result, rem;
498
499
            memset(&tmpctx, 0, sizeof(tmpctx));
500
            /* strict mode consumes pure base64 only and cannot fail */
501
            result = decode_base64_avx2(&tmpctx, t, fx, nx, 0 /* swallow_ws */, &consumed);
502
503
            if (consumed == nx)
504
                return result;
505
506
            /*
507
             * The remainder starts on a quad boundary, so whitespace
508
             * there is mid-input and always an error, whereas a
509
             * remainder call would trim it as leading.
510
             */
511
            if (conv_ascii2bin(fx[consumed], data_ascii2bin) == B64_WS)
512
                return -1;
513
514
            rem = evp_decodeblock_int(NULL, t + result, fx + consumed,
515
                nx - consumed, 0);
516
517
            return rem >= 0 ? result + rem : -1;
518
        }
519
    }
520
#endif
521
0
    return evp_decodeblock_int(NULL, t, f, n, 0);
522
0
}
523
524
int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)
525
0
{
526
0
    int i;
527
528
0
    *outl = 0;
529
0
    if (ctx->num != 0) {
530
0
        i = evp_decodeblock_int(ctx, out, ctx->enc_data, ctx->num, -1);
531
0
        if (i < 0)
532
0
            return -1;
533
0
        ctx->num = 0;
534
0
        *outl = i;
535
0
        return 1;
536
0
    } else
537
0
        return 1;
538
0
}