+#if OPENSSL_VERSION_MAJOR >= 3
+static EVP_PKEY *build_rsa_key(int selection, const BIGNUM *bn_n, const BIGNUM *bn_e, const BIGNUM *bn_d) {
+ assert(bn_n);
+ assert(bn_e);
+
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
+
+ if(!ctx) {
+ openssl_err("initialize key context");
+ return NULL;
+ }
+
+ OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_n);
+ OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_e);
+
+ if(bn_d) {
+ OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, bn_d);
+ }
+
+ OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
+ EVP_PKEY *key = NULL;
+
+ bool ok = EVP_PKEY_fromdata_init(ctx) > 0
+ && EVP_PKEY_fromdata(ctx, &key, selection, params) > 0;
+
+ OSSL_PARAM_free(params);
+ OSSL_PARAM_BLD_free(bld);
+ EVP_PKEY_CTX_free(ctx);
+
+ if(ok) {
+ return key;
+ }
+
+ openssl_err("build key");
+ return NULL;
+}
+#endif
+
+static bool hex_to_bn(BIGNUM **bn, const char *hex) {
+ return (size_t)BN_hex2bn(bn, hex) == strlen(hex);
+}
+
+static rsa_t *rsa_set_hex_key(const char *n, const char *e, const char *d) {
+ rsa_t *rsa = NULL;
+ BIGNUM *bn_n = NULL;
+ BIGNUM *bn_e = NULL;
+ BIGNUM *bn_d = NULL;
+
+ if(!hex_to_bn(&bn_n, n) || !hex_to_bn(&bn_e, e) || (d && !hex_to_bn(&bn_d, d))) {
+ goto exit;
+ }
+
+#if OPENSSL_VERSION_MAJOR < 3
+ rsa = RSA_new();
+
+ if(rsa) {
+ RSA_set0_key(rsa, bn_n, bn_e, bn_d);
+ }
+
+#else
+ int selection = bn_d ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY;
+ rsa = build_rsa_key(selection, bn_n, bn_e, bn_d);
+#endif
+
+exit:
+#if OPENSSL_VERSION_MAJOR < 3
+
+ if(!rsa)
+#endif
+ {
+ BN_free(bn_d);
+ BN_free(bn_e);
+ BN_free(bn_n);
+ }
+
+ return rsa;