base64 encoder: Make ctx->length a constant

It is never changed anywhere.

Fixes #29518

Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
MergeDate: Mon Jan 19 14:20:35 2026
(Merged from https://github.com/openssl/openssl/pull/29550)
This commit is contained in:
Tomas Mraz
2026-01-05 18:47:23 +01:00
committed by Matt Caswell
parent 84eb1d3ac2
commit a27b2ca26a
4 changed files with 94 additions and 95 deletions

View File

@@ -134,7 +134,7 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
int srp = (ctx != NULL
&& (ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0);
int wrap_cnt_by_input = *wrap_cnt / 4 * 3;
const int ctx_length = (ctx != NULL) ? ctx->length : 0;
const int ctx_length = (ctx != NULL) ? EVP_ENCODE_B64_LENGTH : 0;
if (srp) {
e0 = base64_srp_bin2ascii_0;
@@ -168,11 +168,11 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
int wrap_cnt_nm3 = 0;
while (i + 2 < dlen) {
if (ctx != NULL) {
if ((wrap_cnt_nm3 < ctx->length
&& (wrap_cnt_nm3 + 3 + wrap_cnt_by_input) > ctx->length)
if ((wrap_cnt_nm3 < EVP_ENCODE_B64_LENGTH
&& (wrap_cnt_nm3 + 3 + wrap_cnt_by_input) > EVP_ENCODE_B64_LENGTH)
&& ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)) {
switch (ctx->length % 3) {
switch (EVP_ENCODE_B64_LENGTH % 3) {
case 0:
break;
case 1:
@@ -228,7 +228,9 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
ret += 4;
if (ctx != NULL) {
if ((i + 3 + wrap_cnt_by_input) % ctx->length == 0 && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) && ctx->length % 3 == 0) {
if ((i + 3 + wrap_cnt_by_input) % EVP_ENCODE_B64_LENGTH == 0
&& ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
&& EVP_ENCODE_B64_LENGTH % 3 == 0) {
*(t++) = '\n';
ret++;
}
@@ -249,7 +251,9 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
ret += 4;
if (ctx != NULL) {
if ((i + 1 + wrap_cnt_by_input) % ctx->length == 0 && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) && ctx->length % 3 == 0) {
if ((i + 1 + wrap_cnt_by_input) % EVP_ENCODE_B64_LENGTH == 0
&& ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
&& EVP_ENCODE_B64_LENGTH % 3 == 0) {
*(t++) = '\n';
ret++;
}
@@ -266,7 +270,9 @@ int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
ret += 4;
if (ctx != NULL) {
if ((i + 2 + wrap_cnt_by_input) % ctx->length == 0 && ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) && ctx->length % 3 == 0) {
if ((i + 2 + wrap_cnt_by_input) % EVP_ENCODE_B64_LENGTH == 0
&& ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
&& EVP_ENCODE_B64_LENGTH % 3 == 0) {
*(t++) = '\n';
ret++;
}

View File

@@ -9,6 +9,7 @@
#include <stdio.h>
#include <limits.h>
#include <assert.h>
#include "internal/cryptlib.h"
#include <openssl/evp.h>
#include "crypto/evp.h"
@@ -364,7 +365,6 @@ void evp_encode_ctx_set_flags(EVP_ENCODE_CTX *ctx, unsigned int flags)
void EVP_EncodeInit(EVP_ENCODE_CTX *ctx)
{
ctx->length = 48;
ctx->num = 0;
ctx->line_num = 0;
ctx->flags = 0;
@@ -379,19 +379,19 @@ int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
*outl = 0;
if (inl <= 0)
return 0;
OPENSSL_assert(ctx->length <= (int)sizeof(ctx->enc_data));
if (ctx->length - ctx->num > inl) {
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 = ctx->length - ctx->num;
i = EVP_ENCODE_B64_LENGTH - ctx->num;
memcpy(&(ctx->enc_data[ctx->num]), in, i);
in += i;
inl -= i;
int wrap_cnt = 0;
j = evp_encodeblock_int(ctx, out, ctx->enc_data, ctx->length,
j = evp_encodeblock_int(ctx, out, ctx->enc_data, EVP_ENCODE_B64_LENGTH,
&wrap_cnt);
ctx->num = 0;
out += j;
@@ -399,39 +399,39 @@ int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
*out = '\0';
}
int wrap_cnt = 0;
if (ctx->length % 3 != 0) {
j = evp_encodeblock_int(ctx, out, in, inl - (inl % ctx->length),
if (EVP_ENCODE_B64_LENGTH % 3 != 0) {
j = evp_encodeblock_int(ctx, out, in, inl - (inl % EVP_ENCODE_B64_LENGTH),
&wrap_cnt);
} else {
#if defined(__AVX2__)
const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? ctx->length : 0;
const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? EVP_ENCODE_B64_LENGTH : 0;
j = encode_base64_avx2(ctx,
(unsigned char *)out,
(const unsigned char *)in,
inl - (inl % ctx->length), newlines, &wrap_cnt);
inl - (inl % EVP_ENCODE_B64_LENGTH), newlines, &wrap_cnt);
#elif defined(HAS_IA32CAP_IS_64)
if ((OPENSSL_ia32cap_P[2] & (1u << 5)) != 0) {
const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? ctx->length : 0;
const int newlines = !(ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) ? EVP_ENCODE_B64_LENGTH : 0;
j = encode_base64_avx2(ctx,
(unsigned char *)out,
(const unsigned char *)in,
inl - (inl % ctx->length), newlines, &wrap_cnt);
inl - (inl % EVP_ENCODE_B64_LENGTH), newlines, &wrap_cnt);
} else {
j = evp_encodeblock_int(ctx, out, in, inl - (inl % ctx->length),
j = evp_encodeblock_int(ctx, out, in, inl - (inl % EVP_ENCODE_B64_LENGTH),
&wrap_cnt);
}
#else
j = evp_encodeblock_int(ctx, out, in, inl - (inl % ctx->length),
j = evp_encodeblock_int(ctx, out, in, inl - (inl % EVP_ENCODE_B64_LENGTH),
&wrap_cnt);
#endif
}
in += inl - (inl % ctx->length);
inl -= inl - (inl % ctx->length);
in += inl - (inl % EVP_ENCODE_B64_LENGTH);
inl -= inl - (inl % EVP_ENCODE_B64_LENGTH);
out += j;
total += j;
if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0 && ctx->length % 3 != 0) {
if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0 && EVP_ENCODE_B64_LENGTH % 3 != 0) {
*(out++) = '\n';
total++;
}
@@ -487,7 +487,6 @@ void EVP_DecodeInit(EVP_ENCODE_CTX *ctx)
{
/* Only ctx->num and ctx->flags are used during decoding. */
ctx->num = 0;
ctx->length = 0;
ctx->line_num = 0;
ctx->flags = 0;
}

View File

@@ -12,6 +12,12 @@
#define EVP_CTRL_RET_UNSUPPORTED -1
/*
* Length of the BASE64-encoded lines when encoding.
* This needs to be divisible by 3 to keep the AVX2 optimized code path.
*/
#define EVP_ENCODE_B64_LENGTH 48
struct evp_md_ctx_st {
const EVP_MD *reqdigest; /* The original requested digest */
const EVP_MD *digest;
@@ -272,12 +278,6 @@ int PKCS5_v2_PBKDF2_keyivgen_ex(EVP_CIPHER_CTX *ctx, const char *pass,
struct evp_Encode_Ctx_st {
/* number saved in a partial encode/decode */
int num;
/*
* The length is either the output line length (in input bytes) or the
* shortest input line length that is ok. Once decoding begins, the
* length is adjusted up each time a longer line is decoded
*/
int length;
/* data to encode */
unsigned char enc_data[80];
/* number read on current line */

View File

@@ -30,8 +30,7 @@ static void fuzz_fill_encode_ctx(EVP_ENCODE_CTX *ctx, int max_fill)
for (int i = 0; i < num; i++)
ctx->enc_data[i] = (unsigned char)(rand() & 0xFF);
ctx->length = (rand() % 80) + 1;
ctx->line_num = rand() % (ctx->length + 1);
ctx->line_num = rand() % (EVP_ENCODE_B64_LENGTH + 1);
}
static inline uint32_t next_u32(uint32_t *state)
{
@@ -100,18 +99,18 @@ static int evp_encodeupdate_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *ou
*outl = 0;
if (inl <= 0)
return 0;
OPENSSL_assert(ctx->length <= (int)sizeof(ctx->enc_data));
if (ctx->length - ctx->num > inl) {
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 = ctx->length - ctx->num;
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, ctx->length);
j = evp_encodeblock_int_old(ctx, out, ctx->enc_data, EVP_ENCODE_B64_LENGTH);
ctx->num = 0;
out += j;
total = j;
@@ -121,10 +120,10 @@ static int evp_encodeupdate_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *ou
}
*out = '\0';
}
while (inl >= ctx->length) {
j = evp_encodeblock_int_old(ctx, out, in, ctx->length);
in += ctx->length;
inl -= ctx->length;
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) {
@@ -154,7 +153,8 @@ static void evp_encodefinal_old(EVP_ENCODE_CTX *ctx, unsigned char *out, int *ou
}
*outl = ret;
}
static int test_encode_line_lengths_reinforced(void)
static int test_encode_line_length_reinforced(void)
{
const int trials = 50;
uint32_t seed = 12345;
@@ -175,65 +175,59 @@ static int test_encode_line_lengths_reinforced(void)
for (int partial_ctx_fill = 0; partial_ctx_fill <= 80;
partial_ctx_fill += 1) {
for (int ctx_len = 1; ctx_len <= 80; ctx_len += 1) {
ctx_simd = EVP_ENCODE_CTX_new();
ctx_ref = EVP_ENCODE_CTX_new();
ctx_simd = EVP_ENCODE_CTX_new();
ctx_ref = EVP_ENCODE_CTX_new();
if (!ctx_simd || !ctx_ref) {
TEST_error("Out of memory for contexts");
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;
}
fuzz_fill_encode_ctx(ctx_simd, partial_ctx_fill);
EVP_EncodeFinal(ctx_simd, out_simd + outlen_simd,
&finlen_simd);
evp_encodefinal_old(ctx_ref, out_ref + outlen_ref,
&finlen_ref);
memset(out_simd, 0xCC, sizeof(out_simd)); /* poison to catch short writes */
memset(out_ref, 0xDD, sizeof(out_ref));
int total_ref = outlen_ref + finlen_ref;
int total_simd = outlen_simd + finlen_simd;
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);
ctx_simd->length = ctx_len;
ctx_ref->length = ctx_len;
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);
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);
}
}
@@ -247,7 +241,7 @@ fail:
int setup_tests(void)
{
ADD_TEST(test_encode_line_lengths_reinforced);
ADD_TEST(test_encode_line_length_reinforced);
return 1;
}