Patrick Georgi (pgeorgi@google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/11767
-gerrit
commit 710e50a05d6eb7da73510a9dca73c6b7321f213b Author: Patrick Georgi patrick@georgi-clan.de Date: Thu Oct 1 15:54:04 2015 +0200
cbfstool: Add support for hashes as file metadata
They allow optimizing a verification of a whole CBFS image by only dealing with the headers (assuming you choose to trust the hash algorithm(s)).
The format allows for multiple hashes for a single file, and cbfstool can handle them, but right now it can't generate such headers.
Loosely based on Sol's work in http://review.coreboot.org/#/c/10147/, but using the compatible file attribute format. vboot is now a hard dependency of the build process, but we import it into the tree for quite a while now.
Change-Id: I9f14f30537d676ce209ad612e7327c6f4810b313 Signed-off-by: Patrick Georgi patrick@georgi-clan.de --- util/cbfstool/Makefile.inc | 15 +++++++++ util/cbfstool/cbfs.h | 29 +++++++++++++++++ util/cbfstool/cbfs_image.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ util/cbfstool/cbfs_image.h | 10 ++++++ util/cbfstool/cbfstool.c | 38 +++++++++++++++++----- 5 files changed, 163 insertions(+), 8 deletions(-)
diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index a1dbfc3..4994757 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -15,6 +15,11 @@ cbfsobj += lzma.o cbfsobj += LzFind.o cbfsobj += LzmaDec.o cbfsobj += LzmaEnc.o +# CRYPTOLIB +cbfsobj += 2sha_utility.o +cbfsobj += 2sha1.o +cbfsobj += 2sha256.o +cbfsobj += 2sha512.o # FMAP cbfsobj += fmap.o cbfsobj += kv_pair.o @@ -51,6 +56,9 @@ TOOLCPPFLAGS += -I$(top)/util/cbfstool/flashmap TOOLCPPFLAGS += -I$(top)/util/cbfstool TOOLCPPFLAGS += -I$(objutil)/cbfstool TOOLCPPFLAGS += -I$(top)/src/commonlib/include +TOOLCPPFLAGS += -DNEED_VB2_SHA_LIBRARY +TOOLCPPFLAGS += -I$(top)/3rdparty/vboot/firmware/include +TOOLCPPFLAGS += -I$(top)/3rdparty/vboot/firmware/2lib/include TOOLLDFLAGS ?=
ifeq ($(shell uname -s | cut -c-7 2>/dev/null), MINGW32) @@ -77,6 +85,10 @@ $(objutil)/cbfstool/%.o: $(top)/util/cbfstool/lzma/C/%.c printf " HOSTCC $(subst $(objutil)/,,$(@))\n" $(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $<
+$(objutil)/cbfstool/%.o: $(top)/3rdparty/vboot/firmware/2lib/%.c + printf " HOSTCC $(subst $(objutil)/,,$(@))\n" + $(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $< + $(objutil)/cbfstool/cbfstool: $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) @@ -97,6 +109,9 @@ $(objutil)/cbfstool/fmd_scanner.o: TOOLCFLAGS += -Wno-redundant-decls $(objutil)/cbfstool/fmd_scanner.o: TOOLCFLAGS += -Wno-unused-function # Tolerate lzma sdk warnings $(objutil)/cbfstool/LzmaEnc.o: TOOLCFLAGS += -Wno-sign-compare -Wno-cast-qual +# Tolerate vboot warnings +$(objutil)/cbfstool/2sha_utility.o: TOOLCFLAGS += -Wno-sign-compare +$(objutil)/cbfstool/2sha1.o: TOOLCFLAGS += -Wno-cast-qual
$(objutil)/cbfstool/fmd.o: $(objutil)/cbfstool/fmd_parser.h $(objutil)/cbfstool/fmd.o: $(objutil)/cbfstool/fmd_scanner.h diff --git a/util/cbfstool/cbfs.h b/util/cbfstool/cbfs.h index 579afa6..627a2b8 100644 --- a/util/cbfstool/cbfs.h +++ b/util/cbfstool/cbfs.h @@ -21,6 +21,8 @@
#include <stdint.h>
+#include <vb2_api.h> + /* cbfstool will fail when trying to build a cbfs_file header that's larger * than MAX_CBFS_FILE_HEADER_BUFFER. 1K should give plenty of room. */ #define MAX_CBFS_FILE_HEADER_BUFFER 1024 @@ -107,6 +109,7 @@ struct cbfs_file_attribute { #define CBFS_FILE_ATTR_TAG_UNUSED 0 #define CBFS_FILE_ATTR_TAG_UNUSED2 0xffffffff #define CBFS_FILE_ATTR_TAG_COMPRESSION 0x42435a4c +#define CBFS_FILE_ATTR_TAG_HASH 0x68736148
struct cbfs_file_attr_compression { uint32_t tag; @@ -116,6 +119,14 @@ struct cbfs_file_attr_compression { uint32_t decompressed_size; } __PACKED;
+struct cbfs_file_attr_hash { + uint32_t tag; + uint32_t len; + uint32_t hash_type; + /* hash_data is len - sizeof(struct) bytes */ + uint8_t hash_data[]; +} __PACKED; + struct cbfs_stage { uint32_t compression; uint64_t entry; @@ -203,6 +214,24 @@ static struct typedesc_t filetypes[] unused = { {CBFS_COMPONENT_NULL, "null"} };
+static const struct typedesc_t types_cbfs_hash[] unused = { + {VB2_HASH_INVALID, "none"}, + {VB2_HASH_SHA1, "sha1"}, + {VB2_HASH_SHA256, "sha256"}, + {VB2_HASH_SHA512, "sha512"}, + {0, NULL} +}; + +static size_t widths_cbfs_hash[] unused = { + [VB2_HASH_INVALID] = 0, + [VB2_HASH_SHA1] = 20, + [VB2_HASH_SHA256] = 32, + [VB2_HASH_SHA512] = 64, +}; + +#define CBFS_NUM_SUPPORTED_HASHES (sizeof(widths_cbfs_hash) / \ + sizeof(*(widths_cbfs_hash))) + #define CBFS_SUBHEADER(_p) ( (void *) ((((uint8_t *) (_p)) + ntohl((_p)->offset))) )
/* cbfs_image.c */ diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c index 24ab0c4..fa67fc7 100644 --- a/util/cbfstool/cbfs_image.c +++ b/util/cbfstool/cbfs_image.c @@ -85,6 +85,16 @@ int cbfs_parse_comp_algo(const char *name) return lookup_type_by_name(types_cbfs_compression, name); }
+static const char *get_hash_attr_name(uint16_t hash_type) +{ + return lookup_name_by_type(types_cbfs_hash, hash_type, "(invalid)"); +} + +int cbfs_parse_hash_algo(const char *name) +{ + return lookup_type_by_name(types_cbfs_hash, name); +} + /* CBFS image */
size_t cbfs_calculate_file_header_size(const char *name) @@ -173,6 +183,24 @@ static int cbfs_file_get_compression_info(struct cbfs_file *entry, return compression; }
+static struct cbfs_file_attr_hash *cbfs_file_get_next_hash( + struct cbfs_file *entry, struct cbfs_file_attr_hash *cur) +{ + struct cbfs_file_attribute *attr = (struct cbfs_file_attribute *)cur; + if (attr == NULL) { + attr = cbfs_file_first_attr(entry); + if (attr == NULL) + return NULL; + if (ntohl(attr->tag) == CBFS_FILE_ATTR_TAG_HASH) + return (struct cbfs_file_attr_hash *)attr; + } + while ((attr = cbfs_file_next_attr(entry, attr)) != NULL) { + if (ntohl(attr->tag) == CBFS_FILE_ATTR_TAG_HASH) + return (struct cbfs_file_attr_hash *)attr; + }; + return NULL; +} + void cbfs_get_header(struct cbfs_header *header, void *src) { struct buffer outheader; @@ -813,6 +841,31 @@ int cbfs_print_entry_info(struct cbfs_image *image, struct cbfs_file *entry, ); }
+ struct cbfs_file_attr_hash *hash = NULL; + while ((hash = cbfs_file_get_next_hash(entry, hash)) != NULL) { + unsigned int hash_type = ntohl(hash->hash_type); + if (hash_type > CBFS_NUM_SUPPORTED_HASHES) { + fprintf(fp, "invalid hash type %d\n", hash_type); + break; + } + size_t hash_len = widths_cbfs_hash[hash_type]; + char *hash_str = bintohex(hash->hash_data, hash_len); + uint8_t local_hash[hash_len]; + if (vb2_digest_buffer(CBFS_SUBHEADER(entry), + ntohl(entry->len), hash_type, &local_hash, + hash_len) != VB2_SUCCESS) { + fprintf(fp, "failed to hash '%s'\n", name); + break; + } + int valid = memcmp(local_hash, hash->hash_data, hash_len) == 0; + const char *valid_str = valid?"valid":"invalid"; + + fprintf(fp, " hash %s:%s %s\n", + get_hash_attr_name(hash_type), + hash_str, valid_str); + free(hash_str); + } + if (!verbose) return 0;
@@ -1129,6 +1182,32 @@ struct cbfs_file_attribute *cbfs_add_file_attr(struct cbfs_file *header, return attr; }
+int cbfs_add_file_hash(struct cbfs_file *header, struct buffer *buffer, + enum vb2_hash_algorithm hash_type) +{ + if (hash_type >= CBFS_NUM_SUPPORTED_HASHES) + return -1; + + unsigned hash_size = widths_cbfs_hash[hash_type]; + if (hash_size == 0) + return -1; + + struct cbfs_file_attr_hash *attrs = + (struct cbfs_file_attr_hash *)cbfs_add_file_attr(header, + CBFS_FILE_ATTR_TAG_HASH, + sizeof(struct cbfs_file_attr_hash) + hash_size); + + if (attrs == NULL) + return -1; + + attrs->hash_type = htonl(hash_type); + if (vb2_digest_buffer((void *)buffer->data, buffer->size, hash_type, + attrs->hash_data, hash_size) != VB2_SUCCESS) + return -1; + + return 0; +} + /* Finds a place to hold whole data in same memory page. */ static int is_in_same_page(uint32_t start, uint32_t size, uint32_t page) { diff --git a/util/cbfstool/cbfs_image.h b/util/cbfstool/cbfs_image.h index baceb51..f602976 100644 --- a/util/cbfstool/cbfs_image.h +++ b/util/cbfstool/cbfs_image.h @@ -36,6 +36,10 @@ struct cbfs_image { * enum comp_algo if it's supported, or a number < 0 otherwise. */ int cbfs_parse_comp_algo(const char *name);
+/* Given the string name of a hash algorithm, return the corresponding + * id if it's supported, or a number < 0 otherwise. */ +int cbfs_parse_hash_algo(const char *name); + /* Given a pointer, serialize the header from host-native byte format * to cbfs format, i.e. big-endian. */ void cbfs_put_header(void *dest, const struct cbfs_header *header); @@ -184,4 +188,10 @@ struct cbfs_file_attribute *cbfs_file_next_attr(struct cbfs_file *file, struct cbfs_file_attribute *cbfs_add_file_attr(struct cbfs_file *header, uint32_t tag, uint32_t size); + +/* Adds an extended attribute to header, containing a hash of buffer's data of + * the type specified by hash_type. + * Returns 0 on success, -1 on error. */ +int cbfs_add_file_hash(struct cbfs_file *header, struct buffer *buffer, + enum vb2_hash_algorithm hash_type); #endif diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c index 35747d3..2876702 100644 --- a/util/cbfstool/cbfstool.c +++ b/util/cbfstool/cbfstool.c @@ -76,6 +76,7 @@ static struct param { bool stage_xip; int fit_empty_entries; enum comp_algo compression; + enum vb2_hash_algorithm hash; /* for linux payloads */ char *initrd; char *cmdline; @@ -83,6 +84,7 @@ static struct param { /* All variables not listed are initialized as zero. */ .arch = CBFS_ARCHITECTURE_UNKNOWN, .compression = CBFS_COMPRESS_NONE, + .hash = VB2_HASH_INVALID, .headeroffset = ~0, .region_name = SECTION_NAME_PRIMARY_CBFS, }; @@ -327,6 +329,14 @@ static int cbfs_add_component(const char *filename, return 1; }
+ if (param.hash != VB2_HASH_INVALID) + if (cbfs_add_file_hash(header, &buffer, param.hash) == -1) { + ERROR("couldn't add hash for '%s'\n", name); + free(header); + buffer_delete(&buffer); + return 1; + } + if (IS_TOP_ALIGNED_ADDRESS(offset)) offset = convert_to_from_top_aligned(param.image_region, -offset); @@ -882,11 +892,11 @@ static int cbfs_copy(void) }
static const struct command commands[] = { - {"add", "H:r:f:n:t:c:b:a:vh?", cbfs_add, true, true}, - {"add-flat-binary", "H:r:f:n:l:e:c:b:vh?", cbfs_add_flat_binary, true, + {"add", "H:r:f:n:t:c:b:a:vA:h?", cbfs_add, true, true}, + {"add-flat-binary", "H:r:f:n:l:e:c:b:vA:h?", cbfs_add_flat_binary, true, true}, - {"add-payload", "H:r:f:n:t:c:b:C:I:vh?", cbfs_add_payload, true, true}, - {"add-stage", "a:H:r:f:n:t:c:b:P:S:yvh?", cbfs_add_stage, true, true}, + {"add-payload", "H:r:f:n:t:c:b:C:I:vA:h?", cbfs_add_payload, true, true}, + {"add-stage", "a:H:r:f:n:t:c:b:P:S:yvA:h?", cbfs_add_stage, true, true}, {"add-int", "H:r:i:n:b:vh?", cbfs_add_integer, true, true}, {"add-master-header", "H:r:vh?", cbfs_add_master_header, true, true}, {"copy", "H:D:s:h?", cbfs_copy, true, true}, @@ -914,6 +924,7 @@ static struct option long_options[] = { {"fill-upward", no_argument, 0, 'u' }, {"flashmap", required_argument, 0, 'M' }, {"fmap-regions", required_argument, 0, 'r' }, + {"hash-algorithm",required_argument, 0, 'A' }, {"header-offset", required_argument, 0, 'H' }, {"help", no_argument, 0, 'h' }, {"ignore-sec", required_argument, 0, 'S' }, @@ -997,18 +1008,18 @@ static void usage(char *name) " -v Provide verbose output\n" " -h Display this help message\n\n" "COMMANDs:\n" - " add [-r image,regions] -f FILE -n NAME -t TYPE \\n" + " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\n" " [-c compression] [-b base-address | -a alignment] " "Add a component\n" - " add-payload [-r image,regions] -f FILE -n NAME \\n" + " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\n" " [-c compression] [-b base-address] " "Add a payload to the ROM\n" " (linux specific: [-C cmdline] [-I initrd])\n" - " add-stage [-r image,regions] -f FILE -n NAME \\n" + " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\n" " [-c compression] [-b base] [-S section-to-ignore] " " [-a alignment] [-y|--xip] [-P page-size]" "Add a stage to the ROM\n" - " add-flat-binary [-r image,regions] -f FILE -n NAME \\n" + " add-flat-binary [-r image,regions] -f FILE -n NAME [-A hash] \\n" " -l load-address -e entry-point [-c compression] \\n" " [-b base] " "Add a 32bit flat mode binary\n" @@ -1132,6 +1143,17 @@ int main(int argc, char **argv) optarg); break; } + case 'A': { + int algo = cbfs_parse_hash_algo(optarg); + if (algo >= 0) + param.hash = algo; + else { + ERROR("Unknown hash algorithm '%s'.\n", + optarg); + return 1; + } + break; + } case 'M': param.fmap = optarg; break;