blob: d77fd93ccb71da3628238363e406a60b089c0486 [file] [log] [blame]
/*
* Handle the openssl crypto functions
*
* Copyright (C) 2019 James.Bottomley@HansenPartnership.com
*
* SPDX-License-Identifier: LGPL-2.1-only
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <wordexp.h>
#include <libiberty.h>
#include <openssl/engine.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/ui.h>
#include "openssl-pkcs11.h"
/* number of bytes in the serial number */
#define SERIAL_COUNT 10
static void crypto_add_BN(int sec_num, const char *key, const BIGNUM *value,
EVP_MD_CTX *ctx)
{
int len = BN_num_bytes(value);
char *buf;
buf = malloc(len);
if (!buf)
return;
BN_bn2bin(value, (unsigned char *)buf);
EVP_DigestUpdate(ctx, buf, len);
cache_add_by_secnum(sec_num, key, buf, len);
}
static void
crypto_add_serial(int sec_num, EVP_MD_CTX *ctx)
{
unsigned char hash[SHA256_DIGEST_LENGTH];
char *serial;
int i;
EVP_DigestFinal(ctx, hash, NULL);
EVP_MD_CTX_destroy(ctx);
if (cache_get_by_secnum(sec_num, "serial", NULL))
/* the user supplied a serial, don't override */
return;
/* two chars per byte plus trailing zero */
serial = malloc(SERIAL_COUNT*2 + 1);
for (i = 0; i < SERIAL_COUNT; i++)
snprintf(&serial[i*2], 3, "%02x", hash[i]);
cache_add_by_secnum(sec_num, "serial", serial, 0);
}
static void
populate_rsa(int sec_num, EVP_PKEY *pkey)
{
RSA *rsa = EVP_PKEY_get1_RSA(pkey);
unsigned long size = RSA_size(rsa);
const BIGNUM *n, *e;
EVP_MD_CTX *ctx;
#if OPENSSL_VERSION_NUMBER < 0x10100000
n = rsa->n;
e = rsa->e;
#else
RSA_get0_key(rsa, &n, &e, NULL);
#endif
ctx = EVP_MD_CTX_create();
EVP_DigestInit(ctx, EVP_sha256());
crypto_add_BN(sec_num, "CKA_PUBLIC_EXPONENT", e, ctx);
crypto_add_BN(sec_num, "CKA_MODULUS", n, ctx);
crypto_add_serial(sec_num, ctx);
cache_add_by_secnum(sec_num, "CKA_MODULUS_BITS",
(const char *)size, CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_KEY_TYPE",
(const char *)CKK_RSA, CACHE_INT);
/* booleans: only need to add true ones */
cache_add_by_secnum(sec_num, "CKA_TOKEN",
(const char *)(BOOL_FOR_PRIVATE | BOOL_FOR_PUBLIC),
CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_PRIVATE",
(const char *)BOOL_FOR_PRIVATE, CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_ENCRYPT",
(const char *)BOOL_FOR_PUBLIC, CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_DECRYPT",
(const char *)BOOL_FOR_PRIVATE, CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_SIGN",
(const char *)BOOL_FOR_PRIVATE, CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_VERIFY_RECOVER",
(const char *)BOOL_FOR_PUBLIC, CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_SENSITIVE",
(const char *)BOOL_FOR_PRIVATE, CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_NEVER_EXTRACTABLE",
(const char *)BOOL_FOR_PRIVATE, CACHE_INT);
RSA_free(rsa);
}
int crypto_load_public_key(int sec_num, const char *pub)
{
FILE *file;
EVP_PKEY *pkey;
wordexp_t w;
wordexp(pub, &w, 0);
file = fopen(w.we_wordv[0], "r");
wordfree(&w);
if (!file) {
fprintf(stderr, "failed to open public key file %s: %s\n",
pub, strerror(errno));
return -1;
}
pkey = PEM_read_PUBKEY(file, NULL, NULL, NULL);
if (!pkey) {
fprintf(stderr, "failed to read public key %s:\n", pub);
ERR_print_errors_fp(stderr);
return -1;
}
if (EVP_PKEY_type(EVP_PKEY_id(pkey)) == EVP_PKEY_RSA) {
populate_rsa(sec_num, pkey);
} else {
fprintf(stderr, "Unknown key type\n");
return -1;
}
return 0;
}
static int
ui_reader(UI *ui, UI_STRING *uis)
{
char *pin = UI_get0_user_data(ui);
if (UI_get_string_type(uis) == UIT_PROMPT) {
UI_set_result(ui, uis, pin);
return 1;
}
return 0;
}
int pem_cb (char *buf, int size, int rwflag, void *pin)
{
strncpy(buf, pin, size);
return strlen(pin);
}
int crypto_load_private_key(int sec_num, const unsigned char *pin, int pin_len)
{
EVP_PKEY *pkey = NULL;
UI_METHOD *ui;
const char *priv = cache_get_by_secnum(sec_num, "private key", NULL);
const char *engine = cache_get_by_secnum(sec_num, "engine", NULL);
char auth[256];
/* pkcs11 pins may not be NULL terminated, but openssl expects
* its passwords to be */
memcpy(auth, pin, pin_len);
auth[pin_len] = '\0';
if (!priv) {
fprintf(stderr, "No 'private key' directive in section '%s'\n",
cache_get_section(sec_num));
return -1;
}
ui = UI_create_method(PACKAGE_NAME);
if (!ui) {
fprintf(stderr, "UI creation failed\n");
return -1;
}
UI_method_set_reader(ui, ui_reader);
if (engine) {
ENGINE *e = ENGINE_by_id(engine);
if (!e)
goto out;
ENGINE_init(e);
/* cast discards const which is respected by ui_read */
pkey = ENGINE_load_private_key(e, priv, ui, (void *)auth);
ENGINE_finish(e);
} else {
FILE *file;
wordexp_t w;
wordexp(priv, &w, 0);
file = fopen(w.we_wordv[0], "r");
wordfree(&w);
if (!file) {
fprintf(stderr, "failed to open private key file %s: %s\n",
priv, strerror(errno));
return -1;
}
pkey = PEM_read_PrivateKey(file, NULL, pem_cb, (void *)auth);
}
out:
if (!pkey) {
fprintf(stderr, "failed to read private key %s:\n", priv);
ERR_print_errors_fp(stderr);
return -1;
}
if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_RSA) {
fprintf(stderr, "Unknown key type\n");
return -1;
}
cache_add_by_secnum(sec_num, "pkey", (const char *)pkey, CACHE_PKEY);
return 0;
}
void crypto_free_private_key(int sec_num)
{
cache_add_by_secnum(sec_num, "pkey", NULL, CACHE_PKEY);
}
void crypto_cache_free_pkey(void *pkey)
{
EVP_PKEY_free(pkey);
}
static EVP_PKEY_CTX *get_key(int sec_num)
{
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey;
pkey = (EVP_PKEY *)cache_get_by_secnum(sec_num, "pkey", NULL);
if (!pkey) {
fprintf(stderr, "crypto_encrypt internal error: no PKEY\n");
return NULL;
}
ctx = EVP_PKEY_CTX_new(pkey, NULL);
return ctx;
}
const EVP_MD *get_mgf1(unsigned long mgf1)
{
switch (mgf1) {
case CKG_MGF1_SHA1:
return EVP_sha1();
case CKG_MGF1_SHA224:
return EVP_sha224();
case CKG_MGF1_SHA256:
return EVP_sha256();
case CKG_MGF1_SHA384:
return EVP_sha384();
case CKG_MGF1_SHA512:
return EVP_sha512();
default:
fprintf(stderr, "Unknown mgf1 type %ld\n", mgf1);
return NULL;
}
}
const EVP_MD *get_hash(unsigned long hash)
{
switch (hash) {
case CKM_SHA_1:
return EVP_sha1();
case CKM_SHA224:
return EVP_sha224();
case CKM_SHA256:
return EVP_sha256();
case CKM_SHA384:
return EVP_sha384();
case CKM_SHA512:
return EVP_sha512();
default:
fprintf(stderr, "Unknown hash type %ld\n", hash);
return NULL;
}
}
static EVP_PKEY_CTX *add_padding(EVP_PKEY_CTX *ctx, CK_MECHANISM_PTR mech)
{
if (mech->mechanism == CKM_RSA_PKCS) {
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING);
} else if (mech->mechanism == CKM_RSA_X_509) {
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING);
} else if (mech->mechanism == CKM_RSA_PKCS_PSS) {
CK_RSA_PKCS_PSS_PARAMS *p = mech->pParameter;
if (mech->ulParameterLen != sizeof(*p)) {
fprintf(stderr, "PSS mechanism parameter length %ld != %ld\n",
mech->ulParameterLen, (long)sizeof(*p));
goto err;
}
EVP_PKEY_CTX_set_signature_md(ctx, get_hash(p->hashAlg));
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING);
EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, get_mgf1(p->mgf));
EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, p->sLen);
} else if (mech->mechanism == CKM_RSA_PKCS_OAEP) {
CK_RSA_PKCS_OAEP_PARAMS *p = mech->pParameter;
if (mech->ulParameterLen != sizeof(*p)) {
fprintf(stderr, "OAEP mechanism parameter length %ld != %ld\n",
mech->ulParameterLen, (long)sizeof(*p));
goto err;
}
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
EVP_PKEY_CTX_set_rsa_oaep_md(ctx, get_hash(p->hashAlg));
EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, get_mgf1(p->mgf));
if (p->source & CKZ_DATA_SPECIFIED) {
void *l = BUF_memdup(p->pSourceData,
p->ulSourceDataLen);
EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, l,
p->ulSourceDataLen);
}
} else {
fprintf(stderr, "unknown mechanism %ld\n", mech->mechanism);
goto err;
}
return ctx;
err:
EVP_PKEY_CTX_free(ctx);
return NULL;
}
void *crypto_sign_init(int sec_num, CK_MECHANISM_PTR mech)
{
EVP_PKEY_CTX *ctx;
ctx = get_key(sec_num);
EVP_PKEY_sign_init(ctx);
return add_padding(ctx, mech);
}
int crypto_sign(void *opdata, void *data, unsigned long data_len,
void *sig, unsigned long *sig_len)
{
int ret = 0;
EVP_PKEY_CTX *ctx = opdata;
size_t len;
if (sig_len)
len = *sig_len;
if (!sig) {
*sig_len = EVP_PKEY_size(EVP_PKEY_CTX_get0_pkey(ctx));
return 0;
}
if (EVP_PKEY_sign(ctx, sig, &len, data, data_len) <= 0) {
ERR_print_errors_fp(stderr);
ret = -1;
}
if (sig_len)
*sig_len = len;
EVP_PKEY_CTX_free(ctx);
return ret;
}
void *crypto_decrypt_init(int sec_num, CK_MECHANISM_PTR mech)
{
EVP_PKEY_CTX *ctx;
ctx = get_key(sec_num);
EVP_PKEY_decrypt_init(ctx);
return add_padding(ctx, mech);
}
int crypto_decrypt(void *opdata, void *enc_data, unsigned long enc_len,
void *data, unsigned long *data_len)
{
EVP_PKEY_CTX *ctx = opdata;
int ret = 0;
size_t len;
if (data_len)
len = *data_len;
if (!data) {
*data_len = EVP_PKEY_size(EVP_PKEY_CTX_get0_pkey(ctx));
return 0;
}
if (EVP_PKEY_decrypt(ctx, data, &len, enc_data, enc_len) <= 0) {
ERR_print_errors_fp(stderr);
ret = -1;
}
if (data_len)
*data_len = len;
EVP_PKEY_CTX_free(ctx);
return ret;
}
static CK_MECHANISM_TYPE mechanism_types[] = {
CKM_RSA_PKCS,
CKM_RSA_X_509,
CKM_RSA_PKCS_PSS,
CKM_RSA_PKCS_OAEP,
};
void crypto_fill_mechanism_list(int sec_num, unsigned long *mechs,
unsigned long *count)
{
*count = ARRAY_SIZE(mechanism_types);
if (mechs)
memcpy(mechs, mechanism_types, sizeof(mechanism_types));
}
int crypto_check_mechanism(int sec_num, CK_MECHANISM_TYPE mech,
CK_MECHANISM_INFO_PTR info)
{
int i, found = 0;
for (i = 0; i < ARRAY_SIZE(mechanism_types); i++)
if (mech == mechanism_types[i])
found = 1;
if (!found)
return 0;
info->ulMinKeySize = 2048;
info->ulMaxKeySize = 2048;
info->flags = CKF_HW|CKF_ENCRYPT|CKF_DECRYPT;
return 1;
}