summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--firmware/2lib/include/2struct.h36
-rw-r--r--futility/cmd_gbb_utility.c16
-rw-r--r--futility/futility.h8
-rw-r--r--futility/ryu_root_header.c165
5 files changed, 226 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 1273d80c..225097fc 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
+ }
+}