@@ -446,6 +446,174 @@ 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_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
+
449
617
/*
450
618
* call-seq:
451
619
* OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey
@@ -498,6 +666,33 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self)
498
666
return pkey_generate (argc , argv , self , 0 );
499
667
}
500
668
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: Requires 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 , "OpenSSL::PKey.from_parameters requires OpenSSL 3.0 or later" );
693
+ #endif
694
+ }
695
+
501
696
/*
502
697
* TODO: There is no convenient way to check the presence of public key
503
698
* components on OpenSSL 3.0. But since keys are immutable on 3.0, pkeys without
@@ -1751,6 +1946,8 @@ Init_ossl_pkey(void)
1751
1946
rb_define_module_function (mPKey , "read" , ossl_pkey_new_from_data , -1 );
1752
1947
rb_define_module_function (mPKey , "generate_parameters" , ossl_pkey_s_generate_parameters , -1 );
1753
1948
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
+
1754
1951
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
1755
1952
rb_define_module_function (mPKey , "new_raw_private_key" , ossl_pkey_new_raw_private_key , 2 );
1756
1953
rb_define_module_function (mPKey , "new_raw_public_key" , ossl_pkey_new_raw_public_key , 2 );
0 commit comments