[Celinux-dev] RFC - Secure Bootloader patch
Stephen Johnson
steve at research.panasonic.com
Tue Aug 22 12:57:27 PDT 2006
At OLS last month I demoed a Secure Boot Loader that was based on a
u-boot that had been modified to verify an image signature using a
SHA1 digest and RSA encryption/decryption. Because I could find the
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. I'm including the u-boot patch in this message so
that others can look at it for ways to cut the size. The eventual
goal is to release this patch to the community.
Notes:
- The u-boot was downloaded from the u-boot git tree on August 1, but
the patch also applied cleanly with a u-boot version from June.
- I'm linking against openssl-0.9.8b.
- I used crosstool built tool chain based on gcc-3.4.5 and glibc-2.3.6
- It all was built for an omap5912osk board.
- The signature is added to the u-boot header by mkimage.
- 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. The changes
there are straight forward to move to another board in that same
"configs" directory. These are the only board-specific changes
required.
- The patch is applied at the top level of the u-boot source,
patch -p1 < /path/to/patch/file
To compile the patched u-boot, the following are needed:
CPATH should be defined to point to wherever the tool chain 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 to 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 and include the
necessary 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 Celinux-dev
mailing list