From 128ad3cbe043d5153007a50f09208c25703257dd Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 1 Oct 2019 23:28:22 +0200 Subject: scripts: Add rsatoc tool The rsatoc tool converts rsa public keys into C structs suitable to compile with barebox. Most of the openssl rsa related stuff has been taken from the U-Boot mkimage tool. Signed-off-by: Sascha Hauer --- scripts/.gitignore | 1 + scripts/Makefile | 3 + scripts/rsatoc.c | 486 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 490 insertions(+) create mode 100644 scripts/rsatoc.c (limited to 'scripts') diff --git a/scripts/.gitignore b/scripts/.gitignore index 45c81bf8f4..76ea271abb 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -29,3 +29,4 @@ mxs-usb-loader omap4_usbboot omap3-usb-loader mips-relocs +rsatoc diff --git a/scripts/Makefile b/scripts/Makefile index dffab53c73..81d1a501b0 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -10,6 +10,9 @@ hostprogs-y += fix_size hostprogs-y += bareboxenv hostprogs-y += bareboxcrc32 hostprogs-y += kernel-install +hostprogs-$(CONFIG_CRYPTO_RSA_BUILTIN_KEYS) += rsatoc +HOSTCFLAGS_rsatoc = `pkg-config --cflags openssl` +HOSTLDLIBS_rsatoc = `pkg-config --libs openssl` hostprogs-$(CONFIG_IMD) += bareboximd hostprogs-$(CONFIG_KALLSYMS) += kallsyms hostprogs-$(CONFIG_MIPS) += mips-relocs diff --git a/scripts/rsatoc.c b/scripts/rsatoc.c new file mode 100644 index 0000000000..6473802140 --- /dev/null +++ b/scripts/rsatoc.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * rsatoc - utility to convert an RSA key to a C struct + * + * This tool converts an RSA key given as file or PKCS#11 + * URI to a C struct suitable to compile with barebox. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int rsa_err(const char *msg) +{ + unsigned long sslErr = ERR_get_error(); + + fprintf(stderr, "%s", msg); + fprintf(stderr, ": %s\n", + ERR_error_string(sslErr, 0)); + + return -1; +} + +/** + * 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_pem_get_pub_key(const char *path, RSA **rsap) +{ + EVP_PKEY *key; + X509 *cert; + RSA *rsa; + FILE *f; + int ret; + + *rsap = NULL; + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Couldn't open RSA certificate: '%s': %s\n", + path, strerror(errno)); + return -EACCES; + } + + /* Read the certificate */ + cert = NULL; + if (!PEM_read_X509(f, &cert, NULL, NULL)) { + rsa_err("Couldn't read certificate"); + ret = -EINVAL; + goto err_cert; + } + + /* Get the public key from the certificate. */ + key = X509_get_pubkey(cert); + if (!key) { + rsa_err("Couldn't read public key\n"); + ret = -EINVAL; + goto err_pubkey; + } + + /* 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; + } + fclose(f); + EVP_PKEY_free(key); + X509_free(cert); + *rsap = rsa; + + return 0; + +err_rsa: + EVP_PKEY_free(key); +err_pubkey: + X509_free(cert); +err_cert: + fclose(f); + return ret; +} + +/** + * rsa_engine_get_pub_key() - read a public 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_pub_key(const char *key_id, + ENGINE *engine, RSA **rsap) +{ + EVP_PKEY *key; + RSA *rsa; + int ret; + + *rsap = NULL; + + 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_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, 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; + + /* Initialize BIGNUMs */ + big1 = BN_new(); + big2 = BN_new(); + big32 = BN_new(); + r = BN_new(); + r_squared = BN_new(); + tmp = BN_new(); + big2_32 = BN_new(); + n = BN_new(); + if (!big1 || !big2 || !big32 || !r || !r_squared || !tmp || !big2_32 || + !n) { + fprintf(stderr, "Out of memory (bignum)\n"); + return -ENOMEM; + } + + 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; + + /* big2_32 = 2^32 */ + if (!BN_exp(big2_32, big2, big32, bn_ctx)) + ret = -1; + + /* Calculate n0_inv = -1 / n[0] mod 2^32 */ + if (!BN_mod_inverse(tmp, n, big2_32, bn_ctx) || + !BN_sub(tmp, big2_32, tmp)) + ret = -1; + *n0_invp = BN_get_word(tmp); + + /* Calculate R = 2^(# of key bits) */ + if (!BN_set_word(tmp, BN_num_bits(n)) || + !BN_exp(r, big2, tmp, bn_ctx)) + ret = -1; + + /* Calculate r_squared = R^2 mod n */ + if (!BN_copy(r_squared, r) || + !BN_mul(tmp, r_squared, r, bn_ctx) || + !BN_mod(r_squared, tmp, n, bn_ctx)) + ret = -1; + + *modulusp = n; + *r_squaredp = r_squared; + + BN_free(big1); + BN_free(big2); + BN_free(big32); + BN_free(r); + BN_free(tmp); + BN_free(big2_32); + if (ret) { + fprintf(stderr, "Bignum operations failed\n"); + return -ENOMEM; + } + + return ret; +} + +static int rsa_engine_init(ENGINE **pe) +{ + ENGINE *e; + int ret; + const char *key_pass = getenv("KBUILD_SIGN_PIN"); + + ENGINE_load_builtin_engines(); + + e = ENGINE_by_id("pkcs11"); + 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 (key_pass) { + if (!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0)) { + fprintf(stderr, "Cannot set PKCS#11 PIN\n"); + goto err_set_rsa; + } + } + + 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 || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL) + ENGINE_cleanup(); +#endif + return ret; +} + +static FILE *outfilep; + +static int print_bignum(BIGNUM *num, int num_bits) +{ + BIGNUM *tmp, *big2, *big32, *big2_32; + BN_CTX *ctx; + int i; + + tmp = BN_new(); + big2 = BN_new(); + big32 = BN_new(); + big2_32 = BN_new(); + + /* + * Note: This code assumes that all of the above succeed, or all fail. + * In practice memory allocations generally do not fail (unless the + * process is killed), so it does not seem worth handling each of these + * as a separate case. Technicaly this could leak memory on failure, + * but a) it won't happen in practice, and b) it doesn't matter as we + * will immediately exit with a failure code. + */ + if (!tmp || !big2 || !big32 || !big2_32) { + fprintf(stderr, "Out of memory (bignum)\n"); + return -ENOMEM; + } + ctx = BN_CTX_new(); + if (!tmp) { + fprintf(stderr, "Out of memory (bignum context)\n"); + return -ENOMEM; + } + BN_set_word(big2, 2L); + BN_set_word(big32, 32L); + BN_exp(big2_32, big2, big32, ctx); /* B = 2^32 */ + + for (i = 0; i < num_bits / 32; i++) { + BN_mod(tmp, num, big2_32, ctx); /* n = N mod B */ + if (i % 4) + fprintf(outfilep, " "); + else + fprintf(outfilep, "\n\t"); + fprintf(outfilep, "0x%08lx,", BN_get_word(tmp)); + BN_rshift(num, num, 32); /* N = N/B */ + } + + BN_free(tmp); + BN_free(big2); + BN_free(big32); + BN_free(big2_32); + + return 0; +} + +static int gen_key(const char *keyname, const char *path) +{ + BIGNUM *modulus, *r_squared; + uint64_t exponent; + uint32_t n0_inv; + int ret; + int bits; + RSA *rsa; + ENGINE *e = NULL; + char *tmp, *key_name_c; + + tmp = key_name_c = strdup(keyname); + + while (*tmp) { + if (*tmp == '-') + *tmp = '_'; + tmp++; + } + + if (!strncmp(path, "__ENV__", 7)) { + const char *var = getenv(path + 7); + if (!var) { + fprintf(stderr, + "environment variable \"%s\" is empty\n", path + 7); + exit(1); + } + path = var; + } + + if (!strncmp(path, "pkcs11:", 7)) { + ret = rsa_engine_init(&e); + if (ret) + exit(1); + ret = rsa_engine_get_pub_key(path, e, &rsa); + if (ret) + exit(1); + } else { + ret = rsa_pem_get_pub_key(path, &rsa); + if (ret) + exit(1); + } + + ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared); + if (ret) + return ret; + + bits = BN_num_bits(modulus); + + fprintf(outfilep, "\nstatic uint32_t %s_modulus[] = {", key_name_c); + print_bignum(modulus, bits); + fprintf(outfilep, "\n};\n\n"); + + fprintf(outfilep, "static uint32_t %s_rr[] = {", key_name_c); + print_bignum(r_squared, bits); + fprintf(outfilep, "\n};\n\n"); + + fprintf(outfilep, "static struct rsa_public_key %s = {\n", key_name_c); + fprintf(outfilep, "\t.len = %d,\n", bits / 32); + fprintf(outfilep, "\t.n0inv = 0x%0x,\n", n0_inv); + fprintf(outfilep, "\t.modulus = %s_modulus,\n", key_name_c); + fprintf(outfilep, "\t.rr = %s_rr,\n", key_name_c); + fprintf(outfilep, "\t.exponent = 0x%0lx,\n", exponent); + fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", keyname); + fprintf(outfilep, "};\n\n"); + + fprintf(outfilep, "struct rsa_public_key *%sp __attribute__((section(\".rsa_keys.rodata.%s\"))) = &%s;\n", + key_name_c, key_name_c, key_name_c); + + return 0; +} + +int main(int argc, char *argv[]) +{ + char *path, *keyname; + int i, opt; + char *outfile = NULL; + + outfilep = stdout; + + while ((opt = getopt(argc, argv, "o:")) > 0) { + switch (opt) { + case 'o': + outfile = optarg; + break; + } + } + + if (outfile) { + outfilep = fopen(outfile, "w"); + if (!outfilep) { + fprintf(stderr, "cannot open %s: %s\n", outfile, + strerror(errno)); + exit(1); + } + } + + if (optind == argc) { + fprintf(stderr, "Usage: %s : ...\n", argv[0]); + exit(1); + } + + for (i = optind; i < argc; i++) { + keyname = argv[i]; + + path = strchr(keyname, ':'); + if (!path) { + fprintf(stderr, + "keys must be given as :\n"); + exit(1); + } + + *path = 0; + path++; + + if (!strncmp(path, "__ENV__", 7)) { + path = getenv(path + 7); + if (!path) { + fprintf(stderr, "%s doesn't contain a path\n", + path + 7); + exit(1); + } + } + + gen_key(keyname, path); + } + + exit(0); +} -- cgit v1.2.3