blob: e37ef63277923131c956dff5e311d2caf61c58c9 [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 <openssl/engine.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/ui.h>
#include <openssl/x509.h>
#include "openssl-pkcs11.h"
#include "crypto.h"
/* number of bytes in the serial number */
#define SERIAL_COUNT 10
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);
}
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_global(int sec_num)
{
cache_add_by_secnum(sec_num, "CKA_TOKEN",
(const char *)(BOOL_FOR_PRIVATE | BOOL_FOR_PUBLIC |
BOOL_FOR_CERT),
CACHE_INT);
cache_add_by_secnum(sec_num, "CKA_PRIVATE",
(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",
(const char *)(BOOL_FOR_PUBLIC | BOOL_FOR_CERT),
CACHE_INT);
}
static int populate_public_key(int sec_num, EVP_PKEY *pkey)
{
populate_global(sec_num);
switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) {
case EVP_PKEY_RSA:
crypto_rsa_populate(sec_num, pkey);
break;
case EVP_PKEY_EC:
crypto_ec_populate(sec_num, pkey);
break;
default:
fprintf(stderr, "Unknown key type\n");
return -1;
}
return 0;
}
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;
}
return populate_public_key(sec_num, pkey);
}
int crypto_load_cert(int sec_num, const char *cert)
{
FILE *file;
X509 *x509;
X509_NAME *name;
wordexp_t w;
int len, ret;
char *d, *z;
ASN1_INTEGER *sn;
wordexp(cert, &w, 0);
file = fopen(w.we_wordv[0], "r");
wordfree(&w);
if (!file) {
fprintf(stderr, "failed to open public key file %s: %s\n",
cert, strerror(errno));
return -1;
}
x509 = PEM_read_X509(file, NULL, NULL, NULL);
if (!x509) {
fprintf(stderr, "failed to read public key %s:\n", cert);
ERR_print_errors_fp(stderr);
return -1;
}
len = i2d_X509(x509, NULL);
z = d = OPENSSL_malloc(len);
i2d_X509(x509, (unsigned char **)&z);
cache_add_by_secnum(sec_num, "CKA_VALUE", d, len);
name = X509_get_issuer_name(x509);
len = i2d_X509_NAME(name, NULL);
z = d = OPENSSL_malloc(len);
i2d_X509_NAME(name, (unsigned char **)&z);
cache_add_by_secnum(sec_num, "CKA_ISSUER", d, len);
name = X509_get_subject_name(x509);
len = i2d_X509_NAME(name, NULL);
z = d = OPENSSL_malloc(len);
i2d_X509_NAME(name, (unsigned char **)&z);
cache_add_by_secnum(sec_num, "CKA_SUBJECT", d, len);
sn = X509_get_serialNumber(x509);
len = i2d_ASN1_INTEGER(sn, NULL);
z = d = OPENSSL_malloc(len);
i2d_ASN1_INTEGER(sn, (unsigned char **)&z);
cache_add_by_secnum(sec_num, "CKA_SERIAL_NUMBER", d, len);
cache_add_by_secnum(sec_num, "CKA_CERTIFICATE_TYPE",
(const char*)CKC_X_509, CACHE_INT);
ret = populate_public_key(sec_num, X509_get_pubkey(x509));
X509_free(x509);
return ret;
}
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];
/* may need to decrypt private key */
OpenSSL_add_all_ciphers();
OpenSSL_add_all_algorithms();
/* 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_load_builtin_engines();
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 &&
EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
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);
}
EVP_PKEY_CTX *crypto_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;
}
static CK_KEY_TYPE get_key_type(int sec_num)
{
int type;
const char *val;
val = cache_get_by_secnum(sec_num, "CKA_KEY_TYPE", &type);
if (type != CACHE_INT)
return -1;
return (CK_KEY_TYPE)val;
}
void *crypto_sign_init(int sec_num, CK_MECHANISM_PTR mech)
{
EVP_PKEY_CTX *ctx;
ctx = crypto_get_key(sec_num);
EVP_PKEY_sign_init(ctx);
if (get_key_type(sec_num) == CKK_RSA)
ctx = crypto_rsa_add_padding(ctx, mech);
return ctx;
}
int crypto_sign(int sec_num, 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;
/* openssl EC signatures are wrapped */
if (get_key_type(sec_num) == CKK_EC)
return crypto_ec_sign(ctx, data, data_len, sig, sig_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_fill_mechanism_list(int sec_num, unsigned long *mechs,
unsigned long *count)
{
switch (get_key_type(sec_num)) {
case CKK_RSA:
crypto_rsa_fill_mechanism_list(sec_num, mechs, count);
break;
case CKK_EC:
crypto_ec_fill_mechanism_list(sec_num, mechs, count);
break;
}
}
int crypto_check_mechanism(int sec_num, CK_MECHANISM_TYPE mech,
CK_MECHANISM_INFO_PTR info)
{
switch (get_key_type(sec_num)) {
case CKK_RSA:
return crypto_rsa_check_mechanism(sec_num, mech, info);
case CKK_EC:
return crypto_ec_check_mechanism(sec_num, mech, info);
default:
return 0;
}
}