@@ -446,6 +446,186 @@ 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_data_alias {
454+ char alias [10 ];
455+ char param_name [20 ];
456+ };
457+
458+ static const struct pkey_from_data_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_data_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_data_arg {
474+ VALUE options ;
475+ OSSL_PARAM_BLD * param_bld ;
476+ const OSSL_PARAM * settable_params ;
477+ const struct pkey_from_data_alias * aliases ;
478+ };
479+
480+ static int
481+ add_data_to_builder (VALUE key , VALUE value , VALUE arg )
482+ {
483+ if (NIL_P (value ))
484+ return ST_CONTINUE ;
485+
486+ if (SYMBOL_P (key ))
487+ key = rb_sym2str (key );
488+
489+ const char * key_ptr = StringValueCStr (key );
490+ const struct pkey_from_data_arg * params = (const struct pkey_from_data_arg * ) arg ;
491+
492+ for (int i = 0 ; strlen (params -> aliases [i ].alias ) > 0 ; i ++ ) {
493+ if (strcmp (params -> aliases [i ].alias , key_ptr ) == 0 ) {
494+ key_ptr = params -> aliases [i ].param_name ;
495+ break ;
496+ }
497+ }
498+
499+ for (const OSSL_PARAM * settable_params = params -> settable_params ; settable_params -> key != NULL ; settable_params ++ ) {
500+ if (strcmp (settable_params -> key , key_ptr ) == 0 ) {
501+ switch (settable_params -> data_type ) {
502+ case OSSL_PARAM_INTEGER :
503+ case OSSL_PARAM_UNSIGNED_INTEGER :
504+ if (!OSSL_PARAM_BLD_push_BN (params -> param_bld , key_ptr , GetBNPtr (value ))) {
505+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_push_BN" );
506+ }
507+ break ;
508+ case OSSL_PARAM_UTF8_STRING :
509+ StringValue (value );
510+ if (!OSSL_PARAM_BLD_push_utf8_string (params -> param_bld , key_ptr , RSTRING_PTR (value ), RSTRING_LENINT (value ))) {
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_raise (ePKeyError , "OSSL_PARAM_BLD_push_octet_string" );
519+ }
520+ break ;
521+ case OSSL_PARAM_UTF8_PTR :
522+ case OSSL_PARAM_OCTET_PTR :
523+ ossl_raise (ePKeyError , "Unsupported parameter \"%s\", handling of OSSL_PARAM_UTF8_PTR and OSSL_PARAM_OCTET_PTR not implemented" , key_ptr );
524+ break ;
525+ default :
526+ ossl_raise (ePKeyError , "Unsupported parameter \"%s\"" , key_ptr );
527+ break ;
528+ }
529+
530+ return ST_CONTINUE ;
531+ }
532+ }
533+
534+ VALUE supported_parameters = rb_ary_new ();
535+
536+ for (const OSSL_PARAM * settable_params = params -> settable_params ; settable_params -> key != NULL ; settable_params ++ ) {
537+ rb_ary_push (supported_parameters , rb_str_new_cstr (settable_params -> key ));
538+ }
539+
540+ for (int i = 0 ; strlen (params -> aliases [i ].alias ) > 0 ; i ++ ) {
541+ rb_ary_push (supported_parameters , rb_str_new_cstr (params -> aliases [i ].alias ));
542+ }
543+
544+ ossl_raise (ePKeyError , "Invalid parameter \"%s\". Supported parameters: %" PRIsVALUE , key_ptr , rb_ary_join (supported_parameters , rb_str_new2 (", " )));
545+ }
546+
547+ static VALUE
548+ iterate_from_data_options_cb (VALUE value )
549+ {
550+ struct pkey_from_data_arg * args = (void * )value ;
551+
552+ rb_hash_foreach (args -> options , & add_data_to_builder , (VALUE ) args );
553+
554+ return Qnil ;
555+ }
556+
557+ static VALUE
558+ pkey_from_data (int argc , VALUE * argv , VALUE self )
559+ {
560+ VALUE alg , options ;
561+ rb_scan_args (argc , argv , "11" , & alg , & options );
562+
563+ const char * algorithm = StringValueCStr (alg );
564+
565+ EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_from_name (NULL , algorithm , NULL );
566+
567+ if (ctx == NULL )
568+ ossl_raise (ePKeyError , "EVP_PKEY_CTX_new_from_name" );
569+
570+ struct pkey_from_data_arg from_data_args = { 0 };
571+
572+ from_data_args .param_bld = OSSL_PARAM_BLD_new ();
573+ from_data_args .options = options ;
574+
575+ if (from_data_args .param_bld == NULL ) {
576+ EVP_PKEY_CTX_free (ctx );
577+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_new" );
578+ }
579+
580+ from_data_args .settable_params = EVP_PKEY_fromdata_settable (ctx , EVP_PKEY_KEYPAIR );
581+
582+ if (from_data_args .settable_params == NULL ) {
583+ EVP_PKEY_CTX_free (ctx );
584+ OSSL_PARAM_BLD_free (from_data_args .param_bld );
585+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata_settable" );
586+ }
587+
588+ if (strcmp ("RSA" , algorithm ) == 0 )
589+ from_data_args .aliases = rsa_aliases ;
590+ else
591+ from_data_args .aliases = fcc_aliases ;
592+
593+ int state ;
594+ rb_protect (iterate_from_data_options_cb , (VALUE ) & from_data_args , & state );
595+
596+ if (state ) {
597+ EVP_PKEY_CTX_free (ctx );
598+ OSSL_PARAM_BLD_free (from_data_args .param_bld );
599+ rb_jump_tag (state );
600+ }
601+
602+ OSSL_PARAM * params = OSSL_PARAM_BLD_to_param (from_data_args .param_bld );
603+ OSSL_PARAM_BLD_free (from_data_args .param_bld );
604+
605+ if (params == NULL ) {
606+ EVP_PKEY_CTX_free (ctx );
607+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_to_param" );
608+ }
609+
610+ EVP_PKEY * pkey = NULL ;
611+
612+ if (EVP_PKEY_fromdata_init (ctx ) <= 0 ) {
613+ EVP_PKEY_CTX_free (ctx );
614+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata_init" );
615+ }
616+
617+ if (EVP_PKEY_fromdata (ctx , & pkey , EVP_PKEY_KEYPAIR , params ) <= 0 ) {
618+ EVP_PKEY_CTX_free (ctx );
619+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata" );
620+ }
621+
622+ EVP_PKEY_CTX_free (ctx );
623+
624+ return ossl_pkey_new (pkey );
625+ }
626+
627+ #endif
628+
449629/*
450630 * call-seq:
451631 * OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey
@@ -498,6 +678,30 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self)
498678 return pkey_generate (argc , argv , self , 0 );
499679}
500680
681+ /*
682+ * call-seq:
683+ * OpenSSL::PKey.from_data(algo_name, parameters) -> pkey
684+ *
685+ * Generates a new key based on given key parameters.
686+ * NOTE: Requires OpenSSL 3.0 or later.
687+ *
688+ * The first parameter is the type of the key to create, given as a String, for example RSA, DSA, EC etc.
689+ * Second parameter is the parameters to be used for the key.
690+ *
691+ * For details algorithms and parameters see https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html
692+ *
693+ * == Example
694+ * pkey = OpenSSL::PKey.from_data("RSA", n: 3161751493, e: 65537, d: 2064855961)
695+ * pkey.private? #=> true
696+ * pkey.n #=> #<OpenSSL::BN 3161751493>
697+ */
698+ #if OSSL_OPENSSL_PREREQ (3 , 0 , 0 )
699+ static VALUE
700+ ossl_pkey_s_from_data (int argc , VALUE * argv , VALUE self )
701+ {
702+ return pkey_from_data (argc , argv , self );
703+ }
704+ #endif
501705/*
502706 * TODO: There is no convenient way to check the presence of public key
503707 * components on OpenSSL 3.0. But since keys are immutable on 3.0, pkeys without
@@ -1751,6 +1955,9 @@ Init_ossl_pkey(void)
17511955 rb_define_module_function (mPKey , "read" , ossl_pkey_new_from_data , -1 );
17521956 rb_define_module_function (mPKey , "generate_parameters" , ossl_pkey_s_generate_parameters , -1 );
17531957 rb_define_module_function (mPKey , "generate_key" , ossl_pkey_s_generate_key , -1 );
1958+ #if OSSL_OPENSSL_PREREQ (3 , 0 , 0 )
1959+ rb_define_module_function (mPKey , "from_data" , ossl_pkey_s_from_data , -1 );
1960+ #endif
17541961#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
17551962 rb_define_module_function (mPKey , "new_raw_private_key" , ossl_pkey_new_raw_private_key , 2 );
17561963 rb_define_module_function (mPKey , "new_raw_public_key" , ossl_pkey_new_raw_public_key , 2 );
0 commit comments