@@ -446,6 +446,183 @@ pkey_generate(int argc, VALUE *argv, VALUE self, int genparam)
446
446
return ossl_pkey_new (gen_arg .pkey );
447
447
}
448
448
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
+
449
626
/*
450
627
* call-seq:
451
628
* OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey
@@ -498,6 +675,33 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self)
498
675
return pkey_generate (argc , argv , self , 0 );
499
676
}
500
677
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
+
501
705
/*
502
706
* TODO: There is no convenient way to check the presence of public key
503
707
* components on OpenSSL 3.0. But since keys are immutable on 3.0, pkeys without
@@ -1751,6 +1955,8 @@ Init_ossl_pkey(void)
1751
1955
rb_define_module_function (mPKey , "read" , ossl_pkey_new_from_data , -1 );
1752
1956
rb_define_module_function (mPKey , "generate_parameters" , ossl_pkey_s_generate_parameters , -1 );
1753
1957
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
+
1754
1960
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
1755
1961
rb_define_module_function (mPKey , "new_raw_private_key" , ossl_pkey_new_raw_private_key , 2 );
1756
1962
rb_define_module_function (mPKey , "new_raw_public_key" , ossl_pkey_new_raw_public_key , 2 );
0 commit comments