[ruby/openssl] Revert "pkey: stop retrying after non-retryable error from OSSL_DECODER"

This reverts commit https://github.com/ruby/openssl/commit/5347880c6eb0 and
https://github.com/ruby/openssl/commit/985ba27d6339.

These commits attempted to stop processing after the first relevant PEM
block, whether it is successful or not, when the input contains multiple
keys.

It turned out that it cannot be reliably determined using the
OSSL_DECODER API. There is an edge case where OSSL_DECODER_from_bio()
reports "unsupported" even though the input actually contains an error:

https://redirect.github.com/ruby/openssl/pull/931#discussion_r2347813807

Revert the changes for now and keep the existing behavior, as partial
support does not seem worth the added complexity.

https://github.com/ruby/openssl/commit/319cd4952a
This commit is contained in:
Kazuki Yamaguchi 2025-09-16 19:29:08 +09:00 committed by git
parent 9c85a94f5f
commit 08e7b5f2bd
2 changed files with 14 additions and 38 deletions

View File

@ -83,15 +83,13 @@ ossl_pkey_wrap(EVP_PKEY *pkey)
# include <openssl/decoder.h>
static EVP_PKEY *
ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass,
int *retryable)
ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass)
{
void *ppass = (void *)pass;
OSSL_DECODER_CTX *dctx;
EVP_PKEY *pkey = NULL;
int pos = 0, pos2;
*retryable = 0;
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, input_type, NULL, NULL,
selection, NULL, NULL);
if (!dctx)
@ -102,25 +100,17 @@ ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass,
goto out;
while (1) {
if (OSSL_DECODER_from_bio(dctx, bio) == 1)
goto out;
if (BIO_eof(bio))
break;
// Error queue may not be populated in OpenSSL < 3.0.11 and < 3.1.3
// https://github.com/openssl/openssl/pull/21603
unsigned long err = ERR_peek_error();
if (err && ERR_GET_REASON(err) != ERR_R_UNSUPPORTED)
break;
if (BIO_eof(bio) == 1) {
*retryable = 1;
break;
}
pos2 = BIO_tell(bio);
if (pos2 < 0 || pos2 <= pos) {
*retryable = 1;
if (pos2 < 0 || pos2 <= pos)
break;
}
ossl_clear_error();
pos = pos2;
}
out:
OSSL_BIO_reset(bio);
OSSL_DECODER_CTX_free(dctx);
return pkey;
}
@ -128,6 +118,7 @@ ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass,
EVP_PKEY *
ossl_pkey_read_generic(BIO *bio, VALUE pass)
{
EVP_PKEY *pkey = NULL;
/* First check DER, then check PEM. */
const char *input_types[] = {"DER", "PEM"};
int input_type_num = (int)(sizeof(input_types) / sizeof(char *));
@ -176,22 +167,18 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
EVP_PKEY_PUBLIC_KEY
};
int selection_num = (int)(sizeof(selections) / sizeof(int));
int i, j;
for (int i = 0; i < input_type_num; i++) {
for (int j = 0; j < selection_num; j++) {
if (i || j) {
ossl_clear_error();
BIO_reset(bio);
for (i = 0; i < input_type_num; i++) {
for (j = 0; j < selection_num; j++) {
pkey = ossl_pkey_read(bio, input_types[i], selections[j], pass);
if (pkey) {
goto out;
}
int retryable;
EVP_PKEY *pkey = ossl_pkey_read(bio, input_types[i], selections[j],
pass, &retryable);
if (pkey || !retryable)
return pkey;
}
}
return NULL;
out:
return pkey;
}
#else
EVP_PKEY *

View File

@ -124,17 +124,6 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
}
}
assert_equal(1, called)
# Incorrect passphrase returned by the block. The input contains two PEM
# blocks.
called = 0
assert_raise(OpenSSL::PKey::PKeyError) {
OpenSSL::PKey.read(encrypted_pem + encrypted_pem) {
called += 1
"incorrect_passphrase"
}
}
assert_equal(1, called)
end
def test_s_read_passphrase_tty