diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | firmware/2lib/include/2struct.h | 36 | ||||
-rw-r--r-- | futility/cmd_gbb_utility.c | 16 | ||||
-rw-r--r-- | futility/futility.h | 8 | ||||
-rw-r--r-- | futility/ryu_root_header.c | 165 |
5 files changed, 226 insertions, 2 deletions
@@ -615,7 +615,8 @@ FUTIL_STATIC_SRCS = \ futility/futility.c \ futility/cmd_dump_fmap.c \ futility/cmd_gbb_utility.c \ - futility/misc.c + futility/misc.c \ + futility/ryu_root_header.c FUTIL_SRCS = \ ${FUTIL_STATIC_SRCS} \ diff --git a/firmware/2lib/include/2struct.h b/firmware/2lib/include/2struct.h index ca05dfca..2d43218c 100644 --- a/firmware/2lib/include/2struct.h +++ b/firmware/2lib/include/2struct.h @@ -305,4 +305,40 @@ struct vb2_gbb_header { /* The GBB is used outside of vboot_reference, so this size is important. */ #define EXPECTED_VB2_GBB_HEADER_SIZE 128 +/* + * Root key hash for Ryu devices only. Contains the hash of the root key. + * This will be embedded somewhere inside the RO part of the firmware, so that + * it can verify the GBB contains only the official root key. + */ + +#define RYU_ROOT_KEY_HASH_MAGIC "RtKyHash" +#define RYU_ROOT_KEY_HASH_MAGIC_INVCASE "rTkYhASH" +#define RYU_ROOT_KEY_HASH_MAGIC_SIZE 8 + +#define RYU_ROOT_KEY_HASH_VERSION_MAJOR 1 +#define RYU_ROOT_KEY_HASH_VERSION_MINOR 0 + +struct vb2_ryu_root_key_hash { + /* Magic number (RYU_ROOT_KEY_HASH_MAGIC) */ + uint8_t magic[RYU_ROOT_KEY_HASH_MAGIC_SIZE]; + + /* Version of this struct */ + uint16_t header_version_major; + uint16_t header_version_minor; + + /* + * Length of this struct, in bytes, including any variable length data + * which follows (there is none, yet). + */ + uint32_t struct_size; + + /* + * SHA-256 hash digest of the entire root key section from the GBB. If + * all 0 bytes, all root keys will be treated as if matching. + */ + uint8_t root_key_hash_digest[32]; +}; + +#define EXPECTED_VB2_RYU_ROOT_KEY_HASH_SIZE 48 + #endif /* VBOOT_REFERENCE_VBOOT_2STRUCT_H_ */ diff --git a/futility/cmd_gbb_utility.c b/futility/cmd_gbb_utility.c index df0a51db..fe21762e 100644 --- a/futility/cmd_gbb_utility.c +++ b/futility/cmd_gbb_utility.c @@ -30,6 +30,7 @@ static void print_help(int argc, char *argv[]) " --hwid \tReport hardware id (default).\n" " --flags \tReport header flags.\n" " --digest \tReport digest of hwid (>= v1.2)\n" + " --roothash \tCheck ryu root key hash\n" " -k, --rootkey=FILE \tFile name to export Root Key.\n" " -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n" " -r --recoverykey=FILE\tFile name to export Recovery Key.\n" @@ -61,6 +62,7 @@ enum { OPT_FLAGS, OPT_DIGEST, OPT_HELP, + OPT_ROOTHASH, }; /* Command line options */ @@ -77,6 +79,7 @@ static struct option long_opts[] = { {"flags", 0, NULL, OPT_FLAGS}, {"digest", 0, NULL, OPT_DIGEST}, {"help", 0, NULL, OPT_HELP}, + {"roothash", 0, NULL, OPT_ROOTHASH}, {NULL, 0, NULL, 0}, }; @@ -363,6 +366,7 @@ static int do_gbb_utility(int argc, char *argv[]) int sel_hwid = 0; int sel_digest = 0; int sel_flags = 0; + int sel_roothash = 0; uint8_t *inbuf = NULL; off_t filesize; uint8_t *outbuf = NULL; @@ -412,6 +416,9 @@ static int do_gbb_utility(int argc, char *argv[]) case OPT_DIGEST: sel_digest = 1; break; + case OPT_ROOTHASH: + sel_roothash = 1; + break; case OPT_HELP: print_help(argc, argv); return !!errorcnt; @@ -490,6 +497,9 @@ static int do_gbb_utility(int argc, char *argv[]) if (sel_digest) print_hwid_digest(gbb, "digest: ", "\n"); + if (sel_roothash) + verify_ryu_root_header(inbuf, filesize, gbb); + if (sel_flags) printf("flags: 0x%08x\n", gbb->flags); if (opt_rootkey) @@ -591,10 +601,14 @@ static int do_gbb_utility(int argc, char *argv[]) } } - if (opt_rootkey) + if (opt_rootkey) { read_from_file("root_key", opt_rootkey, gbb_base + gbb->rootkey_offset, gbb->rootkey_size); + + if (fill_ryu_root_header(outbuf, filesize, gbb)) + errorcnt++; + } if (opt_bmpfv) read_from_file("bmp_fv", opt_bmpfv, gbb_base + gbb->bmpfv_offset, diff --git a/futility/futility.h b/futility/futility.h index e0754d86..c8150035 100644 --- a/futility/futility.h +++ b/futility/futility.h @@ -116,6 +116,14 @@ int print_hwid_digest(GoogleBinaryBlockHeader *gbb, /* Copies a file or dies with an error message */ void futil_copy_file_or_die(const char *infile, const char *outfile); +/* Update ryu root key header in the image */ +int fill_ryu_root_header(uint8_t *ptr, size_t size, + const GoogleBinaryBlockHeader *gbb); + +/* Verify ryu root key header */ +int verify_ryu_root_header(uint8_t *ptr, size_t size, + const GoogleBinaryBlockHeader *gbb); + /* Possible file operation errors */ enum futil_file_err { FILE_ERR_NONE, diff --git a/futility/ryu_root_header.c b/futility/ryu_root_header.c new file mode 100644 index 00000000..a88f34c2 --- /dev/null +++ b/futility/ryu_root_header.c @@ -0,0 +1,165 @@ +/* + * Copyright 2015 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include <errno.h> +#include <getopt.h> +#include <inttypes.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "2struct.h" +#include "cryptolib.h" +#include "futility.h" +#include "gbb_header.h" + +#define SEARCH_STRIDE 4 + +/** + * Check if the pointer contains the magic string. We need to use a + * case-swapped version, so that the actual magic string doesn't appear in the + * code, to avoid falsely finding it when searching for the struct. + */ +static int is_magic(const void *ptr) +{ + const char magic_inv[RYU_ROOT_KEY_HASH_MAGIC_SIZE] = + RYU_ROOT_KEY_HASH_MAGIC_INVCASE; + const char *magic = ptr; + int i; + + for (i = 0; i < RYU_ROOT_KEY_HASH_MAGIC_SIZE; i++) { + if (magic[i] != (magic_inv[i] ^ 0x20)) + return 0; + } + + return 1; +} + +static int valid_ryu_root_header(struct vb2_ryu_root_key_hash *hash, + size_t size) +{ + if (!is_magic(hash->magic)) + return 0; /* Wrong magic */ + + if (hash->header_version_major != RYU_ROOT_KEY_HASH_VERSION_MAJOR) + return 0; /* Version we can't parse */ + + if (hash->struct_size < EXPECTED_VB2_RYU_ROOT_KEY_HASH_SIZE) + return 0; /* Header too small */ + + if (hash->struct_size > size) + return 0; /* Claimed size doesn't fit in buffer */ + + return 1; +} + +/** + * Find the root key hash struct and return it or NULL if error. + */ +static struct vb2_ryu_root_key_hash *find_ryu_root_header(uint8_t *ptr, + size_t size) +{ + size_t i; + struct vb2_ryu_root_key_hash *tmp, *hash = NULL; + int count = 0; + + /* Look for the ryu root key hash header */ + for (i = 0; i <= size - SEARCH_STRIDE; i += SEARCH_STRIDE) { + if (!is_magic(ptr + i)) + continue; + + /* Found something. See if it's any good. */ + tmp = (struct vb2_ryu_root_key_hash *) (ptr + i); + if (valid_ryu_root_header(tmp, size - i)) + if (!count++) + hash = tmp; + } + + switch (count) { + case 0: + return NULL; + case 1: + return hash; + default: + fprintf(stderr, + "WARNING: multiple ryu root hash headers found\n"); + /* But hey, it's only a warning. Use the first one. */ + return hash; + } +} + +static void calculate_root_key_hash(uint8_t *digest, size_t digest_size, + const GoogleBinaryBlockHeader *gbb) +{ + const uint8_t *gbb_base = (const uint8_t *)gbb; + + internal_SHA256(gbb_base + gbb->rootkey_offset, + gbb->rootkey_size, + digest); +} + +int fill_ryu_root_header(uint8_t *ptr, size_t size, + const GoogleBinaryBlockHeader *gbb) +{ + struct vb2_ryu_root_key_hash *hash; + + /* + * Find the ryu root header. If not found, nothing we can do, but + * that's ok because most images don't have the header. + */ + hash = find_ryu_root_header(ptr, size); + if (!hash) + return 0; + + /* Update the hash stored in the header based on the root key */ + calculate_root_key_hash(hash->root_key_hash_digest, + sizeof(hash->root_key_hash_digest), + gbb); + + printf(" - calculate ryu root hash: success\n"); + return 0; +} + +int verify_ryu_root_header(uint8_t *ptr, size_t size, + const GoogleBinaryBlockHeader *gbb) +{ + uint8_t digest[SHA256_DIGEST_SIZE] = {0}; + + struct vb2_ryu_root_key_hash *hash; + + /* + * Find the ryu root header. If not found, nothing we can do, but + * that's ok because most images don't have the header. + */ + hash = find_ryu_root_header(ptr, size); + if (!hash) { + printf(" - ryu root hash not found\n"); + return 0; + } + + /* Check for all 0's, which means hash hasn't been set */ + if (0 == memcmp(digest, hash->root_key_hash_digest, + SHA256_DIGEST_SIZE)) { + printf(" - ryu root hash is unset\n"); + return 0; + } + + /* Update the hash stored in the header based on the root key */ + calculate_root_key_hash(digest, sizeof(digest), gbb); + + if (0 == memcmp(digest, hash->root_key_hash_digest, + SHA256_DIGEST_SIZE)) { + printf(" - ryu root hash verified\n"); + return 0; + } else { + printf(" - ryu root hash does not verify\n"); + return -1; + } +} |