}
#ifdef HAVE_OSSL_PARAM /* openssl-3 */
+
+static int generate_private_pkey(struct asymmetric_key *priv,
+ const BIGNUM *n, const BIGNUM *e, const BIGNUM *d,
+ const BIGNUM *p, const BIGNUM *q)
+{
+ const BIGNUM *bignums[] = {n, e, d, p, q};
+ const char *strings[] = {"n", "e", "d", "p", "q"};
+ int ret, bytes[ARRAY_SIZE(bignums)];
+ unsigned char *bufs[ARRAY_SIZE(bignums)];
+ OSSL_PARAM params[ARRAY_SIZE(bignums) + 1];
+ /*
+ * Convert bignums to buffers for OSSL_PARAM_construct_BN() and init
+ * params[].
+ */
+ for (int i = 0; i < ARRAY_SIZE(bignums); i++) {
+ bytes[i] = BN_num_bytes(bignums[i]);
+ PARA_DEBUG_LOG("%s: %d bits\n", strings[i], bytes[i] * 8);
+ bufs[i] = alloc(bytes[i]);
+ assert(BN_bn2nativepad(bignums[i], bufs[i], bytes[i]) > 0);
+ params[i] = OSSL_PARAM_construct_BN(strings[i], bufs[i],
+ bytes[i]);
+ }
+ params[ARRAY_SIZE(bignums)] = OSSL_PARAM_construct_end();
+ /* Transfer buffers to openssl to create the pkey from it */
+ priv->ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
+ assert(priv->ctx);
+ assert(EVP_PKEY_fromdata_init(priv->ctx) > 0);
+ ret = EVP_PKEY_fromdata(priv->ctx, &priv->pkey,
+ EVP_PKEY_KEYPAIR, params);
+ for (int i = 0; i < ARRAY_SIZE(bignums); i++)
+ free(bufs[i]);
+ if (ret <= 0) {
+ EVP_PKEY_CTX_free(priv->ctx);
+ return openssl_perror("EVP_PKEY_fromdata()");
+ }
+ assert(priv->pkey);
+ return BN_num_bytes(n) * 8;
+}
+
/*
* Convert bignumns e and n to a pkey and context.
*/
assert(pub->pkey);
return nbytes * 8;
}
+
#endif /* HAVE_OSSL_PARAM */
static int read_public_key(const unsigned char *blob, size_t blen,
static int read_pem_private_key(const char *path, struct asymmetric_key *priv)
{
- EVP_PKEY *pkey;
BIO *bio;
+ int ret;
assert((bio = BIO_new(BIO_s_file())));
- priv->rsa = NULL;
- if (BIO_read_filename(bio, path) <= 0)
- goto bio_free;
- pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
- if (!pkey)
- goto bio_free;
- priv->rsa = EVP_PKEY_get1_RSA(pkey);
- EVP_PKEY_free(pkey);
-bio_free:
+ ret = BIO_read_filename(bio, path);
+ if (ret <= 0) {
+ priv->pkey = NULL;
+ ret = openssl_perror("BIO_read_filename");
+ goto free_bio;
+ }
+ priv->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ if (!priv->pkey) {
+ ret = openssl_perror("PEM_read_bio_PrivateKey");
+ goto free_bio;
+ }
+#ifndef HAVE_OSSL_PARAM /* openssl-1 */
+ priv->rsa = EVP_PKEY_get1_RSA(priv->pkey);
+#endif
+free_bio:
BIO_free(bio);
- return priv->rsa? RSA_size(priv->rsa) : -E_PRIVATE_KEY;
+ return ret;
}
static int read_openssh_private_key(const unsigned char *blob,
const unsigned char *end, struct asymmetric_key *priv)
{
int ret;
- RSA *rsa;
BIGNUM *n, *e, *d, *iqmp, *p, *q; /* stored in the key file */
const unsigned char *cp = blob;
- assert((rsa = RSA_new()));
ret = read_bignum(cp, end - cp, &n);
if (ret < 0)
- goto free_rsa;
+ return ret;
cp += ret;
ret = read_bignum(cp, end - cp, &e);
if (ret < 0)
ret = read_bignum(cp, end - cp, &q);
if (ret < 0)
goto free_p;
-#ifdef HAVE_RSA_SET0_KEY
- RSA_set0_key(rsa, n, e, d);
- RSA_set0_factors(rsa, p, q);
- RSA_set0_crt_params(rsa, NULL, NULL, iqmp);
-
+#ifdef HAVE_OSSL_PARAM /* openssl-3 */
+ /*
+ * Ignore iqmp, the coefficient for Chinese remainder theorem. It is
+ * dispensable because it can be derived from the other values. Passing
+ * it to the EVP API results in a memory leak.
+ */
+ ret = generate_private_pkey(priv, n, e, d, p, q);
#else
- rsa->n = n;
- rsa->e = e;
- rsa->d = d;
- rsa->iqmp = iqmp;
- rsa->p = p;
- rsa->q = q;
-#endif
- priv->rsa = rsa;
+ assert((priv->rsa = RSA_new()));
+ #ifdef HAVE_RSA_SET0_KEY
+ RSA_set0_key(priv->rsa, n, e, d);
+ RSA_set0_factors(priv->rsa, p, q);
+ RSA_set0_crt_params(priv->rsa, NULL, NULL, iqmp);
+ #else
+ priv->rsa->n = n;
+ priv->rsa->e = e;
+ priv->rsa->d = d;
+ priv->rsa->iqmp = iqmp;
+ priv->rsa->p = p;
+ priv->rsa->q = q;
+ #endif
return 1;
+#endif /* HAVE_OSSL_PARAM */
+ BN_clear_free(q);
free_p:
BN_clear_free(p);
free_iqmp:
BN_free(e);
free_n:
BN_free(n);
-free_rsa:
- RSA_free(rsa);
return ret;
}
unsigned char *blob, *end;
size_t blob_size;
- priv->rsa = NULL;
ret = decode_private_key(path, &blob, &blob_size);
if (ret < 0)
return ret;
free(pub);
}
+#ifdef HAVE_OSSL_PARAM /* openssl-3 */
+static int pkey_priv_decrypt(const struct asymmetric_key *priv,
+ unsigned char **outbuf, unsigned char *inbuf, int inlen)
+{
+ EVP_PKEY_CTX *ctx;
+ size_t outlen;
+
+ assert((ctx = EVP_PKEY_CTX_new(priv->pkey, NULL)));
+ assert((EVP_PKEY_decrypt_init(ctx) > 0));
+ assert(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) > 0);
+ if (EVP_PKEY_decrypt(ctx, NULL, &outlen, inbuf, inlen) <= 0) {
+ *outbuf = NULL;
+ EVP_PKEY_CTX_free(ctx);
+ return openssl_perror("EVP_PKEY_encrypt()");
+ }
+ *outbuf = alloc(outlen);
+ assert((EVP_PKEY_decrypt(ctx, *outbuf, &outlen, inbuf, inlen) > 0));
+ EVP_PKEY_CTX_free(ctx);
+ PARA_INFO_LOG("wrote %zu decrypted data bytes\n", outlen);
+ return outlen;
+}
+#endif /* HAVE_OSSL_PARAM */
+
int apc_priv_decrypt(const char *key_file, unsigned char **outbuf,
unsigned char *inbuf, int inlen)
{
return ret;
if (inlen < 0)
return -E_RSA;
- priv = alloc(sizeof(*priv));
+ priv = zalloc(sizeof(*priv)); /* ->pkey needs to start out zeroed */
ret = get_private_key(key_file, priv);
if (ret < 0) {
free(priv);
return ret;
}
+#ifdef HAVE_OSSL_PARAM /* openssl-3 */
+ ret = pkey_priv_decrypt(priv, outbuf, inbuf, inlen);
+ EVP_PKEY_CTX_free(priv->ctx);
+ EVP_PKEY_free(priv->pkey);
+#else
/*
* RSA is vulnerable to timing attacks. Generate a random blinding
* factor to protect against this kind of attack.
}
out:
RSA_free(priv->rsa);
+#endif
free(priv);
return ret;
}