diff --git a/Makefile b/Makefile index a377b76c9a..e30d71628e 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 0000000000..6c320eb9f1 --- /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 0000000000..cd7c06318e --- /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 + +#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 0000000000..b71db8f88d --- /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 + +#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 0000000000..9e7b0c0680 --- /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 + +#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 0000000000..6c4c438710 --- /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 +#include +#include + +#include +#include +#include +#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 0000000000..00770db3b6 --- /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 +#include +#include + +#include +#include +#include + +#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 0000000000..935e77310c --- /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 +#include +#include + +#include +#include +#include + +#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 0000000000..91fc5aff66 --- /dev/null +++ b/host/linktest/main.c @@ -0,0 +1,28 @@ +#include + +#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 055773359c..15799fb214 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 b634b71c3c..025066a470 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 0000000000..6295f4045c --- /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 0000000000..361de83e93 --- /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 +#include + +#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 " + " \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 0000000000..a4e69fd19a --- /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 +#include + +#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 " + " " + " \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 0000000000..e467471e76 --- /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 +#include + +#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 d116eb007c..1f50aebd83 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 da3532c3b6..7beb414ef7 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 b4345e0a96..efdb4db4d1 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 58dd6da55f..312a0aaa6c 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 77e5d50c1c..8dc48fd4a3 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 1a45803717..4ec5ea97b0 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 dadd79846d..2a823a45fd 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 0000000000..b1d15c00e8 --- /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 + +#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 0000000000..de9a8133ef --- /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 + +#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 0000000000..f55fd2c5d1 --- /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 + +#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 0000000000..9957e462b9 --- /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 + + +/* 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 cea6b77157..2a17c6cda4 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 0000000000..df3068adf7 --- /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 /* 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 0000000000..9220550c4e --- /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 0000000000..bcf9e171e8 --- /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 cbefc2f41f..99cdccdd0d 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; }