Skip to content

Commit db688fa

Browse files
committed
ossl_pkey.c: Workaround: Decode with non-zero selections.
This is a workaround for the decoding issue in ossl_pkey_read_generic(). The issue happens in the case that a key management provider is different from a decoding provider. Try all the non-zero selections in order, instead of selection 0 for OpenSSL 3 to avoid the issue.
1 parent d157ba1 commit db688fa

File tree

1 file changed

+59
-52
lines changed

1 file changed

+59
-52
lines changed

ext/openssl/ossl_pkey.c

Lines changed: 59 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -82,30 +82,62 @@ ossl_pkey_new(EVP_PKEY *pkey)
8282
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
8383
# include <openssl/decoder.h>
8484

85-
EVP_PKEY *
86-
ossl_pkey_read_generic(BIO *bio, VALUE pass)
85+
static EVP_PKEY *
86+
ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass)
8787
{
8888
void *ppass = (void *)pass;
8989
OSSL_DECODER_CTX *dctx;
9090
EVP_PKEY *pkey = NULL;
9191
int pos = 0, pos2;
9292

93-
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL, NULL, 0, NULL, NULL);
93+
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, input_type, NULL, NULL,
94+
selection, NULL, NULL);
9495
if (!dctx)
9596
goto out;
96-
if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1)
97-
goto out;
98-
99-
/* First check DER */
100-
if (OSSL_DECODER_from_bio(dctx, bio) == 1)
97+
if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb,
98+
ppass) != 1)
10199
goto out;
100+
while (1) {
101+
if (OSSL_DECODER_from_bio(dctx, bio) == 1)
102+
goto out;
103+
if (BIO_eof(bio))
104+
break;
105+
pos2 = BIO_tell(bio);
106+
if (pos2 < 0 || pos2 <= pos)
107+
break;
108+
ossl_clear_error();
109+
pos = pos2;
110+
}
111+
out:
102112
OSSL_BIO_reset(bio);
113+
OSSL_DECODER_CTX_free(dctx);
114+
return pkey;
115+
}
103116

117+
EVP_PKEY *
118+
ossl_pkey_read_generic(BIO *bio, VALUE pass)
119+
{
120+
EVP_PKEY *pkey = NULL;
121+
/* First check DER, then check PEM. */
122+
const char *input_types[] = {"DER", "PEM"};
123+
int input_type_num = (int)(sizeof(input_types) / sizeof(char *));
104124
/*
105-
* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed.
125+
* Non-zero selections to try to decode.
126+
*
127+
* See EVP_PKEY_fromdata(3) - Selections to see all the selections.
106128
*
107-
* First check for private key formats. This is to keep compatibility with
108-
* ruby/openssl < 3.0 which decoded the following as a private key.
129+
* This is a workaround for the decoder failing to decode or returning
130+
* bogus keys with selection 0, if a key management provider is different
131+
* from a decoder provider. The workaround is to avoid using selection 0.
132+
*
133+
* Affected OpenSSL versions: >= 3.1.0, <= 3.1.2, or >= 3.0.0, <= 3.0.10
134+
* Fixed OpenSSL versions: 3.2, next release of the 3.1.z and 3.0.z
135+
*
136+
* See https://github.com/openssl/openssl/pull/21519 for details.
137+
*
138+
* First check for private key formats (EVP_PKEY_KEYPAIR). This is to keep
139+
* compatibility with ruby/openssl < 3.0 which decoded the following as a
140+
* private key.
109141
*
110142
* $ openssl ecparam -name prime256v1 -genkey -outform PEM
111143
* -----BEGIN EC PARAMETERS-----
@@ -126,50 +158,25 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
126158
*
127159
* Note that we need to create the OSSL_DECODER_CTX variable each time when
128160
* we use the different selection as a workaround.
129-
* https://github.com/openssl/openssl/issues/20657
161+
* See https://github.com/openssl/openssl/issues/20657 for details.
130162
*/
131-
OSSL_DECODER_CTX_free(dctx);
132-
dctx = NULL;
133-
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, NULL,
134-
EVP_PKEY_KEYPAIR, NULL, NULL);
135-
if (!dctx)
136-
goto out;
137-
if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1)
138-
goto out;
139-
while (1) {
140-
if (OSSL_DECODER_from_bio(dctx, bio) == 1)
141-
goto out;
142-
if (BIO_eof(bio))
143-
break;
144-
pos2 = BIO_tell(bio);
145-
if (pos2 < 0 || pos2 <= pos)
146-
break;
147-
ossl_clear_error();
148-
pos = pos2;
149-
}
150-
151-
OSSL_BIO_reset(bio);
152-
OSSL_DECODER_CTX_free(dctx);
153-
dctx = NULL;
154-
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, NULL, 0, NULL, NULL);
155-
if (!dctx)
156-
goto out;
157-
if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1)
158-
goto out;
159-
while (1) {
160-
if (OSSL_DECODER_from_bio(dctx, bio) == 1)
161-
goto out;
162-
if (BIO_eof(bio))
163-
break;
164-
pos2 = BIO_tell(bio);
165-
if (pos2 < 0 || pos2 <= pos)
166-
break;
167-
ossl_clear_error();
168-
pos = pos2;
163+
int selections[] = {
164+
EVP_PKEY_KEYPAIR,
165+
EVP_PKEY_KEY_PARAMETERS,
166+
EVP_PKEY_PUBLIC_KEY
167+
};
168+
int selection_num = (int)(sizeof(selections) / sizeof(int));
169+
int i, j;
170+
171+
for (i = 0; i < input_type_num; i++) {
172+
for (j = 0; j < selection_num; j++) {
173+
pkey = ossl_pkey_read(bio, input_types[i], selections[j], pass);
174+
if (pkey) {
175+
goto out;
176+
}
177+
}
169178
}
170-
171179
out:
172-
OSSL_DECODER_CTX_free(dctx);
173180
return pkey;
174181
}
175182
#else

0 commit comments

Comments
 (0)