summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2010-06-10 09:59:04 -0700
committerRandall Spangler <rspangler@chromium.org>2010-06-10 09:59:04 -0700
commitd183644564ec27c106a3eb1931f565fae167a058 (patch)
tree195f2d44c8800b797189e6e0b9245541848f896b
parent59204c57d0a4889e3cace81b3361ea06f7b3fb45 (diff)
downloadvboot-d183644564ec27c106a3eb1931f565fae167a058.tar.gz
Major refactoring of structures, with unit tests. This matches the doc I sent out earlier.
Firmware-side code for LoadKernel() is in place now. LoadFirmware() replacement coming soon. The new functions are implemented in parallel to the existing ones (i.e., everything that used to work still does). Review URL: http://codereview.chromium.org/2745007
-rw-r--r--Makefile4
-rw-r--r--host/Makefile45
-rw-r--r--host/include/host_common.h51
-rw-r--r--host/include/host_key.h59
-rw-r--r--host/include/host_signature.h48
-rw-r--r--host/lib/host_common.c185
-rw-r--r--host/lib/host_key.c141
-rw-r--r--host/lib/host_signature.c135
-rw-r--r--host/linktest/main.c28
-rw-r--r--tests/Makefile17
-rwxr-xr-xtests/run_image_verification_tests.sh1
-rwxr-xr-xtests/run_vboot_common_tests.sh91
-rw-r--r--tests/vboot_common2_tests.c210
-rw-r--r--tests/vboot_common3_tests.c295
-rw-r--r--tests/vboot_common_tests.c101
-rw-r--r--utility/Makefile31
-rw-r--r--utility/load_kernel_test.c2
-rw-r--r--vboot_firmware/Makefile5
-rw-r--r--vboot_firmware/include/load_firmware_fw.h6
-rw-r--r--vboot_firmware/include/load_kernel_fw.h9
-rw-r--r--vboot_firmware/lib/cryptolib/include/rsa.h3
-rw-r--r--vboot_firmware/lib/cryptolib/rsa_utility.c2
-rw-r--r--vboot_firmware/lib/include/vboot_common.h92
-rw-r--r--vboot_firmware/lib/include/vboot_firmware.h23
-rw-r--r--vboot_firmware/lib/include/vboot_kernel.h36
-rw-r--r--vboot_firmware/lib/include/vboot_struct.h124
-rw-r--r--vboot_firmware/lib/load_kernel_fw.c20
-rw-r--r--vboot_firmware/lib/vboot_common.c312
-rw-r--r--vboot_firmware/lib/vboot_firmware.c173
-rw-r--r--vboot_firmware/lib/vboot_kernel.c267
-rw-r--r--vboot_firmware/linktest/main.c39
31 files changed, 2519 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index a377b76c..e30d7162 100644
--- a/Makefile
+++ b/Makefile
@@ -6,13 +6,15 @@ export CC ?= gcc
export CFLAGS = -Wall -DNDEBUG -O3 -Werror
export TOP = $(shell pwd)
export FWDIR=$(TOP)/vboot_firmware
+export HOSTDIR=$(TOP)/host
export INCLUDES = \
-I$(FWDIR)/include \
-I$(TOP)/misclibs/include
export FWLIB=$(FWDIR)/vboot_fw.a
+export HOSTLIB=$(HOSTDIR)/vboot_host.a
-SUBDIRS=vboot_firmware misclibs vfirmware vkernel utility tests
+SUBDIRS=vboot_firmware misclibs host vfirmware vkernel utility tests
all:
set -e; \
diff --git a/host/Makefile b/host/Makefile
new file mode 100644
index 00000000..6c320eb9
--- /dev/null
+++ b/host/Makefile
@@ -0,0 +1,45 @@
+# Copyright (c) 2010 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.
+
+LIBNAME = vboot_host.a
+
+CC ?= gcc
+CFLAGS = -Wall -DNDEBUG -O3 -Werror
+
+HOSTTOP := $(shell pwd)
+LIBDIR = $(HOSTTOP)/lib
+TESTDIR = $(HOSTTOP)/linktest
+
+INCLUDES += \
+ -I$(HOSTTOP)/include \
+ -I$(FWDIR)/lib/include \
+ -I$(FWDIR)/lib/cgptlib/include \
+ -I$(FWDIR)/lib/cryptolib/include
+
+# find ./lib -iname '*.c' | sort
+LIB_SRCS = \
+ ./lib/host_common.c \
+ ./lib/host_key.c \
+ ./lib/host_signature.c
+
+LIB_OBJS = $(LIB_SRCS:%.c=%.o)
+
+test : $(LIBNAME)
+ $(CC) $(CFLAGS) $(INCLUDES) -o $(TESTDIR)/a.out $(TESTDIR)/main.c \
+ $(LIBNAME) $(FWLIB) $(TOP)/misclibs/file_keys.o -lcrypto
+
+$(LIBNAME) : $(LIB_OBJS) $(STUB_OBJS)
+ rm -f $@
+ ar qc $@ $^
+
+%o : %c
+ $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+
+clean: FORCE
+ rm -f $(LIBNAME) $(LIB_OBJS) $(STUB_OBJS) $(TESTDIR)/a.out
+
+FORCE:
+
+
+.PHONY: FORCE
diff --git a/host/include/host_common.h b/host/include/host_common.h
new file mode 100644
index 00000000..cd7c0631
--- /dev/null
+++ b/host/include/host_common.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2010 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.
+ *
+ * Host-side functions for verified boot.
+ */
+
+#ifndef VBOOT_REFERENCE_HOST_COMMON_H_
+#define VBOOT_REFERENCE_HOST_COMMON_H_
+
+#include <stdint.h>
+
+#include "cryptolib.h"
+#include "host_key.h"
+#include "host_signature.h"
+#include "utility.h"
+#include "vboot_struct.h"
+
+
+/* Create a key block header containing [data_key] and [flags], signed
+ * by [signing_key]. Caller owns the returned pointer, and must free
+ * it with Free(). */
+VbKeyBlockHeader* CreateKeyBlock(const VbPublicKey* data_key,
+ const VbPrivateKey* signing_key,
+ uint64_t flags);
+
+
+/* Creates a firmware preamble, signed with [signing_key].
+ * Caller owns the returned pointer, and must free it with Free().
+ *
+ * Returns NULL if error. */
+VbFirmwarePreambleHeader* CreateFirmwarePreamble(
+ uint64_t firmware_version,
+ const VbPublicKey* kernel_subkey,
+ const VbSignature* body_signature,
+ const VbPrivateKey* signing_key);
+
+
+/* Creates a kernel preamble, signed with [signing_key].
+ * Caller owns the returned pointer, and must free it with Free().
+ *
+ * Returns NULL if error. */
+VbKernelPreambleHeader* CreateKernelPreamble(
+ uint64_t kernel_version,
+ uint64_t body_load_address,
+ uint64_t bootloader_address,
+ uint64_t bootloader_size,
+ const VbSignature* body_signature,
+ const VbPrivateKey* signing_key);
+
+#endif /* VBOOT_REFERENCE_HOST_COMMON_H_ */
diff --git a/host/include/host_key.h b/host/include/host_key.h
new file mode 100644
index 00000000..b71db8f8
--- /dev/null
+++ b/host/include/host_key.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2010 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.
+ *
+ * Host-side functions for verified boot.
+ */
+
+#ifndef VBOOT_REFERENCE_HOST_KEY_H_
+#define VBOOT_REFERENCE_HOST_KEY_H_
+
+#include <stdint.h>
+
+#include "cryptolib.h"
+#include "utility.h"
+#include "vboot_struct.h"
+
+
+typedef struct rsa_st RSA;
+
+/* Private key data */
+typedef struct VbPrivateKey {
+ RSA* rsa_private_key; /* Private key data */
+ uint64_t algorithm; /* Algorithm to use when signing */
+} VbPrivateKey;
+
+
+/* Read a private key from a file. Caller owns the returned pointer,
+ * and must free it with PrivateKeyFree(). */
+VbPrivateKey* PrivateKeyRead(const char* filename, uint64_t algorithm);
+
+
+/* Free a private key. */
+void PrivateKeyFree(VbPrivateKey* key);
+
+
+/* Initialize a public key to refer to [key_data]. */
+void PublicKeyInit(VbPublicKey* key, uint8_t* key_data, uint64_t key_size);
+
+
+/* Allocate a new public key with space for a [key_size] byte key. */
+VbPublicKey* PublicKeyAlloc(uint64_t key_size, uint64_t algorithm,
+ uint64_t version);
+
+
+/* Copy a public key from [src] to [dest].
+ *
+ * Returns 0 if success, non-zero if error. */
+int PublicKeyCopy(VbPublicKey* dest, const VbPublicKey* src);
+
+
+/* Read a public key from a file. Caller owns the returned pointer,
+ * and must free it with Free().
+ *
+ * Returns NULL if error. */
+/* TODO: should really store public keys in files as VbPublicKey */
+VbPublicKey* PublicKeyRead(const char* filename, uint64_t algorithm,
+ uint64_t version);
+
+#endif /* VBOOT_REFERENCE_HOST_KEY_H_ */
diff --git a/host/include/host_signature.h b/host/include/host_signature.h
new file mode 100644
index 00000000..9e7b0c06
--- /dev/null
+++ b/host/include/host_signature.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2010 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.
+ *
+ * Host-side functions for verified boot.
+ */
+
+#ifndef VBOOT_REFERENCE_HOST_SIGNATURE_H_
+#define VBOOT_REFERENCE_HOST_SIGNATURE_H_
+
+#include <stdint.h>
+
+#include "cryptolib.h"
+#include "host_key.h"
+#include "utility.h"
+#include "vboot_struct.h"
+
+
+/* Initialize a signature struct. */
+void SignatureInit(VbSignature* sig, uint8_t* sig_data,
+ uint64_t sig_size, uint64_t data_size);
+
+
+/* Allocate a new signature with space for a [sig_size] byte signature. */
+VbSignature* SignatureAlloc(uint64_t sig_size, uint64_t data_size);
+
+
+/* Copy a signature key from [src] to [dest].
+ *
+ * Returns 0 if success, non-zero if error. */
+int SignatureCopy(VbSignature* dest, const VbSignature* src);
+
+
+/* Calculates a SHA-512 checksum.
+ * Caller owns the returned pointer, and must free it with Free().
+ *
+ * Returns NULL if error. */
+VbSignature* CalculateChecksum(const uint8_t* data, uint64_t size);
+
+
+/* Calculates a signature for the data using the specified key.
+ * Caller owns the returned pointer, and must free it with Free().
+ *
+ * Returns NULL if error. */
+VbSignature* CalculateSignature(const uint8_t* data, uint64_t size,
+ const VbPrivateKey* key);
+
+#endif /* VBOOT_REFERENCE_HOST_SIGNATURE_H_ */
diff --git a/host/lib/host_common.c b/host/lib/host_common.c
new file mode 100644
index 00000000..6c4c4387
--- /dev/null
+++ b/host/lib/host_common.c
@@ -0,0 +1,185 @@
+/* Copyright (c) 2010 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.
+ *
+ * Host functions for verified boot.
+ */
+
+/* TODO: change all 'return 0', 'return 1' into meaningful return codes */
+
+#if 0
+#define OPENSSL_NO_SHA
+#include <openssl/engine.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "file_keys.h"
+#endif
+
+#include "host_common.h"
+
+#include "cryptolib.h"
+#include "utility.h"
+#include "vboot_common.h"
+
+
+VbKeyBlockHeader* CreateKeyBlock(const VbPublicKey* data_key,
+ const VbPrivateKey* signing_key,
+ uint64_t flags) {
+
+ VbKeyBlockHeader* h;
+ uint64_t signed_size = sizeof(VbKeyBlockHeader) + data_key->key_size;
+ uint64_t block_size = (signed_size + SHA512_DIGEST_SIZE +
+ siglen_map[signing_key->algorithm]);
+ uint8_t* data_key_dest;
+ uint8_t* block_sig_dest;
+ uint8_t* block_chk_dest;
+ VbSignature *sigtmp;
+
+ /* Allocate key block */
+ h = (VbKeyBlockHeader*)Malloc(block_size);
+ if (!h)
+ return NULL;
+ data_key_dest = (uint8_t*)(h + 1);
+ block_chk_dest = data_key_dest + data_key->key_size;
+ block_sig_dest = block_chk_dest + SHA512_DIGEST_SIZE;
+
+ Memcpy(h->magic, KEY_BLOCK_MAGIC, KEY_BLOCK_MAGIC_SIZE);
+ h->header_version_major = KEY_BLOCK_HEADER_VERSION_MAJOR;
+ h->header_version_minor = KEY_BLOCK_HEADER_VERSION_MINOR;
+ h->key_block_size = block_size;
+ h->key_block_flags = flags;
+
+ /* Copy data key */
+ PublicKeyInit(&h->data_key, data_key_dest, data_key->key_size);
+ PublicKeyCopy(&h->data_key, data_key);
+
+ /* Set up signature structs so we can calculate the signatures */
+ SignatureInit(&h->key_block_checksum, block_chk_dest,
+ SHA512_DIGEST_SIZE, signed_size);
+ SignatureInit(&h->key_block_signature, block_sig_dest,
+ siglen_map[signing_key->algorithm], signed_size);
+
+ /* Calculate checksum */
+ sigtmp = CalculateChecksum((uint8_t*)h, signed_size);
+ SignatureCopy(&h->key_block_checksum, sigtmp);
+ Free(sigtmp);
+
+ /* Calculate signature */
+ sigtmp = CalculateSignature((uint8_t*)h, signed_size, signing_key);
+ SignatureCopy(&h->key_block_signature, sigtmp);
+ Free(sigtmp);
+
+ /* Return the header */
+ return h;
+}
+
+
+VbFirmwarePreambleHeader* CreateFirmwarePreamble(
+ uint64_t firmware_version,
+ const VbPublicKey* kernel_subkey,
+ const VbSignature* body_signature,
+ const VbPrivateKey* signing_key) {
+
+ VbFirmwarePreambleHeader* h;
+ uint64_t signed_size = (sizeof(VbFirmwarePreambleHeader) +
+ kernel_subkey->key_size +
+ body_signature->sig_size);
+ uint64_t block_size = signed_size + siglen_map[signing_key->algorithm];
+ uint8_t* kernel_subkey_dest;
+ uint8_t* body_sig_dest;
+ uint8_t* block_sig_dest;
+ VbSignature *sigtmp;
+
+ /* Allocate key block */
+ h = (VbFirmwarePreambleHeader*)Malloc(block_size);
+ if (!h)
+ return NULL;
+ kernel_subkey_dest = (uint8_t*)(h + 1);
+ body_sig_dest = kernel_subkey_dest + kernel_subkey->key_size;
+ block_sig_dest = body_sig_dest + body_signature->sig_size;
+
+ h->header_version_major = FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR;
+ h->header_version_minor = FIRMWARE_PREAMBLE_HEADER_VERSION_MINOR;
+ h->preamble_size = block_size;
+ h->firmware_version = firmware_version;
+
+ /* Copy data key */
+ PublicKeyInit(&h->kernel_subkey, kernel_subkey_dest,
+ kernel_subkey->key_size);
+ PublicKeyCopy(&h->kernel_subkey, kernel_subkey);
+
+ /* Copy body signature */
+ SignatureInit(&h->body_signature, body_sig_dest,
+ body_signature->sig_size, 0);
+ SignatureCopy(&h->body_signature, body_signature);
+
+ /* Set up signature struct so we can calculate the signature */
+ SignatureInit(&h->preamble_signature, block_sig_dest,
+ siglen_map[signing_key->algorithm], signed_size);
+
+ /* Calculate signature */
+ sigtmp = CalculateSignature((uint8_t*)h, signed_size, signing_key);
+ SignatureCopy(&h->preamble_signature, sigtmp);
+ Free(sigtmp);
+
+ /* Return the header */
+ return h;
+}
+
+
+/* Creates a kernel preamble, signed with [signing_key].
+ * Caller owns the returned pointer, and must free it with Free().
+ *
+ * Returns NULL if error. */
+VbKernelPreambleHeader* CreateKernelPreamble(
+ uint64_t kernel_version,
+ uint64_t body_load_address,
+ uint64_t bootloader_address,
+ uint64_t bootloader_size,
+ const VbSignature* body_signature,
+ const VbPrivateKey* signing_key) {
+
+ VbKernelPreambleHeader* h;
+ uint64_t signed_size = (sizeof(VbKernelPreambleHeader) +
+ body_signature->sig_size);
+ uint64_t block_size = signed_size + siglen_map[signing_key->algorithm];
+ uint8_t* body_sig_dest;
+ uint8_t* block_sig_dest;
+ VbSignature *sigtmp;
+
+ /* Allocate key block */
+ h = (VbKernelPreambleHeader*)Malloc(block_size);
+ if (!h)
+ return NULL;
+ body_sig_dest = (uint8_t*)(h + 1);
+ block_sig_dest = body_sig_dest + body_signature->sig_size;
+
+ h->header_version_major = KERNEL_PREAMBLE_HEADER_VERSION_MAJOR;
+ h->header_version_minor = KERNEL_PREAMBLE_HEADER_VERSION_MINOR;
+ h->preamble_size = block_size;
+ h->kernel_version = kernel_version;
+ h->body_load_address = body_load_address;
+ h->bootloader_address = bootloader_address;
+ h->bootloader_size = bootloader_size;
+
+ /* Copy body signature */
+ SignatureInit(&h->body_signature, body_sig_dest,
+ body_signature->sig_size, 0);
+ SignatureCopy(&h->body_signature, body_signature);
+
+ /* Set up signature struct so we can calculate the signature */
+ SignatureInit(&h->preamble_signature, block_sig_dest,
+ siglen_map[signing_key->algorithm], signed_size);
+
+ /* Calculate signature */
+ sigtmp = CalculateSignature((uint8_t*)h, signed_size, signing_key);
+ SignatureCopy(&h->preamble_signature, sigtmp);
+ Free(sigtmp);
+
+ /* Return the header */
+ return h;
+}
diff --git a/host/lib/host_key.c b/host/lib/host_key.c
new file mode 100644
index 00000000..00770db3
--- /dev/null
+++ b/host/lib/host_key.c
@@ -0,0 +1,141 @@
+/* Copyright (c) 2010 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.
+ *
+ * Host functions for keys.
+ */
+
+/* TODO: change all 'return 0', 'return 1' into meaningful return codes */
+
+#define OPENSSL_NO_SHA
+#include <openssl/engine.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "host_key.h"
+
+#include "cryptolib.h"
+#include "file_keys.h"
+#include "utility.h"
+#include "vboot_common.h"
+
+
+VbPrivateKey* PrivateKeyRead(const char* filename, uint64_t algorithm) {
+
+ VbPrivateKey* key;
+ RSA* rsa_key;
+ FILE* f;
+
+ if (algorithm >= kNumAlgorithms) {
+ debug("PrivateKeyRead() called with invalid algorithm!\n");
+ return NULL;
+ }
+
+ /* Read private key */
+ f = fopen(filename, "r");
+ if (!f) {
+ debug("PrivateKeyRead(): Couldn't open key file: %s\n", filename);
+ return NULL;
+ }
+ rsa_key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL);
+ fclose(f);
+ if (!rsa_key) {
+ debug("PrivateKeyRead(): Couldn't read private key from file: %s\n",
+ filename);
+ return NULL;
+ }
+
+ /* Store key and algorithm in our struct */
+ key = (VbPrivateKey*)Malloc(sizeof(VbPrivateKey));
+ if (!key) {
+ RSA_free(rsa_key);
+ return NULL;
+ }
+ key->rsa_private_key = rsa_key;
+ key->algorithm = algorithm;
+
+ /* Return the key */
+ return key;
+}
+
+
+void PrivateKeyFree(VbPrivateKey* key) {
+ if (!key)
+ return;
+ if (key->rsa_private_key)
+ RSA_free(key->rsa_private_key);
+ Free(key);
+}
+
+
+void PublicKeyInit(VbPublicKey* key, uint8_t* key_data, uint64_t key_size) {
+ key->key_offset = OffsetOf(key, key_data);
+ key->key_size = key_size;
+ key->algorithm = kNumAlgorithms; /* Key not present yet */
+ key->key_version = 0;
+}
+
+
+VbPublicKey* PublicKeyAlloc(uint64_t key_size, uint64_t algorithm,
+ uint64_t version) {
+ VbPublicKey* key = (VbPublicKey*)Malloc(sizeof(VbPublicKey) + key_size);
+ if (!key)
+ return NULL;
+
+ key->algorithm = algorithm;
+ key->key_version = version;
+ key->key_size = key_size;
+ key->key_offset = sizeof(VbPublicKey);
+ return key;
+}
+
+
+int PublicKeyCopy(VbPublicKey* dest, const VbPublicKey* src) {
+ if (dest->key_size < src->key_size)
+ return 1;
+
+ dest->key_size = src->key_size;
+ dest->algorithm = src->algorithm;
+ dest->key_version = src->key_version;
+ Memcpy(GetPublicKeyData(dest), GetPublicKeyDataC(src), src->key_size);
+ return 0;
+}
+
+
+VbPublicKey* PublicKeyRead(const char* filename, uint64_t algorithm,
+ uint64_t version) {
+
+ VbPublicKey* key;
+ uint8_t* key_data;
+ uint64_t key_size;
+
+ if (algorithm >= kNumAlgorithms) {
+ debug("PublicKeyRead() called with invalid algorithm!\n");
+ return NULL;
+ }
+ if (version > 0xFFFF) {
+ /* Currently, TPM only supports 16-bit version */
+ debug("PublicKeyRead() called with invalid version!\n");
+ return NULL;
+ }
+
+ key_data = BufferFromFile(filename, &key_size);
+ if (!key_data)
+ return NULL;
+
+ /* TODO: sanity-check key length based on algorithm */
+
+ key = PublicKeyAlloc(key_size, algorithm, version);
+ if (!key) {
+ Free(key_data);
+ return NULL;
+ }
+ Memcpy(GetPublicKeyData(key), key_data, key_size);
+
+ Free(key_data);
+ return key;
+}
diff --git a/host/lib/host_signature.c b/host/lib/host_signature.c
new file mode 100644
index 00000000..935e7731
--- /dev/null
+++ b/host/lib/host_signature.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2010 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.
+ *
+ * Host functions for signature generation.
+ */
+
+/* TODO: change all 'return 0', 'return 1' into meaningful return codes */
+
+#define OPENSSL_NO_SHA
+#include <openssl/engine.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "cryptolib.h"
+#include "file_keys.h"
+#include "utility.h"
+#include "vboot_common.h"
+#include "host_common.h"
+
+
+VbSignature* SignatureAlloc(uint64_t sig_size, uint64_t data_size) {
+ VbSignature* sig = (VbSignature*)Malloc(sizeof(VbSignature) + sig_size);
+ if (!sig)
+ return NULL;
+
+ sig->sig_offset = sizeof(VbSignature);
+ sig->sig_size = sig_size;
+ sig->data_size = data_size;
+ return sig;
+}
+
+
+void SignatureInit(VbSignature* sig, uint8_t* sig_data,
+ uint64_t sig_size, uint64_t data_size) {
+ sig->sig_offset = OffsetOf(sig, sig_data);
+ sig->sig_size = sig_size;
+ sig->data_size = data_size;
+}
+
+
+int SignatureCopy(VbSignature* dest, const VbSignature* src) {
+ if (dest->sig_size < src->sig_size)
+ return 1;
+ dest->sig_size = src->sig_size;
+ dest->data_size = src->data_size;
+ Memcpy(GetSignatureData(dest), GetSignatureDataC(src), src->sig_size);
+ return 0;
+}
+
+
+VbSignature* CalculateChecksum(const uint8_t* data, uint64_t size) {
+
+ uint8_t* header_checksum;
+ VbSignature* sig;
+
+ header_checksum = DigestBuf(data, size, SHA512_DIGEST_ALGORITHM);
+ if (!header_checksum)
+ return NULL;
+
+ sig = SignatureAlloc(SHA512_DIGEST_SIZE, 0);
+ if (!sig) {
+ Free(header_checksum);
+ return NULL;
+ }
+ sig->sig_offset = sizeof(VbSignature);
+ sig->sig_size = SHA512_DIGEST_SIZE;
+ sig->data_size = size;
+
+ /* Signature data immediately follows the header */
+ Memcpy(GetSignatureData(sig), header_checksum, SHA512_DIGEST_SIZE);
+ Free(header_checksum);
+ return sig;
+}
+
+
+VbSignature* CalculateSignature(const uint8_t* data, uint64_t size,
+ const VbPrivateKey* key) {
+
+ uint8_t* digest;
+ int digest_size = hash_size_map[key->algorithm];
+
+ const uint8_t* digestinfo = hash_digestinfo_map[key->algorithm];
+ int digestinfo_size = digestinfo_size_map[key->algorithm];
+
+ uint8_t* signature_digest;
+ int signature_digest_len = digest_size + digestinfo_size;
+
+ VbSignature* sig;
+ int rv;
+
+ /* Calculate the digest */
+ /* TODO: rename param 3 of DigestBuf to hash_type */
+ digest = DigestBuf(data, size, hash_type_map[key->algorithm]);
+ if (!digest)
+ return NULL;
+
+ /* Prepend the digest info to the digest */
+ signature_digest = Malloc(signature_digest_len);
+ if (!signature_digest) {
+ Free(digest);
+ return NULL;
+ }
+ Memcpy(signature_digest, digestinfo, digestinfo_size);
+ Memcpy(signature_digest + digestinfo_size, digest, digest_size);
+ Free(digest);
+
+ /* Allocate output signature */
+ sig = SignatureAlloc(siglen_map[key->algorithm], size);
+ if (!sig) {
+ Free(signature_digest);
+ return NULL;
+ }
+
+ /* Sign the signature_digest into our output buffer */
+ rv = RSA_private_encrypt(signature_digest_len, /* Input length */
+ signature_digest, /* Input data */
+ GetSignatureData(sig), /* Output sig */
+ key->rsa_private_key, /* Key to use */
+ RSA_PKCS1_PADDING); /* Padding to use */
+ Free(signature_digest);
+
+ if (-1 == rv) {
+ debug("SignatureBuf(): RSA_private_encrypt() failed.\n");
+ Free(sig);
+ return NULL;
+ }
+
+ /* Return the signature */
+ return sig;
+}
diff --git a/host/linktest/main.c b/host/linktest/main.c
new file mode 100644
index 00000000..91fc5aff
--- /dev/null
+++ b/host/linktest/main.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+
+#include "host_common.h"
+
+int main(void)
+{
+ /* host_key.h */
+ PrivateKeyRead(0, 0);
+ PrivateKeyFree(0);
+ PublicKeyInit(0, 0, 0);
+ PublicKeyAlloc(0, 0, 0);
+ PublicKeyCopy(0, 0);
+ PublicKeyRead(0, 0, 0);
+
+ /* host_signature.h */
+ SignatureInit(0, 0, 0, 0);
+ SignatureAlloc(0, 0);
+ SignatureCopy(0, 0);
+ CalculateChecksum(0, 0);
+ CalculateSignature(0, 0, 0);
+
+ /* host_common.h */
+ CreateKeyBlock(0, 0, 0);
+ CreateFirmwarePreamble(0, 0, 0, 0);
+ CreateKernelPreamble(0, 0, 0, 0, 0, 0);
+
+ return 0;
+}
diff --git a/tests/Makefile b/tests/Makefile
index 05577335..15799fb2 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -6,8 +6,10 @@ TOP ?= ../
CC ?= gcc
CFLAGS ?= -Wall -DNDEBUG -O3 -Werror
INCLUDES += -I./include \
+ -I$(FWDIR)/lib/include \
-I$(FWDIR)/lib/cgptlib/include \
-I$(FWDIR)/lib/cryptolib/include \
+ -I../host/include \
-I../misclibs/include \
-I../vboot_firmware/lib/include\
-I../vfirmware/include\
@@ -15,7 +17,7 @@ INCLUDES += -I./include \
IMAGE_LIBS = $(TOP)/vfirmware/firmware_image.o \
$(TOP)/vkernel/kernel_image.o
UTIL_LIBS = $(TOP)/misclibs/file_keys.o $(TOP)/misclibs/signature_digest.o
-LIBS = $(IMAGE_LIBS) $(UTIL_LIBS) $(FWLIB) -lcrypto
+LIBS = $(IMAGE_LIBS) $(UTIL_LIBS) $(HOSTLIB) $(FWLIB) -lcrypto
TEST_BINS = big_firmware_tests \
big_kernel_tests \
@@ -32,6 +34,9 @@ TEST_BINS = big_firmware_tests \
rsa_verify_benchmark \
sha_benchmark \
sha_tests \
+ vboot_common_tests \
+ vboot_common2_tests \
+ vboot_common3_tests \
verify_firmware_fuzz_driver \
verify_kernel_fuzz_driver
@@ -90,6 +95,15 @@ sha_benchmark: sha_benchmark.c timer_utils.c
sha_tests: sha_tests.c
$(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(FWLIB)
+vboot_common_tests: vboot_common_tests.c rollback_index_mock.c test_common.c
+ $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
+
+vboot_common2_tests: vboot_common2_tests.c test_common.c $(HOSTLIB) $(FWLIB)
+ $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
+
+vboot_common3_tests: vboot_common3_tests.c test_common.c $(HOSTLIB) $(FWLIB)
+ $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
+
verify_firmware_fuzz_driver: verify_firmware_fuzz_driver.c \
rollback_index_mock.c
$(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
@@ -101,6 +115,7 @@ runtests:
# Crypto tests
./run_rsa_tests.sh
./sha_tests
+ ./run_vboot_common_tests.sh
./run_image_verification_tests.sh
# Splicing tests
./firmware_splicing_tests
diff --git a/tests/run_image_verification_tests.sh b/tests/run_image_verification_tests.sh
index b634b71c..025066a4 100755
--- a/tests/run_image_verification_tests.sh
+++ b/tests/run_image_verification_tests.sh
@@ -69,6 +69,7 @@ ${kernel_hashalgo}${COL_STOP}"
}
check_test_keys
+
echo
echo "Testing high-level firmware image verification..."
test_firmware_verification
diff --git a/tests/run_vboot_common_tests.sh b/tests/run_vboot_common_tests.sh
new file mode 100755
index 00000000..6295f404
--- /dev/null
+++ b/tests/run_vboot_common_tests.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+
+# Copyright (c) 2010 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.
+
+# Run verified boot firmware and kernel verification tests.
+
+# Load common constants and variables.
+. "$(dirname "$0")/common.sh"
+
+return_code=0
+
+function test_vboot_common {
+ ${TEST_DIR}/vboot_common_tests
+ if [ $? -ne 0 ]
+ then
+ return_code=255
+ fi
+}
+
+function test_vboot_common2 {
+ algorithmcounter=0
+ for keylen in ${key_lengths[@]}
+ do
+ for hashalgo in ${hash_algos[@]}
+ do
+ echo -e "For signing key ${COL_YELLOW}RSA-$keylen/$hashalgo${COL_STOP}:"
+ ${TEST_DIR}/vboot_common2_tests $algorithmcounter \
+ ${TESTKEY_DIR}/key_rsa${keylen}.pem \
+ ${TESTKEY_DIR}/key_rsa${keylen}.keyb
+ if [ $? -ne 0 ]
+ then
+ return_code=255
+ fi
+ let algorithmcounter=algorithmcounter+1
+ done
+ done
+}
+
+function test_vboot_common3 {
+# Test for various combinations of firmware signing algorithm and
+# kernel signing algorithm
+ firmware_algorithmcounter=0
+ kernel_algorithmcounter=0
+ for firmware_keylen in ${key_lengths[@]}
+ do
+ for firmware_hashalgo in ${hash_algos[@]}
+ do
+ let kernel_algorithmcounter=0
+ for kernel_keylen in ${key_lengths[@]}
+ do
+ for kernel_hashalgo in ${hash_algos[@]}
+ do
+ echo -e "For ${COL_YELLOW}signing algorithm \
+RSA-${firmware_keylen}/${firmware_hashalgo}${COL_STOP} \
+and ${COL_YELLOW}data signing algorithm RSA-${kernel_keylen}/\
+${kernel_hashalgo}${COL_STOP}"
+ ${TEST_DIR}/vboot_common3_tests \
+ $firmware_algorithmcounter $kernel_algorithmcounter \
+ ${TESTKEY_DIR}/key_rsa${firmware_keylen}.pem \
+ ${TESTKEY_DIR}/key_rsa${firmware_keylen}.keyb \
+ ${TESTKEY_DIR}/key_rsa${kernel_keylen}.pem \
+ ${TESTKEY_DIR}/key_rsa${kernel_keylen}.keyb
+ if [ $? -ne 0 ]
+ then
+ return_code=255
+ fi
+ let kernel_algorithmcounter=kernel_algorithmcounter+1
+ done
+ done
+ let firmware_algorithmcounter=firmware_algorithmcounter+1
+ done
+ done
+}
+
+check_test_keys
+echo
+echo "Testing vboot_common tests which don't depend on keys..."
+test_vboot_common
+
+echo
+echo "Testing vboot_common tests which depend on one key..."
+test_vboot_common2
+
+echo
+echo "Testing vboot_common tests which depend on two keys..."
+test_vboot_common3
+
+
+exit $return_code
diff --git a/tests/vboot_common2_tests.c b/tests/vboot_common2_tests.c
new file mode 100644
index 00000000..361de83e
--- /dev/null
+++ b/tests/vboot_common2_tests.c
@@ -0,0 +1,210 @@
+/* Copyright (c) 2010 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.
+ *
+ * Tests for firmware image library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cryptolib.h"
+#include "file_keys.h"
+#include "firmware_image.h"
+#include "host_common.h"
+#include "test_common.h"
+#include "utility.h"
+#include "vboot_common.h"
+
+
+static void VerifyPublicKeyToRSA(const VbPublicKey* orig_key) {
+
+ RSAPublicKey *rsa;
+ VbPublicKey *key = PublicKeyAlloc(orig_key->key_size, 0, 0);
+
+ PublicKeyCopy(key, orig_key);
+ key->algorithm = kNumAlgorithms;
+ TEST_EQ((size_t)PublicKeyToRSA(key), 0,
+ "PublicKeyToRSA() invalid algorithm");
+
+ PublicKeyCopy(key, orig_key);
+ key->key_size -= 1;
+ TEST_EQ((size_t)PublicKeyToRSA(key), 0,
+ "PublicKeyToRSA() invalid size");
+
+ rsa = PublicKeyToRSA(orig_key);
+ TEST_NEQ((size_t)rsa, 0, "PublicKeyToRSA() ok");
+ if (rsa) {
+ TEST_EQ(rsa->algorithm, key->algorithm, "PublicKeyToRSA() algorithm");
+ RSAPublicKeyFree(rsa);
+ }
+}
+
+
+static void VerifyDataTest(const VbPublicKey* public_key,
+ const VbPrivateKey* private_key) {
+
+ const uint8_t test_data[] = "This is some test data to sign.";
+ VbSignature *sig;
+ RSAPublicKey *rsa;
+
+ sig = CalculateSignature(test_data, sizeof(test_data), private_key);
+ rsa = PublicKeyToRSA(public_key);
+ TEST_NEQ(sig && rsa, 0, "VerifyData() prerequisites");
+ if (!sig || !rsa)
+ return;
+
+ TEST_EQ(VerifyData(test_data, sig, rsa), 0, "VerifyData() ok");
+
+ sig->sig_size -= 16;
+ TEST_EQ(VerifyData(test_data, sig, rsa), 1, "VerifyData() wrong sig size");
+ sig->sig_size += 16;
+
+ GetSignatureData(sig)[0] ^= 0x5A;
+ TEST_EQ(VerifyData(test_data, sig, rsa), 1, "VerifyData() wrong sig");
+
+ RSAPublicKeyFree(rsa);
+ Free(sig);
+}
+
+
+static void ReSignKernelPreamble(VbKernelPreambleHeader *h,
+ const VbPrivateKey *key) {
+ VbSignature *sig = CalculateSignature((const uint8_t*)h,
+ h->preamble_signature.data_size, key);
+
+ SignatureCopy(&h->preamble_signature, sig);
+ Free(sig);
+}
+
+
+static void VerifyKernelPreambleTest(const VbPublicKey* public_key,
+ const VbPrivateKey* private_key) {
+
+ VbKernelPreambleHeader *hdr;
+ VbKernelPreambleHeader *h;
+ RSAPublicKey* rsa;
+ uint64_t hsize;
+
+ /* Create a dummy signature */
+ VbSignature *body_sig = SignatureAlloc(56, 78);
+
+ rsa = PublicKeyToRSA(public_key);
+ hdr = CreateKernelPreamble(0x1234, 0x100000, 0x300000, 0x4000, body_sig,
+ private_key);
+ TEST_NEQ(hdr && rsa, 0, "VerifyKernelPreamble2() prerequisites");
+ if (!hdr)
+ return;
+ hsize = hdr->preamble_size;
+ h = (VbKernelPreambleHeader*)Malloc(hsize + 16384);
+
+ TEST_EQ(VerifyKernelPreamble2(hdr, hsize, rsa), 0,
+ "VerifyKernelPreamble2() ok using key");
+ TEST_NEQ(VerifyKernelPreamble2(hdr, hsize-1, rsa), 0,
+ "VerifyKernelPreamble2() size");
+
+ /* Care about major version but not minor */
+ Memcpy(h, hdr, hsize);
+ h->header_version_major++;
+ ReSignKernelPreamble(h, private_key);
+ TEST_NEQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() major++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_major--;
+ ReSignKernelPreamble(h, private_key);
+ TEST_NEQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() major--");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor++;
+ ReSignKernelPreamble(h, private_key);
+ TEST_EQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() minor++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor--;
+ ReSignKernelPreamble(h, private_key);
+ TEST_EQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() minor--");
+
+ /* Check signature */
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.sig_offset = hsize;
+ ReSignKernelPreamble(h, private_key);
+ TEST_NEQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() sig off end");
+
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.sig_size--;
+ ReSignKernelPreamble(h, private_key);
+ TEST_NEQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() sig too small");
+
+ Memcpy(h, hdr, hsize);
+ GetSignatureData(&h->body_signature)[0] ^= 0x34;
+ TEST_NEQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() sig mismatch");
+
+ /* Check that we signed header and body sig */
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.data_size = 4;
+ h->body_signature.sig_offset = 0;
+ h->body_signature.sig_size = 0;
+ ReSignKernelPreamble(h, private_key);
+ TEST_NEQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() didn't sign header");
+
+ Memcpy(h, hdr, hsize);
+ h->body_signature.sig_offset = hsize;
+ ReSignKernelPreamble(h, private_key);
+ TEST_NEQ(VerifyKernelPreamble2(h, hsize, rsa), 0,
+ "VerifyKernelPreamble2() body sig off end");
+
+ /* TODO: verify parser can support a bigger header. */
+
+ Free(h);
+ RSAPublicKeyFree(rsa);
+ Free(hdr);
+}
+
+
+int main(int argc, char* argv[]) {
+ VbPrivateKey* private_key = NULL;
+ VbPublicKey* public_key = NULL;
+ int key_algorithm;
+
+ int error_code = 0;
+
+ if(argc != 4) {
+ fprintf(stderr, "Usage: %s <key_algorithm> <key> <processed pubkey>"
+ " <signing key> <processed signing key>\n", argv[0]);
+ return -1;
+ }
+
+ /* Read verification keys and create a test image. */
+ key_algorithm = atoi(argv[1]);
+
+ private_key = PrivateKeyRead(argv[2], key_algorithm);
+ if (!private_key) {
+ fprintf(stderr, "Error reading private_key");
+ return 1;
+ }
+
+ public_key = PublicKeyRead(argv[3], key_algorithm, 1);
+ if (!public_key) {
+ fprintf(stderr, "Error reading public_key");
+ return 1;
+ }
+
+ VerifyPublicKeyToRSA(public_key);
+ VerifyDataTest(public_key, private_key);
+ VerifyKernelPreambleTest(public_key, private_key);
+
+ if (public_key)
+ Free(public_key);
+ if (private_key)
+ Free(private_key);
+
+ return error_code;
+}
diff --git a/tests/vboot_common3_tests.c b/tests/vboot_common3_tests.c
new file mode 100644
index 00000000..a4e69fd1
--- /dev/null
+++ b/tests/vboot_common3_tests.c
@@ -0,0 +1,295 @@
+/* Copyright (c) 2010 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.
+ *
+ * Tests for firmware image library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cryptolib.h"
+#include "file_keys.h"
+#include "firmware_image.h"
+#include "host_common.h"
+#include "test_common.h"
+#include "utility.h"
+#include "vboot_common.h"
+
+
+static void ReChecksumKeyBlock(VbKeyBlockHeader *h) {
+ uint8_t* newchk = DigestBuf((const uint8_t*)h,
+ h->key_block_checksum.data_size,
+ SHA512_DIGEST_ALGORITHM);
+ Memcpy(GetSignatureData(&h->key_block_checksum), newchk, SHA512_DIGEST_SIZE);
+ Free(newchk);
+}
+
+
+static void VerifyKeyBlockTest(const VbPublicKey* public_key,
+ const VbPrivateKey* private_key,
+ const VbPublicKey* data_key) {
+
+ VbKeyBlockHeader *hdr;
+ VbKeyBlockHeader *h;
+ uint64_t hsize;
+
+ hdr = CreateKeyBlock(data_key, private_key, 0x1234);
+ TEST_NEQ((size_t)hdr, 0, "VerifyKeyBlock() prerequisites");
+ if (!hdr)
+ return;
+ hsize = hdr->key_block_size;
+ h = (VbKeyBlockHeader*)Malloc(hsize + 1024);
+
+ TEST_EQ(VerifyKeyBlock(hdr, hsize, NULL), 0,
+ "VerifyKeyBlock() ok using checksum");
+ TEST_EQ(VerifyKeyBlock(hdr, hsize, public_key), 0,
+ "VerifyKeyBlock() ok using key");
+
+ TEST_NEQ(VerifyKeyBlock(hdr, hsize-1, NULL), 0, "VerifyKeyBlock() size");
+
+ Memcpy(h, hdr, hsize);
+ h->magic[0] &= 0x12;
+ TEST_NEQ(VerifyKeyBlock(h, hsize, NULL), 0, "VerifyKeyBlock() magic");
+
+ /* Care about major version but not minor */
+ Memcpy(h, hdr, hsize);
+ h->header_version_major++;
+ ReChecksumKeyBlock(h);
+ TEST_NEQ(VerifyKeyBlock(h, hsize, NULL), 0, "VerifyKeyBlock() major++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_major--;
+ ReChecksumKeyBlock(h);
+ TEST_NEQ(VerifyKeyBlock(h, hsize, NULL), 0, "VerifyKeyBlock() major--");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor++;
+ ReChecksumKeyBlock(h);
+ TEST_EQ(VerifyKeyBlock(h, hsize, NULL), 0, "VerifyKeyBlock() minor++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor--;
+ ReChecksumKeyBlock(h);
+ TEST_EQ(VerifyKeyBlock(h, hsize, NULL), 0, "VerifyKeyBlock() minor--");
+
+ /* Check hash */
+ Memcpy(h, hdr, hsize);
+ h->key_block_checksum.sig_offset = hsize;
+ ReChecksumKeyBlock(h);
+ TEST_NEQ(VerifyKeyBlock(h, hsize, NULL), 0,
+ "VerifyKeyBlock() checksum off end");
+
+ Memcpy(h, hdr, hsize);
+ h->key_block_checksum.sig_size /= 2;
+ ReChecksumKeyBlock(h);
+ TEST_NEQ(VerifyKeyBlock(h, hsize, NULL), 0,
+ "VerifyKeyBlock() checksum too small");
+
+ Memcpy(h, hdr, hsize);
+ GetPublicKeyData(&h->data_key)[0] ^= 0x34;
+ TEST_NEQ(VerifyKeyBlock(h, hsize, NULL), 0,
+ "VerifyKeyBlock() checksum mismatch");
+
+ /* Check signature */
+ Memcpy(h, hdr, hsize);
+ h->key_block_signature.sig_offset = hsize;
+ ReChecksumKeyBlock(h);
+ TEST_NEQ(VerifyKeyBlock(h, hsize, public_key), 0,
+ "VerifyKeyBlock() sig off end");
+
+ Memcpy(h, hdr, hsize);
+ h->key_block_signature.sig_size--;
+ ReChecksumKeyBlock(h);
+ TEST_NEQ(VerifyKeyBlock(h, hsize, public_key), 0,
+ "VerifyKeyBlock() sig too small");
+
+ Memcpy(h, hdr, hsize);
+ GetPublicKeyData(&h->data_key)[0] ^= 0x34;
+ TEST_NEQ(VerifyKeyBlock(h, hsize, public_key), 0,
+ "VerifyKeyBlock() sig mismatch");
+
+ /* Check that we signed header and data key */
+ Memcpy(h, hdr, hsize);
+ h->key_block_checksum.data_size = 4;
+ h->data_key.key_offset = 0;
+ h->data_key.key_size = 0;
+ ReChecksumKeyBlock(h);
+ TEST_NEQ(VerifyKeyBlock(h, hsize, NULL), 0,
+ "VerifyKeyBlock() didn't sign header");
+
+ Memcpy(h, hdr, hsize);
+ h->data_key.key_offset = hsize;
+ ReChecksumKeyBlock(h);
+ TEST_NEQ(VerifyKeyBlock(h, hsize, NULL), 0,
+ "VerifyKeyBlock() data key off end");
+
+ /* TODO: verify parser can support a bigger header (i.e., one where
+ * data_key.key_offset is bigger than expected). */
+
+ Free(h);
+ Free(hdr);
+}
+
+
+static void ReSignFirmwarePreamble(VbFirmwarePreambleHeader *h,
+ const VbPrivateKey *key) {
+ VbSignature *sig = CalculateSignature((const uint8_t*)h,
+ h->preamble_signature.data_size, key);
+
+ SignatureCopy(&h->preamble_signature, sig);
+ Free(sig);
+}
+
+
+static void VerifyFirmwarePreambleTest(const VbPublicKey* public_key,
+ const VbPrivateKey* private_key,
+ const VbPublicKey* kernel_subkey) {
+
+ VbFirmwarePreambleHeader *hdr;
+ VbFirmwarePreambleHeader *h;
+ RSAPublicKey* rsa;
+ uint64_t hsize;
+
+ /* Create a dummy signature */
+ VbSignature *body_sig = SignatureAlloc(56, 78);
+
+ rsa = PublicKeyToRSA(public_key);
+ hdr = CreateFirmwarePreamble(0x1234, kernel_subkey, body_sig, private_key);
+ TEST_NEQ(hdr && rsa, 0, "VerifyFirmwarePreamble2() prerequisites");
+ if (!hdr)
+ return;
+ hsize = hdr->preamble_size;
+ h = (VbFirmwarePreambleHeader*)Malloc(hsize + 16384);
+
+ TEST_EQ(VerifyFirmwarePreamble2(hdr, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() ok using key");
+ TEST_NEQ(VerifyFirmwarePreamble2(hdr, hsize-1, rsa), 0,
+ "VerifyFirmwarePreamble2() size");
+
+ /* Care about major version but not minor */
+ Memcpy(h, hdr, hsize);
+ h->header_version_major++;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_NEQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() major++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_major--;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_NEQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() major--");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor++;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_EQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() minor++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor--;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_EQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() minor--");
+
+ /* Check signature */
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.sig_offset = hsize;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_NEQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() sig off end");
+
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.sig_size--;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_NEQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() sig too small");
+
+ Memcpy(h, hdr, hsize);
+ GetPublicKeyData(&h->kernel_subkey)[0] ^= 0x34;
+ TEST_NEQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() sig mismatch");
+
+ /* Check that we signed header, kernel subkey, and body sig */
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.data_size = 4;
+ h->kernel_subkey.key_offset = 0;
+ h->kernel_subkey.key_size = 0;
+ h->body_signature.sig_offset = 0;
+ h->body_signature.sig_size = 0;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_NEQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() didn't sign header");
+
+ Memcpy(h, hdr, hsize);
+ h->kernel_subkey.key_offset = hsize;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_NEQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() kernel subkey off end");
+
+ Memcpy(h, hdr, hsize);
+ h->body_signature.sig_offset = hsize;
+ ReSignFirmwarePreamble(h, private_key);
+ TEST_NEQ(VerifyFirmwarePreamble2(h, hsize, rsa), 0,
+ "VerifyFirmwarePreamble2() body sig off end");
+
+ /* TODO: verify parser can support a bigger header. */
+
+ Free(h);
+ RSAPublicKeyFree(rsa);
+ Free(hdr);
+}
+
+
+int main(int argc, char* argv[]) {
+ VbPrivateKey* signing_private_key = NULL;
+ VbPublicKey* signing_public_key = NULL;
+ int signing_key_algorithm;
+
+ VbPublicKey* data_public_key = NULL;
+ int data_key_algorithm;
+
+ int error_code = 0;
+
+ if(argc != 7) {
+ fprintf(stderr, "Usage: %s <signing_key_algorithm> <data_key_algorithm>"
+ " <signing key> <processed signing pubkey>"
+ " <data key> <processed data pubkey>\n", argv[0]);
+ return -1;
+ }
+
+ /* Read verification keys and create a test image. */
+ signing_key_algorithm = atoi(argv[1]);
+ data_key_algorithm = atoi(argv[2]);
+
+ signing_private_key = PrivateKeyRead(argv[3], signing_key_algorithm);
+ if (!signing_private_key) {
+ fprintf(stderr, "Error reading signing_private_key");
+ return 1;
+ }
+
+ signing_public_key = PublicKeyRead(argv[4], signing_key_algorithm, 1);
+ if (!signing_public_key) {
+ fprintf(stderr, "Error reading signing_public_key");
+ return 1;
+ }
+
+ data_public_key = PublicKeyRead(argv[6], data_key_algorithm, 1);
+ if (!data_public_key) {
+ fprintf(stderr, "Error reading data_public_key");
+ return 1;
+ }
+
+ VerifyKeyBlockTest(signing_public_key, signing_private_key, data_public_key);
+ VerifyFirmwarePreambleTest(signing_public_key, signing_private_key,
+ data_public_key);
+
+ if (signing_public_key)
+ Free(signing_public_key);
+ if (signing_private_key)
+ Free(signing_private_key);
+ if (data_public_key)
+ Free(data_public_key);
+
+ return error_code;
+}
diff --git a/tests/vboot_common_tests.c b/tests/vboot_common_tests.c
new file mode 100644
index 00000000..e467471e
--- /dev/null
+++ b/tests/vboot_common_tests.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2010 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.
+ *
+ * Tests for firmware image library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test_common.h"
+#include "vboot_common.h"
+
+
+/* Helper functions not dependent on specific key sizes */
+void VerifyHelperFunctions(void) {
+
+ {
+ uint8_t p[1];
+ TEST_EQ(OffsetOf(p, p), 0, "OffsetOf() equal");
+ TEST_EQ(OffsetOf(p, p+10), 10, "OffsetOf() positive");
+ TEST_EQ(OffsetOf(p, p+0x12345678), 0x12345678, "OffsetOf() large");
+ }
+
+ {
+ VbPublicKey k = {sizeof(k), 2, 3, 4};
+ TEST_EQ(OffsetOf(&k, GetPublicKeyData(&k)), sizeof(k),
+ "GetPublicKeyData() adjacent");
+ TEST_EQ(OffsetOf(&k, GetPublicKeyDataC(&k)), sizeof(k),
+ "GetPublicKeyDataC() adjacent");
+ }
+
+ {
+ VbPublicKey k = {123, 2, 3, 4};
+ TEST_EQ(OffsetOf(&k, GetPublicKeyData(&k)), 123,
+ "GetPublicKeyData() spaced");
+ TEST_EQ(OffsetOf(&k, GetPublicKeyDataC(&k)), 123,
+ "GetPublicKeyDataC() spaced");
+ }
+
+ {
+ uint8_t p[1];
+ TEST_EQ(VerifyMemberInside(p, 20, p, 6, 11, 3), 0, "MemberInside ok 1");
+ TEST_EQ(VerifyMemberInside(p, 20, p+4, 4, 8, 4), 0, "MemberInside ok 2");
+ TEST_EQ(VerifyMemberInside(p, 20, p-4, 4, 8, 4), 1,
+ "MemberInside member before parent");
+ TEST_EQ(VerifyMemberInside(p, 20, p+20, 4, 8, 4), 1,
+ "MemberInside member after parent");
+ TEST_EQ(VerifyMemberInside(p, 20, p, 21, 0, 0), 1,
+ "MemberInside member too big");
+ TEST_EQ(VerifyMemberInside(p, 20, p, 4, 21, 0), 1,
+ "MemberInside data after parent");
+ TEST_EQ(VerifyMemberInside(p, 20, p, 4, -1, 0), 1,
+ "MemberInside data before parent");
+ TEST_EQ(VerifyMemberInside(p, 20, p, 4, 4, 17), 1,
+ "MemberInside data too big");
+ }
+
+ {
+ VbPublicKey k = {sizeof(k), 128, 0, 0};
+ TEST_EQ(VerifyPublicKeyInside(&k, sizeof(k)+128, &k), 0,
+ "PublicKeyInside ok 1");
+ TEST_EQ(VerifyPublicKeyInside(&k - 1, 2*sizeof(k)+128, &k), 0,
+ "PublicKeyInside ok 2");
+ TEST_EQ(VerifyPublicKeyInside(&k, 128, &k), 1,
+ "PublicKeyInside key too big");
+ }
+ {
+ VbPublicKey k = {100, 4, 0, 0};
+ TEST_EQ(VerifyPublicKeyInside(&k, 99, &k), 1,
+ "PublicKeyInside offset too big");
+ }
+ {
+ VbSignature s = {sizeof(s), 128, 2000};
+ TEST_EQ(VerifySignatureInside(&s, sizeof(s)+128, &s), 0,
+ "SignatureInside ok 1");
+ TEST_EQ(VerifySignatureInside(&s - 1, 2*sizeof(s)+128, &s), 0,
+ "SignatureInside ok 2");
+ TEST_EQ(VerifySignatureInside(&s, 128, &s), 1,
+ "SignatureInside sig too big");
+ }
+ {
+ VbSignature s = {100, 4, 0};
+ TEST_EQ(VerifySignatureInside(&s, 99, &s), 1,
+ "SignatureInside offset too big");
+ }
+
+}
+
+
+int main(int argc, char* argv[]) {
+ int error_code = 0;
+
+ /* Test helper functions */
+ VerifyHelperFunctions();
+
+ if (!gTestSuccess)
+ error_code = 255;
+
+ return error_code;
+}
diff --git a/utility/Makefile b/utility/Makefile
index d116eb00..1f50aebd 100644
--- a/utility/Makefile
+++ b/utility/Makefile
@@ -10,6 +10,7 @@ INCLUDES += -I./include \
-I$(FWDIR)/lib/include \
-I$(FWDIR)/lib/cgptlib/include \
-I$(FWDIR)/lib/cryptolib/include \
+ -I$(HOSTDIR)/include \
-I../misclibs/include \
-I../vfirmware/include\
-I../vboot_firmware/include\
@@ -18,7 +19,9 @@ CFLAGS ?= -Wall -DNDEBUG -O3 -Werror $(INCLUDES)
LIBS = $(TOP)/misclibs/file_keys.o \
$(TOP)/misclibs/signature_digest.o \
$(TOP)/vfirmware/firmware_image.o \
- $(TOP)/vkernel/kernel_image.o
+ $(TOP)/vkernel/kernel_image.o \
+ $(FWLIB) \
+ $(HOSTLIB)
SUBDIRS = cgpt
DESTDIR ?= /opt/bin
@@ -43,25 +46,28 @@ subdirs:
dumpRSAPublicKey: dumpRSAPublicKey.c
$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ -lcrypto
-firmware_utility: firmware_utility.cc $(LIBS) $(FWLIB)
+firmware_utility: firmware_utility.cc $(LIBS)
$(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \
- -o $@ $(LIBS) $(FWLIB) -lcrypto
+ -o $@ $(LIBS) -lcrypto
gbb_utility: gbb_utility.cc
$(CXX) -DWITH_UTIL_MAIN $(CFLAGS) $(INCLUDES) $< -o $@
-kernel_utility: kernel_utility.cc $(LIBS) $(FWLIB)
+load_kernel_test: load_kernel_test.c $(LIBS)
+ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto
+
+kernel_utility: kernel_utility.cc $(LIBS)
$(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \
- -o $@ $(LIBS) $(FWLIB) -lcrypto
+ -o $@ $(LIBS) -lcrypto
-load_kernel_test: load_kernel_test.c $(LIBS) $(FWLIB)
- $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FWLIB) -lcrypto
+load_kernel_test: load_kernel_test.c $(LIBS)
+ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto
-signature_digest_utility: signature_digest_utility.c $(LIBS) $(FWLIB)
- $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FWLIB) -lcrypto
+signature_digest_utility: signature_digest_utility.c $(LIBS)
+ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto
-verify_data: verify_data.c $(LIBS) $(FWLIB)
- $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FWLIB) -lcrypto
+verify_data: verify_data.c $(LIBS)
+ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto
clean:
set -e; \
@@ -74,3 +80,6 @@ install: $(TARGET_BINS) subdirs
mkdir -p $(DESTDIR)
cp -f $(TARGET_BINS) $(DESTDIR)
chmod a+rx $(patsubst %,$(DESTDIR)/%,$(TARGET_BINS))
+
+%o : %c
+ $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
diff --git a/utility/load_kernel_test.c b/utility/load_kernel_test.c
index da3532c3..7beb414e 100644
--- a/utility/load_kernel_test.c
+++ b/utility/load_kernel_test.c
@@ -135,7 +135,7 @@ int main(int argc, char* argv[]) {
}
/* TODO: Option for boot mode */
- lkp.boot_mode = BOOT_MODE_NORMAL;
+ lkp.boot_flags = 0;
/* Call LoadKernel() */
rv = LoadKernel(&lkp);
diff --git a/vboot_firmware/Makefile b/vboot_firmware/Makefile
index b4345e0a..efdb4db4 100644
--- a/vboot_firmware/Makefile
+++ b/vboot_firmware/Makefile
@@ -35,7 +35,10 @@ LIB_SRCS = \
./lib/load_firmware_fw.c \
./lib/load_kernel_fw.c \
./lib/rollback_index.c \
- ./lib/stateful_util.c
+ ./lib/stateful_util.c \
+ ./lib/vboot_common.c \
+ ./lib/vboot_firmware.c \
+ ./lib/vboot_kernel.c
LIB_OBJS = $(LIB_SRCS:%.c=%.o)
diff --git a/vboot_firmware/include/load_firmware_fw.h b/vboot_firmware/include/load_firmware_fw.h
index 58dd6da5..312a0aaa 100644
--- a/vboot_firmware/include/load_firmware_fw.h
+++ b/vboot_firmware/include/load_firmware_fw.h
@@ -44,8 +44,10 @@ void UpdateFirmwareBodyHash(uint8_t* data, uint64_t size);
typedef struct LoadFirmwareParams {
/* Inputs to LoadFirmware() */
void *firmware_root_key_blob; /* Key used to sign firmware header */
- void *verification_block_0; /* Key block + preamble for firmware 0 */
- void *verification_block_1; /* Key block + preamble for firmware 1 */
+ void *verification_block_0; /* Key block + preamble for firmware 0 */
+ void *verification_block_1; /* Key block + preamble for firmware 1 */
+ uint64_t verification_size_0; /* Verification block 0 size in bytes */
+ uint64_t verification_size_1; /* Verification block 1 size in bytes */
/* Outputs from LoadFirmware(); valid only if LoadFirmware() returns
* LOAD_FIRMWARE_SUCCESS. */
diff --git a/vboot_firmware/include/load_kernel_fw.h b/vboot_firmware/include/load_kernel_fw.h
index 77e5d50c..8dc48fd4 100644
--- a/vboot_firmware/include/load_kernel_fw.h
+++ b/vboot_firmware/include/load_kernel_fw.h
@@ -19,10 +19,9 @@
#define LOAD_KERNEL_INVALID 2 /* Only invalid kernels found on device */
#define LOAD_KERNEL_RECOVERY 3 /* Internal error; reboot to recovery mode */
-/* Boot modes for LoadKernel() */
-#define BOOT_MODE_NORMAL 0
-#define BOOT_MODE_DEVELOPER 1
-#define BOOT_MODE_RECOVERY 2
+/* Boot flags for LoadKernel().boot_flags */
+#define BOOT_FLAG_DEVELOPER UINT64_C(0x01) /* Developer switch is on */
+#define BOOT_FLAG_RECOVERY UINT64_C(0x02) /* In recovery mode */
typedef struct LoadKernelParams {
/* Inputs to LoadKernel() */
@@ -33,7 +32,7 @@ typedef struct LoadKernelParams {
void *kernel_buffer; /* Destination buffer for kernel
* (normally at 0x100000) */
uint64_t kernel_buffer_size; /* Size of kernel buffer in bytes */
- uint8_t boot_mode; /* Boot mode */
+ uint64_t boot_flags; /* Boot flags */
/* Outputs from LoadKernel(); valid only if LoadKernel() returns
* LOAD_KERNEL_SUCCESS */
diff --git a/vboot_firmware/lib/cryptolib/include/rsa.h b/vboot_firmware/lib/cryptolib/include/rsa.h
index 1a458037..4ec5ea97 100644
--- a/vboot_firmware/lib/cryptolib/include/rsa.h
+++ b/vboot_firmware/lib/cryptolib/include/rsa.h
@@ -23,10 +23,11 @@
#define RSA8192NUMWORDS (RSA8192NUMBYTES / sizeof(uint32_t))
typedef struct RSAPublicKey {
- int len; /* Length of n[] in number of uint32_t */
+ uint32_t len; /* Length of n[] in number of uint32_t */
uint32_t n0inv; /* -1 / n[0] mod 2^32 */
uint32_t* n; /* modulus as little endian array */
uint32_t* rr; /* R^2 as little endian array */
+ int algorithm; /* Algorithm to use when verifying binaries with the key */
} RSAPublicKey;
/* Verify a RSA PKCS1.5 signature [sig] of [sig_type] and length [sig_len]
diff --git a/vboot_firmware/lib/cryptolib/rsa_utility.c b/vboot_firmware/lib/cryptolib/rsa_utility.c
index dadd7984..2a823a45 100644
--- a/vboot_firmware/lib/cryptolib/rsa_utility.c
+++ b/vboot_firmware/lib/cryptolib/rsa_utility.c
@@ -15,7 +15,7 @@ int RSAProcessedKeySize(int algorithm) {
* 2 * key_len bytes for the n and rr arrays
* + sizeof len + sizeof n0inv.
*/
- return (2 * key_len + sizeof(int) + sizeof(uint32_t));
+ return (2 * key_len + sizeof(uint32_t) + sizeof(uint32_t));
}
RSAPublicKey* RSAPublicKeyNew(void) {
diff --git a/vboot_firmware/lib/include/vboot_common.h b/vboot_firmware/lib/include/vboot_common.h
new file mode 100644
index 00000000..b1d15c00
--- /dev/null
+++ b/vboot_firmware/lib/include/vboot_common.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2010 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.
+ *
+ * Common functions between firmware and kernel verified boot.
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_COMMON_H_
+#define VBOOT_REFERENCE_VBOOT_COMMON_H_
+
+#include <stdint.h>
+
+#include "cryptolib.h"
+#include "vboot_struct.h"
+
+/* Error Codes for VerifyFirmware. */
+#define VBOOT_SUCCESS 0
+#define VBOOT_INVALID_IMAGE 1
+#define VBOOT_KEY_SIGNATURE_FAILED 2
+#define VBOOT_INVALID_ALGORITHM 3
+#define VBOOT_PREAMBLE_SIGNATURE_FAILED 4
+#define VBOOT_SIGNATURE_FAILED 5
+#define VBOOT_WRONG_MAGIC 6
+#define VBOOT_ERROR_MAX 7 /* Generic catch-all. */
+
+extern char* kVbootErrors[VBOOT_ERROR_MAX];
+
+
+/* Return offset of ptr from base. */
+uint64_t OffsetOf(const void* base, const void* ptr);
+
+
+/* Helper functions to get data pointed to by a public key or signature. */
+uint8_t* GetPublicKeyData(VbPublicKey* key);
+const uint8_t* GetPublicKeyDataC(const VbPublicKey* key);
+uint8_t* GetSignatureData(VbSignature* sig);
+const uint8_t* GetSignatureDataC(const VbSignature* sig);
+
+
+/* Helper functions to verify the data pointed to by a subfield is inside
+ * the parent data. Returns 0 if inside, 1 if error. */
+int VerifyMemberInside(const void* parent, uint64_t parent_size,
+ const void* member, uint64_t member_size,
+ uint64_t member_data_offset,
+ uint64_t member_data_size);
+
+int VerifyPublicKeyInside(const void* parent, uint64_t parent_size,
+ const VbPublicKey* key);
+
+int VerifySignatureInside(const void* parent, uint64_t parent_size,
+ const VbSignature* sig);
+
+
+/* Converts a public key to RsaPublicKey format. The returned key must
+ * be freed using RSAPublicKeyFree().
+ *
+ * Returns NULL if error. */
+RSAPublicKey* PublicKeyToRSA(const VbPublicKey* key);
+
+
+/* Verifies [data] matches signature [sig] using [key]. */
+int VerifyData(const uint8_t* data, const VbSignature* sig,
+ const RSAPublicKey* key);
+
+
+/* Checks the sanity of a key block of size [size] bytes, using public
+ * key [key]. If [key]==NULL, uses only the block checksum to verify
+ * the key block. Header fields are also checked for sanity. Does not
+ * verify key index or key block flags. */
+int VerifyKeyBlock(const VbKeyBlockHeader* block, uint64_t size,
+ const VbPublicKey *key);
+
+
+/* Checks the sanity of a firmware preamble of size [size] bytes,
+ * using public key [key].
+ *
+ * Returns VBOOT_SUCCESS if successful. */
+int VerifyFirmwarePreamble2(const VbFirmwarePreambleHeader* preamble,
+ uint64_t size, const RSAPublicKey* key);
+
+
+/* Checks the sanity of a kernel preamble of size [size] bytes,
+ * using public key [key].
+ *
+ * Returns VBOOT_SUCCESS if successful. */
+int VerifyKernelPreamble2(const VbKernelPreambleHeader* preamble,
+ uint64_t size, const RSAPublicKey* key);
+
+
+
+
+#endif /* VBOOT_REFERENCE_VBOOT_COMMON_H_ */
diff --git a/vboot_firmware/lib/include/vboot_firmware.h b/vboot_firmware/lib/include/vboot_firmware.h
new file mode 100644
index 00000000..de9a8133
--- /dev/null
+++ b/vboot_firmware/lib/include/vboot_firmware.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2010 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.
+ *
+ * Data structure and API definitions for a verified boot kernel image.
+ * (Firmware Portion)
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_FIRMWARE_H_
+#define VBOOT_REFERENCE_VBOOT_FIRMWARE_H_
+
+#include <stdint.h>
+
+#include "cgptlib.h"
+#include "cryptolib.h"
+#include "load_firmware_fw.h"
+#include "vboot_common.h"
+
+/* Alternate LoadFirmware() implementation; see load_firmware_fw.h */
+void UpdateFirmwareBodyHash2(uint8_t* data, uint64_t size);
+int LoadFirmware2(LoadFirmwareParams* params);
+
+#endif /* VBOOT_REFERENCE_VBOOT_FIRMWARE_H_ */
diff --git a/vboot_firmware/lib/include/vboot_kernel.h b/vboot_firmware/lib/include/vboot_kernel.h
new file mode 100644
index 00000000..f55fd2c5
--- /dev/null
+++ b/vboot_firmware/lib/include/vboot_kernel.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2010 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.
+ *
+ * Data structure and API definitions for a verified boot kernel image.
+ * (Firmware Portion)
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_KERNEL_H_
+#define VBOOT_REFERENCE_VBOOT_KERNEL_H_
+
+#include <stdint.h>
+
+#include "cgptlib.h"
+#include "cryptolib.h"
+#include "load_kernel_fw.h"
+#include "vboot_common.h"
+
+/* TODO: temporary hack */
+void FakePartitionAttributes(GptData* gpt);
+
+/* Allocates and reads GPT data from the drive. The sector_bytes and
+ * drive_sectors fields should be filled on input. The primary and
+ * secondary header and entries are filled on output.
+ *
+ * Returns 0 if successful, 1 if error. */
+int AllocAndReadGptData(GptData* gptdata);
+
+/* Writes any changes for the GPT data back to the drive, then frees the
+ * buffers. */
+void WriteAndFreeGptData(GptData* gptdata);
+
+/* Alternate LoadKernel() implementation; see load_kernel_fw.h */
+int LoadKernel2(LoadKernelParams* params);
+
+#endif /* VBOOT_REFERENCE_VBOOT_KERNEL_H_ */
diff --git a/vboot_firmware/lib/include/vboot_struct.h b/vboot_firmware/lib/include/vboot_struct.h
new file mode 100644
index 00000000..9957e462
--- /dev/null
+++ b/vboot_firmware/lib/include/vboot_struct.h
@@ -0,0 +1,124 @@
+/* Copyright (c) 2010 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.
+ *
+ * Data structure definitions for verified boot, for on-disk / in-eeprom
+ * data.
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_STRUCT_H_
+#define VBOOT_REFERENCE_VBOOT_STRUCT_H_
+
+#include <stdint.h>
+
+
+/* Public key data */
+typedef struct VbPublicKey {
+ uint64_t key_offset; /* Offset of key data from start of this struct */
+ uint64_t key_size; /* Size of key data in bytes (NOT strength of key
+ * in bits) */
+ uint64_t algorithm; /* Signature algorithm used by the key */
+ uint64_t key_version; /* Key version */
+} VbPublicKey;
+
+
+/* Signature data (a secure hash, possibly signed) */
+typedef struct VbSignature {
+ uint64_t sig_offset; /* Offset of signature data from start of this
+ * struct */
+ uint64_t sig_size; /* Size of signature data from start of this struct */
+ uint64_t data_size; /* Size of the data block which was signed in bytes */
+} VbSignature;
+
+
+#define KEY_BLOCK_MAGIC "CHROMEOS"
+#define KEY_BLOCK_MAGIC_SIZE 8
+
+#define KEY_BLOCK_HEADER_VERSION_MAJOR 2
+#define KEY_BLOCK_HEADER_VERSION_MINOR 1
+
+/* Flags for key_block_flags */
+/* The following flags set where the key is valid */
+#define KEY_BLOCK_FLAG_DEVELOPER_0 UINT64_C(0x01) /* Developer switch off */
+#define KEY_BLOCK_FLAG_DEVELOPER_1 UINT64_C(0x02) /* Developer switch on */
+#define KEY_BLOCK_FLAG_RECOVERY_0 UINT64_C(0x04) /* Not recovery mode */
+#define KEY_BLOCK_FLAG_RECOVERY_1 UINT64_C(0x08) /* Recovery mode */
+
+/* Key block, containing the public key used to sign some other chunk
+ * of data. */
+typedef struct VbKeyBlockHeader {
+ uint8_t magic[KEY_BLOCK_MAGIC_SIZE]; /* Magic number */
+ uint32_t header_version_major; /* Version of this header format */
+ uint32_t header_version_minor; /* Version of this header format */
+ uint64_t key_block_size; /* Length of this entire key block,
+ * including keys, signatures, and
+ * padding, in bytes */
+ VbSignature key_block_signature; /* Signature for this key block
+ * (header + data pointed to by data_key)
+ * For use with signed data keys*/
+ VbSignature key_block_checksum; /* SHA-512 checksum for this key block
+ * (header + data pointed to by data_key)
+ * For use with unsigned data keys */
+ uint64_t key_block_flags; /* Flags for key (KEY_BLOCK_FLAG_*) */
+ VbPublicKey data_key; /* Key to verify the chunk of data */
+} VbKeyBlockHeader;
+/* This should be followed by:
+ * 1) The data_key key data, pointed to by data_key.key_offset.
+ * 2) The checksum data for (VBKeyBlockHeader + data_key data), pointed to
+ * by key_block_checksum.sig_offset.
+ * 3) The signature data for (VBKeyBlockHeader + data_key data), pointed to
+ * by key_block_signature.sig_offset. */
+
+
+#define FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR 2
+#define FIRMWARE_PREAMBLE_HEADER_VERSION_MINOR 0
+
+/* Preamble block for rewritable firmware */
+typedef struct VbFirmwarePreambleHeader {
+ uint64_t preamble_size; /* Size of this preamble, including keys,
+ * signatures, and padding, in bytes */
+ VbSignature preamble_signature; /* Signature for this preamble
+ * (header + kernel subkey +
+ * body signature) */
+ uint32_t header_version_major; /* Version of this header format */
+ uint32_t header_version_minor; /* Version of this header format */
+
+ uint64_t firmware_version; /* Firmware version */
+ VbPublicKey kernel_subkey; /* Key to verify kernel key block */
+ VbSignature body_signature; /* Signature for the firmware body */
+} VbFirmwarePreambleHeader;
+/* This should be followed by:
+ * 1) The kernel_subkey key data, pointed to by kernel_subkey.key_offset.
+ * 2) The signature data for the firmware body, pointed to by
+ * body_signature.sig_offset.
+ * 3) The signature data for (VBFirmwarePreambleHeader + kernel_subkey data
+ * + body signature data), pointed to by
+ * preamble_signature.sig_offset. */
+
+
+#define KERNEL_PREAMBLE_HEADER_VERSION_MAJOR 2
+#define KERNEL_PREAMBLE_HEADER_VERSION_MINOR 0
+
+/* Preamble block for kernel */
+typedef struct VbKernelPreambleHeader {
+ uint64_t preamble_size; /* Size of this preamble, including keys,
+ * signatures, and padding, in bytes */
+ VbSignature preamble_signature; /* Signature for this preamble
+ * (header + body signature) */
+ uint32_t header_version_major; /* Version of this header format */
+ uint32_t header_version_minor; /* Version of this header format */
+
+ uint64_t kernel_version; /* Kernel version */
+ uint64_t body_load_address; /* Load address for kernel body */
+ uint64_t bootloader_address; /* Address of bootloader, after body is
+ * loaded at body_load_address */
+ uint64_t bootloader_size; /* Size of bootloader in bytes */
+ VbSignature body_signature; /* Signature for the kernel body */
+} VbKernelPreambleHeader;
+/* This should be followed by:
+ * 2) The signature data for the kernel body, pointed to by
+ * body_signature.sig_offset.
+ * 3) The signature data for (VBFirmwarePreambleHeader + body signature
+ * data), pointed to by preamble_signature.sig_offset. */
+
+#endif /* VBOOT_REFERENCE_VBOOT_STRUCT_H_ */
diff --git a/vboot_firmware/lib/load_kernel_fw.c b/vboot_firmware/lib/load_kernel_fw.c
index cea6b771..2a17c6cd 100644
--- a/vboot_firmware/lib/load_kernel_fw.c
+++ b/vboot_firmware/lib/load_kernel_fw.c
@@ -164,13 +164,17 @@ int LoadKernel(LoadKernelParams* params) {
uint16_t lowest_kernel_key_version = 0xFFFF;
uint16_t lowest_kernel_version = 0xFFFF;
KernelImage *kim = NULL;
+ int is_dev = ((BOOT_FLAG_DEVELOPER & params->boot_flags) &&
+ !(BOOT_FLAG_RECOVERY & params->boot_flags));
+ int is_normal = (!(BOOT_FLAG_DEVELOPER & params->boot_flags) &&
+ !(BOOT_FLAG_RECOVERY & params->boot_flags));
/* Clear output params in case we fail */
params->partition_number = 0;
params->bootloader_address = 0;
params->bootloader_size = 0;
- if (BOOT_MODE_NORMAL == params->boot_mode) {
+ if (is_normal) {
/* Read current kernel key index from TPM. Assumes TPM is already
* initialized. */
if (0 != GetStoredVersions(KERNEL_VERSIONS,
@@ -222,7 +226,7 @@ int LoadKernel(LoadKernelParams* params) {
params->header_sign_key_blob,
kbuf,
KBUF_SIZE,
- (BOOT_MODE_DEVELOPER == params->boot_mode ? 1 : 0),
+ (is_dev ? 1 : 0),
kim,
&kernel_sign_key)) {
continue;
@@ -265,6 +269,12 @@ int LoadKernel(LoadKernelParams* params) {
lowest_kernel_version = kim->kernel_version;
}
+ /* If we already have a good kernel, no need to read another
+ * one; we only needed to look at the versions to check for
+ * rollback. */
+ if (-1 != good_partition)
+ continue;
+
/* Verify kernel padding is a multiple of sector size. */
if (0 != kim->padded_header_size % blba) {
RSAPublicKeyFree(kernel_sign_key);
@@ -304,7 +314,7 @@ int LoadKernel(LoadKernelParams* params) {
/* If we're in developer or recovery mode, there's no rollback
* protection, so we can stop at the first valid kernel. */
- if (BOOT_MODE_NORMAL != params->boot_mode)
+ if (!is_normal)
break;
/* Otherwise, we're in normal boot mode, so we do care about
@@ -331,7 +341,7 @@ int LoadKernel(LoadKernelParams* params) {
/* Handle finding a good partition */
if (good_partition >= 0) {
- if (BOOT_MODE_NORMAL == params->boot_mode) {
+ if (is_normal) {
/* See if we need to update the TPM, for normal boot mode only. */
if ((lowest_kernel_key_version > tpm_kernel_key_version) ||
(lowest_kernel_key_version == tpm_kernel_key_version &&
@@ -343,7 +353,7 @@ int LoadKernel(LoadKernelParams* params) {
}
}
- if (BOOT_MODE_RECOVERY != params->boot_mode) {
+ if (!(BOOT_FLAG_RECOVERY & params->boot_flags)) {
/* We can lock the TPM now, since we've decided which kernel we
* like. If we don't find a good kernel, we leave the TPM
* unlocked so we can try again on the next boot device. If no
diff --git a/vboot_firmware/lib/vboot_common.c b/vboot_firmware/lib/vboot_common.c
new file mode 100644
index 00000000..df3068ad
--- /dev/null
+++ b/vboot_firmware/lib/vboot_common.c
@@ -0,0 +1,312 @@
+/* Copyright (c) 2010 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.
+ *
+ * Common functions between firmware and kernel verified boot.
+ * (Firmware portion)
+ */
+
+/* TODO: change all 'return 0', 'return 1' into meaningful return codes */
+
+#include "vboot_common.h"
+#include "utility.h"
+
+#include <stdio.h> /* TODO: FOR TESTING */
+
+char* kVbootErrors[VBOOT_ERROR_MAX] = {
+ "Success.",
+ "Invalid Image.",
+ "Kernel Key Signature Failed.",
+ "Invalid Kernel Verification Algorithm.",
+ "Preamble Signature Failed.",
+ "Kernel Signature Failed.",
+ "Wrong Kernel Magic.",
+};
+
+
+uint64_t OffsetOf(const void *base, const void *ptr) {
+ return (uint64_t)(size_t)ptr - (uint64_t)(size_t)base;
+}
+
+
+/* Helper functions to get data pointed to by a public key or signature. */
+uint8_t* GetPublicKeyData(VbPublicKey* key) {
+ return (uint8_t*)key + key->key_offset;
+}
+
+const uint8_t* GetPublicKeyDataC(const VbPublicKey* key) {
+ return (const uint8_t*)key + key->key_offset;
+}
+
+uint8_t* GetSignatureData(VbSignature* sig) {
+ return (uint8_t*)sig + sig->sig_offset;
+}
+
+const uint8_t* GetSignatureDataC(const VbSignature* sig) {
+ return (const uint8_t*)sig + sig->sig_offset;
+}
+
+
+/* Helper functions to verify the data pointed to by a subfield is inside
+ * the parent data. Returns 0 if inside, 1 if error. */
+int VerifyMemberInside(const void* parent, uint64_t parent_size,
+ const void* member, uint64_t member_size,
+ uint64_t member_data_offset,
+ uint64_t member_data_size) {
+ uint64_t end = OffsetOf(parent, member);
+
+ if (end > parent_size)
+ return 1;
+
+ if (end + member_size > parent_size)
+ return 1;
+
+ end += member_data_offset;
+ if (end > parent_size)
+ return 1;
+ if (end + member_data_size > parent_size)
+ return 1;
+
+ return 0;
+}
+
+
+int VerifyPublicKeyInside(const void* parent, uint64_t parent_size,
+ const VbPublicKey* key) {
+ return VerifyMemberInside(parent, parent_size,
+ key, sizeof(VbPublicKey),
+ key->key_offset, key->key_size);
+}
+
+
+int VerifySignatureInside(const void* parent, uint64_t parent_size,
+ const VbSignature* sig) {
+ return VerifyMemberInside(parent, parent_size,
+ sig, sizeof(VbSignature),
+ sig->sig_offset, sig->sig_size);
+}
+
+
+RSAPublicKey* PublicKeyToRSA(const VbPublicKey* key) {
+ RSAPublicKey *rsa;
+
+ if (kNumAlgorithms <= key->algorithm) {
+ debug("Invalid algorithm.\n");
+ return NULL;
+ }
+ if (RSAProcessedKeySize(key->algorithm) != key->key_size) {
+ debug("Wrong key size for algorithm\n");
+ return NULL;
+ }
+
+ rsa = RSAPublicKeyFromBuf(GetPublicKeyDataC(key), key->key_size);
+ if (!rsa)
+ return NULL;
+
+ rsa->algorithm = key->algorithm;
+ return rsa;
+}
+
+
+int VerifyData(const uint8_t* data, const VbSignature *sig,
+ const RSAPublicKey* key) {
+
+ if (sig->sig_size != siglen_map[key->algorithm]) {
+ debug("Wrong signature size for algorithm.\n");
+ return 1;
+ }
+
+ if (!RSAVerifyBinary_f(NULL, key, data, sig->data_size,
+ GetSignatureDataC(sig), key->algorithm))
+ return 1;
+
+ return 0;
+}
+
+
+int VerifyKeyBlock(const VbKeyBlockHeader* block, uint64_t size,
+ const VbPublicKey *key) {
+
+ const VbSignature* sig;
+
+ /* Sanity checks before attempting signature of data */
+ if (SafeMemcmp(block->magic, KEY_BLOCK_MAGIC, KEY_BLOCK_MAGIC_SIZE)) {
+ debug("Not a valid verified boot key block.\n");
+ return 1;
+ }
+ if (block->header_version_major != KEY_BLOCK_HEADER_VERSION_MAJOR) {
+ debug("Incompatible key block header version.\n");
+ return 1;
+ }
+ if (size < block->key_block_size) {
+ debug("Not enough data for key block.\n");
+ return 1;
+ }
+
+ /* Check signature or hash, depending on whether we have a key. */
+ if (key) {
+ /* Check signature */
+ RSAPublicKey* rsa;
+ int rv;
+
+ sig = &block->key_block_signature;
+
+ if (VerifySignatureInside(block, block->key_block_size, sig)) {
+ debug("Key block signature off end of block\n");
+ return 1;
+ }
+
+ if (!((rsa = PublicKeyToRSA(key)))) {
+ debug("Invalid public key\n");
+ return 1;
+ }
+ rv = VerifyData((const uint8_t*)block, sig, rsa);
+ RSAPublicKeyFree(rsa);
+
+ if (rv)
+ return rv;
+
+ } else {
+ /* Check hash */
+ uint8_t* header_checksum = NULL;
+ int rv;
+
+ sig = &block->key_block_checksum;
+
+ if (VerifySignatureInside(block, block->key_block_size, sig)) {
+ debug("Key block hash off end of block\n");
+ return 1;
+ }
+ if (sig->sig_size != SHA512_DIGEST_SIZE) {
+ debug("Wrong hash size for key block.\n");
+ return 1;
+ }
+
+ header_checksum = DigestBuf((const uint8_t*)block, sig->data_size,
+ SHA512_DIGEST_ALGORITHM);
+ rv = SafeMemcmp(header_checksum, GetSignatureDataC(sig),
+ SHA512_DIGEST_SIZE);
+ Free(header_checksum);
+ if (rv) {
+ debug("Invalid key block hash.\n");
+ return 1;
+ }
+ }
+
+ /* Verify we signed enough data */
+ if (sig->data_size < sizeof(VbKeyBlockHeader)) {
+ debug("Didn't sign enough data\n");
+ return 1;
+ }
+
+ /* Verify data key is inside the block and inside signed data */
+ if (VerifyPublicKeyInside(block, block->key_block_size, &block->data_key)) {
+ debug("Data key off end of key block\n");
+ return 1;
+ }
+ if (VerifyPublicKeyInside(block, sig->data_size, &block->data_key)) {
+ debug("Data key off end of signed data\n");
+ return 1;
+ }
+
+ /* Success */
+ return 0;
+}
+
+
+int VerifyFirmwarePreamble2(const VbFirmwarePreambleHeader* preamble,
+ uint64_t size, const RSAPublicKey* key) {
+
+ const VbSignature* sig = &preamble->preamble_signature;
+
+ /* TODO: caller needs to make sure key version is valid */
+
+ /* Sanity checks before attempting signature of data */
+ if (preamble->header_version_major !=
+ FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR) {
+ debug("Incompatible firmware preamble header version.\n");
+ return 1;
+ }
+ if (size < preamble->preamble_size) {
+ debug("Not enough data for preamble.\n");
+ return 1;
+ }
+
+ /* Check signature */
+ if (VerifySignatureInside(preamble, preamble->preamble_size, sig)) {
+ debug("Preamble signature off end of preamble\n");
+ return 1;
+ }
+ if (VerifyData((const uint8_t*)preamble, sig, key)) {
+ debug("Preamble signature validation failed\n");
+ return 1;
+ }
+
+ /* Verify we signed enough data */
+ if (sig->data_size < sizeof(VbFirmwarePreambleHeader)) {
+ debug("Didn't sign enough data\n");
+ return 1;
+ }
+
+ /* Verify body signature is inside the block */
+ if (VerifySignatureInside(preamble, preamble->preamble_size,
+ &preamble->body_signature)) {
+ debug("Firmware body signature off end of preamble\n");
+ return 1;
+ }
+
+ /* Verify kernel subkey is inside the block */
+ if (VerifyPublicKeyInside(preamble, preamble->preamble_size,
+ &preamble->kernel_subkey)) {
+ debug("Kernel subkey off end of preamble\n");
+ return 1;
+ }
+
+ /* Success */
+ return 0;
+}
+
+
+int VerifyKernelPreamble2(const VbKernelPreambleHeader* preamble,
+ uint64_t size, const RSAPublicKey* key) {
+
+ const VbSignature* sig = &preamble->preamble_signature;
+
+ /* TODO: caller needs to make sure key version is valid */
+
+ /* Sanity checks before attempting signature of data */
+ if (preamble->header_version_major != KERNEL_PREAMBLE_HEADER_VERSION_MAJOR) {
+ debug("Incompatible kernel preamble header version.\n");
+ return 1;
+ }
+ if (size < preamble->preamble_size) {
+ debug("Not enough data for preamble.\n");
+ return 1;
+ }
+
+ /* Check signature */
+ if (VerifySignatureInside(preamble, preamble->preamble_size, sig)) {
+ debug("Preamble signature off end of preamble\n");
+ return 1;
+ }
+ if (VerifyData((const uint8_t*)preamble, sig, key)) {
+ debug("Preamble signature validation failed\n");
+ return 1;
+ }
+
+ /* Verify we signed enough data */
+ if (sig->data_size < sizeof(VbKernelPreambleHeader)) {
+ debug("Didn't sign enough data\n");
+ return 1;
+ }
+
+ /* Verify body signature is inside the block */
+ if (VerifySignatureInside(preamble, preamble->preamble_size,
+ &preamble->body_signature)) {
+ debug("Kernel body signature off end of preamble\n");
+ return 1;
+ }
+
+ /* Success */
+ return 0;
+}
diff --git a/vboot_firmware/lib/vboot_firmware.c b/vboot_firmware/lib/vboot_firmware.c
new file mode 100644
index 00000000..9220550c
--- /dev/null
+++ b/vboot_firmware/lib/vboot_firmware.c
@@ -0,0 +1,173 @@
+/* Copyright (c) 2010 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.
+ *
+ * High-level firmware API for loading and verifying rewritable firmware.
+ * (Firmware portion)
+ */
+
+#include "vboot_firmware.h"
+
+#include "load_firmware_fw.h"
+#include "rollback_index.h"
+#include "utility.h"
+#include "vboot_common.h"
+
+
+void UpdateFirmwareBodyHash2(uint8_t* data, uint64_t size) {
+ /* TODO: actually update the hash. */
+}
+
+
+int LoadFirmware2(LoadFirmwareParams* params) {
+
+ VbPublicKey* root_key = (VbPublicKey*)params->firmware_root_key_blob;
+
+ uint16_t tpm_key_version = 0;
+ uint16_t tpm_fw_version = 0;
+ uint64_t lowest_key_version = 0xFFFF;
+ uint64_t lowest_fw_version = 0xFFFF;
+ int good_index = -1;
+ int index;
+
+ /* Clear output params in case we fail */
+ params->firmware_index = 0;
+ params->kernel_sign_key_blob = NULL;
+ params->kernel_sign_key_size = 0;
+
+ /* Must have a root key */
+ if (!root_key)
+ return LOAD_FIRMWARE_RECOVERY;
+
+ /* Initialize the TPM and read rollback indices. */
+ if (0 != SetupTPM() )
+ return LOAD_FIRMWARE_RECOVERY;
+ if (0 != GetStoredVersions(FIRMWARE_VERSIONS,
+ &tpm_key_version, &tpm_fw_version))
+ return LOAD_FIRMWARE_RECOVERY;
+
+ /* Loop over indices */
+ for (index = 0; index < 2; index++) {
+ VbKeyBlockHeader* key_block;
+ uint64_t vblock_size;
+ VbFirmwarePreambleHeader* preamble;
+ RSAPublicKey* data_key;
+ uint64_t key_version;
+ uint8_t* body_data;
+ uint64_t body_size;
+
+ /* Verify the key block */
+ if (0 == index) {
+ key_block = (VbKeyBlockHeader*)params->verification_block_0;
+ vblock_size = params->verification_size_0;
+ } else {
+ key_block = (VbKeyBlockHeader*)params->verification_block_1;
+ vblock_size = params->verification_size_1;
+ }
+ if ((0 != VerifyKeyBlock(key_block, vblock_size, root_key)))
+ continue;
+
+ /* Check for rollback of key version. */
+ key_version = key_block->data_key.key_version;
+ if (key_version < tpm_key_version)
+ continue;
+
+ /* Get the key for preamble/data verification from the key block. */
+ data_key = PublicKeyToRSA(&key_block->data_key);
+ if (!data_key)
+ continue;
+
+ /* Verify the preamble, which follows the key block. */
+ preamble = (VbFirmwarePreambleHeader*)((uint8_t*)key_block +
+ key_block->key_block_size);
+ if ((0 != VerifyFirmwarePreamble2(preamble,
+ vblock_size - key_block->key_block_size,
+ data_key))) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Check for rollback of firmware version. */
+ if (key_version == tpm_key_version &&
+ preamble->firmware_version < tpm_fw_version) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Check for lowest key version from a valid header. */
+ if (lowest_key_version > key_version) {
+ lowest_key_version = key_version;
+ lowest_fw_version = preamble->firmware_version;
+ }
+ else if (lowest_key_version == key_version &&
+ lowest_fw_version > preamble->firmware_version) {
+ lowest_fw_version = preamble->firmware_version;
+ }
+
+ /* If we already have good firmware, no need to read another one;
+ * we only needed to look at the versions to check for
+ * rollback. */
+ if (-1 != good_index)
+ continue;
+
+ /* Read the firmware data */
+ /* TODO: should set up hash for UpdateFirmwareBodyHash(). */
+ body_data = GetFirmwareBody(index, &body_size);
+ if (!body_data || (body_size != preamble->body_signature.data_size)) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Verify firmware data */
+ /* TODO: should use hash from UpdateFirmwareBodyHash() rather than
+ * recalculating it in VerifyData(). */
+ if (0 != VerifyData(body_data, &preamble->body_signature, data_key)) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Done with the data key, so can free it now */
+ RSAPublicKeyFree(data_key);
+
+ /* If we're still here, the firmware is valid. */
+ /* Save the first good firmware we find; that's the one we'll boot */
+ if (-1 == good_index) {
+ good_index = index;
+ params->firmware_index = index;
+ params->kernel_sign_key_blob = &preamble->kernel_subkey;
+ params->kernel_sign_key_size = (preamble->kernel_subkey.key_offset +
+ preamble->kernel_subkey.key_size);
+
+ /* If the good firmware's key version is the same as the tpm,
+ * then the TPM doesn't need updating; we can stop now.
+ * Otherwise, we'll check all the other headers to see if they
+ * contain a newer key. */
+ if (key_version == tpm_key_version &&
+ preamble->firmware_version == tpm_fw_version)
+ break;
+ }
+ }
+
+ /* Handle finding good firmware */
+ if (good_index >= 0) {
+
+ /* Update TPM if necessary */
+ if ((lowest_key_version > tpm_key_version) ||
+ (lowest_key_version == tpm_key_version &&
+ lowest_fw_version > tpm_fw_version)) {
+ if (0 != WriteStoredVersions(FIRMWARE_VERSIONS,
+ lowest_key_version,
+ lowest_fw_version))
+ return LOAD_FIRMWARE_RECOVERY;
+ }
+
+ /* Lock Firmware TPM rollback indices from further writes. In
+ * this design, this is done by setting the globalLock bit, which
+ * is cleared only by TPM_Init at reboot. */
+ if (0 != LockFirmwareVersions())
+ return LOAD_FIRMWARE_RECOVERY;
+ }
+
+ /* If we're still here, no good firmware, so go to recovery mode. */
+ return LOAD_FIRMWARE_RECOVERY;
+}
diff --git a/vboot_firmware/lib/vboot_kernel.c b/vboot_firmware/lib/vboot_kernel.c
new file mode 100644
index 00000000..bcf9e171
--- /dev/null
+++ b/vboot_firmware/lib/vboot_kernel.c
@@ -0,0 +1,267 @@
+/* Copyright (c) 2010 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.
+ *
+ * Functions for loading a kernel from disk.
+ * (Firmware portion)
+ */
+
+#include "vboot_kernel.h"
+
+#include "boot_device.h"
+#include "cgptlib.h"
+#include "load_kernel_fw.h"
+#include "rollback_index.h"
+#include "utility.h"
+
+#define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */
+
+int LoadKernel2(LoadKernelParams* params) {
+
+ VbPublicKey* kernel_subkey = (VbPublicKey*)params->header_sign_key_blob;
+
+ GptData gpt;
+ uint64_t part_start, part_size;
+ uint64_t blba = params->bytes_per_lba;
+ uint64_t kbuf_sectors = KBUF_SIZE / blba;
+ uint8_t* kbuf = NULL;
+ int found_partitions = 0;
+ int good_partition = -1;
+ uint16_t tpm_key_version = 0;
+ uint16_t tpm_kernel_version = 0;
+ uint64_t lowest_key_version = 0xFFFF;
+ uint64_t lowest_kernel_version = 0xFFFF;
+ int is_dev = ((BOOT_FLAG_DEVELOPER & params->boot_flags) &&
+ !(BOOT_FLAG_RECOVERY & params->boot_flags));
+ int is_normal = (!(BOOT_FLAG_DEVELOPER & params->boot_flags) &&
+ !(BOOT_FLAG_RECOVERY & params->boot_flags));
+
+ /* Clear output params in case we fail */
+ params->partition_number = 0;
+ params->bootloader_address = 0;
+ params->bootloader_size = 0;
+
+ if (is_normal) {
+ /* Read current kernel key index from TPM. Assumes TPM is already
+ * initialized. */
+ if (0 != GetStoredVersions(KERNEL_VERSIONS,
+ &tpm_key_version,
+ &tpm_kernel_version))
+ return LOAD_KERNEL_RECOVERY;
+ } else if (is_dev) {
+ /* In developer mode, we ignore the kernel subkey, and just use
+ * the SHA-512 hash to verify the key block. */
+ kernel_subkey = NULL;
+ }
+
+ do {
+ /* Read GPT data */
+ gpt.sector_bytes = blba;
+ gpt.drive_sectors = params->ending_lba + 1;
+ if (0 != AllocAndReadGptData(&gpt))
+ break;
+
+ /* Initialize GPT library */
+ if (GPT_SUCCESS != GptInit(&gpt))
+ break;
+
+ /* TODO: TERRIBLE KLUDGE - fake partition attributes */
+ FakePartitionAttributes(&gpt);
+
+ /* Allocate kernel header buffers */
+ kbuf = (uint8_t*)Malloc(KBUF_SIZE);
+ if (!kbuf)
+ break;
+
+ /* Loop over candidate kernel partitions */
+ while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) {
+ VbKeyBlockHeader* key_block;
+ VbKernelPreambleHeader* preamble;
+ RSAPublicKey* data_key;
+ uint64_t key_version;
+ uint64_t body_offset;
+
+ /* Found at least one kernel partition. */
+ found_partitions++;
+
+ /* Read the first part of the kernel partition */
+ if (part_size < kbuf_sectors)
+ continue;
+ if (0 != BootDeviceReadLBA(part_start, kbuf_sectors, kbuf))
+ continue;
+
+ /* Verify the key block */
+ key_block = (VbKeyBlockHeader*)kbuf;
+ if ((0 != VerifyKeyBlock(key_block, KBUF_SIZE, kernel_subkey)))
+ continue;
+
+ /* Check the key block flags against the current boot mode */
+ if (!(key_block->key_block_flags &&
+ ((BOOT_FLAG_DEVELOPER & params->boot_flags) ?
+ KEY_BLOCK_FLAG_DEVELOPER_1 : KEY_BLOCK_FLAG_DEVELOPER_0)))
+ continue;
+ if (!(key_block->key_block_flags &&
+ ((BOOT_FLAG_RECOVERY & params->boot_flags) ?
+ KEY_BLOCK_FLAG_RECOVERY_1 : KEY_BLOCK_FLAG_RECOVERY_0)))
+ continue;
+
+ /* Check for rollback of key version. Note this is implicitly
+ * skipped in recovery and developer modes because those set
+ * key_version=0 above. */
+ key_version = key_block->data_key.key_version;
+ if (key_version < tpm_key_version)
+ continue;
+
+ /* Get the key for preamble/data verification from the key block */
+ data_key = PublicKeyToRSA(&key_block->data_key);
+ if (!data_key)
+ continue;
+
+ /* Verify the preamble, which follows the key block */
+ preamble = (VbKernelPreambleHeader*)(kbuf + key_block->key_block_size);
+ if ((0 != VerifyKernelPreamble2(preamble,
+ KBUF_SIZE - key_block->key_block_size,
+ data_key))) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Check for rollback of kernel version. Note this is implicitly
+ * skipped in recovery and developer modes because those set
+ * key_version=0 and kernel_version=0 above. */
+ if (key_version == tpm_key_version &&
+ preamble->kernel_version < tpm_kernel_version) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Check for lowest key version from a valid header. */
+ if (lowest_key_version > key_version) {
+ lowest_key_version = key_version;
+ lowest_kernel_version = preamble->kernel_version;
+ }
+ else if (lowest_key_version == key_version &&
+ lowest_kernel_version > preamble->kernel_version) {
+ lowest_kernel_version = preamble->kernel_version;
+ }
+
+ /* If we already have a good kernel, no need to read another
+ * one; we only needed to look at the versions to check for
+ * rollback. */
+ if (-1 != good_partition)
+ continue;
+
+ /* Verify body load address matches what we expect */
+ if (preamble->body_load_address != (size_t)params->kernel_buffer) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Verify kernel body starts at a multiple of the sector size. */
+ body_offset = key_block->key_block_size + preamble->preamble_size;
+ if (0 != body_offset % blba) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Verify kernel body fits in the partition */
+ if (body_offset + preamble->body_signature.data_size >
+ part_size * blba) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Read the kernel data */
+ if (0 != BootDeviceReadLBA(
+ part_start + (body_offset / blba),
+ (preamble->body_signature.data_size + blba - 1) / blba,
+ params->kernel_buffer)) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Verify kernel data */
+ if (0 != VerifyData((const uint8_t*)params->kernel_buffer,
+ &preamble->body_signature, data_key)) {
+ RSAPublicKeyFree(data_key);
+ continue;
+ }
+
+ /* Done with the kernel signing key, so can free it now */
+ RSAPublicKeyFree(data_key);
+
+ /* If we're still here, the kernel is valid. */
+ /* Save the first good partition we find; that's the one we'll boot */
+ if (-1 == good_partition) {
+ good_partition = gpt.current_kernel;
+ params->partition_number = gpt.current_kernel;
+ params->bootloader_address = preamble->bootloader_address;
+ params->bootloader_size = preamble->bootloader_size;
+ /* If we're in developer or recovery mode, there's no rollback
+ * protection, so we can stop at the first valid kernel. */
+ if (!is_normal)
+ break;
+
+ /* Otherwise, we're in normal boot mode, so we do care about
+ * the key index in the TPM. If the good partition's key
+ * version is the same as the tpm, then the TPM doesn't need
+ * updating; we can stop now. Otherwise, we'll check all the
+ * other headers to see if they contain a newer key. */
+ if (key_version == tpm_key_version &&
+ preamble->kernel_version == tpm_kernel_version)
+ break;
+ }
+ } /* while(GptNextKernelEntry) */
+ } while(0);
+
+ /* Free kernel buffer */
+ if (kbuf)
+ Free(kbuf);
+
+ /* Write and free GPT data */
+ WriteAndFreeGptData(&gpt);
+
+ /* Handle finding a good partition */
+ if (good_partition >= 0) {
+
+ /* See if we need to update the TPM */
+ if (is_normal) {
+ /* We only update the TPM in normal boot mode. In developer
+ * mode, the kernel is self-signed by the developer, so we can't
+ * trust the key version and wouldn't want to roll the TPM
+ * forward. In recovery mode, the TPM stays PP-unlocked, so
+ * anything we write gets blown away by the firmware when we go
+ * back to normal mode. */
+ if ((lowest_key_version > tpm_key_version) ||
+ (lowest_key_version == tpm_key_version &&
+ lowest_kernel_version > tpm_kernel_version)) {
+ if (0 != WriteStoredVersions(KERNEL_VERSIONS,
+ lowest_key_version,
+ lowest_kernel_version))
+ return LOAD_KERNEL_RECOVERY;
+ }
+ }
+
+ if (!(BOOT_FLAG_RECOVERY & params->boot_flags)) {
+ /* We can lock the TPM now, since we've decided which kernel we
+ * like. If we don't find a good kernel, we leave the TPM
+ * unlocked so we can try again on the next boot device. If no
+ * kernels are good, we'll reboot to recovery mode, so it's ok to
+ * leave the TPM unlocked in that case too.
+ *
+ * If we're already in recovery mode, we need to leave PP unlocked,
+ * so don't lock the kernel versions. */
+ if (0 != LockKernelVersionsByLockingPP())
+ return LOAD_KERNEL_RECOVERY;
+ }
+
+ /* Success! */
+ return LOAD_KERNEL_SUCCESS;
+ }
+
+ // Handle error cases
+ if (found_partitions)
+ return LOAD_KERNEL_INVALID;
+ else
+ return LOAD_KERNEL_NOT_FOUND;
+}
diff --git a/vboot_firmware/linktest/main.c b/vboot_firmware/linktest/main.c
index cbefc2f4..99cdccdd 100644
--- a/vboot_firmware/linktest/main.c
+++ b/vboot_firmware/linktest/main.c
@@ -7,17 +7,20 @@
#include "load_kernel_fw.h"
#include "rollback_index.h"
#include "tlcl.h"
+#include "vboot_common.h"
+#include "vboot_firmware.h"
+#include "vboot_kernel.h"
int main(void)
{
uint16_t x, y;
- // cgptlib.h
+ /* cgptlib.h */
GptInit(0);
GptNextKernelEntry(0, 0, 0);
GptUpdateKernelEntry(0, 0);
- // firmware_image_fw.h
+ /* firmware_image_fw.h */
VerifyFirmwareHeader(0, 0, 0, 0);
VerifyFirmwarePreamble(0, 0, 0, 0);
VerifyFirmwareData(0, 0, 0, 0, 0);
@@ -25,7 +28,7 @@ int main(void)
GetLogicalFirmwareVersion(0);
VerifyFirmwareDriver_f(0, 0, 0, 0, 0);
- // kernel_image_fw.h
+ /* kernel_image_fw.h */
VerifyKernelKeyHeader(0, 0, 0, 0, 0, 0);
VerifyKernelPreamble(0, 0, 0, 0);
VerifyKernelData(0, 0, 0, 0, 0);
@@ -33,21 +36,21 @@ int main(void)
VerifyKernel(0, 0, 0);
GetLogicalKernelVersion(0);
- // load_firmware_fw.h
+ /* load_firmware_fw.h */
UpdateFirmwareBodyHash(0, 0);
LoadFirmware(0);
- // load_kernel_fw.h
+ /* load_kernel_fw.h */
LoadKernel(0);
- // rollback_index.h
+ /* rollback_index.h */
SetupTPM();
GetStoredVersions(0, &x, &y);
WriteStoredVersions(0, 0, 0);
LockFirmwareVersions();
LockKernelVersionsByLockingPP();
- // tlcl.h
+ /* tlcl.h */
TlclLibInit();
TlclStartup();
TlclSelftestfull();
@@ -65,5 +68,27 @@ int main(void)
TlclSetDeactivated(0);
TlclGetFlags(0, 0);
+ /* vboot_common.h */
+ OffsetOf(0, 0);
+ GetPublicKeyData(0);
+ GetPublicKeyDataC(0);
+ GetSignatureData(0);
+ GetSignatureDataC(0);
+ VerifyMemberInside(0, 0, 0, 0, 0, 0);
+ VerifyPublicKeyInside(0, 0, 0);
+ VerifySignatureInside(0, 0, 0);
+ PublicKeyToRSA(0);
+ VerifyData(0, 0, 0);
+ VerifyKeyBlock(0, 0, 0);
+ VerifyFirmwarePreamble2(0, 0, 0);
+ VerifyKernelPreamble2(0, 0, 0);
+
+ /* vboot_kernel.h */
+ LoadKernel2(0);
+
+ /* vboot_firmware.h */
+ UpdateFirmwareBodyHash2(0, 0);
+ LoadFirmware2(0);
+
return 0;
}