arm-trusted-firmware/tools/cert_create/src/key.c
Robin van der Gracht 616b3ce27d feat(cert-create): add pkcs11 engine support
Add pkcs11 engine support which allows using keys that are securely
stored on a HSM or TPM. To use this feature the user has to supply
an RFC 7512 compliant PKCS11 URI to a key instead of a file as an
argument to one of the key options. This change is fully backwards
compatible.

This change makes use of the openssl engine API which is deprecated
since openssl 3.0 and will most likely be removed in version 4. So
pkcs11 support will have to be updated to the openssl provider API
in the near future.

Signed-off-by: Robin van der Gracht <robin@protonic.nl>
Change-Id: If96725988ca62c5613ec59123943bf15922f5d1f
2023-09-21 13:27:25 +02:00

365 lines
6.8 KiB
C

/*
* Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Suppress OpenSSL engine deprecation warnings */
#define OPENSSL_SUPPRESS_DEPRECATED
#include <openssl/conf.h>
#include <openssl/engine.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include "cert.h"
#include "cmd_opt.h"
#include "debug.h"
#include "key.h"
#include "sha.h"
#define MAX_FILENAME_LEN 1024
key_t *keys;
unsigned int num_keys;
#if !USING_OPENSSL3
/*
* Create a new key container
*/
int key_new(key_t *key)
{
/* Create key pair container */
key->key = EVP_PKEY_new();
if (key->key == NULL) {
return 0;
}
return 1;
}
#endif
static int key_create_rsa(key_t *key, int key_bits)
{
#if USING_OPENSSL3
EVP_PKEY *rsa = EVP_RSA_gen(key_bits);
if (rsa == NULL) {
printf("Cannot generate RSA key\n");
return 0;
}
key->key = rsa;
return 1;
#else
BIGNUM *e;
RSA *rsa = NULL;
e = BN_new();
if (e == NULL) {
printf("Cannot create RSA exponent\n");
return 0;
}
if (!BN_set_word(e, RSA_F4)) {
printf("Cannot assign RSA exponent\n");
goto err2;
}
rsa = RSA_new();
if (rsa == NULL) {
printf("Cannot create RSA key\n");
goto err2;
}
if (!RSA_generate_key_ex(rsa, key_bits, e, NULL)) {
printf("Cannot generate RSA key\n");
goto err;
}
if (!EVP_PKEY_assign_RSA(key->key, rsa)) {
printf("Cannot assign RSA key\n");
goto err;
}
BN_free(e);
return 1;
err:
RSA_free(rsa);
err2:
BN_free(e);
return 0;
#endif
}
#ifndef OPENSSL_NO_EC
#if USING_OPENSSL3
static int key_create_ecdsa(key_t *key, int key_bits, const char *curve)
{
EVP_PKEY *ec = EVP_EC_gen(curve);
if (ec == NULL) {
printf("Cannot generate EC key\n");
return 0;
}
key->key = ec;
return 1;
}
static int key_create_ecdsa_nist(key_t *key, int key_bits)
{
return key_create_ecdsa(key, key_bits, "prime256v1");
}
static int key_create_ecdsa_brainpool_r(key_t *key, int key_bits)
{
return key_create_ecdsa(key, key_bits, "brainpoolP256r1");
}
static int key_create_ecdsa_brainpool_t(key_t *key, int key_bits)
{
return key_create_ecdsa(key, key_bits, "brainpoolP256t1");
}
#else
static int key_create_ecdsa(key_t *key, int key_bits, const int curve_id)
{
EC_KEY *ec;
ec = EC_KEY_new_by_curve_name(curve_id);
if (ec == NULL) {
printf("Cannot create EC key\n");
return 0;
}
if (!EC_KEY_generate_key(ec)) {
printf("Cannot generate EC key\n");
goto err;
}
EC_KEY_set_flags(ec, EC_PKEY_NO_PARAMETERS);
EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE);
if (!EVP_PKEY_assign_EC_KEY(key->key, ec)) {
printf("Cannot assign EC key\n");
goto err;
}
return 1;
err:
EC_KEY_free(ec);
return 0;
}
static int key_create_ecdsa_nist(key_t *key, int key_bits)
{
return key_create_ecdsa(key, key_bits, NID_X9_62_prime256v1);
}
static int key_create_ecdsa_brainpool_r(key_t *key, int key_bits)
{
return key_create_ecdsa(key, key_bits, NID_brainpoolP256r1);
}
static int key_create_ecdsa_brainpool_t(key_t *key, int key_bits)
{
return key_create_ecdsa(key, key_bits, NID_brainpoolP256t1);
}
#endif /* USING_OPENSSL3 */
#endif /* OPENSSL_NO_EC */
typedef int (*key_create_fn_t)(key_t *key, int key_bits);
static const key_create_fn_t key_create_fn[KEY_ALG_MAX_NUM] = {
[KEY_ALG_RSA] = key_create_rsa,
#ifndef OPENSSL_NO_EC
[KEY_ALG_ECDSA_NIST] = key_create_ecdsa_nist,
[KEY_ALG_ECDSA_BRAINPOOL_R] = key_create_ecdsa_brainpool_r,
[KEY_ALG_ECDSA_BRAINPOOL_T] = key_create_ecdsa_brainpool_t,
#endif /* OPENSSL_NO_EC */
};
int key_create(key_t *key, int type, int key_bits)
{
if (type >= KEY_ALG_MAX_NUM) {
printf("Invalid key type\n");
return 0;
}
if (key_create_fn[type]) {
return key_create_fn[type](key, key_bits);
}
return 0;
}
static EVP_PKEY *key_load_pkcs11(const char *uri)
{
char *key_pass;
EVP_PKEY *pkey;
ENGINE *e;
ENGINE_load_builtin_engines();
e = ENGINE_by_id("pkcs11");
if (!e) {
fprintf(stderr, "Cannot Load PKCS#11 ENGINE\n");
return NULL;
}
if (!ENGINE_init(e)) {
fprintf(stderr, "Cannot ENGINE_init\n");
goto err;
}
key_pass = getenv("PKCS11_PIN");
if (key_pass) {
if (!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0)) {
fprintf(stderr, "Cannot Set PKCS#11 PIN\n");
goto err;
}
}
pkey = ENGINE_load_private_key(e, uri, NULL, NULL);
if (pkey)
return pkey;
err:
ENGINE_free(e);
return NULL;
}
int key_load(key_t *key, unsigned int *err_code)
{
FILE *fp;
if (key->fn) {
if (!strncmp(key->fn, "pkcs11:", 7)) {
/* Load key through pkcs11 */
key->key = key_load_pkcs11(key->fn);
} else {
/* Load key from file */
fp = fopen(key->fn, "r");
if (fp) {
key->key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
fclose(fp);
} else {
WARN("Cannot open file %s\n", key->fn);
*err_code = KEY_ERR_OPEN;
}
}
if (key->key) {
*err_code = KEY_ERR_NONE;
return 1;
} else {
ERROR("Cannot load key from %s\n", key->fn);
*err_code = KEY_ERR_LOAD;
}
} else {
VERBOSE("Key not specified\n");
*err_code = KEY_ERR_FILENAME;
}
return 0;
}
int key_store(key_t *key)
{
FILE *fp;
if (key->fn) {
if (!strncmp(key->fn, "pkcs11:", 7)) {
ERROR("PKCS11 URI provided instead of a file");
return 0;
}
fp = fopen(key->fn, "w");
if (fp) {
PEM_write_PrivateKey(fp, key->key,
NULL, NULL, 0, NULL, NULL);
fclose(fp);
return 1;
} else {
ERROR("Cannot create file %s\n", key->fn);
}
} else {
ERROR("Key filename not specified\n");
}
return 0;
}
int key_init(void)
{
cmd_opt_t cmd_opt;
key_t *key;
unsigned int i;
keys = malloc((num_def_keys * sizeof(def_keys[0]))
#ifdef PDEF_KEYS
+ (num_pdef_keys * sizeof(pdef_keys[0]))
#endif
);
if (keys == NULL) {
ERROR("%s:%d Failed to allocate memory.\n", __func__, __LINE__);
return 1;
}
memcpy(&keys[0], &def_keys[0], (num_def_keys * sizeof(def_keys[0])));
#ifdef PDEF_KEYS
memcpy(&keys[num_def_keys], &pdef_keys[0],
(num_pdef_keys * sizeof(pdef_keys[0])));
num_keys = num_def_keys + num_pdef_keys;
#else
num_keys = num_def_keys;
#endif
;
for (i = 0; i < num_keys; i++) {
key = &keys[i];
if (key->opt != NULL) {
cmd_opt.long_opt.name = key->opt;
cmd_opt.long_opt.has_arg = required_argument;
cmd_opt.long_opt.flag = NULL;
cmd_opt.long_opt.val = CMD_OPT_KEY;
cmd_opt.help_msg = key->help_msg;
cmd_opt_add(&cmd_opt);
}
}
return 0;
}
key_t *key_get_by_opt(const char *opt)
{
key_t *key;
unsigned int i;
/* Sequential search. This is not a performance concern since the number
* of keys is bounded and the code runs on a host machine */
for (i = 0; i < num_keys; i++) {
key = &keys[i];
if (0 == strcmp(key->opt, opt)) {
return key;
}
}
return NULL;
}
void key_cleanup(void)
{
unsigned int i;
for (i = 0; i < num_keys; i++) {
EVP_PKEY_free(keys[i].key);
if (keys[i].fn != NULL) {
void *ptr = keys[i].fn;
free(ptr);
keys[i].fn = NULL;
}
}
free(keys);
}