Files
s390-tools/libseckey/sk_pkeymeth.c
Ingo Franzki b1d44876bd libseckey: FIx memory leak in error path (OpenSSL 1.1.1 only)
When EVP_PKEY_get0_EC_KEY(), EC_KEY_set_ex_data(), EVP_PKEY_get0_RSA()
or RSA_set_ex_data() fails, the function returns -EIO without freeing
the data struct or the data->key_blob that were allocated before.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00

1127 lines
29 KiB
C

/*
* libseckey - Secure key library
*
* Copyright IBM Corp. 2021
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <stdbool.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/rsa.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include "lib/zt_common.h"
#include "libseckey/sk_openssl.h"
#include "libseckey/sk_utilities.h"
/*
* This source file is only used with OpenSSL < 3.0.
* PKEY method functions are deprecated since OpenSSL 3.0
*/
#if !OPENSSL_VERSION_PREREQ(3, 0)
static int sk_pkey_data_ec_index = -1;
static int sk_pkey_data_rsa_index = -1;
static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_ec;
static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_rsa;
static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_rsa_pss;
static int sk_pkey_meth_lib = -1;
int sk_openssl_get_pkey_ec(const unsigned char *secure_key,
size_t secure_key_size, int nid, size_t prime_len,
const unsigned char *x, const unsigned char *y,
const struct sk_funcs *sk_funcs, const void *private,
EVP_PKEY **pkey, bool debug);
int sk_openssl_get_pkey_rsa(const unsigned char *secure_key,
size_t secure_key_size,
const unsigned char *modulus, size_t modulus_length,
const unsigned char *pub_exp, size_t pub_exp_length,
int pkey_type, const struct sk_funcs *sk_funcs,
const void *private, EVP_PKEY **pkey, bool debug);
#define sk_debug_data(data, fmt...) sk_debug(data->debug, fmt)
static void sk_pkey_meth_put_error(int err, const char *file, int line,
char *fmt, ...)
{
char text[200];
va_list ap;
va_start(ap, fmt);
ERR_put_error(sk_pkey_meth_lib, 0, err, file, line);
vsnprintf(text, sizeof(text), fmt, ap);
ERR_add_error_data(1, text);
va_end(ap);
}
#define put_error_data(data, err, fmt...) \
do { \
sk_debug_data(data, "ERROR: "fmt); \
sk_pkey_meth_put_error(err, __FILE__, __LINE__, \
fmt); \
} while (0)
/* Secure key PKEY extra data */
struct sk_pkey_data {
unsigned char *key_blob;
size_t key_blob_size;
struct sk_funcs *funcs;
void *private;
bool debug;
};
/*
* Free secure key PKEY data attached to a PKEY
*/
static void sk_pkey_data_free(void *UNUSED(parent), void *ptr,
CRYPTO_EX_DATA *UNUSED(ad), int UNUSED(idx),
long UNUSED(argl), void *UNUSED(argp))
{
struct sk_pkey_data *data = ptr;
if (data == NULL)
return;
sk_debug_data(data, "data: %p", data);
if (data != NULL) {
if (data->key_blob != NULL)
OPENSSL_free(data->key_blob);
OPENSSL_free(data);
}
}
/*
* Duplicate secure key PKEY data attached to a PKEY
*/
static int sk_pkey_data_dup(CRYPTO_EX_DATA *UNUSED(to),
const CRYPTO_EX_DATA *UNUSED(from),
void *from_d, int UNUSED(idx), long UNUSED(argl),
void *UNUSED(argp))
{
struct sk_pkey_data *from_data;
struct sk_pkey_data *data;
void **pptr = from_d;
from_data = *pptr;
sk_debug_data(from_data, "from_data: %p", from_data);
data = OPENSSL_zalloc(sizeof(struct sk_pkey_data));
if (data == NULL) {
put_error_data(from_data, ERR_R_MALLOC_FAILURE,
"OPENSSL_malloc failed");
return 0;
}
memcpy(data, from_data, sizeof(struct sk_pkey_data));
data->key_blob = OPENSSL_malloc(data->key_blob_size);
if (data->key_blob == NULL) {
put_error_data(from_data, ERR_R_MALLOC_FAILURE,
"OPENSSL_malloc failed");
OPENSSL_free(data);
return 0;
}
memcpy(data->key_blob, from_data->key_blob, data->key_blob_size);
*pptr = data;
sk_debug_data(from_data, "data: %p", data);
return 1;
}
/*
* Get the default PKEY method for a POKEY id
*/
static const EVP_PKEY_METHOD *sk_pkey_meth_get_default_method(int pkey_id)
{
switch (pkey_id) {
case EVP_PKEY_EC:
return sk_pkey_meth_default_method_ec;
case EVP_PKEY_RSA:
return sk_pkey_meth_default_method_rsa;
case EVP_PKEY_RSA_PSS:
return sk_pkey_meth_default_method_rsa_pss;
default:
return NULL;
}
}
static int sk_pkey_meth_get_pkey_data(EVP_PKEY_CTX *ctx, EVP_PKEY **pkey,
struct sk_pkey_data **sk_data)
{
*pkey = EVP_PKEY_CTX_get0_pkey(ctx);
if (*pkey == NULL)
return 0;
switch (EVP_PKEY_id(*pkey)) {
case EVP_PKEY_EC:
*sk_data = EC_KEY_get_ex_data(EVP_PKEY_get0_EC_KEY(*pkey),
sk_pkey_data_ec_index);
break;
case EVP_PKEY_RSA:
case EVP_PKEY_RSA_PSS:
*sk_data = RSA_get_ex_data(EVP_PKEY_get0_RSA(*pkey),
sk_pkey_data_rsa_index);
break;
default:
return 0;
}
return 1;
}
static int sk_pkey_meth_sign_init(EVP_PKEY_CTX *ctx)
{
int (*sign_init)(EVP_PKEY_CTX *ctx);
const EVP_PKEY_METHOD *default_meth;
struct sk_pkey_data *sk_data;
EVP_PKEY *pkey;
if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data))
return 0;
if (sk_data == NULL) {
/* If no secure key is attached, call default implementation */
default_meth = sk_pkey_meth_get_default_method(
EVP_PKEY_id(pkey));
if (default_meth == NULL)
return 0;
EVP_PKEY_meth_get_sign(default_meth, &sign_init, NULL);
if (sign_init == NULL)
return 1;
return sign_init(ctx);
}
sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey,
EVP_PKEY_id(pkey));
return 1;
}
static int sk_pkey_meth_sign(EVP_PKEY_CTX *ctx, unsigned char *sig,
size_t *siglen, const unsigned char *tbs,
size_t tbslen)
{
int (*sign)(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
const unsigned char *tbs, size_t tbslen);
int sig_sz, pad_mode, hlen, saltlen, max_saltlen;
const EVP_PKEY_METHOD *default_meth;
struct sk_pkey_data *sk_data;
int rc, md_type, mgf_md_type;
const EVP_MD *sigmd, *mgf1md;
const EC_KEY *ec;
EVP_PKEY *pkey;
if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data))
return 0;
if (sk_data == NULL) {
/* If no secure key is attached, call default implementation */
default_meth = sk_pkey_meth_get_default_method(
EVP_PKEY_id(pkey));
if (default_meth == NULL)
return 0;
EVP_PKEY_meth_get_sign(default_meth, NULL, &sign);
if (sign == NULL)
return 0;
return sign(ctx, sig, siglen, tbs, tbslen);
}
/* A secure key is attached, implement secure key sign */
sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey,
EVP_PKEY_id(pkey));
if (sig == NULL) {
*siglen = EVP_PKEY_size(pkey);
sk_debug_data(sk_data, "siglen: %lu", *siglen);
return 1;
}
if (*siglen < (size_t)EVP_PKEY_size(pkey)) {
put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT,
"signature buffer too small");
return 0;
}
*siglen = EVP_PKEY_size(pkey);
if (sk_data->funcs == NULL) {
put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER,
"no secure key funcs");
return 0;
}
if (EVP_PKEY_CTX_get_signature_md(ctx, &sigmd) != 1)
return 0;
md_type = sigmd != NULL ? EVP_MD_type(sigmd) : NID_sha1;
switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_RSA:
if (sk_data->funcs->rsa_sign == NULL) {
put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER,
"no secure key sign function");
return 0;
}
if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1)
return 0;
rc = sk_data->funcs->rsa_sign(sk_data->key_blob,
sk_data->key_blob_size,
sig, siglen, tbs, tbslen,
pad_mode, md_type,
sk_data->private,
sk_data->debug);
break;
case EVP_PKEY_RSA_PSS:
if (sk_data->funcs->rsa_pss_sign == NULL) {
put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER,
"no secure key sign function");
return 0;
}
if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1)
return 0;
if (EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md) != 1)
return 0;
if (EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen) != 1)
return 0;
hlen = sigmd != NULL ? EVP_MD_size(sigmd) : SHA_DIGEST_LENGTH;
if (mgf1md != NULL) {
mgf_md_type = EVP_MD_type(mgf1md);
hlen = EVP_MD_size(mgf1md);
} else {
mgf_md_type = md_type;
}
/*
* We should be using RSA_bits(EVP_PKEY_get0_RSA(pkey)) here,
* but EVP_PKEY_get0_RSA(pkey) does not work with PKEY/type
* EVP_PKEY_RSA_PSS on older OpenSSL versions, so we fall back
* on EVP_PKEY_bits in this case.
*/
max_saltlen = (EVP_PKEY_get0_RSA(pkey) != NULL ?
RSA_bits(EVP_PKEY_get0_RSA(pkey)) :
EVP_PKEY_bits(pkey)) / 8 - hlen - 2;
switch (saltlen) {
case RSA_PSS_SALTLEN_DIGEST:
saltlen = hlen;
break;
case RSA_PSS_SALTLEN_AUTO:
case RSA_PSS_SALTLEN_MAX:
saltlen = max_saltlen;
break;
default:
break;
}
if (saltlen > max_saltlen || saltlen < 0) {
put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT,
"invalid salt length: %d", saltlen);
return 0;
}
rc = sk_data->funcs->rsa_pss_sign(sk_data->key_blob,
sk_data->key_blob_size,
sig, siglen, tbs, tbslen,
md_type, mgf_md_type, saltlen,
sk_data->private,
sk_data->debug);
break;
case EVP_PKEY_EC:
ec = EVP_PKEY_get0_EC_KEY(pkey);
if (ec == NULL) {
put_error_data(sk_data, ERR_R_INTERNAL_ERROR,
"EVP_PKEY_get0_EC_KEY failed");
return 0;
}
sig_sz = ECDSA_size(ec);
if (sig == NULL) {
*siglen = (size_t)sig_sz;
return 0;
}
if (*siglen < (size_t)sig_sz) {
put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT,
"siglen too small");
return 0;
}
if (sk_data->funcs->ecdsa_sign == NULL) {
put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER,
"no secure key sign function");
return 0;
}
rc = sk_data->funcs->ecdsa_sign(sk_data->key_blob,
sk_data->key_blob_size,
sig, siglen,
tbs, tbslen, md_type,
sk_data->private,
sk_data->debug);
break;
default:
rc = -1;
put_error_data(sk_data, ERR_R_INTERNAL_ERROR,
"invalid PKEY type");
}
if (rc != 0) {
put_error_data(sk_data, ERR_R_OPERATION_FAIL,
"secure key sign operation failed");
return 0;
}
sk_debug_data(sk_data, "siglen: %lu", *siglen);
return 1;
}
static int sk_pkey_meth_decrypt_init(EVP_PKEY_CTX *ctx)
{
int (*decrypt_init)(EVP_PKEY_CTX *ctx);
const EVP_PKEY_METHOD *default_meth;
struct sk_pkey_data *sk_data;
EVP_PKEY *pkey;
if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data))
return 0;
if (sk_data == NULL) {
/* If no secure key is attached, call default implementation */
default_meth = sk_pkey_meth_get_default_method(
EVP_PKEY_id(pkey));
if (default_meth == NULL)
return 0;
EVP_PKEY_meth_get_decrypt(default_meth, &decrypt_init, NULL);
if (decrypt_init == NULL)
return 1;
return decrypt_init(ctx);
}
sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey,
EVP_PKEY_id(pkey));
return 1;
}
static int sk_pkey_meth_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out,
size_t *outlen, const unsigned char *in,
size_t inlen)
{
int (*decrypt)(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen);
int rc, pad_mode, label_len, md_type, mgfmd_type;
const EVP_PKEY_METHOD *default_meth;
struct sk_pkey_data *sk_data;
const EVP_MD *md, *mgf1md;
unsigned char *label;
EVP_PKEY *pkey;
if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data))
return 0;
if (sk_data == NULL) {
/* If no secure key is attached, call default implementation */
default_meth = sk_pkey_meth_get_default_method(
EVP_PKEY_id(pkey));
if (default_meth == NULL)
return 0;
EVP_PKEY_meth_get_decrypt(default_meth, NULL, &decrypt);
if (decrypt == NULL)
return 0;
return decrypt(ctx, out, outlen, in, inlen);
}
/* A secure key is attached, implement secure key decrypt */
sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey,
EVP_PKEY_id(pkey));
if (out == NULL) {
*outlen = EVP_PKEY_size(pkey);
sk_debug_data(sk_data, "outlen: %lu", *outlen);
return 1;
}
if (*outlen < (size_t)EVP_PKEY_size(pkey)) {
put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT,
"output buffer too small");
return 0;
}
*outlen = EVP_PKEY_size(pkey);
if (sk_data->funcs == NULL) {
put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER,
"no secure key funcs");
return 0;
}
switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_RSA:
if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1)
return 0;
switch (pad_mode) {
case RSA_PKCS1_OAEP_PADDING:
label_len = EVP_PKEY_CTX_get0_rsa_oaep_label(ctx,
&label);
if (label_len < 0)
return 0;
if (EVP_PKEY_CTX_get_rsa_oaep_md(ctx, &md) != 1)
return 0;
if (EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md) != 1)
return 0;
md_type = md != NULL ? EVP_MD_type(md) : NID_sha1;
mgfmd_type = mgf1md != NULL ?
EVP_MD_type(mgf1md) : md_type;
if (sk_data->funcs->rsa_decrypt_oaep == NULL) {
put_error_data(sk_data,
ERR_R_PASSED_NULL_PARAMETER,
"no secure key decrypt function");
return 0;
}
rc = sk_data->funcs->rsa_decrypt_oaep(
sk_data->key_blob,
sk_data->key_blob_size,
out, outlen, in, inlen,
md_type, mgfmd_type,
label, label_len,
sk_data->private,
sk_data->debug);
break;
default:
if (sk_data->funcs->rsa_decrypt == NULL) {
put_error_data(sk_data,
ERR_R_PASSED_NULL_PARAMETER,
"no secure key decrypt function");
return 0;
}
rc = sk_data->funcs->rsa_decrypt(sk_data->key_blob,
sk_data->key_blob_size,
out, outlen, in, inlen,
pad_mode,
sk_data->private,
sk_data->debug);
break;
}
break;
case EVP_PKEY_RSA_PSS:
case EVP_PKEY_EC:
default:
/* encrypt not supported */
put_error_data(sk_data, ERR_R_INTERNAL_ERROR,
"invalid PKEY type");
return 0;
}
if (rc != 0) {
put_error_data(sk_data, ERR_R_OPERATION_FAIL,
"secure key decrypt operation failed");
return 0;
}
sk_debug_data(sk_data, "outlen: %lu", *outlen);
return 1;
}
static int sk_pkey_meth_derive_init(EVP_PKEY_CTX *ctx)
{
int (*derive_init)(EVP_PKEY_CTX *ctx);
const EVP_PKEY_METHOD *default_meth;
struct sk_pkey_data *sk_data;
EVP_PKEY *pkey;
if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data))
return 0;
if (sk_data == NULL) {
/* If no secure key is attached, call default implementation */
default_meth = sk_pkey_meth_get_default_method(
EVP_PKEY_id(pkey));
if (default_meth == NULL)
return 0;
EVP_PKEY_meth_get_derive(default_meth, &derive_init, NULL);
if (derive_init == NULL)
return 1;
return derive_init(ctx);
}
/* We can not derive with a secure key attached */
put_error_data(sk_data, ERR_R_OPERATION_FAIL,
"secure key derive not supported");
return 0;
}
static int sk_pkey_meth_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
size_t *keylen)
{
int (*derive)(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
const EVP_PKEY_METHOD *default_meth;
struct sk_pkey_data *sk_data;
EVP_PKEY *pkey;
if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data))
return 0;
if (sk_data == NULL) {
/* If no secure key is attached, call default implementation */
default_meth = sk_pkey_meth_get_default_method(
EVP_PKEY_id(pkey));
if (default_meth == NULL)
return 0;
EVP_PKEY_meth_get_derive(default_meth, NULL, &derive);
if (derive == NULL)
return 1;
return derive(ctx, key, keylen);
}
/* We can not derive with a secure key attached */
put_error_data(sk_data, ERR_R_OPERATION_FAIL,
"secure key derive not supported");
return 0;
}
static int sk_pkey_meth_setup(int pkey_id, bool debug)
{
const EVP_PKEY_METHOD *default_meth;
EVP_PKEY_METHOD *pkey_meth;
int flags = 0;
sk_debug(debug, "pkey_id: %d", pkey_id);
default_meth = sk_pkey_meth_get_default_method(pkey_id);
if (default_meth == NULL) {
sk_debug(debug, "ERROR: get_default_pkey_method failed");
return -EIO;
}
EVP_PKEY_meth_get0_info(NULL, &flags, default_meth);
pkey_meth = EVP_PKEY_meth_new(pkey_id, flags);
if (pkey_meth == NULL) {
sk_debug(debug, "ERROR: EVP_PKEY_meth_new failed");
return -ENOMEM;
}
/* Inherit all functions from the default PKEY method */
EVP_PKEY_meth_copy(pkey_meth, default_meth);
/* Override private key operations */
EVP_PKEY_meth_set_sign(pkey_meth, sk_pkey_meth_sign_init,
sk_pkey_meth_sign);
EVP_PKEY_meth_set_decrypt(pkey_meth, sk_pkey_meth_decrypt_init,
sk_pkey_meth_decrypt);
EVP_PKEY_meth_set_derive(pkey_meth, sk_pkey_meth_derive_init,
sk_pkey_meth_derive);
/*
* Do not provide signctx and digestsign functions, even if the default
* method would support them. If not available, it will fall back to
* regular sign function usage, which we do override.
*/
EVP_PKEY_meth_set_signctx(pkey_meth, NULL, NULL);
EVP_PKEY_meth_set_digestsign(pkey_meth, NULL);
/* Add this as the preferred PKEY method */
if (EVP_PKEY_meth_add0(pkey_meth) != 1) {
sk_debug(debug, "ERROR: EVP_PKEY_meth_add0 failed");
return -EIO;
}
return 0;
}
static int sk_pkey_meth_cleanup(int pkey_id)
{
const EVP_PKEY_METHOD *pkey_meth;
pkey_meth = EVP_PKEY_meth_find(pkey_id);
if (pkey_meth == NULL)
return -ENOENT;
if (EVP_PKEY_meth_remove(pkey_meth) != 1)
return -EIO;
EVP_PKEY_meth_free((EVP_PKEY_METHOD *)pkey_meth);
return 0;
}
static int sk_pkey_meth_setup_pkey(EVP_PKEY *pkey,
const unsigned char *secure_key,
size_t secure_key_size,
const struct sk_funcs *funcs,
const void *private, bool debug)
{
struct sk_pkey_data *data;
EC_KEY *ec;
RSA *rsa;
int rc;
if (pkey == NULL || secure_key == NULL || secure_key_size == 0 ||
funcs == NULL)
return -EINVAL;
sk_debug(debug, "pkey: %p type: %d", pkey, EVP_PKEY_id(pkey));
if (sk_pkey_data_ec_index < 0 || sk_pkey_data_rsa_index < 0) {
sk_debug(debug, "sk_pkey_meth support not initialized");
return -ENODEV;
}
data = OPENSSL_zalloc(sizeof(struct sk_pkey_data));
if (data == NULL) {
sk_debug(debug, "OPENSSL_zalloc failed");
return -ENOMEM;
}
data->key_blob = OPENSSL_malloc(secure_key_size);
if (data->key_blob == NULL) {
sk_debug(debug, "OPENSSL_malloc failed");
OPENSSL_free(data);
return -ENOMEM;
}
memcpy(data->key_blob, secure_key, secure_key_size);
data->key_blob_size = secure_key_size;
data->funcs = (struct sk_funcs *)funcs;
data->private = (void *)private;
data->debug = debug;
switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_EC:
ec = EVP_PKEY_get0_EC_KEY(pkey);
if (ec == NULL) {
sk_debug(debug, "EVP_PKEY_get0_EC_KEY failed");
rc = -EIO;
goto error;
}
if (!EC_KEY_set_ex_data(ec, sk_pkey_data_ec_index, data)) {
sk_debug(debug, "EC_KEY_set_ex_data failed");
rc = -EIO;
goto error;
}
break;
case EVP_PKEY_RSA:
case EVP_PKEY_RSA_PSS:
rsa = EVP_PKEY_get0_RSA(pkey);
if (rsa == NULL) {
sk_debug(debug, "EVP_PKEY_get0_RSA failed");
rc = -EIO;
goto error;
}
if (!RSA_set_ex_data(rsa, sk_pkey_data_rsa_index, data)) {
sk_debug(debug, "RSA_set_ex_data failed");
rc = -EIO;
goto error;
}
break;
default:
rc = -EIO;
goto error;
}
return 0;
error:
if (data != NULL) {
OPENSSL_free(data->key_blob);
OPENSSL_free(data);
}
return rc;
}
/**
* Initializes the secure key support for OpenSSL.
*
* @param debug true to enable internal debugging
*
* @returns zero for success, a negative errno in case of an error:
* -ENOMEM: failed to allocate memory
* -EIO: OpenSSL failed for various reasons
*/
int SK_OPENSSL_init(bool debug)
{
int rc;
if (sk_pkey_data_ec_index < 0)
sk_pkey_data_ec_index = CRYPTO_get_ex_new_index(
CRYPTO_EX_INDEX_EC_KEY, 0, NULL,
NULL, sk_pkey_data_dup, sk_pkey_data_free);
if (sk_pkey_data_ec_index < 0) {
sk_debug(debug, "ERROR: CRYPTO_get_ex_new_index(EC) failed");
return -EIO;
}
if (sk_pkey_data_rsa_index < 0)
sk_pkey_data_rsa_index = CRYPTO_get_ex_new_index(
CRYPTO_EX_INDEX_RSA, 0, NULL,
NULL, sk_pkey_data_dup, sk_pkey_data_free);
if (sk_pkey_data_rsa_index < 0) {
sk_debug(debug, "ERROR: CRYPTO_get_ex_new_index(RSA) failed");
return -EIO;
}
sk_pkey_meth_default_method_ec = EVP_PKEY_meth_find(EVP_PKEY_EC);
if (sk_pkey_meth_default_method_ec == NULL) {
sk_debug(debug, "ERROR: EVP_PKEY_meth_find(EC) failed");
return -EIO;
}
sk_pkey_meth_default_method_rsa = EVP_PKEY_meth_find(EVP_PKEY_RSA);
if (sk_pkey_meth_default_method_rsa == NULL) {
sk_debug(debug, "ERROR: EVP_PKEY_meth_find(RSA) failed");
return -EIO;
}
sk_pkey_meth_default_method_rsa_pss =
EVP_PKEY_meth_find(EVP_PKEY_RSA_PSS);
if (sk_pkey_meth_default_method_rsa_pss == NULL) {
sk_debug(debug, "ERROR: EVP_PKEY_meth_find(RSA-PSS) failed");
return -EIO;
}
rc = sk_pkey_meth_setup(EVP_PKEY_EC, debug);
if (rc != 0) {
sk_debug(debug, "ERROR: sk_pkey_meth_setup(EC) "
"failed");
return rc;
}
rc = sk_pkey_meth_setup(EVP_PKEY_RSA, debug);
if (rc != 0) {
sk_debug(debug, "ERROR: sk_pkey_meth_setup(RSA) "
"failed");
return rc;
}
rc = sk_pkey_meth_setup(EVP_PKEY_RSA_PSS, debug);
if (rc != 0) {
sk_debug(debug, "ERROR: sk_pkey_meth_setup(RSA-PSS) "
"failed");
return rc;
}
if (sk_pkey_meth_lib <= 0)
sk_pkey_meth_lib = ERR_get_next_error_library();
if (sk_pkey_meth_lib <= 0) {
sk_debug(debug, "ERROR: ERR_get_next_error_library failed");
return -EIO;
}
sk_debug(debug, "sk_pkey_meth support initialized");
return 0;
}
/**
* Terminate the secure key support for OpenSSL.
*/
void SK_OPENSSL_term(void)
{
if (sk_pkey_data_ec_index >= 0)
CRYPTO_free_ex_index(CRYPTO_EX_INDEX_EC_KEY,
sk_pkey_data_ec_index);
sk_pkey_data_ec_index = -1;
if (sk_pkey_data_rsa_index >= 0)
CRYPTO_free_ex_index(CRYPTO_EX_INDEX_RSA,
sk_pkey_data_rsa_index);
sk_pkey_data_rsa_index = -1;
sk_pkey_meth_cleanup(EVP_PKEY_EC);
sk_pkey_meth_cleanup(EVP_PKEY_RSA);
sk_pkey_meth_cleanup(EVP_PKEY_RSA_PSS);
sk_pkey_meth_default_method_ec = NULL;
sk_pkey_meth_default_method_rsa = NULL;
sk_pkey_meth_default_method_rsa_pss = NULL;
sk_pkey_meth_lib = -1;
}
/**
* Converts an EC key given by the nid and the x and y coordinates into an
* OpenSSL PKEY and attaches the secure key together with secure key functions
* and private pointer to it. If no secure key is provided, a public EC key
* only PKEY is returned.
*
* @param secure_key the secure key blob.
* If NULL, a clear key PKEY is created.
* @param secure_key_size the size of the secure key blob (ignored if
* secure_key is NULL)
* @param nid the OpenSSL nid of the EC curve used
* @param prime_len the length of the prime in bytes. This is also the
* length of the x and y coordinates.
* @param x the x coordinate as big endian binary number in
* prime_len size
* @param y the y coordinate as big endian binary number in
* prime_len size
* @param sk_funcs the secure key functions to operate with the key.
* Ignored if secure_key is NULL, required otherwise.
* @param private a private pointer that is passed to the secure key
* functions (can be NULL)
* @param pkey On return: A PKEY containing the EC public key.
* @param debug true to enable internal debugging
*
* @returns zero for success, a negative errno in case of an error:
* -EINVAL: a function parameter is invalid
* -ENOMEM: failed to allocate memory
* -EIO: OpenSSL failed to generate the PKEY
* -ENOENT: OpenSSL does not know/support the curve (nid)
*/
int sk_openssl_get_pkey_ec(const unsigned char *secure_key,
size_t secure_key_size, int nid, size_t prime_len,
const unsigned char *x, const unsigned char *y,
const struct sk_funcs *sk_funcs, const void *private,
EVP_PKEY **pkey, bool debug)
{
BIGNUM *bn_x = NULL, *bn_y = NULL;
EC_GROUP *group = NULL;
EC_KEY *ec = NULL;
int rc;
if (pkey == NULL || x == NULL || y == NULL)
return -EINVAL;
if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL))
return -EINVAL;
*pkey = NULL;
bn_x = BN_bin2bn(x, prime_len, NULL);
bn_y = BN_bin2bn(y, prime_len, NULL);
if (bn_x == NULL || bn_y == NULL) {
sk_debug(debug, "ERROR: BN_bin2bn failed");
rc = -ENOMEM;
goto out;
}
group = EC_GROUP_new_by_curve_name(nid);
if (group == NULL) {
sk_debug(debug, "ERROR: EC_GROUP_new_by_curve_name failed");
rc = -ENOENT;
goto out;
}
ec = EC_KEY_new();
if (ec == NULL) {
sk_debug(debug, "ERROR: EC_KEY_new failed");
rc = -ENOMEM;
goto out;
}
rc = EC_KEY_set_group(ec, group);
if (rc != 1) {
sk_debug(debug, "ERROR: EC_KEY_set_group failed");
rc = -EIO;
goto out;
}
rc = EC_KEY_set_public_key_affine_coordinates(ec, bn_x, bn_y);
if (rc != 1) {
sk_debug(debug,
"ERROR: EC_KEY_set_public_key_affine_coordinates failed");
rc = -EIO;
goto out;
}
*pkey = EVP_PKEY_new();
if (*pkey == NULL) {
sk_debug(debug, "ERROR: EVP_PKEY_new failed");
rc = -ENOMEM;
goto out;
}
rc = EVP_PKEY_assign_EC_KEY(*pkey, ec);
if (rc != 1) {
sk_debug(debug, "ERROR: EVP_PKEY_assign_EC_KEY failed");
rc = -EIO;
goto out;
}
if (secure_key != NULL) {
rc = sk_pkey_meth_setup_pkey(*pkey, secure_key, secure_key_size,
sk_funcs, private, debug);
if (rc != 0) {
sk_debug(debug,
"ERROR: sk_pkey_meth_setup_pkey failed");
goto out;
}
}
rc = 0;
sk_debug(debug, "pkey created: %p", *pkey);
out:
if (bn_x != NULL)
BN_free(bn_x);
if (bn_y != NULL)
BN_free(bn_y);
if (group != NULL)
EC_GROUP_free(group);
if (rc != 0 && *pkey != NULL) {
EVP_PKEY_free(*pkey);
*pkey = NULL;
}
return rc;
}
/**
* Converts an RSA key given by the modulus and public exponent into an
* OpenSSL PKEY and attaches the secure key together with secure key functions
* and private pointer to it. If no secure key is provided, a public RSA key
* only PKEY is returned.
*
* @param secure_key the secure key blob.
* If NULL, a clear key PKEY is created.
* @param secure_key_size the size of the secure key blob (ignored if
* secure_key is NULL)
* @param modulus the modulus as big endian number
* @param modulus_length the length of the modulus in bytes
* @param pub_exp the public exponent as big endian number
* @param pub_exp_length the length of the public exponent in bytes
* @param pkey_type the PKEY type (EVP_PKEY_RSA or EVP_PKEY_RSA_PSS)
* @param sk_funcs the secure key functions to operate with the key.
* Ignored if secure_key is NULL, required otherwise.
* @param private a private pointer that is passed to the secure key
* functions (can be NULL)
* @param pkey On return: A PKEY containing the RSA public key.
* @param debug true to enable internal debugging
*
* @returns zero for success, a negative errno in case of an error:
* -EINVAL: a function parameter is invalid
* -ENOMEM: failed to allocate memory
* -EIO: OpenSSL failed to generate the PKEY
*/
int sk_openssl_get_pkey_rsa(const unsigned char *secure_key,
size_t secure_key_size,
const unsigned char *modulus, size_t modulus_length,
const unsigned char *pub_exp, size_t pub_exp_length,
int pkey_type, const struct sk_funcs *sk_funcs,
const void *private, EVP_PKEY **pkey, bool debug)
{
BIGNUM *bn_modulus = NULL, *bn_pub_exp = NULL;
RSA *rsa;
int rc;
if (pkey == NULL || modulus == NULL || pub_exp == NULL)
return -EINVAL;
if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL))
return -EINVAL;
if (pkey_type != EVP_PKEY_RSA && pkey_type != EVP_PKEY_RSA_PSS)
return -EINVAL;
*pkey = NULL;
bn_modulus = BN_bin2bn(modulus, modulus_length, NULL);
bn_pub_exp = BN_bin2bn(pub_exp, pub_exp_length, NULL);
if (bn_modulus == NULL || bn_pub_exp == NULL) {
sk_debug(debug, "ERROR: BN_bin2bn failed");
rc = -ENOMEM;
goto out;
}
rsa = RSA_new();
if (rsa == NULL) {
sk_debug(debug, "ERROR: RSA_new failed");
rc = -ENOMEM;
goto out;
}
rc = RSA_set0_key(rsa, bn_modulus, bn_pub_exp, NULL);
if (rc != 1) {
sk_debug(debug, "ERROR: RSA_set0_key failed");
rc = -EIO;
goto out;
}
*pkey = EVP_PKEY_new();
if (*pkey == NULL) {
sk_debug(debug, "ERROR: EVP_PKEY_new failed");
rc = -ENOMEM;
goto out;
}
rc = EVP_PKEY_assign(*pkey, pkey_type, rsa);
if (rc != 1) {
sk_debug(debug, "ERROR: EVP_PKEY_assign failed");
rc = -EIO;
goto out;
}
if (secure_key != NULL) {
rc = sk_pkey_meth_setup_pkey(*pkey, secure_key, secure_key_size,
sk_funcs, private, debug);
if (rc != 0) {
sk_debug(debug,
"ERROR: sk_pkey_meth_setup_pkey failed");
goto out;
}
}
rc = 0;
sk_debug(debug, "pkey created: %p", *pkey);
out:
if (rc != 0 && bn_modulus != NULL)
BN_free(bn_modulus);
if (rc != 0 && bn_pub_exp != NULL)
BN_free(bn_pub_exp);
return rc;
}
/**
* Get the curve NID of the EC pkey
*/
int SK_OPENSSL_get_curve_from_ec_pkey(EVP_PKEY *pkey)
{
if (EVP_PKEY_id(pkey) != EVP_PKEY_EC)
return NID_undef;
return EC_GROUP_get_curve_name(EC_KEY_get0_group(
EVP_PKEY_get0_EC_KEY(pkey)));
}
#endif