@@ -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