@@ -446,6 +446,183 @@ 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+ if (NIL_P (value ))
483+ return ST_CONTINUE ;
484+
485+ if (SYMBOL_P (key ))
486+ key = rb_sym2str (key );
487+
488+ const char * key_ptr = StringValueCStr (key );
489+ const struct pkey_from_data_arg * params = (const struct pkey_from_data_arg * ) arg ;
490+
491+ for (int i = 0 ; strlen (params -> aliases [i ].alias ) > 0 ; i ++ ) {
492+ if (strcmp (params -> aliases [i ].alias , key_ptr ) == 0 ) {
493+ key_ptr = params -> aliases [i ].param_name ;
494+ break ;
495+ }
496+ }
497+
498+ for (const OSSL_PARAM * settable_params = params -> settable_params ; settable_params -> key != NULL ; settable_params ++ ) {
499+ if (strcmp (settable_params -> key , key_ptr ) == 0 ) {
500+ switch (settable_params -> data_type ) {
501+ case OSSL_PARAM_INTEGER :
502+ case OSSL_PARAM_UNSIGNED_INTEGER :
503+ if (!OSSL_PARAM_BLD_push_BN (params -> param_bld , key_ptr , GetBNPtr (value ))) {
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_raise (ePKeyError , "OSSL_PARAM_BLD_push_utf8_string" );
511+ }
512+ break ;
513+
514+ case OSSL_PARAM_OCTET_STRING :
515+ StringValue (value );
516+ if (!OSSL_PARAM_BLD_push_octet_string (params -> param_bld , key_ptr , RSTRING_PTR (value ), RSTRING_LENINT (value ))) {
517+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_push_octet_string" );
518+ }
519+ break ;
520+ case OSSL_PARAM_UTF8_PTR :
521+ case OSSL_PARAM_OCTET_PTR :
522+ ossl_raise (ePKeyError , "Unsupported parameter \"%s\", handling of OSSL_PARAM_UTF8_PTR and OSSL_PARAM_OCTET_PTR not implemented" , key_ptr );
523+ break ;
524+ }
525+
526+ return ST_CONTINUE ;
527+ }
528+ }
529+
530+ VALUE supported_parameters = rb_ary_new ();
531+
532+ for (const OSSL_PARAM * settable_params = params -> settable_params ; settable_params -> key != NULL ; settable_params ++ ) {
533+ rb_ary_push (supported_parameters , rb_str_new_cstr (settable_params -> key ));
534+ }
535+
536+ for (int i = 0 ; strlen (params -> aliases [i ].alias ) > 0 ; i ++ ) {
537+ rb_ary_push (supported_parameters , rb_str_new_cstr (params -> aliases [i ].alias ));
538+ }
539+
540+ ossl_raise (ePKeyError , "Invalid parameter \"%s\". Supported parameters: %" PRIsVALUE , key_ptr , rb_ary_join (supported_parameters , rb_str_new2 (", " )));
541+ }
542+
543+ static VALUE
544+ iterate_from_data_options_cb (VALUE value )
545+ {
546+ struct pkey_from_data_arg * args = (void * )value ;
547+
548+ rb_hash_foreach (args -> options , & add_data_to_builder , (VALUE ) args );
549+
550+ return Qnil ;
551+ }
552+
553+ static VALUE
554+ pkey_from_data (int argc , VALUE * argv , VALUE self )
555+ {
556+ VALUE alg , options ;
557+ rb_scan_args (argc , argv , "11" , & alg , & options );
558+
559+ const char * algorithm = StringValueCStr (alg );
560+
561+ EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_from_name (NULL , algorithm , NULL );
562+
563+ if (ctx == NULL )
564+ ossl_raise (ePKeyError , "EVP_PKEY_CTX_new_from_name" );
565+
566+ struct pkey_from_data_arg from_data_args = { 0 };
567+
568+ from_data_args .param_bld = OSSL_PARAM_BLD_new ();
569+ from_data_args .options = options ;
570+
571+ if (from_data_args .param_bld == NULL ) {
572+ EVP_PKEY_CTX_free (ctx );
573+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_new" );
574+ }
575+
576+ from_data_args .settable_params = EVP_PKEY_fromdata_settable (ctx , EVP_PKEY_KEYPAIR );
577+
578+ if (from_data_args .settable_params == NULL ) {
579+ EVP_PKEY_CTX_free (ctx );
580+ OSSL_PARAM_BLD_free (from_data_args .param_bld );
581+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata_settable" );
582+ }
583+
584+ if (strcmp ("RSA" , algorithm ) == 0 )
585+ from_data_args .aliases = rsa_aliases ;
586+ else
587+ from_data_args .aliases = fcc_aliases ;
588+
589+ int state ;
590+ rb_protect (iterate_from_data_options_cb , (VALUE ) & from_data_args , & state );
591+
592+ if (state ) {
593+ EVP_PKEY_CTX_free (ctx );
594+ OSSL_PARAM_BLD_free (from_data_args .param_bld );
595+ rb_jump_tag (state );
596+ }
597+
598+ OSSL_PARAM * params = OSSL_PARAM_BLD_to_param (from_data_args .param_bld );
599+ OSSL_PARAM_BLD_free (from_data_args .param_bld );
600+
601+ if (params == NULL ) {
602+ EVP_PKEY_CTX_free (ctx );
603+ ossl_raise (ePKeyError , "OSSL_PARAM_BLD_to_param" );
604+ }
605+
606+ EVP_PKEY * pkey = NULL ;
607+
608+ if (EVP_PKEY_fromdata_init (ctx ) <= 0 ) {
609+ EVP_PKEY_CTX_free (ctx );
610+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata_init" );
611+ }
612+
613+ if (EVP_PKEY_fromdata (ctx , & pkey , EVP_PKEY_KEYPAIR , params ) <= 0 ) {
614+ EVP_PKEY_CTX_free (ctx );
615+ EVP_PKEY_free (pkey );
616+ ossl_raise (ePKeyError , "EVP_PKEY_fromdata" );
617+ }
618+
619+ EVP_PKEY_CTX_free (ctx );
620+
621+ return ossl_pkey_new (pkey );
622+ }
623+
624+ #endif
625+
449626/*
450627 * call-seq:
451628 * OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey
@@ -498,6 +675,33 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self)
498675 return pkey_generate (argc , argv , self , 0 );
499676}
500677
678+ /*
679+ * call-seq:
680+ * OpenSSL::PKey.from_data(algo_name, parameters) -> pkey
681+ *
682+ * Generates a new key based on given key parameters.
683+ * NOTE: Requires OpenSSL 3.0 or later.
684+ *
685+ * The first parameter is the type of the key to create, given as a String, for example RSA, DSA, EC etc.
686+ * Second parameter is the parameters to be used for the key.
687+ *
688+ * For details algorithms and parameters see https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html
689+ *
690+ * == Example
691+ * pkey = OpenSSL::PKey.from_data("RSA", n: 3161751493, e: 65537, d: 2064855961)
692+ * pkey.private? #=> true
693+ * pkey.public_key #=> #<OpenSSL::PKey::RSA...
694+ */
695+ static VALUE
696+ ossl_pkey_s_from_data (int argc , VALUE * argv , VALUE self )
697+ {
698+ #if OSSL_OPENSSL_PREREQ (3 , 0 , 0 )
699+ return pkey_from_data (argc , argv , self );
700+ #else
701+ rb_raise (ePKeyError , "OpenSSL::PKey.from_data requires OpenSSL 3.0 or later" );
702+ #endif
703+ }
704+
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,8 @@ 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+ rb_define_module_function (mPKey , "from_data" , ossl_pkey_s_from_data , -1 );
1959+
17541960#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
17551961 rb_define_module_function (mPKey , "new_raw_private_key" , ossl_pkey_new_raw_private_key , 2 );
17561962 rb_define_module_function (mPKey , "new_raw_public_key" , ossl_pkey_new_raw_public_key , 2 );
0 commit comments