[U-Boot-Users] Secure Bootloader patch
Stephen Johnson
steve at research.panasonic.com
Wed Sep 13 21:49:43 CEST 2006
This message contains a patch to add RSA signature validation to
U-Boot. I previously sent a similar e-mail to the CELinux-dev mailing
list for comments where I received the following:
1. I shouldn't be using OpenSSL (due to licensing concerns),
2. alternate RSA libraries are GNU TLS and MatrixSSL, and
3. the U-Boot mailing list might be a more appropriate place
The patch modifies u-boot to verify an image signature created with a
SHA1 digest and RSA encryption/decryption. Because I found the
necessary information fairly easily about SHA1 and RSA from the
OpenSSL package, that's what I used. Hence, the modified u-boot ran
quite quickly, but was rather large. The eventual goal is to release
this patch to the community.
I'm looking for additional comments, especially about other encryption
libraries that would be better to use, and whether or not something
like this is seen as useful.
Notes:
- I'm linking against openssl-0.9.8b.
- I used crosstool based on gcc-3.4.5 and glibc-2.3.6 for the tool
chain.
- It all was built for an omap5912osk board.
- The signature is added to the u-boot header by a modified mkimage
(patch is included).
- The signature is verified in cmd_bootm.c.
- In u-boot/include/configs/omap5912osk.h there is a CONFIG_SIGNATURE
that turns on/off the signature checking and creating.
To compile the modified u-boot the following were needed:
CPATH should be defined to point to where ever crosstool has it's
generic include files, e.g.
export CPATH="/opt/crosstool/gcc-3.4.5-glibc-2.3.6/arm-softfloat-linux-gnu/arm-softfloat-linux-gnu/include"
CRYPTO_INC needs to point at the openssl include files, e.g.
export CRYPTO_INC="-I/home/steve/src/SecureBoot/openssl-0.9.8b/include"
CRYPTO_LIBS needs to point the the openssl libraries, e.g.
export CRYPTO_LIBS="-L /home/steve/src/SecureBoot/openssl-0.9.8b -lssl -lcrypto -lm -lc"
If anyone has any problems or even better, suggestions, don't hesitate
to let me know.
Best regards,
Steve
=========================================
diff -Naur u-boot.orig/common/cmd_bootm.c u-boot/common/cmd_bootm.c
--- u-boot.orig/common/cmd_bootm.c 2006-05-10 11:43:20.000000000 -0400
+++ u-boot/common/cmd_bootm.c 2006-06-12 10:35:57.000000000 -0400
@@ -79,6 +79,12 @@
# define CHUNKSZ (64 * 1024)
#endif
+#ifdef CONFIG_SIGNATURE
+extern int verify_signature (const unsigned char *signature,
+ const unsigned char *buf,
+ unsigned int len);
+#endif /* CONFIG_SIGNATURE */
+
int gunzip (void *, int, unsigned char *, unsigned long *);
static void *zalloc(void *, unsigned, unsigned);
@@ -238,6 +244,19 @@
}
puts ("OK\n");
}
+
+#ifdef CONFIG_SIGNATURE
+ puts (" Verifying Signature ... ");
+ if (verify_signature(hdr->ih_sign,
+ (const unsigned char *)data,
+ len) == 0) {
+ puts("Invalid image signature\n");
+ SHOW_BOOT_PROGRESS(-3);
+ return 1;
+ }
+ puts ("OK\n");
+#endif /* CONFIG_SIGNATURE */
+
SHOW_BOOT_PROGRESS (4);
len_ptr = (ulong *)data;
diff -Naur u-boot.orig/config.mk u-boot/config.mk
--- u-boot.orig/config.mk 2006-05-10 11:43:20.000000000 -0400
+++ u-boot/config.mk 2006-06-08 09:41:17.000000000 -0400
@@ -126,7 +126,7 @@
-D__KERNEL__ -DTEXT_BASE=$(TEXT_BASE) \
-I$(TOPDIR)/include \
-fno-builtin -ffreestanding -nostdinc -isystem \
- $(gccincdir) -pipe $(PLATFORM_CPPFLAGS)
+ $(gccincdir) -pipe $(PLATFORM_CPPFLAGS) $(CRYPTO_INC)
ifdef BUILD_TAG
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes \
diff -Naur u-boot.orig/include/configs/omap5912osk.h u-boot/include/configs/omap5912osk.h
--- u-boot.orig/include/configs/omap5912osk.h 2006-05-10 11:43:20.000000000 -0400
+++ u-boot/include/configs/omap5912osk.h 2006-06-08 15:34:00.000000000 -0400
@@ -38,6 +38,8 @@
#define CONFIG_DISPLAY_CPUINFO 1 /* display cpu info (and speed) */
#define CONFIG_DISPLAY_BOARDINFO 1 /* display board info */
+#define CONFIG_SIGNATURE 1
+
/* input clock of PLL */
/* the OMAP5912 OSK has 12MHz input clock */
#define CONFIG_SYS_CLK_FREQ 12000000
@@ -112,7 +122,11 @@
*/
#define CFG_LONGHELP /* undef to save memory */
#define CFG_PROMPT "OMAP5912 OSK # " /* Monitor Command Prompt */
+#ifdef CONFIG_SIGNATURE
+#define CFG_CBSIZE 512 /* Console I/O Buffer Size */
+#else
#define CFG_CBSIZE 256 /* Console I/O Buffer Size */
+#endif
/* Print Buffer Size */
#define CFG_PBSIZE (CFG_CBSIZE+sizeof(CFG_PROMPT)+16)
#define CFG_MAXARGS 16 /* max number of command args */
@@ -183,9 +197,9 @@
*/
#define CFG_ENV_IS_IN_FLASH 1
/* addr of environment */
-#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x020000)
+#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x0E0000)
#define CFG_ENV_SIZE 0x20000 /* Total Size of Environment Sector */
-#define CFG_ENV_OFFSET 0x20000 /* environment starts here */
+#define CFG_ENV_OFFSET 0xE0000 /* environment starts here */
#endif /* __CONFIG_H */
diff -Naur u-boot.orig/include/image.h u-boot/include/image.h
--- u-boot.orig/include/image.h 2006-05-10 11:43:20.000000000 -0400
+++ u-boot/include/image.h 2006-06-08 09:09:09.000000000 -0400
@@ -134,6 +134,9 @@
#define IH_MAGIC 0x27051956 /* Image Magic Number */
#define IH_NMLEN 32 /* Image Name Length */
+#ifdef CONFIG_SIGNATURE
+#define IH_SIGN 256 /* Image Signature Length */
+#endif /* CONFIG_SIGNATURE */
/*
* all data in network byte order (aka natural aka bigendian)
@@ -152,6 +155,9 @@
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
+#ifdef CONFIG_SIGNATURE
+ uint8_t ih_sign[IH_SIGN]; /* Image Signature */
+#endif /* CONFIG_SIGNATURE */
} image_header_t;
diff -Naur u-boot.orig/lib_crypto/Makefile u-boot/lib_crypto/Makefile
--- u-boot.orig/lib_crypto/Makefile 1969-12-31 19:00:00.000000000 -0500
+++ u-boot/lib_crypto/Makefile 2006-06-08 10:17:56.000000000 -0400
@@ -0,0 +1,40 @@
+#
+# (C) Copyright 2000-2002
+# Wolfgang Denk, DENX Software Engineering, wd at denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB = libcrypto.a
+
+OBJS = signature.o
+
+$(LIB): .depend $(OBJS)
+ $(AR) crv $@ $(OBJS)
+
+#########################################################################
+
+.depend: Makefile $(OBJS:.o=.c)
+ $(CC) -M $(CFLAGS) $(CRYPTO_INC) $(OBJS:.o=.c) > $@
+
+sinclude .depend
+
+#########################################################################
diff -Naur u-boot.orig/lib_crypto/signature.c u-boot/lib_crypto/signature.c
--- u-boot.orig/lib_crypto/signature.c 1969-12-31 19:00:00.000000000 -0500
+++ u-boot/lib_crypto/signature.c 2006-06-12 10:41:51.000000000 -0400
@@ -0,0 +1,121 @@
+#include <stdio.h>
+
+#include <openssl/ssl.h>
+#include <openssl/rsa.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/sha.h>
+#include <openssl/bio.h>
+
+#define IH_SIGN 256
+
+unsigned char key_string[] = "-----BEGIN PUBLIC KEY-----\n\
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IgnrRN+ER3GG/ZNDQk/\n\
+JByf09bYPgHfERJdHUb8p+6wM7cb7RvoxyVyaFQFA7RKErr6kvzjIKz/tyDnKKzK\n\
+0kAE0vE5prSUDxhoExM9YBAAjc6TAQ0kKjerIwZx5iT9vYV7YSpG5U7Sycw2NrjP\n\
+dMS2X8v1pHZSSgQAYPs3zGlC+oyMebd7vpsFKihc2vr2J0PXdD1Owh8gEIiZjm5o\n\
+eXL93BGFUVMZa040YUvTn9627eEg6hJJqmRZ4Myx90585J/2Jmhztv2U2t2Rl2T9\n\
+mentqOvFO0QhhadniRgDzhsLM6wbpwOqlRROcVGnkqmFckLKlLWqYbzoZOJs4xO3\n\
+oQIDAQAB\n\
+-----END PUBLIC KEY-----\n";
+
+
+void SSL_load_error_strings(void);
+
+static void err_msg(void);
+static RSA *load_key(void) ;
+static RSA *get_public_key(char *key_string);
+
+/*
+ =========================================================================
+*/
+
+static void
+err_msg(void)
+{
+ char err_buff[1024];
+ unsigned long err;
+
+ SSL_load_error_strings();
+
+ while ((err = ERR_get_error())) {
+ ERR_error_string_n(err, err_buff, sizeof(err_buff));
+ fprintf(stderr, "%s\n", err_buff);
+ }
+
+ ERR_free_strings();
+}
+
+
+int
+verify_signature(const unsigned char *signature,
+ const unsigned char *buf,
+ unsigned int len)
+{
+ RSA *public_key;
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ int res;
+ int i;
+
+ /* Do SHA1 encryption of buffer */
+ (void)SHA1(buf, len, digest);
+
+ /* Load public key */
+ if ((public_key = load_key()) == NULL) {
+ return 0;
+ }
+
+ /* verify digest */
+ res = RSA_verify(NID_sha1,
+ digest, SHA_DIGEST_LENGTH,
+ signature, IH_SIGN,
+ public_key);
+
+ /* Clean up */
+ RSA_free(public_key);
+
+ return res;
+}
+
+
+static RSA *
+load_key(void)
+{
+ RSA *key;
+ char *key_string;
+
+ key = get_public_key(key_string);
+
+ return key;
+}
+
+
+static RSA *
+get_public_key(char *key_string)
+{
+ RSA *key;
+ int res;
+ BIO *bp;
+
+ /* Load public key from string */
+ bp = BIO_new_mem_buf(key_string, -1);
+ if (bp == NULL) {
+ err_msg();
+ return NULL;
+ }
+
+ /* Convert string to internal key */
+ key = PEM_read_bio_RSA_PUBKEY(bp, NULL, NULL, NULL);
+ if (key == NULL) {
+ err_msg();
+ return NULL;
+ }
+
+ res = BIO_free(bp);
+ if (res == 0) {
+ err_msg();
+ return NULL;
+ }
+
+ return key;
+}
diff -Naur u-boot.orig/Makefile u-boot/Makefile
--- u-boot.orig/Makefile 2006-05-10 11:43:20.000000000 -0400
+++ u-boot/Makefile 2006-06-08 08:55:24.000000000 -0400
@@ -120,6 +120,7 @@
endif
LIBS = lib_generic/libgeneric.a
+LIBS += lib_crypto/libcrypto.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
@@ -144,7 +145,6 @@
# Add GCC lib
PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
-
# The "tools" are needed early, so put this first
# Don't include stuff already done in $(LIBS)
SUBDIRS = tools \
@@ -182,7 +182,7 @@
u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) \
- --start-group $(LIBS) --end-group $(PLATFORM_LIBS) \
+ --start-group $(LIBS) --end-group $(CRYPTO_LIBS) $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
$(LIBS):
@@ -206,14 +206,14 @@
tags:
ctags -w `find $(SUBDIRS) include \
- lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
+ lib_generic lib_crypto board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
fs/cramfs fs/fat fs/fdos fs/jffs2 \
net disk rtc dtt drivers drivers/sk98lin common \
\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
etags:
etags -a `find $(SUBDIRS) include \
- lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
+ lib_generic lib_crypto board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
fs/cramfs fs/fat fs/fdos fs/jffs2 \
net disk rtc dtt drivers drivers/sk98lin common \
\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
diff -Naur u-boot.orig/tools/Makefile u-boot/tools/Makefile
--- u-boot.orig/tools/Makefile 2006-05-10 11:43:20.000000000 -0400
+++ u-boot/tools/Makefile 2006-06-08 08:56:16.000000000 -0400
@@ -23,7 +23,7 @@
BINS = img2srec$(SFX) mkimage$(SFX) envcrc$(SFX) gen_eth_addr$(SFX) bmp_logo$(SFX)
-OBJS = environment.o img2srec.o mkimage.o crc32.o envcrc.o gen_eth_addr.o bmp_logo.o
+OBJS = environment.o img2srec.o mkimage.o crc32.o envcrc.o gen_eth_addr.o bmp_logo.o make_sign.o
ifeq ($(ARCH),mips)
BINS += inca-swap-bytes$(SFX)
@@ -125,8 +125,8 @@
$(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^
$(STRIP) $@
-mkimage$(SFX): mkimage.o crc32.o
- $(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^
+mkimage$(SFX): mkimage.o crc32.o make_sign.o
+ $(CC) $(CFLAGS) $(HOST_LDFLAGS) -o $@ $^ -lssl
$(STRIP) $@
ncb$(SFX): ncb.o
@@ -155,6 +155,9 @@
crc32.o: crc32.c
$(CC) -g $(CFLAGS) -c $<
+make_sign.o: make_sign.c
+ $(CC) -g $(CFLAGS) -c $<
+
mkimage.o: mkimage.c
$(CC) -g $(CFLAGS) -c $<
diff -Naur u-boot.orig/tools/make_sign.c u-boot/tools/make_sign.c
--- u-boot.orig/tools/make_sign.c 1969-12-31 19:00:00.000000000 -0500
+++ u-boot/tools/make_sign.c 2006-06-08 08:56:37.000000000 -0400
@@ -0,0 +1,202 @@
+#include <fcntl.h>
+/* #include <stdio.h> */
+#include <string.h>
+/* #include <sys/types.h> */
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* #include <asm/types.h> */
+/* #include <image.h> */
+
+#include <openssl/ssl.h>
+#include <openssl/rsa.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/sha.h>
+
+#define PRIVATE_KEY "/etc/ssl/uboot"
+
+
+void SSL_load_error_strings(void);
+
+static RSA *load_key(void) ;
+static char *get_key_file_name(void);
+static FILE *open_key_file(char *filename);
+static RSA *get_private_key(FILE * fp);
+static size_t sign_digest(const unsigned char *digest,
+ RSA * private_key,
+ unsigned char *signature);
+
+/*
+ =========================================================================
+*/
+
+static void
+err_msg(void)
+{
+ char err_buff[1024];
+ unsigned long err;
+
+ SSL_load_error_strings();
+
+ while ((err = ERR_get_error())) {
+ ERR_error_string_n(err, err_buff, sizeof(err_buff));
+ fprintf(stderr, "%s\n", err_buff);
+ }
+
+ ERR_free_strings();
+}
+
+
+size_t
+get_signature(const unsigned char *buf,
+ unsigned int len,
+ unsigned char *signature)
+{
+ RSA *private_key;
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ size_t sign_len;
+
+ /* Do SHA1 encryption of buffer */
+ (void)SHA1(buf, len, digest);
+
+ /* Load private key */
+ if ((private_key = load_key()) == NULL) {
+ return 0;
+ }
+
+ /* Sign digest */
+ sign_len = sign_digest(digest, private_key, signature);
+
+ /* Clean up */
+ RSA_free(private_key);
+
+ return sign_len;
+}
+
+
+static RSA *
+load_key(void)
+{
+ RSA *key;
+ char *key_name;
+ FILE *fp;
+ int res;
+
+ key_name = get_key_file_name();
+ if (key_name == NULL) {
+ return NULL;
+ }
+
+ fp = open_key_file(key_name);
+ if (fp == NULL) {
+ free(key_name);
+ return NULL;
+ }
+
+ free(key_name);
+
+ key = get_private_key(fp);
+
+ if (key == NULL) {
+ return NULL;
+ }
+
+ res = fclose(fp);
+ if (res) {
+ fprintf(stderr, "Can't close file: %s\n", strerror(errno));
+ RSA_free(key);
+ key = NULL;
+ }
+
+ return key;
+}
+
+
+static char *
+get_key_file_name(void)
+{
+ char *file_name = NULL;
+
+ file_name = malloc(strlen(PRIVATE_KEY));
+ strncpy(file_name, PRIVATE_KEY, strlen(PRIVATE_KEY));
+
+ return file_name;
+}
+
+
+static FILE *
+open_key_file(char *file_name)
+{
+ FILE *fp;
+ int fd;
+ struct stat st;
+
+ /* Open key and check size of file */
+ fd = open(file_name, O_RDONLY);
+ if (fstat(fd, &st) < 0 ) {
+ fprintf(stderr, "fstat for key file %s failed: %s\n",
+ file_name, strerror(errno));
+ return NULL;
+ }
+
+ if (st.st_size > 1 * 1024 * 1024) {
+ fprintf(stderr, "key file %s is too large\n", file_name);
+ return NULL;
+ }
+
+ fp = fdopen(fd, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Can't fdopen file: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ return fp;
+}
+
+
+static RSA *
+get_private_key( FILE *fp)
+{
+ RSA *key;
+ int res;
+
+ /* Load private key */
+ key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+ res = RSA_check_key(key);
+ if (res == 0) {
+ RSA_free(key);
+ key = NULL;
+ }
+
+ if (key == NULL) {
+ err_msg();
+ }
+
+ return key;
+}
+
+
+/* Do SHA1 encryption of buffer. Result is in digest*/
+static size_t
+sign_digest(const unsigned char *digest,
+ RSA * private_key,
+ unsigned char *signature)
+{
+ size_t sign_len;
+ int res;
+
+ /* Allocate space for signature */
+ sign_len = RSA_size(private_key);
+
+ /* Sign digest */
+ res = RSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH,
+ signature, &sign_len, private_key);
+ if (res == 0) {
+ err_msg();
+ sign_len = 0;
+ }
+
+ return sign_len;
+}
+
diff -Naur u-boot.orig/tools/mkimage.c u-boot/tools/mkimage.c
--- u-boot.orig/tools/mkimage.c 2006-05-10 11:43:20.000000000 -0400
+++ u-boot/tools/mkimage.c 2006-06-08 08:56:30.000000000 -0400
@@ -70,6 +70,13 @@
extern unsigned long crc32 (unsigned long crc, const char *buf, unsigned int len);
+#ifdef CONFIG_SIGNATURE
+extern size_t get_signature (const char *buf,
+ unsigned int len,
+ unsigned char *signature);
+#endif /* CONFIG_SIGNATURE */
+
+
typedef struct table_entry {
int val; /* as defined in image.h */
char *sname; /* short (input) name */
@@ -155,7 +162,6 @@
static int get_os (char *);
static int get_type(char *);
-
char *datafile;
char *imagefile;
@@ -182,6 +188,10 @@
struct stat sbuf;
unsigned char *ptr;
char *name = "";
+#ifdef CONFIG_SIGNATURE
+ unsigned char signature[IH_SIGN];
+ size_t sign_len;
+#endif /* CONFIG_SIGNATURES */
cmdname = *argv;
@@ -362,6 +372,15 @@
cmdname, imagefile);
exit (EXIT_FAILURE);
}
+
+#ifdef CONFIG_SIGNATURE
+ sign_len = get_signature(data, len, signature);
+ if (memcmp(signature, hdr->ih_sign, sign_len)) {
+ fprintf (stderr,
+ "*** Warning: \"%s\" has invalid signature!\n",
+ imagefile);
+ }
+#endif /* CONFIG_SIGNATURE */
/* for multi-file images we need the data part, too */
print_header ((image_header_t *)ptr);
@@ -485,6 +504,18 @@
strncpy((char *)hdr->ih_name, name, IH_NMLEN);
+#ifdef CONFIG_SIGNATURE
+ sign_len = get_signature((const char *)(ptr + sizeof(image_header_t)),
+ sbuf.st_size - sizeof(image_header_t),
+ signature);
+ if (sign_len == 0) {
+ fprintf(stderr, "Error getting signature\n");
+ exit (EXIT_FAILURE);
+ }
+
+ memcpy((char *)hdr->ih_sign, signature, sign_len);
+#endif /* CONFIG_SIGNATURE */
+
checksum = crc32(0,(const char *)hdr,sizeof(image_header_t));
hdr->ih_hcrc = htonl(checksum);
More information about the U-Boot
mailing list