@@ -191,6 +191,226 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
191191 return ossl_pkey_new (pkey );
192192}
193193
194+ static VALUE
195+ pkey_gen_apply_options_i (RB_BLOCK_CALL_FUNC_ARGLIST (i , ctx_v ))
196+ {
197+ VALUE key = rb_ary_entry (i , 0 ), value = rb_ary_entry (i , 1 );
198+ EVP_PKEY_CTX * ctx = (EVP_PKEY_CTX * )ctx_v ;
199+
200+ if (SYMBOL_P (key ))
201+ key = rb_sym2str (key );
202+ value = rb_String (value );
203+
204+ if (EVP_PKEY_CTX_ctrl_str (ctx , StringValueCStr (key ), StringValueCStr (value )) <= 0 )
205+ ossl_raise (ePKeyError , "EVP_PKEY_CTX_ctrl_str(ctx, %+" PRIsVALUE ", %+" PRIsVALUE ")" ,
206+ key , value );
207+ return Qnil ;
208+ }
209+
210+ static VALUE
211+ pkey_gen_apply_options0 (VALUE args_v )
212+ {
213+ VALUE * args = (VALUE * )args_v ;
214+
215+ rb_block_call (args [1 ], rb_intern ("each" ), 0 , NULL ,
216+ pkey_gen_apply_options_i , args [0 ]);
217+ return Qnil ;
218+ }
219+
220+ struct pkey_blocking_generate_arg {
221+ EVP_PKEY_CTX * ctx ;
222+ EVP_PKEY * pkey ;
223+ int state ;
224+ int yield : 1 ;
225+ int genparam : 1 ;
226+ int stop : 1 ;
227+ };
228+
229+ static VALUE
230+ pkey_gen_cb_yield (VALUE ctx_v )
231+ {
232+ EVP_PKEY_CTX * ctx = (void * )ctx_v ;
233+ int i , info_num ;
234+ VALUE * argv ;
235+
236+ info_num = EVP_PKEY_CTX_get_keygen_info (ctx , -1 );
237+ argv = ALLOCA_N (VALUE , info_num );
238+ for (i = 0 ; i < info_num ; i ++ )
239+ argv [i ] = INT2NUM (EVP_PKEY_CTX_get_keygen_info (ctx , i ));
240+
241+ return rb_yield_values2 (info_num , argv );
242+ }
243+
244+ static int
245+ pkey_gen_cb (EVP_PKEY_CTX * ctx )
246+ {
247+ struct pkey_blocking_generate_arg * arg = EVP_PKEY_CTX_get_app_data (ctx );
248+
249+ if (arg -> yield ) {
250+ int state ;
251+ rb_protect (pkey_gen_cb_yield , (VALUE )ctx , & state );
252+ if (state ) {
253+ arg -> stop = 1 ;
254+ arg -> state = state ;
255+ }
256+ }
257+ return !arg -> stop ;
258+ }
259+
260+ static void
261+ pkey_blocking_gen_stop (void * ptr )
262+ {
263+ struct pkey_blocking_generate_arg * arg = ptr ;
264+ arg -> stop = 1 ;
265+ }
266+
267+ static void *
268+ pkey_blocking_gen (void * ptr )
269+ {
270+ struct pkey_blocking_generate_arg * arg = ptr ;
271+
272+ if (arg -> genparam && EVP_PKEY_paramgen (arg -> ctx , & arg -> pkey ) <= 0 )
273+ return NULL ;
274+ if (!arg -> genparam && EVP_PKEY_keygen (arg -> ctx , & arg -> pkey ) <= 0 )
275+ return NULL ;
276+ return arg -> pkey ;
277+ }
278+
279+ static VALUE
280+ pkey_generate (int argc , VALUE * argv , VALUE self , int genparam )
281+ {
282+ EVP_PKEY_CTX * ctx ;
283+ VALUE alg , options ;
284+ struct pkey_blocking_generate_arg gen_arg = { 0 };
285+ int state ;
286+
287+ rb_scan_args (argc , argv , "11" , & alg , & options );
288+ if (rb_obj_is_kind_of (alg , cPKey )) {
289+ EVP_PKEY * base_pkey ;
290+
291+ GetPKey (alg , base_pkey );
292+ ctx = EVP_PKEY_CTX_new (base_pkey , NULL /* engine */ );
293+ if (!ctx )
294+ ossl_raise (ePKeyError , "EVP_PKEY_CTX_new" );
295+ }
296+ else {
297+ const EVP_PKEY_ASN1_METHOD * ameth ;
298+ ENGINE * tmpeng ;
299+ int pkey_id ;
300+
301+ StringValue (alg );
302+ ameth = EVP_PKEY_asn1_find_str (& tmpeng , RSTRING_PTR (alg ),
303+ RSTRING_LENINT (alg ));
304+ if (!ameth )
305+ ossl_raise (ePKeyError , "algorithm %" PRIsVALUE " not found" , alg );
306+ EVP_PKEY_asn1_get0_info (& pkey_id , NULL , NULL , NULL , NULL , ameth );
307+ #if !defined(OPENSSL_NO_ENGINE )
308+ if (tmpeng )
309+ ENGINE_finish (tmpeng );
310+ #endif
311+
312+ ctx = EVP_PKEY_CTX_new_id (pkey_id , NULL /* engine */ );
313+ if (!ctx )
314+ ossl_raise (ePKeyError , "EVP_PKEY_CTX_new_id" );
315+ }
316+
317+ if (genparam && EVP_PKEY_paramgen_init (ctx ) <= 0 ) {
318+ EVP_PKEY_CTX_free (ctx );
319+ ossl_raise (ePKeyError , "EVP_PKEY_paramgen_init" );
320+ }
321+ if (!genparam && EVP_PKEY_keygen_init (ctx ) <= 0 ) {
322+ EVP_PKEY_CTX_free (ctx );
323+ ossl_raise (ePKeyError , "EVP_PKEY_keygen_init" );
324+ }
325+
326+ if (!NIL_P (options )) {
327+ VALUE args [2 ];
328+
329+ args [0 ] = (VALUE )ctx ;
330+ args [1 ] = options ;
331+ rb_protect (pkey_gen_apply_options0 , (VALUE )args , & state );
332+ if (state ) {
333+ EVP_PKEY_CTX_free (ctx );
334+ rb_jump_tag (state );
335+ }
336+ }
337+
338+ gen_arg .genparam = genparam ;
339+ gen_arg .ctx = ctx ;
340+ gen_arg .yield = rb_block_given_p ();
341+ EVP_PKEY_CTX_set_app_data (ctx , & gen_arg );
342+ EVP_PKEY_CTX_set_cb (ctx , pkey_gen_cb );
343+ if (gen_arg .yield )
344+ pkey_blocking_gen (& gen_arg );
345+ else
346+ rb_thread_call_without_gvl (pkey_blocking_gen , & gen_arg ,
347+ pkey_blocking_gen_stop , & gen_arg );
348+ EVP_PKEY_CTX_free (ctx );
349+ if (!gen_arg .pkey ) {
350+ if (gen_arg .state ) {
351+ ossl_clear_error ();
352+ rb_jump_tag (gen_arg .state );
353+ }
354+ else {
355+ ossl_raise (ePKeyError , genparam ? "EVP_PKEY_paramgen" : "EVP_PKEY_keygen" );
356+ }
357+ }
358+
359+ return ossl_pkey_new (gen_arg .pkey );
360+ }
361+
362+ /*
363+ * call-seq:
364+ * OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey
365+ *
366+ * Generates new parameters for the algorithm. _algo_name_ is a String that
367+ * represents the algorithm. The optional argument _options_ is a Hash that
368+ * specifies the options specific to the algorithm. The order of the options
369+ * can be important.
370+ *
371+ * A block can be passed optionally. The meaning of the arguments passed to
372+ * the block varies depending on the implementation of the algorithm. The block
373+ * may be called once or multiple times, or may not even be called.
374+ *
375+ * For the supported options, see the documentation for the 'openssl genpkey'
376+ * utility command.
377+ *
378+ * == Example
379+ * pkey = OpenSSL::PKey.generate_parameters("DSA", "dsa_paramgen_bits" => 2048)
380+ * p pkey.p.num_bits #=> 2048
381+ */
382+ static VALUE
383+ ossl_pkey_s_generate_parameters (int argc , VALUE * argv , VALUE self )
384+ {
385+ return pkey_generate (argc , argv , self , 1 );
386+ }
387+
388+ /*
389+ * call-seq:
390+ * OpenSSL::PKey.generate_key(algo_name [, options]) -> pkey
391+ * OpenSSL::PKey.generate_key(pkey [, options]) -> pkey
392+ *
393+ * Generates a new key (pair).
394+ *
395+ * If a String is given as the first argument, it generates a new random key
396+ * for the algorithm specified by the name just as ::generate_parameters does.
397+ * If an OpenSSL::PKey::PKey is given instead, it generates a new random key
398+ * for the same algorithm as the key, using the parameters the key contains.
399+ *
400+ * See ::generate_parameters for the details of _options_ and the given block.
401+ *
402+ * == Example
403+ * pkey_params = OpenSSL::PKey.generate_parameters("DSA", "dsa_paramgen_bits" => 2048)
404+ * pkey_params.priv_key #=> nil
405+ * pkey = OpenSSL::PKey.generate_key(pkey_params)
406+ * pkey.priv_key #=> #<OpenSSL::BN 6277...
407+ */
408+ static VALUE
409+ ossl_pkey_s_generate_key (int argc , VALUE * argv , VALUE self )
410+ {
411+ return pkey_generate (argc , argv , self , 0 );
412+ }
413+
194414void
195415ossl_pkey_check_public_key (const EVP_PKEY * pkey )
196416{
@@ -655,6 +875,8 @@ Init_ossl_pkey(void)
655875 cPKey = rb_define_class_under (mPKey , "PKey" , rb_cObject );
656876
657877 rb_define_module_function (mPKey , "read" , ossl_pkey_new_from_data , -1 );
878+ rb_define_module_function (mPKey , "generate_parameters" , ossl_pkey_s_generate_parameters , -1 );
879+ rb_define_module_function (mPKey , "generate_key" , ossl_pkey_s_generate_key , -1 );
658880
659881 rb_define_alloc_func (cPKey , ossl_pkey_alloc );
660882 rb_define_method (cPKey , "initialize" , ossl_pkey_initialize , 0 );
0 commit comments