@@ -446,6 +446,174 @@ pkey_generate(int argc, VALUE *argv, VALUE self, int genparam)
446446 return ossl_pkey_new (gen_arg .pkey );
447447}
448448
449+ #if OSSL_OPENSSL_PREREQ (3 , 0 , 0 )
450+ #include <openssl/param_build.h>
451+ #include <openssl/core_names.h>
452+
453+ struct pkey_from_parameters_alias {
454+ char alias [10 ];
455+ char param_name [20 ];
456+ };
457+
458+ static const struct pkey_from_parameters_alias rsa_aliases [] = {
459+ { "p" , OSSL_PKEY_PARAM_RSA_FACTOR1 },
460+ { "q" , OSSL_PKEY_PARAM_RSA_FACTOR2 },
461+ { "dmp1" , OSSL_PKEY_PARAM_RSA_EXPONENT1 },
462+ { "dmq1" , OSSL_PKEY_PARAM_RSA_EXPONENT2 },
463+ { "iqmp" , OSSL_PKEY_PARAM_RSA_COEFFICIENT1 },
464+ { "" , "" }
465+ };
466+
467+ static const struct pkey_from_parameters_alias fcc_aliases [] = {
468+ { "pub_key" , OSSL_PKEY_PARAM_PUB_KEY },
469+ { "priv_key" , OSSL_PKEY_PARAM_PRIV_KEY },
470+ { "" , "" }
471+ };
472+
473+ struct pkey_from_parameters_arg {
474+ OSSL_PARAM_BLD * param_bld ;
475+ const OSSL_PARAM * settable_params ;
476+ const struct pkey_from_parameters_alias * aliases ;
477+ };
478+
479+ static int
480+ add_parameter_to_builder (VALUE key , VALUE value , VALUE arg ) {
481+ if (NIL_P (value ))
482+ return ST_CONTINUE ;
483+
484+ if (SYMBOL_P (key ))
485+ key = rb_sym2str (key );
486+
487+ const char * key_ptr = StringValueCStr (key );
488+ const struct pkey_from_parameters_arg * params = (const struct pkey_from_parameters_arg * ) arg ;
489+
490+ for (int i = 0 ; strlen (params -> aliases [i ].alias ) > 0 ; i ++ ) {
491+ if (strcmp (params -> aliases [i ].alias , key_ptr ) == 0 ) {
492+ key_ptr = params -> aliases [i ].param_name ;
493+ break ;
494+ }
495+ }
496+
497+ for (const OSSL_PARAM * settable_params = params -> settable_params ; settable_params -> key != NULL ; settable_params ++ ) {
498+ if (strcmp (settable_params -> key , key_ptr ) == 0 ) {
499+ switch (settable_params -> data_type ) {
500+ case OSSL_PARAM_INTEGER :
501+ case OSSL_PARAM_UNSIGNED_INTEGER :
502+ if (!OSSL_PARAM_BLD_push_BN (params -> param_bld , key_ptr , GetBNPtr (value ))) {
503+ OSSL_PARAM_BLD_free (params -> param_bld );
504+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_push_BN" );
505+ }
506+ break ;
507+ case OSSL_PARAM_UTF8_STRING :
508+ StringValue (value );
509+ if (!OSSL_PARAM_BLD_push_utf8_string (params -> param_bld , key_ptr , RSTRING_PTR (value ), RSTRING_LENINT (value ))) {
510+ OSSL_PARAM_BLD_free (params -> param_bld );
511+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_push_utf8_string" );
512+ }
513+ break ;
514+
515+ case OSSL_PARAM_OCTET_STRING :
516+ StringValue (value );
517+ if (!OSSL_PARAM_BLD_push_octet_string (params -> param_bld , key_ptr , RSTRING_PTR (value ), RSTRING_LENINT (value ))) {
518+ OSSL_PARAM_BLD_free (params -> param_bld );
519+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_push_octet_string" );
520+ }
521+ break ;
522+ case OSSL_PARAM_UTF8_PTR :
523+ case OSSL_PARAM_OCTET_PTR :
524+ OSSL_PARAM_BLD_free (params -> param_bld );
525+ ossl_raise (ePKeyError , "Unsupported parameter \"%s\", handling of OSSL_PARAM_UTF8_PTR and OSSL_PARAM_OCTET_PTR not implemented" , key_ptr );
526+ break ;
527+ }
528+
529+ return ST_CONTINUE ;
530+ }
531+ }
532+ OSSL_PARAM_BLD_free (params -> param_bld );
533+
534+ char message_buffer [512 ] = { 0 };
535+ char * cur = message_buffer ;
536+ char * end = message_buffer + sizeof (message_buffer );
537+ for (const OSSL_PARAM * settable_params = params -> settable_params ; settable_params -> key != NULL ; settable_params ++ ) {
538+ const char * fmt = cur == message_buffer ? "%s" : ", %s" ;
539+ if (cur > end )
540+ break ;
541+ cur += snprintf (cur , end - cur , fmt , settable_params -> key );
542+ }
543+
544+ for (int i = 0 ; strlen (params -> aliases [i ].alias ) > 0 ; i ++ ) {
545+ const char * fmt = cur == message_buffer ? "%s" : ", %s" ;
546+ if (cur > end )
547+ break ;
548+ cur += snprintf (cur , end - cur , fmt , params -> aliases [i ].alias );
549+ }
550+
551+ ossl_raise (ePKeyError , "Invalid parameter \"%s\". Supported parameters: \"%s\"" , key_ptr , message_buffer );
552+ }
553+
554+ static VALUE
555+ pkey_from_parameters (int argc , VALUE * argv , VALUE self )
556+ {
557+ VALUE alg , options ;
558+ rb_scan_args (argc , argv , "11" , & alg , & options );
559+
560+ const char * algorithm = StringValueCStr (alg );
561+
562+ EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_from_name (NULL , algorithm , NULL );
563+
564+ if (ctx == NULL )
565+ ossl_raise (ePKeyError , "EVP_PKEY_CTX_new_from_name" );
566+
567+ struct pkey_from_parameters_arg from_params_args = { 0 };
568+
569+ from_params_args .param_bld = OSSL_PARAM_BLD_new ();
570+
571+ if (from_params_args .param_bld == NULL ) {
572+ EVP_PKEY_CTX_free (ctx );
573+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_new" );
574+ }
575+
576+ from_params_args .settable_params = EVP_PKEY_fromdata_settable (ctx , EVP_PKEY_KEYPAIR );
577+
578+ if (from_params_args .settable_params == NULL ) {
579+ EVP_PKEY_CTX_free (ctx );
580+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata_settable" );
581+ }
582+
583+ if (strcmp ("RSA" , algorithm ) == 0 )
584+ from_params_args .aliases = rsa_aliases ;
585+ else
586+ from_params_args .aliases = fcc_aliases ;
587+
588+ rb_hash_foreach (options , & add_parameter_to_builder , (VALUE ) & from_params_args );
589+
590+ OSSL_PARAM * params = OSSL_PARAM_BLD_to_param (from_params_args .param_bld );
591+ OSSL_PARAM_BLD_free (from_params_args .param_bld );
592+
593+ if (params == NULL ) {
594+ EVP_PKEY_CTX_free (ctx );
595+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_to_param" );
596+ }
597+
598+ EVP_PKEY * pkey = NULL ;
599+
600+ if (EVP_PKEY_fromdata_init (ctx ) <= 0 ) {
601+ EVP_PKEY_CTX_free (ctx );
602+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata_init" );
603+ }
604+
605+ if (EVP_PKEY_fromdata (ctx , & pkey , EVP_PKEY_KEYPAIR , params ) <= 0 ) {
606+ EVP_PKEY_CTX_free (ctx );
607+ EVP_PKEY_free (pkey );
608+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata" );
609+ }
610+
611+ EVP_PKEY_CTX_free (ctx );
612+
613+ return ossl_pkey_new (pkey );
614+ }
615+ #endif
616+
449617/*
450618 * call-seq:
451619 * OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey
@@ -498,6 +666,33 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self)
498666 return pkey_generate (argc , argv , self , 0 );
499667}
500668
669+ /*
670+ * call-seq:
671+ * OpenSSL::PKey.from_parameters(algo_name, parameters) -> pkey
672+ *
673+ * Generates a new key based on given key parameters.
674+ * NOTE: Will only work on OpenSSL 3.0 or later.
675+ *
676+ * The first parameter is the type of the key to create, given as a String, for example RSA, DSA, EC etc.
677+ * Second parameter is the parameters to be used for the key.
678+ *
679+ * For details algorithms and parameters see https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html
680+ *
681+ * == Example
682+ * pkey = OpenSSL::PKey.from_parameters("RSA", n: 3161751493, e: 65537, d: 2064855961)
683+ * pkey.private? #=> true
684+ * pkey.public_key #=> #<OpenSSL::PKey::RSA...
685+ */
686+ static VALUE
687+ ossl_pkey_s_from_parameters (int argc , VALUE * argv , VALUE self )
688+ {
689+ #if OSSL_OPENSSL_PREREQ (3 , 0 , 0 )
690+ return pkey_from_parameters (argc , argv , self );
691+ #else
692+ rb_raise (ePKeyError , "Only supported with OpenSSL 3.0" );
693+ #endif
694+ }
695+
501696/*
502697 * TODO: There is no convenient way to check the presence of public key
503698 * components on OpenSSL 3.0. But since keys are immutable on 3.0, pkeys without
@@ -1751,6 +1946,8 @@ Init_ossl_pkey(void)
17511946 rb_define_module_function (mPKey , "read" , ossl_pkey_new_from_data , -1 );
17521947 rb_define_module_function (mPKey , "generate_parameters" , ossl_pkey_s_generate_parameters , -1 );
17531948 rb_define_module_function (mPKey , "generate_key" , ossl_pkey_s_generate_key , -1 );
1949+ rb_define_module_function (mPKey , "from_parameters" , ossl_pkey_s_from_parameters , -1 );
1950+
17541951#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
17551952 rb_define_module_function (mPKey , "new_raw_private_key" , ossl_pkey_new_raw_private_key , 2 );
17561953 rb_define_module_function (mPKey , "new_raw_public_key" , ossl_pkey_new_raw_public_key , 2 );
0 commit comments