blob: 0659fe0472566eed9d1acce896898c696464441a [file] [log] [blame]
From 1e0921b292481b097c612ec581d333ca4c0612dd Mon Sep 17 00:00:00 2001
From: KH Hung <kh.hung@mediatek.com>
Date: Tue, 19 Feb 2019 13:35:15 +0800
Subject: [PATCH] 201807-secure-boot-ECC-support
---
Kconfig | 1 +
common/image-sig.c | 23 ++-
include/image.h | 1 +
include/u-boot/ecc.h | 63 +++++++
lib/Kconfig | 2 +
lib/Makefile | 3 +
lib/ecc/Kconfig | 8 +
lib/ecc/Makefile | 3 +
lib/ecc/ecc-sig.c | 413 +++++++++++++++++++++++++++++++++++++++++++
lib/ecc/ecc-verify.c | 26 +++
tools/Makefile | 7 +-
11 files changed, 548 insertions(+), 2 deletions(-)
create mode 100644 include/u-boot/ecc.h
create mode 100644 lib/ecc/Kconfig
create mode 100644 lib/ecc/Makefile
create mode 100644 lib/ecc/ecc-sig.c
create mode 100644 lib/ecc/ecc-verify.c
diff --git a/Kconfig b/Kconfig
index 513f23cb4c..a9b15d2cfc 100644
--- a/Kconfig
+++ b/Kconfig
@@ -271,6 +271,7 @@ config FIT_SIGNATURE
depends on DM
select RSA
select HASH
+ select ECC
help
This option enables signature verification of FIT uImages,
using a hash signed and verified using RSA. If
diff --git a/common/image-sig.c b/common/image-sig.c
index acbd1297e4..2ba1eac9fc 100644
--- a/common/image-sig.c
+++ b/common/image-sig.c
@@ -14,6 +14,7 @@ DECLARE_GLOBAL_DATA_PTR;
#include <image.h>
#include <u-boot/rsa.h>
#include <u-boot/rsa-checksum.h>
+#include <u-boot/ecc.h>
#define IMAGE_MAX_HASHED_NODES 100
@@ -115,8 +116,28 @@ struct crypto_algo crypto_algos[] = {
.sign = rsassa_pss_sign,
.add_verify_data = rsa_add_verify_data,
.verify = rsa_verify,
+ },
+ {
+ .name = "nistp256",
+ .key_len = ECC256_BYTES,
+ .sign = ecc_sign,
+ .add_verify_data = ecc_add_verify_data,
+ .verify = ecc_verify,
+ },
+ {
+ .name = "nistp384",
+ .key_len = ECC384_BYTES,
+ .sign = ecc_sign,
+ .add_verify_data = ecc_add_verify_data,
+ .verify = ecc_verify,
+ },
+ {
+ .name = "nistp521",
+ .key_len = ECC521_BYTES,
+ .sign = ecc_sign,
+ .add_verify_data = ecc_add_verify_data,
+ .verify = ecc_verify,
}
-
};
struct checksum_algo *image_get_checksum_algo(const char *full_name)
diff --git a/include/image.h b/include/image.h
index 475af42a7a..3bde55763b 100644
--- a/include/image.h
+++ b/include/image.h
@@ -37,6 +37,7 @@ struct fdt_region;
#define CONFIG_SHA256
#define CONFIG_SHA384
#define CONFIG_SHA512
+#define CONFIG_ECC
#define IMAGE_ENABLE_IGNORE 0
#define IMAGE_INDENT_STRING ""
diff --git a/include/u-boot/ecc.h b/include/u-boot/ecc.h
new file mode 100644
index 0000000000..e3b564effd
--- /dev/null
+++ b/include/u-boot/ecc.h
@@ -0,0 +1,63 @@
+
+#ifndef _ECC_H
+#define _ECC_H
+
+#include <errno.h>
+#include <image.h>
+
+#define ECC256_BYTES (32)
+#define ECC384_BYTES (48)
+#define ECC521_BYTES (66)
+
+/**
+ * sign() - calculate and return signature for given input data
+ *
+ * @info: Specifies key and FIT information
+ * @data: Pointer to the input data
+ * @data_len: Data length
+ * @sigp: Set to an allocated buffer holding the signature
+ * @sig_len: Set to length of the calculated hash
+ *
+ * This computes input data signature according to selected algorithm.
+ * Resulting signature value is placed in an allocated buffer, the
+ * pointer is returned as *sigp. The length of the calculated
+ * signature is returned via the sig_len pointer argument. The caller
+ * should free *sigp.
+ *
+ * @return: 0, on success, -ve on error
+ */
+int ecc_sign(struct image_sign_info *info,
+ const struct image_region region[],
+ int region_count, uint8_t **sigp, uint *sig_len);
+
+/**
+ * add_verify_data() - Add verification information to FDT
+ *
+ * Add public key information to the FDT node, suitable for
+ * verification at run-time. The information added depends on the
+ * algorithm being used.
+ *
+ * @info: Specifies key and FIT information
+ * @keydest: Destination FDT blob for public key data
+ * @return: 0, on success, -ENOSPC if the keydest FDT blob ran out of space,
+ other -ve value on error
+*/
+int ecc_add_verify_data(struct image_sign_info *info, void *keydest);
+
+/**
+ * rsa_verify() - Verify a signature against some data
+ *
+ * Verify a RSA PKCS1.5 signature against an expected hash.
+ *
+ * @info: Specifies key and FIT information
+ * @data: Pointer to the input data
+ * @data_len: Data length
+ * @sig: Signature
+ * @sig_len: Number of bytes in signature
+ * @return 0 if verified, -ve on error
+ */
+int ecc_verify(struct image_sign_info *info,
+ const struct image_region region[], int region_count,
+ uint8_t *sig, uint sig_len);
+
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index c3bed74860..c2c8160651 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -174,6 +174,8 @@ config AES
source lib/rsa/Kconfig
+source lib/ecc/Kconfig
+
config TPM
bool "Trusted Platform Module (TPM) Support"
depends on DM
diff --git a/lib/Makefile b/lib/Makefile
index b3560729a3..3d9edde6bd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -50,10 +50,13 @@ endif
obj-$(CONFIG_RSA) += rsa/
obj-$(CONFIG_SHA1) += sha1.o
+obj-$(CONFIG_SHA1) += sha4.o
obj-$(CONFIG_SHA256) += sha256.o
obj-$(CONFIG_SHA384) += sha384.o
obj-$(CONFIG_SHA512) += sha512.o
+obj-$(CONFIG_ECC) += ecc/
+
obj-$(CONFIG_$(SPL_)ZLIB) += zlib/
obj-$(CONFIG_$(SPL_)GZIP) += gunzip.o
obj-$(CONFIG_$(SPL_)LZO) += lzo/
diff --git a/lib/ecc/Kconfig b/lib/ecc/Kconfig
new file mode 100644
index 0000000000..bd6f99aa19
--- /dev/null
+++ b/lib/ecc/Kconfig
@@ -0,0 +1,8 @@
+config ECC
+ bool "Use ECC Library"
+ help
+ ECC support. This enables the ECC algorithm used for FIT image
+ verification in U-Boot.
+ The signing part is build into mkimage regardless of this
+ option. The software based modular exponentiation is built into
+ mkimage irrespective of this option.
diff --git a/lib/ecc/Makefile b/lib/ecc/Makefile
new file mode 100644
index 0000000000..b63aaff164
--- /dev/null
+++ b/lib/ecc/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_ECC) += ecc-sig.o
+obj-$(CONFIG_ECC) += ecc-verify.o
diff --git a/lib/ecc/ecc-sig.c b/lib/ecc/ecc-sig.c
new file mode 100644
index 0000000000..582c69e17a
--- /dev/null
+++ b/lib/ecc/ecc-sig.c
@@ -0,0 +1,413 @@
+
+#include "mkimage.h"
+#include <stdio.h>
+#include <string.h>
+#include <image.h>
+#include <time.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+
+static int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
+ BIGNUM *num, int num_bits)
+{
+ int nwords = num_bits / 32;
+ int size;
+ uint32_t *buf, *ptr;
+ BIGNUM *tmp, *big2, *big32, *big2_32;
+ BN_CTX *ctx;
+ int ret;
+
+ 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 */
+
+ size = nwords * sizeof(uint32_t);
+ buf = malloc(size);
+ if (!buf) {
+ fprintf(stderr, "Out of memory (%d bytes)\n", size);
+ return -ENOMEM;
+ }
+
+ /* Write out modulus as big endian array of integers */
+ for (ptr = buf + nwords - 1; ptr >= buf; ptr--) {
+ BN_mod(tmp, num, big2_32, ctx); /* n = N mod B */
+ *ptr = cpu_to_fdt32(BN_get_word(tmp));
+ 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);
+
+ free(buf);
+ BN_free(tmp);
+ BN_free(big2);
+ BN_free(big32);
+ BN_free(big2_32);
+
+ return ret ? -FDT_ERR_NOSPACE : 0;
+}
+
+static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey)
+{
+ EC_KEY *dtmp;
+ if (!key)
+ return NULL;
+ dtmp = EVP_PKEY_get1_EC_KEY(key);
+ EVP_PKEY_free(key);
+ if (!dtmp)
+ return NULL;
+ if (eckey) {
+ EC_KEY_free(*eckey);
+ *eckey = dtmp;
+ }
+ return dtmp;
+}
+
+/**
+ * ec_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 EC object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *ecp will be set to NULL)
+ */
+static int ec_pem_get_priv_key(const char *keydir, const char *name,
+ EC_KEY **ecp)
+{
+ char path[1024];
+ EC_KEY *ec;
+ FILE *f;
+ EVP_PKEY *pktmp;
+
+ *ecp = NULL;
+ snprintf(path, sizeof(path), "%s/%s_priv.key", keydir, name);
+ f = fopen(path, "r");
+ if (!f) {
+ fprintf(stderr, "Couldn't open EC private key: '%s': %s\n",
+ path, strerror(errno));
+ return -ENOENT;
+ }
+
+ pktmp = PEM_read_PrivateKey(f, 0, NULL, path);
+ ec = pkey_get_eckey(pktmp, ecp);
+ if (!ec) {
+ fprintf(stderr, "Failure reading EC private key\n");
+ fclose(f);
+ return -EPROTO;
+ }
+ fclose(f);
+ *ecp = ec;
+
+ return 0;
+}
+
+/**
+ * ec_pem_get_pub_key() - read a public key from a .key file
+ *
+ * @keydir: Directory containing the key
+ * @name Name of key file (will have a .key extension)
+ * @pub_key Returns BIGNUM object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *pub_key will be set to NULL)
+ */
+static int ec_pem_get_pub_key(const char *keydir, const char *name, BIGNUM **pub_key)
+{
+ char path[1024];
+ EC_KEY *ec;
+ FILE *f;
+ unsigned char *pub_k_cp = NULL;
+ int ret;
+
+ snprintf(path, sizeof(path), "%s/%s_public.key", keydir, name);
+ f = fopen(path, "r");
+ if (!f) {
+ fprintf(stderr, "Couldn't open EC public key: '%s': %s\n",
+ path, strerror(errno));
+ return -ENOENT;
+ }
+
+ ec = PEM_read_EC_PUBKEY(f, 0, NULL, path);
+
+ if (!ec) {
+ fprintf(stderr, "Failure reading EC public key\n");
+ fclose(f);
+ return -EPROTO;
+ }
+ fclose(f);
+
+ /* Transform from EC_POINT to char */
+ ret = i2o_ECPublicKey(ec, &pub_k_cp);
+ if (ret == 0) {
+ return -1;
+ } else {
+ /* Transform from char to BIGNUM */
+ *pub_key = BN_bin2bn(pub_k_cp, ret , NULL);
+ }
+
+ return 0;
+}
+
+int ecc_add_verify_data(struct image_sign_info *info, void *keydest)
+{
+ BIGNUM *pub_key;
+ int parent, node;
+ char name[100];
+ int ret=0;
+ int bits;
+
+ // get public key
+ ret = ec_pem_get_pub_key(info->keydir, info->keyname, &pub_key);
+
+ if (ret)
+ goto err_get_pub_key;
+
+ 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) {
+ 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);
+ node = fdt_subnode_offset(keydest, parent, name);
+ if (node == -FDT_ERR_NOTFOUND) {
+ node = fdt_add_subnode(keydest, parent, name);
+ if (node < 0) {
+ 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));
+ ret = node;
+ }
+
+ if (!ret) {
+ ret = fdt_setprop_string(keydest, node, "key-name-hint", info->keyname);
+ }
+
+ if (!ret) {
+ bits = info->crypto->key_len * 16;
+ ret = fdt_add_bignum(keydest, node, "ecc,public-key", pub_key, 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(pub_key);
+ if (ret) {
+ ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
+ }
+
+err_get_pub_key:
+
+
+ return ret;
+}
+
+static int ec_sign_with_key(EC_KEY *ec, struct checksum_algo *checksum_algo,
+ const struct image_region region[], int region_count,
+ uint8_t **sigp, uint *sig_size)
+{
+ EVP_PKEY *key;
+ EVP_PKEY_CTX *keyctx;
+ EVP_MD_CTX *context;
+ int size, ret = 0;
+ uint8_t *sig;
+ int i;
+
+ key = EVP_PKEY_new();
+ if (!key) {
+ fprintf(stderr, "EVP_PKEY object creation failed \n");
+ return -1;
+ }
+
+ if (!EVP_PKEY_set1_EC_KEY(key, ec)) {
+ fprintf(stderr, "EVP key setup failed \n");
+ ret = -1;
+ goto err_set;
+ }
+
+ keyctx = EVP_PKEY_CTX_new(key, NULL);
+ if (!keyctx) {
+ fprintf(stderr, "EVP_PKEY_CTX object creation failed \n");
+ ret = -1;
+ goto err_set;
+ }
+
+ size = EVP_PKEY_size(key);
+ sig = malloc(size);
+ if (!sig) {
+ fprintf(stderr, "Out of memory for signature (%d bytes)\n", size);
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ context = EVP_MD_CTX_create();
+ if (!context) {
+ fprintf(stderr, "EVP context creation failed \n");
+ ret = -1;
+ goto err_create;
+ }
+ EVP_MD_CTX_init(context);
+ if (!EVP_DigestSignInit(context, &keyctx, checksum_algo->calculate_sign(), NULL, key)) {
+ fprintf(stderr, "Signer setup failed \n");
+ ret = -1;
+ goto err_sign;
+ }
+
+ for (i = 0; i < region_count; i++) {
+
+ if (!EVP_DigestSignUpdate(context, region[i].data, region[i].size)) {
+ fprintf(stderr, "Signing data failed \n");
+ ret = -1;
+ goto err_sign;
+ }
+ }
+
+ if (!EVP_DigestSignFinal(context, sig, (size_t *)sig_size)) {
+ fprintf(stderr, "Could not obtain signature \n");
+ ret = -1;
+ goto err_sign;
+ }
+
+#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);
+
+ fprintf(stderr, "Got signature: %d bytes, expected %d\n", *sig_size, size);
+ *sigp = sig;
+ *sig_size = size;
+
+ return 0;
+
+err_sign:
+ EVP_MD_CTX_destroy(context);
+err_create:
+ free(sig);
+err_alloc:
+err_set:
+ EVP_PKEY_free(key);
+ return ret;
+}
+
+int ecc_sign(struct image_sign_info *info,
+ const struct image_region region[], int region_count,
+ uint8_t **sigp, uint *sig_len)
+{
+ EC_KEY *ec;
+ int ret = 0;
+
+ /* To read private key from pem */
+ ret = ec_pem_get_priv_key(info->keydir, info->keyname, &ec);
+
+ if (ret) {
+ fprintf(stderr, "Failure reading EC private key\n");
+ goto err_priv;
+ }
+
+ /* To signature */
+ ret = ec_sign_with_key(ec, info->checksum, region,
+ region_count, sigp, sig_len);
+ if (ret) {
+ goto err_sign;
+ }
+
+
+err_sign:
+
+err_priv:
+
+ return ret;
+}
+
+/**
+ * ec_pem_get_pub_key_by_path() - read a public key from a .key file
+ *
+ * @path Path of key file (will have a .key extension)
+ * @pub_key Returns BIGNUM object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *pub_key will be set to NULL)
+ */
+static int ec_pem_get_pub_key_by_path(const char *keydir, const char *name, BIGNUM **pub_key)
+{
+ char path[1024];
+ EC_KEY *ec;
+ FILE *f;
+ unsigned char *pub_k_cp = NULL;
+ int ret;
+
+ snprintf(path, sizeof(path), "%s/tier_public.key", keydir);
+ f = fopen(path, "r");
+ if (!f) {
+ fprintf(stderr, "Couldn't open EC public key: '%s': %s\n",
+ path, strerror(errno));
+ return -ENOENT;
+ }
+
+ ec = PEM_read_EC_PUBKEY(f, 0, NULL, path);
+
+ if (!ec) {
+ fprintf(stderr, "Failure reading EC public key\n");
+ fclose(f);
+ return -EPROTO;
+ }
+ fclose(f);
+
+ /* Transform from EC_POINT to char */
+ ret = i2o_ECPublicKey(ec, &pub_k_cp);
+ if (ret == 0) {
+ return -1;
+ } else {
+ /* Transform from char to BIGNUM */
+ *pub_key = BN_bin2bn(pub_k_cp, ret , NULL);
+ }
+
+ return 0;
+}
diff --git a/lib/ecc/ecc-verify.c b/lib/ecc/ecc-verify.c
new file mode 100644
index 0000000000..49251a3f11
--- /dev/null
+++ b/lib/ecc/ecc-verify.c
@@ -0,0 +1,26 @@
+
+#ifndef USE_HOSTCC
+#include <common.h>
+#include <fdtdec.h>
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+#include <asm/unaligned.h>
+#include <dm.h>
+#else
+#include "fdt_host.h"
+//#include "mkimage.h"
+#include <fdt_support.h>
+#endif
+
+#include <u-boot/ecc.h>
+
+
+int ecc_verify(struct image_sign_info *info,
+ const struct image_region region[], int region_count,
+ uint8_t *sig, uint sig_len)
+{
+
+ return 0;
+}
diff --git a/tools/Makefile b/tools/Makefile
index ae6451178b..4ed3489a79 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -75,6 +75,9 @@ RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \
rsa-sign.o rsa-verify.o rsa-checksum.o \
rsa-mod-exp.o)
+ECC_OBJS-$(CONFIG_ECC) := $(addprefix lib/ecc/, \
+ ecc-sig.o ecc-verify.o)
+
ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
# common objs for dumpimage and mkimage
@@ -116,7 +119,8 @@ dumpimage-mkimage-objs := aisimage.o \
$(LIBFDT_OBJS) \
gpimage.o \
gpimage-common.o \
- $(RSA_OBJS-y)
+ $(RSA_OBJS-y) \
+ $(ECC_OBJS-y)
dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o
mkimage-objs := $(dumpimage-mkimage-objs) mkimage.o
@@ -155,6 +159,7 @@ ifeq ($(HOSTOS),darwin)
HOSTCFLAGS_mxsimage.o += -Wno-deprecated-declarations
HOSTCFLAGS_image-sig.o += -Wno-deprecated-declarations
HOSTCFLAGS_rsa-sign.o += -Wno-deprecated-declarations
+HOSTCFLAGS_ecc-sig.o += -Wno-deprecated-declarations
endif
endif
--
2.18.0