+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013, Google Inc.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include "mkimage.h"
#include <string.h>
#include <image.h>
#include <time.h>
+#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
+#include <openssl/engine.h>
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#define HAVE_ERR_REMOVE_THREAD_STATE
#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static void RSA_get0_key(const RSA *r,
+ const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+{
+ if (n != NULL)
+ *n = r->n;
+ if (e != NULL)
+ *e = r->e;
+ if (d != NULL)
+ *d = r->d;
+}
+#endif
+
static int rsa_err(const char *msg)
{
unsigned long sslErr = ERR_get_error();
}
/**
- * 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;
rsa = EVP_PKEY_get1_RSA(key);
if (!rsa) {
rsa_err("Couldn't convert to a RSA style key");
+ ret = -EINVAL;
goto err_rsa;
}
fclose(f);
}
/**
- * 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;
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;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
ret = SSL_library_init();
+#else
+ ret = OPENSSL_init_ssl(0, NULL);
+#endif
if (!ret) {
fprintf(stderr, "Failure to init SSL library\n");
return -1;
}
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_digests();
OpenSSL_add_all_ciphers();
+#endif
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:
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ ENGINE_cleanup();
+#endif
+ return ret;
+}
+
static void rsa_remove(void)
{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
#ifdef HAVE_ERR_REMOVE_THREAD_STATE
ERR_remove_state(0);
#endif
EVP_cleanup();
+#endif
+}
+
+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,
ret = rsa_err("Could not obtain signature");
goto err_sign;
}
- EVP_MD_CTX_cleanup(context);
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
+ EVP_MD_CTX_cleanup(context);
+ #else
+ EVP_MD_CTX_reset(context);
+ #endif
EVP_MD_CTX_destroy(context);
EVP_PKEY_free(key);
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->algo->checksum, region,
+ ret = rsa_sign_with_key(rsa, info->checksum, region,
region_count, sigp, sig_len);
if (ret)
goto err_sign;
RSA_free(rsa);
+ if (info->engine_id)
+ rsa_engine_remove(e);
rsa_remove();
return ret;
err_sign:
RSA_free(rsa);
err_priv:
+ if (info->engine_id)
+ rsa_engine_remove(e);
+err_engine:
rsa_remove();
return ret;
}
+/*
+ * rsa_get_exponent(): - Get the public exponent from an RSA key
+ */
+static int rsa_get_exponent(RSA *key, uint64_t *e)
+{
+ int ret;
+ BIGNUM *bn_te;
+ const BIGNUM *key_e;
+ uint64_t te;
+
+ ret = -EINVAL;
+ bn_te = NULL;
+
+ if (!e)
+ goto cleanup;
+
+ RSA_get0_key(key, NULL, &key_e, NULL);
+ if (BN_num_bits(key_e) > 64)
+ goto cleanup;
+
+ *e = BN_get_word(key_e);
+
+ if (BN_num_bits(key_e) < 33) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ bn_te = BN_dup(key_e);
+ if (!bn_te)
+ goto cleanup;
+
+ if (!BN_rshift(bn_te, bn_te, 32))
+ goto cleanup;
+
+ if (!BN_mask_bits(bn_te, 32))
+ goto cleanup;
+
+ te = BN_get_word(bn_te);
+ te <<= 32;
+ *e |= te;
+ ret = 0;
+
+cleanup:
+ if (bn_te)
+ BN_free(bn_te);
+
+ return ret;
+}
+
/*
* rsa_get_params(): - Get the important parameters of an RSA public key
*/
-int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp,
- BIGNUM **r_squaredp)
+int rsa_get_params(RSA *key, uint64_t *exponent, uint32_t *n0_invp,
+ BIGNUM **modulusp, BIGNUM **r_squaredp)
{
BIGNUM *big1, *big2, *big32, *big2_32;
BIGNUM *n, *r, *r_squared, *tmp;
+ const BIGNUM *key_n;
BN_CTX *bn_ctx = BN_CTX_new();
int ret = 0;
return -ENOMEM;
}
- if (!BN_copy(n, key->n) || !BN_set_word(big1, 1L) ||
+ if (0 != rsa_get_exponent(key, exponent))
+ ret = -1;
+
+ RSA_get0_key(key, &key_n, NULL, NULL);
+ if (!BN_copy(n, key_n) || !BN_set_word(big1, 1L) ||
!BN_set_word(big2, 2L) || !BN_set_word(big32, 32L))
ret = -1;
BN_rshift(num, num, 32); /* N = N/B */
}
+ /*
+ * We try signing with successively increasing size values, so this
+ * might fail several times
+ */
ret = fdt_setprop(blob, noffset, prop_name, buf, size);
- if (ret) {
- fprintf(stderr, "Failed to write public key to FIT\n");
- return -ENOSPC;
- }
+ if (ret)
+ return -FDT_ERR_NOSPACE;
free(buf);
BN_free(tmp);
BN_free(big2);
int rsa_add_verify_data(struct image_sign_info *info, void *keydest)
{
BIGNUM *modulus, *r_squared;
+ uint64_t exponent;
uint32_t n0_inv;
int parent, node;
char name[100];
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;
- ret = rsa_get_params(rsa, &n0_inv, &modulus, &r_squared);
+ 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) {
parent = fdt_add_subnode(keydest, 0, FIT_SIG_NODENAME);
if (parent < 0) {
- fprintf(stderr, "Couldn't create signature node: %s\n",
- fdt_strerror(parent));
- return -EINVAL;
+ ret = parent;
+ if (ret != -FDT_ERR_NOSPACE) {
+ fprintf(stderr, "Couldn't create signature node: %s\n",
+ fdt_strerror(parent));
+ }
}
}
+ if (ret)
+ goto done;
/* Either create or overwrite the named key node */
snprintf(name, sizeof(name), "key-%s", info->keyname);
if (node == -FDT_ERR_NOTFOUND) {
node = fdt_add_subnode(keydest, parent, name);
if (node < 0) {
- fprintf(stderr, "Could not create key subnode: %s\n",
- fdt_strerror(node));
- return -EINVAL;
+ ret = node;
+ if (ret != -FDT_ERR_NOSPACE) {
+ fprintf(stderr, "Could not create key subnode: %s\n",
+ fdt_strerror(node));
+ }
}
} else if (node < 0) {
fprintf(stderr, "Cannot select keys parent: %s\n",
fdt_strerror(node));
- return -ENOSPC;
+ ret = node;
}
- ret = fdt_setprop_string(keydest, node, "key-name-hint",
+ if (!ret) {
+ ret = fdt_setprop_string(keydest, node, "key-name-hint",
info->keyname);
- ret |= fdt_setprop_u32(keydest, node, "rsa,num-bits", bits);
- ret |= fdt_setprop_u32(keydest, node, "rsa,n0-inverse", n0_inv);
- ret |= fdt_add_bignum(keydest, node, "rsa,modulus", modulus, bits);
- ret |= fdt_add_bignum(keydest, node, "rsa,r-squared", r_squared, bits);
- ret |= fdt_setprop_string(keydest, node, FIT_ALGO_PROP,
- info->algo->name);
- if (info->require_keys) {
- fdt_setprop_string(keydest, node, "required",
- info->require_keys);
}
+ if (!ret)
+ ret = fdt_setprop_u32(keydest, node, "rsa,num-bits", bits);
+ if (!ret)
+ ret = fdt_setprop_u32(keydest, node, "rsa,n0-inverse", n0_inv);
+ if (!ret) {
+ ret = fdt_setprop_u64(keydest, node, "rsa,exponent", exponent);
+ }
+ if (!ret) {
+ ret = fdt_add_bignum(keydest, node, "rsa,modulus", modulus,
+ bits);
+ }
+ if (!ret) {
+ ret = fdt_add_bignum(keydest, node, "rsa,r-squared", r_squared,
+ bits);
+ }
+ if (!ret) {
+ ret = fdt_setprop_string(keydest, node, FIT_ALGO_PROP,
+ info->name);
+ }
+ if (!ret && info->require_keys) {
+ ret = fdt_setprop_string(keydest, node, "required",
+ info->require_keys);
+ }
+done:
BN_free(modulus);
BN_free(r_squared);
if (ret)
- return -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;
}