X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=lib%2Frsa%2Frsa-sign.c;h=d2788bf79a4ef81ec5b78d097ae53ca3d08d99e4;hb=817669231c89a97e3934baf07d457ca9671947ea;hp=a75ae245ef121dc748da229cdea9260050b69969;hpb=58a5c43c09398d7cc68417117f1d882f1ca831cc;p=oweals%2Fu-boot.git diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c index a75ae245ef..d2788bf79a 100644 --- a/lib/rsa/rsa-sign.c +++ b/lib/rsa/rsa-sign.c @@ -1,38 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2013, Google Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA */ #include "mkimage.h" #include #include -#include #include #include +#include #include #include #include #include #include +#include #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(); @@ -45,14 +45,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; @@ -90,6 +90,7 @@ static int rsa_get_pub_key(const char *keydir, const char *name, RSA **rsap) 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); @@ -109,14 +110,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; @@ -143,26 +220,149 @@ 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; +#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 @@ -171,10 +371,20 @@ static void rsa_remove(void) 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, const struct image_region region[], - int region_count, uint8_t **sigp, uint *sig_size) +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) { EVP_PKEY *key; EVP_MD_CTX *context; @@ -206,7 +416,7 @@ static int rsa_sign_with_key(RSA *rsa, const struct image_region region[], goto err_create; } EVP_MD_CTX_init(context); - if (!EVP_SignInit(context, EVP_sha1())) { + if (!EVP_SignInit(context, checksum_algo->calculate_sign())) { ret = rsa_err("Signer setup failed"); goto err_sign; } @@ -222,7 +432,11 @@ static int rsa_sign_with_key(RSA *rsa, const struct image_region region[], 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); @@ -247,20 +461,30 @@ 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, region, region_count, sigp, sig_len); + 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; @@ -268,18 +492,71 @@ 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; } +/* + * 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; @@ -298,7 +575,11 @@ int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp, 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; @@ -381,11 +662,13 @@ static int fdt_add_bignum(void *blob, int noffset, const char *prop_name, 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); @@ -398,30 +681,41 @@ static int fdt_add_bignum(void *blob, int noffset, const char *prop_name, 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); @@ -429,32 +723,55 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest) 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; }