From f1ca1fdebf1cde1c37c91b3d85f8b7af111112ea Mon Sep 17 00:00:00 2001 From: George McCollister Date: Fri, 6 Jan 2017 13:14:17 -0600 Subject: mkimage: Add support for signing with pkcs11 Add support for signing with the pkcs11 engine. This allows FIT images to be signed with keys securely stored on a smartcard, hardware security module, etc without exposing the keys. Support for other engines can be added in the future by modifying rsa_engine_get_pub_key() and rsa_engine_get_priv_key() to construct correct key_id strings. Signed-off-by: George McCollister --- lib/rsa/rsa-sign.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 233 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c index 9a09280..8c6637e 100644 --- a/lib/rsa/rsa-sign.c +++ b/lib/rsa/rsa-sign.c @@ -14,6 +14,7 @@ #include #include #include +#include #if OPENSSL_VERSION_NUMBER >= 0x10000000L #define HAVE_ERR_REMOVE_THREAD_STATE @@ -31,14 +32,14 @@ static int rsa_err(const char *msg) } /** - * rsa_get_pub_key() - read a public key from a .crt file + * rsa_pem_get_pub_key() - read a public key from a .crt file * * @keydir: Directory containins the key * @name Name of key file (will have a .crt extension) * @rsap Returns RSA object, or NULL on failure * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) */ -static int rsa_get_pub_key(const char *keydir, const char *name, RSA **rsap) +static int rsa_pem_get_pub_key(const char *keydir, const char *name, RSA **rsap) { char path[1024]; EVP_PKEY *key; @@ -96,14 +97,90 @@ err_cert: } /** - * rsa_get_priv_key() - read a private key from a .key file + * rsa_engine_get_pub_key() - read a public key from given engine * - * @keydir: Directory containins the key + * @keydir: Key prefix + * @name Name of key + * @engine Engine to use + * @rsap Returns RSA object, or NULL on failure + * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) + */ +static int rsa_engine_get_pub_key(const char *keydir, const char *name, + ENGINE *engine, RSA **rsap) +{ + const char *engine_id; + char key_id[1024]; + EVP_PKEY *key; + RSA *rsa; + int ret; + + *rsap = NULL; + + engine_id = ENGINE_get_id(engine); + + if (engine_id && !strcmp(engine_id, "pkcs11")) { + if (keydir) + snprintf(key_id, sizeof(key_id), + "pkcs11:%s;object=%s;type=public", + keydir, name); + else + snprintf(key_id, sizeof(key_id), + "pkcs11:object=%s;type=public", + name); + } else { + fprintf(stderr, "Engine not supported\n"); + return -ENOTSUP; + } + + key = ENGINE_load_public_key(engine, key_id, NULL, NULL); + if (!key) + return rsa_err("Failure loading public key from engine"); + + /* Convert to a RSA_style key. */ + rsa = EVP_PKEY_get1_RSA(key); + if (!rsa) { + rsa_err("Couldn't convert to a RSA style key"); + ret = -EINVAL; + goto err_rsa; + } + + EVP_PKEY_free(key); + *rsap = rsa; + + return 0; + +err_rsa: + EVP_PKEY_free(key); + return ret; +} + +/** + * rsa_get_pub_key() - read a public key + * + * @keydir: Directory containing the key (PEM file) or key prefix (engine) + * @name Name of key file (will have a .crt extension) + * @engine Engine to use + * @rsap Returns RSA object, or NULL on failure + * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) + */ +static int rsa_get_pub_key(const char *keydir, const char *name, + ENGINE *engine, RSA **rsap) +{ + if (engine) + return rsa_engine_get_pub_key(keydir, name, engine, rsap); + return rsa_pem_get_pub_key(keydir, name, rsap); +} + +/** + * rsa_pem_get_priv_key() - read a private key from a .key file + * + * @keydir: Directory containing the key * @name Name of key file (will have a .key extension) * @rsap Returns RSA object, or NULL on failure * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) */ -static int rsa_get_priv_key(const char *keydir, const char *name, RSA **rsap) +static int rsa_pem_get_priv_key(const char *keydir, const char *name, + RSA **rsap) { char path[1024]; RSA *rsa; @@ -130,6 +207,81 @@ static int rsa_get_priv_key(const char *keydir, const char *name, RSA **rsap) return 0; } +/** + * rsa_engine_get_priv_key() - read a private key from given engine + * + * @keydir: Key prefix + * @name Name of key + * @engine Engine to use + * @rsap Returns RSA object, or NULL on failure + * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) + */ +static int rsa_engine_get_priv_key(const char *keydir, const char *name, + ENGINE *engine, RSA **rsap) +{ + const char *engine_id; + char key_id[1024]; + EVP_PKEY *key; + RSA *rsa; + int ret; + + *rsap = NULL; + + engine_id = ENGINE_get_id(engine); + + if (engine_id && !strcmp(engine_id, "pkcs11")) { + if (keydir) + snprintf(key_id, sizeof(key_id), + "pkcs11:%s;object=%s;type=private", + keydir, name); + else + snprintf(key_id, sizeof(key_id), + "pkcs11:object=%s;type=private", + name); + } else { + fprintf(stderr, "Engine not supported\n"); + return -ENOTSUP; + } + + key = ENGINE_load_private_key(engine, key_id, NULL, NULL); + if (!key) + return rsa_err("Failure loading private key from engine"); + + /* Convert to a RSA_style key. */ + rsa = EVP_PKEY_get1_RSA(key); + if (!rsa) { + rsa_err("Couldn't convert to a RSA style key"); + ret = -EINVAL; + goto err_rsa; + } + + EVP_PKEY_free(key); + *rsap = rsa; + + return 0; + +err_rsa: + EVP_PKEY_free(key); + return ret; +} + +/** + * rsa_get_priv_key() - read a private key + * + * @keydir: Directory containing the key (PEM file) or key prefix (engine) + * @name Name of key + * @engine Engine to use for signing + * @rsap Returns RSA object, or NULL on failure + * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) + */ +static int rsa_get_priv_key(const char *keydir, const char *name, + ENGINE *engine, RSA **rsap) +{ + if (engine) + return rsa_engine_get_priv_key(keydir, name, engine, rsap); + return rsa_pem_get_priv_key(keydir, name, rsap); +} + static int rsa_init(void) { int ret; @@ -148,6 +300,45 @@ static int rsa_init(void) return 0; } +static int rsa_engine_init(const char *engine_id, ENGINE **pe) +{ + ENGINE *e; + int ret; + + ENGINE_load_builtin_engines(); + + e = ENGINE_by_id(engine_id); + if (!e) { + fprintf(stderr, "Engine isn't available\n"); + ret = -1; + goto err_engine_by_id; + } + + if (!ENGINE_init(e)) { + fprintf(stderr, "Couldn't initialize engine\n"); + ret = -1; + goto err_engine_init; + } + + if (!ENGINE_set_default_RSA(e)) { + fprintf(stderr, "Couldn't set engine as default for RSA\n"); + ret = -1; + goto err_set_rsa; + } + + *pe = e; + + return 0; + +err_set_rsa: + ENGINE_finish(e); +err_engine_init: + ENGINE_free(e); +err_engine_by_id: + ENGINE_cleanup(); + return ret; +} + static void rsa_remove(void) { CRYPTO_cleanup_all_ex_data(); @@ -160,6 +351,14 @@ static void rsa_remove(void) EVP_cleanup(); } +static void rsa_engine_remove(ENGINE *e) +{ + if (e) { + ENGINE_finish(e); + ENGINE_free(e); + } +} + static int rsa_sign_with_key(RSA *rsa, struct checksum_algo *checksum_algo, const struct image_region region[], int region_count, uint8_t **sigp, uint *sig_size) @@ -235,13 +434,20 @@ int rsa_sign(struct image_sign_info *info, uint8_t **sigp, uint *sig_len) { RSA *rsa; + ENGINE *e = NULL; int ret; ret = rsa_init(); if (ret) return ret; - ret = rsa_get_priv_key(info->keydir, info->keyname, &rsa); + if (info->engine_id) { + ret = rsa_engine_init(info->engine_id, &e); + if (ret) + goto err_engine; + } + + ret = rsa_get_priv_key(info->keydir, info->keyname, e, &rsa); if (ret) goto err_priv; ret = rsa_sign_with_key(rsa, info->checksum, region, @@ -250,6 +456,8 @@ int rsa_sign(struct image_sign_info *info, goto err_sign; RSA_free(rsa); + if (info->engine_id) + rsa_engine_remove(e); rsa_remove(); return ret; @@ -257,6 +465,9 @@ int rsa_sign(struct image_sign_info *info, err_sign: RSA_free(rsa); err_priv: + if (info->engine_id) + rsa_engine_remove(e); +err_engine: rsa_remove(); return ret; } @@ -446,14 +657,20 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest) int ret; int bits; RSA *rsa; + ENGINE *e = NULL; debug("%s: Getting verification data\n", __func__); - ret = rsa_get_pub_key(info->keydir, info->keyname, &rsa); + if (info->engine_id) { + ret = rsa_engine_init(info->engine_id, &e); + if (ret) + return ret; + } + ret = rsa_get_pub_key(info->keydir, info->keyname, e, &rsa); if (ret) - return ret; + goto err_get_pub_key; ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared); if (ret) - return ret; + goto err_get_params; bits = BN_num_bits(modulus); parent = fdt_subnode_offset(keydest, 0, FIT_SIG_NODENAME); if (parent == -FDT_ERR_NOTFOUND) { @@ -518,7 +735,12 @@ done: BN_free(modulus); BN_free(r_squared); if (ret) - return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO; + ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO; +err_get_params: + RSA_free(rsa); +err_get_pub_key: + if (info->engine_id) + rsa_engine_remove(e); - return 0; + return ret; } -- cgit v1.1