Skip to content

Commit aedee8b

Browse files
committed
OpenSSL::PKey.from_data for generating PKey objects from user data
1 parent 85fc53e commit aedee8b

File tree

2 files changed

+410
-0
lines changed

2 files changed

+410
-0
lines changed

ext/openssl/ossl_pkey.c

+207
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)