Commit 6f73fe1c68 for openssl.org
commit 6f73fe1c688dc3e4c3815e8f33e5b672abeb28d3
Author: Tomas Mraz <tomas@openssl.org>
Date: Thu Dec 18 17:07:44 2025 +0100
Remove BIO_f_reliable() as it is broken
It was broken since the OpenSSL 3.0 release and
nobody complained. Apparently nobody is using it.
It would be practically impossible to reimplement
it with the provided EVP_MDs in backwards-compatible
manner.
Fixes #29413
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/29445)
diff --git a/CHANGES.md b/CHANGES.md
index 0e5a3c813f..3bf218a50a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -101,6 +101,11 @@ OpenSSL 4.0
*Milan Broz*, *Neil Horman*, *Norbert Pocs*
+ * BIO_f_reliable() implementation was removed without replacement.
+ It was broken since 3.0 release without any complaints.
+
+ *Tomáš Mráz*
+
* Added SNMP KDF (EVP_KDF_SNMPKDF) to EVP_KDF
*Barry Fussell and Helen Zhang*
diff --git a/crypto/evp/bio_ok.c b/crypto/evp/bio_ok.c
deleted file mode 100644
index 9b48c3c745..0000000000
--- a/crypto/evp/bio_ok.c
+++ /dev/null
@@ -1,606 +0,0 @@
-/*
- * Copyright 1995-2025 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
- */
-
-/*-
- From: Arne Ansper
-
- Why BIO_f_reliable?
-
- I wrote function which took BIO* as argument, read data from it
- and processed it. Then I wanted to store the input file in
- encrypted form. OK I pushed BIO_f_cipher to the BIO stack
- and everything was OK. BUT if user types wrong password
- BIO_f_cipher outputs only garbage and my function crashes. Yes
- I can and I should fix my function, but BIO_f_cipher is
- easy way to add encryption support to many existing applications
- and it's hard to debug and fix them all.
-
- So I wanted another BIO which would catch the incorrect passwords and
- file damages which cause garbage on BIO_f_cipher's output.
-
- The easy way is to push the BIO_f_md and save the checksum at
- the end of the file. However there are several problems with this
- approach:
-
- 1) you must somehow separate checksum from actual data.
- 2) you need lot's of memory when reading the file, because you
- must read to the end of the file and verify the checksum before
- letting the application to read the data.
-
- BIO_f_reliable tries to solve both problems, so that you can
- read and write arbitrary long streams using only fixed amount
- of memory.
-
- BIO_f_reliable splits data stream into blocks. Each block is prefixed
- with its length and suffixed with its digest. So you need only
- several Kbytes of memory to buffer single block before verifying
- its digest.
-
- BIO_f_reliable goes further and adds several important capabilities:
-
- 1) the digest of the block is computed over the whole stream
- -- so nobody can rearrange the blocks or remove or replace them.
-
- 2) to detect invalid passwords right at the start BIO_f_reliable
- adds special prefix to the stream. In order to avoid known plain-text
- attacks this prefix is generated as follows:
-
- *) digest is initialized with random seed instead of
- standardized one.
- *) same seed is written to output
- *) well-known text is then hashed and the output
- of the digest is also written to output.
-
- reader can now read the seed from stream, hash the same string
- and then compare the digest output.
-
- Bad things: BIO_f_reliable knows what's going on in EVP_Digest. I
- initially wrote and tested this code on x86 machine and wrote the
- digests out in machine-dependent order :( There are people using
- this code and I cannot change this easily without making existing
- data files unreadable.
-
-*/
-
-#include <stdio.h>
-#include <errno.h>
-#include <assert.h>
-#include "internal/cryptlib.h"
-#include <openssl/buffer.h>
-#include "internal/bio.h"
-#include <openssl/evp.h>
-#include <openssl/rand.h>
-#include "internal/endian.h"
-#include "internal/numbers.h" /* includes SIZE_MAX */
-#include "crypto/evp.h"
-
-static int ok_write(BIO *h, const char *buf, int num);
-static int ok_read(BIO *h, char *buf, int size);
-static long ok_ctrl(BIO *h, int cmd, long arg1, void *arg2);
-static int ok_new(BIO *h);
-static int ok_free(BIO *data);
-static long ok_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
-
-static __owur int sig_out(BIO *b);
-static __owur int sig_in(BIO *b);
-static __owur int block_out(BIO *b);
-static __owur int block_in(BIO *b);
-#define OK_BLOCK_SIZE (1024 * 4)
-#define OK_BLOCK_BLOCK 4
-#define IOBS (OK_BLOCK_SIZE + OK_BLOCK_BLOCK + 3 * EVP_MAX_MD_SIZE)
-#define WELLKNOWN "The quick brown fox jumped over the lazy dog's back."
-
-typedef struct ok_struct {
- size_t buf_len;
- size_t buf_off;
- size_t buf_len_save;
- size_t buf_off_save;
- int cont; /* <= 0 when finished */
- int finished;
- EVP_MD_CTX *md;
- int blockout; /* output block is ready */
- int sigio; /* must process signature */
- unsigned char buf[IOBS];
-} BIO_OK_CTX;
-
-static const BIO_METHOD methods_ok = {
- BIO_TYPE_CIPHER,
- "reliable",
- bwrite_conv,
- ok_write,
- bread_conv,
- ok_read,
- NULL, /* ok_puts, */
- NULL, /* ok_gets, */
- ok_ctrl,
- ok_new,
- ok_free,
- ok_callback_ctrl,
-};
-
-const BIO_METHOD *BIO_f_reliable(void)
-{
- return &methods_ok;
-}
-
-static int ok_new(BIO *bi)
-{
- BIO_OK_CTX *ctx;
-
- if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
- return 0;
-
- ctx->cont = 1;
- ctx->sigio = 1;
- ctx->md = EVP_MD_CTX_new();
- if (ctx->md == NULL) {
- OPENSSL_free(ctx);
- return 0;
- }
- BIO_set_init(bi, 0);
- BIO_set_data(bi, ctx);
-
- return 1;
-}
-
-static int ok_free(BIO *a)
-{
- BIO_OK_CTX *ctx;
-
- if (a == NULL)
- return 0;
-
- ctx = BIO_get_data(a);
-
- EVP_MD_CTX_free(ctx->md);
- OPENSSL_clear_free(ctx, sizeof(BIO_OK_CTX));
- BIO_set_data(a, NULL);
- BIO_set_init(a, 0);
-
- return 1;
-}
-
-static int ok_read(BIO *b, char *out, int outl)
-{
- int ret = 0, i, n;
- BIO_OK_CTX *ctx;
- BIO *next;
-
- if (out == NULL)
- return 0;
-
- ctx = BIO_get_data(b);
- next = BIO_next(b);
-
- if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0))
- return 0;
-
- while (outl > 0) {
-
- /* copy clean bytes to output buffer */
- if (ctx->blockout) {
- i = (int)(ctx->buf_len - ctx->buf_off);
- if (i > outl)
- i = outl;
- memcpy(out, &(ctx->buf[ctx->buf_off]), i);
- ret += i;
- out += i;
- outl -= i;
- ctx->buf_off += i;
-
- /* all clean bytes are out */
- if (ctx->buf_len == ctx->buf_off) {
- ctx->buf_off = 0;
-
- /*
- * copy start of the next block into proper place
- */
- if (ctx->buf_len_save > ctx->buf_off_save) {
- ctx->buf_len = ctx->buf_len_save - ctx->buf_off_save;
- memmove(ctx->buf, &(ctx->buf[ctx->buf_off_save]),
- ctx->buf_len);
- } else {
- ctx->buf_len = 0;
- }
- ctx->blockout = 0;
- }
- }
-
- /* output buffer full -- cancel */
- if (outl == 0)
- break;
-
- /* no clean bytes in buffer -- fill it */
- n = (int)(IOBS - ctx->buf_len);
- i = BIO_read(next, &(ctx->buf[ctx->buf_len]), n);
-
- if (i <= 0)
- break; /* nothing new */
-
- ctx->buf_len += i;
-
- /* no signature yet -- check if we got one */
- if (ctx->sigio == 1) {
- if (!sig_in(b)) {
- BIO_clear_retry_flags(b);
- return 0;
- }
- }
-
- /* signature ok -- check if we got block */
- if (ctx->sigio == 0) {
- if (!block_in(b)) {
- BIO_clear_retry_flags(b);
- return 0;
- }
- }
-
- /* invalid block -- cancel */
- if (ctx->cont <= 0)
- break;
- }
-
- BIO_clear_retry_flags(b);
- BIO_copy_next_retry(b);
- return ret;
-}
-
-static int ok_write(BIO *b, const char *in, int inl)
-{
- int ret = 0, n, i;
- BIO_OK_CTX *ctx;
- BIO *next;
-
- if (inl <= 0)
- return inl;
-
- ctx = BIO_get_data(b);
- next = BIO_next(b);
- ret = inl;
-
- if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0))
- return 0;
-
- if (ctx->sigio && !sig_out(b))
- return 0;
-
- do {
- BIO_clear_retry_flags(b);
- n = (int)(ctx->buf_len - ctx->buf_off);
- while (ctx->blockout && n > 0) {
- i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n);
- if (i <= 0) {
- BIO_copy_next_retry(b);
- if (!BIO_should_retry(b))
- ctx->cont = 0;
- return i;
- }
- ctx->buf_off += i;
- n -= i;
- }
-
- /* at this point all pending data has been written */
- ctx->blockout = 0;
- if (ctx->buf_len == ctx->buf_off) {
- ctx->buf_len = OK_BLOCK_BLOCK;
- ctx->buf_off = 0;
- }
-
- if ((in == NULL) || (inl <= 0))
- return 0;
-
- n = (inl + ctx->buf_len > OK_BLOCK_SIZE + OK_BLOCK_BLOCK) ? (int)(OK_BLOCK_SIZE + OK_BLOCK_BLOCK - ctx->buf_len) : inl;
-
- memcpy(&ctx->buf[ctx->buf_len], in, n);
- ctx->buf_len += n;
- inl -= n;
- in += n;
-
- if (ctx->buf_len >= OK_BLOCK_SIZE + OK_BLOCK_BLOCK) {
- if (!block_out(b)) {
- BIO_clear_retry_flags(b);
- return 0;
- }
- }
- } while (inl > 0);
-
- BIO_clear_retry_flags(b);
- BIO_copy_next_retry(b);
- return ret;
-}
-
-static long ok_ctrl(BIO *b, int cmd, long num, void *ptr)
-{
- BIO_OK_CTX *ctx;
- EVP_MD *md;
- const EVP_MD **ppmd;
- long ret = 1;
- int i;
- BIO *next;
-
- ctx = BIO_get_data(b);
- next = BIO_next(b);
-
- switch (cmd) {
- case BIO_CTRL_RESET:
- ctx->buf_len = 0;
- ctx->buf_off = 0;
- ctx->buf_len_save = 0;
- ctx->buf_off_save = 0;
- ctx->cont = 1;
- ctx->finished = 0;
- ctx->blockout = 0;
- ctx->sigio = 1;
- ret = BIO_ctrl(next, cmd, num, ptr);
- break;
- case BIO_CTRL_EOF: /* More to read */
- if (ctx->cont <= 0)
- ret = 1;
- else
- ret = BIO_ctrl(next, cmd, num, ptr);
- break;
- case BIO_CTRL_PENDING: /* More to read in buffer */
- case BIO_CTRL_WPENDING: /* More to read in buffer */
- ret = ctx->blockout ? (long)(ctx->buf_len - ctx->buf_off) : 0;
- if (ret <= 0)
- ret = BIO_ctrl(next, cmd, num, ptr);
- break;
- case BIO_CTRL_FLUSH:
- /* do a final write */
- if (ctx->blockout == 0)
- if (!block_out(b))
- return 0;
-
- while (ctx->blockout) {
- i = ok_write(b, NULL, 0);
- if (i < 0) {
- ret = i;
- break;
- }
- }
-
- ctx->finished = 1;
- ctx->buf_off = ctx->buf_len = 0;
- ctx->cont = (int)ret;
-
- /* Finally flush the underlying BIO */
- ret = BIO_ctrl(next, cmd, num, ptr);
- BIO_copy_next_retry(b);
- break;
- case BIO_C_DO_STATE_MACHINE:
- BIO_clear_retry_flags(b);
- ret = BIO_ctrl(next, cmd, num, ptr);
- BIO_copy_next_retry(b);
- break;
- case BIO_CTRL_INFO:
- ret = (long)ctx->cont;
- break;
- case BIO_C_SET_MD:
- md = ptr;
- if (!EVP_DigestInit_ex(ctx->md, md, NULL))
- return 0;
- BIO_set_init(b, 1);
- break;
- case BIO_C_GET_MD:
- if (BIO_get_init(b)) {
- ppmd = ptr;
- *ppmd = EVP_MD_CTX_get0_md(ctx->md);
- } else
- ret = 0;
- break;
- default:
- ret = BIO_ctrl(next, cmd, num, ptr);
- break;
- }
- return ret;
-}
-
-static long ok_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
-{
- BIO *next;
-
- next = BIO_next(b);
-
- if (next == NULL)
- return 0;
-
- return BIO_callback_ctrl(next, cmd, fp);
-}
-
-static void longswap(void *_ptr, size_t len)
-{
- DECLARE_IS_ENDIAN;
-
- if (IS_LITTLE_ENDIAN) {
- size_t i;
- unsigned char *p = _ptr, c;
-
- for (i = 0; i < len; i += 4) {
- c = p[0], p[0] = p[3], p[3] = c;
- c = p[1], p[1] = p[2], p[2] = c;
- }
- }
-}
-
-static int sig_out(BIO *b)
-{
- BIO_OK_CTX *ctx;
- EVP_MD_CTX *md;
- const EVP_MD *digest;
- int md_size;
- void *md_data;
-
- ctx = BIO_get_data(b);
- md = ctx->md;
- digest = EVP_MD_CTX_get0_md(md);
- md_size = EVP_MD_get_size(digest);
- md_data = EVP_MD_CTX_get0_md_data(md);
-
- if (md_size <= 0)
- goto berr;
- if (ctx->buf_len + 2 * md_size > OK_BLOCK_SIZE)
- return 1;
-
- if (!EVP_DigestInit_ex(md, digest, NULL))
- goto berr;
- /*
- * FIXME: there's absolutely no guarantee this makes any sense at all,
- * particularly now EVP_MD_CTX has been restructured.
- */
- if (RAND_bytes(md_data, md_size) <= 0)
- goto berr;
- memcpy(&(ctx->buf[ctx->buf_len]), md_data, md_size);
- longswap(&(ctx->buf[ctx->buf_len]), md_size);
- ctx->buf_len += md_size;
-
- if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
- goto berr;
- if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
- goto berr;
- ctx->buf_len += md_size;
- ctx->blockout = 1;
- ctx->sigio = 0;
- return 1;
-berr:
- BIO_clear_retry_flags(b);
- return 0;
-}
-
-static int sig_in(BIO *b)
-{
- BIO_OK_CTX *ctx;
- EVP_MD_CTX *md;
- unsigned char tmp[EVP_MAX_MD_SIZE];
- int ret = 0;
- const EVP_MD *digest;
- int md_size;
- void *md_data;
-
- ctx = BIO_get_data(b);
- if ((md = ctx->md) == NULL)
- goto berr;
- digest = EVP_MD_CTX_get0_md(md);
- if ((md_size = EVP_MD_get_size(digest)) <= 0)
- goto berr;
- md_data = EVP_MD_CTX_get0_md_data(md);
-
- if ((int)(ctx->buf_len - ctx->buf_off) < 2 * md_size)
- return 1;
-
- if (!EVP_DigestInit_ex(md, digest, NULL))
- goto berr;
- memcpy(md_data, &(ctx->buf[ctx->buf_off]), md_size);
- longswap(md_data, md_size);
- ctx->buf_off += md_size;
-
- if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
- goto berr;
- if (!EVP_DigestFinal_ex(md, tmp, NULL))
- goto berr;
- ret = memcmp(&(ctx->buf[ctx->buf_off]), tmp, md_size) == 0;
- ctx->buf_off += md_size;
- if (ret == 1) {
- ctx->sigio = 0;
- if (ctx->buf_len != ctx->buf_off) {
- memmove(ctx->buf, &(ctx->buf[ctx->buf_off]),
- ctx->buf_len - ctx->buf_off);
- }
- ctx->buf_len -= ctx->buf_off;
- ctx->buf_off = 0;
- } else {
- ctx->cont = 0;
- }
- return 1;
-berr:
- BIO_clear_retry_flags(b);
- return 0;
-}
-
-static int block_out(BIO *b)
-{
- BIO_OK_CTX *ctx;
- EVP_MD_CTX *md;
- unsigned long tl;
- const EVP_MD *digest;
- int md_size;
-
- ctx = BIO_get_data(b);
- md = ctx->md;
- digest = EVP_MD_CTX_get0_md(md);
- md_size = EVP_MD_get_size(digest);
- if (md_size <= 0)
- goto berr;
-
- tl = (unsigned long)(ctx->buf_len - OK_BLOCK_BLOCK);
- ctx->buf[0] = (unsigned char)(tl >> 24);
- ctx->buf[1] = (unsigned char)(tl >> 16);
- ctx->buf[2] = (unsigned char)(tl >> 8);
- ctx->buf[3] = (unsigned char)(tl);
- if (!EVP_DigestUpdate(md,
- (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
- goto berr;
- if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
- goto berr;
- ctx->buf_len += md_size;
- ctx->blockout = 1;
- return 1;
-berr:
- BIO_clear_retry_flags(b);
- return 0;
-}
-
-static int block_in(BIO *b)
-{
- BIO_OK_CTX *ctx;
- EVP_MD_CTX *md;
- size_t tl = 0;
- unsigned char tmp[EVP_MAX_MD_SIZE];
- int md_size;
-
- ctx = BIO_get_data(b);
- md = ctx->md;
- md_size = EVP_MD_get_size(EVP_MD_CTX_get0_md(md));
- if (md_size <= 0)
- goto berr;
-
- assert(sizeof(tl) >= OK_BLOCK_BLOCK); /* always true */
- tl = ((size_t)ctx->buf[0] << 24)
- | ((size_t)ctx->buf[1] << 16)
- | ((size_t)ctx->buf[2] << 8)
- | ((size_t)ctx->buf[3]);
-
- if (tl > OK_BLOCK_SIZE)
- goto berr;
-
- if (tl > SIZE_MAX - OK_BLOCK_BLOCK - (size_t)md_size)
- goto berr;
-
- if (ctx->buf_len < tl + OK_BLOCK_BLOCK + (size_t)md_size)
- return 1;
-
- if (!EVP_DigestUpdate(md,
- (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
- goto berr;
- if (!EVP_DigestFinal_ex(md, tmp, NULL))
- goto berr;
- if (memcmp(&(ctx->buf[tl + OK_BLOCK_BLOCK]), tmp, (size_t)md_size) == 0) {
- /* there might be parts from next block lurking around ! */
- ctx->buf_off_save = tl + OK_BLOCK_BLOCK + md_size;
- ctx->buf_len_save = ctx->buf_len;
- ctx->buf_off = OK_BLOCK_BLOCK;
- ctx->buf_len = tl + OK_BLOCK_BLOCK;
- ctx->blockout = 1;
- } else {
- ctx->cont = 0;
- }
- return 1;
-berr:
- BIO_clear_retry_flags(b);
- return 0;
-}
diff --git a/crypto/evp/build.info b/crypto/evp/build.info
index 1cbf6e8920..45945afcab 100644
--- a/crypto/evp/build.info
+++ b/crypto/evp/build.info
@@ -13,7 +13,7 @@ SOURCE[../../libcrypto]=$COMMON\
e_xcbc_d.c e_rc2.c e_cast.c e_rc5.c m_null.c \
p_seal.c p_sign.c p_verify.c p_legacy.c \
bio_md.c bio_b64.c bio_enc.c evp_err.c e_null.c \
- c_allc.c c_alld.c bio_ok.c \
+ c_allc.c c_alld.c \
evp_pkey.c evp_pbe.c p5_crpt.c p5_crpt2.c pbe_scrypt.c \
e_aes_cbc_hmac_sha1.c e_aes_cbc_hmac_sha256.c e_rc4_hmac_md5.c \
e_chacha20_poly1305.c \
diff --git a/doc/man7/ossl-removed-api.pod b/doc/man7/ossl-removed-api.pod
index d086b68011..3189bdfeec 100644
--- a/doc/man7/ossl-removed-api.pod
+++ b/doc/man7/ossl-removed-api.pod
@@ -93,6 +93,7 @@ EVP_PKEY_meth_get_check,
EVP_PKEY_meth_get_public_check,
EVP_PKEY_meth_get_param_check,
EVP_PKEY_meth_get_digest_custom,
+BIO_f_reliable,
ossl-removed-api - API that has been removed from OpenSSL
=head1 SYNOPSIS
@@ -304,6 +305,8 @@ This includes consulting the L<ossl-guide-migration(7)> documentation.
=item EVP_PKEY_meth_get_digest_custom (Deprecated in 3.0.0) - consult L<ossl-guide-migration(7)>
+=item BIO_f_reliable (Broken since 3.0.0) - removed without replacement
+
=back
=head1 SEE ALSO
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index b00c2044c0..d2a5736ad2 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -848,7 +848,6 @@ int EVP_CIPHER_CTX_get_algor(EVP_CIPHER_CTX *ctx, X509_ALGOR **alg);
const BIO_METHOD *BIO_f_md(void);
const BIO_METHOD *BIO_f_base64(void);
const BIO_METHOD *BIO_f_cipher(void);
-const BIO_METHOD *BIO_f_reliable(void);
__owur int BIO_set_cipher(BIO *b, const EVP_CIPHER *c, const unsigned char *k,
const unsigned char *i, int enc);
diff --git a/util/libcrypto.num b/util/libcrypto.num
index c1b1367028..78d5fa5114 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -1001,7 +1001,6 @@ EVP_CIPHER_CTX_get_algor ? 4_0_0 EXIST::FUNCTION:
BIO_f_md ? 4_0_0 EXIST::FUNCTION:
BIO_f_base64 ? 4_0_0 EXIST::FUNCTION:
BIO_f_cipher ? 4_0_0 EXIST::FUNCTION:
-BIO_f_reliable ? 4_0_0 EXIST::FUNCTION:
BIO_set_cipher ? 4_0_0 EXIST::FUNCTION:
EVP_md_null ? 4_0_0 EXIST::FUNCTION:
EVP_md2 ? 4_0_0 EXIST::FUNCTION:MD2
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index 7c997abb56..49765d4f7e 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -186,7 +186,6 @@ BIO_dup_chain(3)
BIO_f_asn1(3)
BIO_f_linebuffer(3)
BIO_f_nbio_test(3)
-BIO_f_reliable(3)
BIO_fd_non_fatal_error(3)
BIO_fd_should_retry(3)
BIO_get_accept_socket(3)