| /* | |
| * Copyright 2025-2026 The OpenSSL Project Authors. All Rights Reserved. | |
| * | |
| * Licensed under the Apache License 2.0 (the "License"). You may not use | |
| * this file except in compliance with the License. You can obtain a copy | |
| * in the file LICENSE in the source distribution or at | |
| * https://www.openssl.org/source/license.html | |
| */ | |
| #include <stdio.h> | |
| #include "testutil.h" | |
| #include <openssl/evp.h> | |
| #include "internal/cryptlib.h" | |
| #include "crypto/evp.h" | |
| #include "evp_local.h" | |
| #define MAX_INPUT_LEN 3000 | |
| static void fuzz_fill_encode_ctx(EVP_ENCODE_CTX *ctx, int max_fill) | |
| { | |
| static int seeded = 0; | |
| if (!seeded) { | |
| srand((unsigned)time(NULL)); | |
| seeded = 1; | |
| } | |
| int num = rand() % (max_fill + 1); | |
| ctx->num = num; | |
| for (int i = 0; i < num; i++) | |
| ctx->enc_data[i] = (unsigned char)(rand() & 0xFF); | |
| ctx->line_num = rand() % (EVP_ENCODE_B64_LENGTH + 1); | |
| } | |
| static inline uint32_t next_u32(uint32_t *state) | |
| { | |
| *state = (*state * 1664525u) + 1013904223u; | |
| return *state; | |
| } | |
| static const unsigned char data_bin2ascii[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
| /* SRP uses a different base64 alphabet */ | |
| static const unsigned char srpdata_bin2ascii[65] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./"; | |
| #ifndef CHARSET_EBCDIC | |
| #define conv_bin2ascii(a, table) ((table)[(a) & 0x3f]) | |
| #else | |
| /* | |
| * We assume that PEM encoded files are EBCDIC files (i.e., printable text | |
| * files). Convert them here while decoding. When encoding, output is EBCDIC | |
| * (text) format again. (No need for conversion in the conv_bin2ascii macro, | |
| * as the underlying textstring data_bin2ascii[] is already EBCDIC) | |
| */ | |
| #define conv_bin2ascii(a, table) ((table)[(a) & 0x3f]) | |
| #endif | |
| static int evp_encodeblock_int_old(EVP_ENCODE_CTX *ctx, unsigned char *t, | |
| const unsigned char *f, int dlen) | |
| { | |
| int i, ret = 0; | |
| unsigned long l; | |
| const unsigned char *table; | |
| if (ctx != NULL && (ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0) | |
| table = srpdata_bin2ascii; | |
| else | |
| table = data_bin2ascii; | |
| for (i = dlen; i > 0; i -= 3) { | |
| if (i >= 3) { | |
| l = (((unsigned long)f[0]) << 16L) | (((unsigned long)f[1]) << 8L) | f[2]; | |
| *(t++) = conv_bin2ascii(l >> 18L, table); | |
| *(t++) = conv_bin2ascii(l >> 12L, table); | |
| *(t++) = conv_bin2ascii(l >> 6L, table); | |
| *(t++) = conv_bin2ascii(l, table); | |
| } else { | |
| l = ((unsigned long)f[0]) << 16L; | |
| if (i == 2) | |
| l |= ((unsigned long)f[1] << 8L); | |
| *(t++) = conv_bin2ascii(l >> 18L, table); | |
| *(t++) = conv_bin2ascii(l >> 12L, table); | |
| *(t++) = (i == 1) ? '=' : conv_bin2ascii(l >> 6L, table); | |
| *(t++) = '='; | |
| } | |
| ret += 4; | |
| f += 3; | |
| } | |
| *t = '\0'; | |
| return ret; | |
| } | |
| static int evp_encodeupdate_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl, | |
| const unsigned char *in, int inl) | |
| { | |
| int i, j; | |
| int total = 0; | |
| *outl = 0; | |
| if (inl <= 0) | |
| return 0; | |
| OPENSSL_assert(EVP_ENCODE_B64_LENGTH <= (int)sizeof(ctx->enc_data)); | |
| if (EVP_ENCODE_B64_LENGTH - ctx->num > inl) { | |
| memcpy(&(ctx->enc_data[ctx->num]), in, inl); | |
| ctx->num += inl; | |
| return 1; | |
| } | |
| if (ctx->num != 0) { | |
| i = EVP_ENCODE_B64_LENGTH - ctx->num; | |
| memcpy(&(ctx->enc_data[ctx->num]), in, i); | |
| in += i; | |
| inl -= i; | |
| j = evp_encodeblock_int_old(ctx, out, ctx->enc_data, EVP_ENCODE_B64_LENGTH); | |
| ctx->num = 0; | |
| out += j; | |
| total = j; | |
| if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) { | |
| *(out++) = '\n'; | |
| total++; | |
| } | |
| *out = '\0'; | |
| } | |
| while (inl >= EVP_ENCODE_B64_LENGTH) { | |
| j = evp_encodeblock_int_old(ctx, out, in, EVP_ENCODE_B64_LENGTH); | |
| in += EVP_ENCODE_B64_LENGTH; | |
| inl -= EVP_ENCODE_B64_LENGTH; | |
| out += j; | |
| total += j; | |
| if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) { | |
| *(out++) = '\n'; | |
| total++; | |
| } | |
| *out = '\0'; | |
| } | |
| if (inl != 0) | |
| memcpy(&(ctx->enc_data[0]), in, inl); | |
| ctx->num = inl; | |
| *outl = total; | |
| return 1; | |
| } | |
| static void evp_encodefinal_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl) | |
| { | |
| unsigned int ret = 0; | |
| if (ctx->num != 0) { | |
| ret = evp_encodeblock_int_old(ctx, out, ctx->enc_data, ctx->num); | |
| if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) | |
| out[ret++] = '\n'; | |
| out[ret] = '\0'; | |
| ctx->num = 0; | |
| } | |
| *outl = ret; | |
| } | |
| static int test_encode_line_length_reinforced(void) | |
| { | |
| const int trials = 50; | |
| uint32_t seed = 12345; | |
| /* Generous output buffers (Update + Final + newlines), plus a guard byte */ | |
| unsigned char out_simd[9000 * 2 + 1] = { 0 }; | |
| unsigned char out_ref[9000 * 2 + 1] = { 0 }; | |
| EVP_ENCODE_CTX *ctx_simd = NULL; | |
| EVP_ENCODE_CTX *ctx_ref = NULL; | |
| for (int t = 0; t < trials; t++) { | |
| uint32_t r = next_u32(&seed); | |
| int inl = r % MAX_INPUT_LEN; | |
| /* Fresh random input */ | |
| unsigned char input[MAX_INPUT_LEN]; | |
| for (int i = 0; i < inl; i++) | |
| input[i] = (unsigned char)(r % 256); | |
| for (int partial_ctx_fill = 0; partial_ctx_fill <= 80; | |
| partial_ctx_fill += 1) { | |
| ctx_simd = EVP_ENCODE_CTX_new(); | |
| ctx_ref = EVP_ENCODE_CTX_new(); | |
| if (!TEST_ptr(ctx_simd) || !TEST_ptr(ctx_ref)) | |
| goto fail; | |
| fuzz_fill_encode_ctx(ctx_simd, partial_ctx_fill); | |
| memset(out_simd, 0xCC, sizeof(out_simd)); /* poison to catch short writes */ | |
| memset(out_ref, 0xDD, sizeof(out_ref)); | |
| int outlen_simd = 0, outlen_ref = 0; /* bytes produced by Update */ | |
| int finlen_simd = 0, finlen_ref = 0; /* bytes produced by Final */ | |
| EVP_EncodeInit(ctx_simd); | |
| EVP_EncodeInit(ctx_ref); | |
| for (int i = 0; i < 2; i++) { | |
| if (i % 2 == 0) { | |
| /* Turn SRP alphabet OFF */ | |
| ctx_simd->flags &= ~EVP_ENCODE_CTX_USE_SRP_ALPHABET; | |
| ctx_ref->flags &= ~EVP_ENCODE_CTX_USE_SRP_ALPHABET; | |
| } else { | |
| /* Turn SRP alphabet ON */ | |
| ctx_simd->flags |= EVP_ENCODE_CTX_USE_SRP_ALPHABET; | |
| ctx_ref->flags |= EVP_ENCODE_CTX_USE_SRP_ALPHABET; | |
| } | |
| int ret_simd = EVP_EncodeUpdate(ctx_simd, out_simd, &outlen_simd, | |
| input, (int)inl); | |
| int ret_ref = evp_encodeupdate_old(ctx_ref, out_ref, &outlen_ref, | |
| input, (int)inl); | |
| if (!TEST_int_eq(ret_simd, ret_ref) | |
| || !TEST_mem_eq(out_ref, outlen_ref, out_simd, outlen_simd) | |
| || !TEST_int_eq(outlen_simd, outlen_ref)) | |
| goto fail; | |
| EVP_EncodeFinal(ctx_simd, out_simd + outlen_simd, | |
| &finlen_simd); | |
| evp_encodefinal_old(ctx_ref, out_ref + outlen_ref, | |
| &finlen_ref); | |
| int total_ref = outlen_ref + finlen_ref; | |
| int total_simd = outlen_simd + finlen_simd; | |
| if (!TEST_int_eq(finlen_simd, finlen_ref) | |
| || !TEST_mem_eq(out_ref, total_ref, out_simd, total_simd)) | |
| goto fail; | |
| } | |
| EVP_ENCODE_CTX_free(ctx_simd); | |
| EVP_ENCODE_CTX_free(ctx_ref); | |
| } | |
| } | |
| return 1; | |
| fail: | |
| EVP_ENCODE_CTX_free(ctx_simd); | |
| EVP_ENCODE_CTX_free(ctx_ref); | |
| return 0; | |
| } | |
| int setup_tests(void) | |
| { | |
| ADD_TEST(test_encode_line_length_reinforced); | |
| return 1; | |
| } |