Skip to content

Commit 70bb9d4

Browse files
committed
OpenSSL::PKey.from_parameters for generating PKey objects from parameter values
1 parent 1fa9fc5 commit 70bb9d4

File tree

2 files changed

+410
-0
lines changed

2 files changed

+410
-0
lines changed

ext/openssl/ossl_pkey.c

+197
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,174 @@ 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_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+
449617
/*
450618
* call-seq:
451619
* OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey
@@ -498,6 +666,33 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self)
498666
return pkey_generate(argc, argv, self, 0);
499667
}
500668

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+
501696
/*
502697
* TODO: There is no convenient way to check the presence of public key
503698
* components on OpenSSL 3.0. But since keys are immutable on 3.0, pkeys without
@@ -1751,6 +1946,8 @@ Init_ossl_pkey(void)
17511946
rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1);
17521947
rb_define_module_function(mPKey, "generate_parameters", ossl_pkey_s_generate_parameters, -1);
17531948
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+
17541951
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
17551952
rb_define_module_function(mPKey, "new_raw_private_key", ossl_pkey_new_raw_private_key, 2);
17561953
rb_define_module_function(mPKey, "new_raw_public_key", ossl_pkey_new_raw_public_key, 2);

0 commit comments

Comments
 (0)