diff --git a/common/Makefile b/common/Makefile index fc73ec23a2..b9b4a5129b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -2,13 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -SRCS = utility_stub.c +SRCS = utility_stub.c tlcl_stub.c OBJS = $(SRCS:.c=.o) all: libcommon.a libcommon.a: $(OBJS) - ar rs $@ $< + ar rs $@ $^ .c.o: $(OBJS) $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ diff --git a/common/tlcl_stub.c b/common/tlcl_stub.c new file mode 100644 index 0000000000..0b4625b343 --- /dev/null +++ b/common/tlcl_stub.c @@ -0,0 +1,28 @@ +/* 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. + * + * Stub implementations of TPM Lite Library. + */ + +#include + +void TlclLibinit(void) { return; } +void TlclStartup(void) { return; } +void TlclSelftestfull(void) { return; } +void TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { return; } +uint32_t TlclWrite(uint32_t index, uint8_t *data, uint32_t length) { + return TPM_SUCCESS; +} +uint32_t TlclRead(uint32_t index, uint8_t *data, uint32_t length) { + return TPM_SUCCESS; +} +void TlclWriteLock(uint32_t index) { return; } +void TlclReadLock(uint32_t index) { return; } +void TlclAssertPhysicalPresence(void) { return; } +void TlclSetNvLocked(void) { return; } +int TlclIsOwned(void) { return 0; } +void TlclForceClear(void) { return; } +void TlclPhysicalEnable(void) { return; } +int TlclPhysicalSetDeactivated(uint8_t flag) { return TPM_SUCCESS; } +int TlclGetFlags(uint8_t* disable, uint8_t* deactivated) { return TPM_SUCCESS; } diff --git a/include/firmware_image.h b/include/firmware_image.h index 3f7bc801d3..611d31d70d 100644 --- a/include/firmware_image.h +++ b/include/firmware_image.h @@ -27,8 +27,8 @@ typedef struct FirmwareImage { uint16_t header_len; /* Length of the header. */ uint16_t firmware_sign_algorithm; /* Signature algorithm used by the signing * key. */ - uint8_t* firmware_sign_key; /* Pre-processed public half of signing key. */ uint16_t firmware_key_version; /* Key Version# for preventing rollbacks. */ + uint8_t* firmware_sign_key; /* Pre-processed public half of signing key. */ uint8_t header_checksum[SHA512_DIGEST_SIZE]; /* SHA-512 hash of the header.*/ uint8_t firmware_key_signature[RSA8192NUMBYTES]; /* Signature of the header @@ -113,7 +113,10 @@ void PrintFirmwareImage(const FirmwareImage* image); #define VERIFY_FIRMWARE_PREAMBLE_SIGNATURE_FAILED 4 #define VERIFY_FIRMWARE_SIGNATURE_FAILED 5 #define VERIFY_FIRMWARE_WRONG_MAGIC 6 -#define VERIFY_FIRMWARE_MAX 7 /* Generic catch-all. */ +#define VERIFY_FIRMWARE_WRONG_HEADER_CHECKSUM 7 +#define VERIFY_FIRMWARE_KEY_ROLLBACK 8 +#define VERIFY_FIRMWARE_VERSION_ROLLBACK 9 +#define VERIFY_FIRMWARE_MAX 10 /* Total number of error codes. */ extern char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX]; @@ -198,4 +201,26 @@ int AddFirmwareKeySignature(FirmwareImage* image, const char* root_key_file); */ int AddFirmwareSignature(FirmwareImage* image, const char* signing_key_file); +/* Returns the logical version of a firmware blob which is calculated as + * (firmware_key_version << 16 | firmware_version). */ +uint32_t GetLogicalFirmwareVersion(uint8_t* firmware_blob); + +#define BOOT_FIRMWARE_A_CONTINUE 1 +#define BOOT_FIRMWARE_B_CONTINUE 2 +#define BOOT_FIRMWARE_RECOVERY_CONTINUE 3 + +/* This function is the driver used by the RO firmware to + * determine which copy of the firmware to boot from. It performs + * the requisite rollback index checking, including updating them, + * if required. + * + * Returns the code path to follow. It is one of: + * BOOT_FIRMWARE_A_CONTINUE Boot from Firmware A + * BOOT_FIRMWARE_B_CONTINUE Boot from Firmware B + * BOOT_FIRMWARE_RECOVERY_CONTINUE Jump to recovery mode + */ +int VerifyFirmwareDriver_f(uint8_t* root_key_blob, + uint8_t* firmwareA, + uint8_t* firmwareB); + #endif /* VBOOT_REFERENCE_FIRMWARE_IMAGE_H_ */ diff --git a/include/rollback_index.h b/include/rollback_index.h new file mode 100644 index 0000000000..d4e47ac893 --- /dev/null +++ b/include/rollback_index.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. + * + * Functions for querying, manipulating and locking rollback indices + * stored in the TPM NVRAM. + */ + +#ifndef VBOOT_REFERENCE_ROLLBACK_INDEX_H_ +#define VBOOT_REFERENCE_ROLLBACK_INDEX_H_ + +#include + +extern uint16_t g_firmware_key_version; +extern uint16_t g_firmware_version; +extern uint16_t g_kernel_key_version; +extern uint16_t g_kernel_version; + +/* Rollback version types. */ +#define FIRMWARE_KEY_VERSION 0 +#define FIRMWARE_VERSION 1 +#define KERNEL_KEY_VERSION 2 +#define KERNEL_VERSION 3 + +/* TPM NVRAM location indices. */ +#define FIRMWARE_KEY_VERSION_NV_INDEX 0x1001 +#define FIRMWARE_VERSION_NV_INDEX 0x1002 +#define KERNEL_KEY_VERSION_NV_INDEX 0x1003 +#define KERNEL_VERSION_NV_INDEX 0x1004 + +void SetupTPM(void); +uint16_t GetStoredVersion(int type); +int WriteStoredVersion(int type, uint16_t version); +void LockStoredVersion(int type); + +#endif /* VBOOT_REFERENCE_ROLLBACK_INDEX_H_ */ diff --git a/include/utility.h b/include/utility.h index 5dc6d28389..79713477c8 100644 --- a/include/utility.h +++ b/include/utility.h @@ -13,6 +13,14 @@ #include #include +/* Combine [msw] and [lsw] uint16s to a uint32_t with its [msw] and + * [lsw] forming the most and least signficant 16-bit words. + */ +#define CombineUint16Pair(msw,lsw) (((msw) << 16) | \ + (((lsw)) & 0xFFFF)) +/* Return the minimum of (a) or (b). */ +#define Min(a, b) (((a) < (b)) ? (a) : (b)) + /* Allocate [size] bytes and return a pointer to the allocated memory. Abort * on error. */ diff --git a/tests/Makefile b/tests/Makefile index 6617a4157f..931e3f5d45 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,11 +7,13 @@ CFLAGS ?= -Wall -DNDEBUG -O3 -Werror INCLUDES ?= -I../include/ TOP ?= ../ -FIRMWARE_LIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a -LIBS = $(TOP)/utils/kernel_image.o $(TOP)/utils/firmware_image.o \ - $(TOP)/utils/file_keys.o $(TOP)/utils/signature_digest.o -lcrypto +BASE_LIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a +IMAGE_LIBS = $(TOP)/utils/firmware_image.o $(TOP)/utils/kernel_image.o +UTIL_LIBS = $(TOP)/utils/file_keys.o $(TOP)/utils/signature_digest.o +LIBS = $(IMAGE_LIBS) $(UTIL_LIBS) -lcrypto $(BASE_LIBS) tests: firmware_image_tests \ + firmware_rollback_tests \ firmware_verify_benchmark \ kernel_image_tests \ kernel_verify_benchmark \ @@ -22,38 +24,47 @@ tests: firmware_image_tests \ verify_firmware_fuzz_driver \ verify_kernel_fuzz_driver -firmware_image_tests: firmware_image_tests.c - $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARE_LIBS) +firmware_image_tests: firmware_image_tests.c rollback_index_mock.c + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) -firmware_verify_benchmark: firmware_verify_benchmark.c timer_utils.c - $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt $(LIBS) $(FIRMWARE_LIBS) +firmware_rollback_tests: firmware_rollback_tests.c rollback_index_mock.c test_common.c + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) -kernel_image_tests: kernel_image_tests.c - $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARE_LIBS) +firmware_verify_benchmark: firmware_verify_benchmark.c timer_utils.c \ + rollback_index_mock.c + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt $(LIBS) -kernel_verify_benchmark: kernel_verify_benchmark.c timer_utils.c - $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt $(LIBS) $(FIRMWARE_LIBS) +kernel_image_tests: kernel_image_tests.c rollback_index_mock.c + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) + +kernel_verify_benchmark: kernel_verify_benchmark.c timer_utils.c \ + rollback_index_mock.c + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt $(LIBS) rsa_padding_test: rsa_padding_test.c - $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) $(FIRMWARE_LIBS) + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(UTIL_LIBS) $(BASE_LIBS) \ + -lcrypto rsa_verify_benchmark: rsa_verify_benchmark.c timer_utils.c - $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt $(LIBS) $(FIRMWARE_LIBS) + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt $(UTIL_LIBS) $(BASE_LIBS) \ + -lcrypto -sha_benchmark: sha_benchmark.c timer_utils.c $(FIRMWARE_LIBS) - $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt +sha_benchmark: sha_benchmark.c timer_utils.c + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt $(BASE_LIBS) sha_tests: sha_tests.c - $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(FIRMWARE_LIBS) + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(BASE_LIBS) -verify_firmware_fuzz_driver: verify_firmware_fuzz_driver.c - $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARE_LIBS) +verify_firmware_fuzz_driver: verify_firmware_fuzz_driver.c \ + rollback_index_mock.c + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) -verify_kernel_fuzz_driver: verify_kernel_fuzz_driver.c - $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARE_LIBS) +verify_kernel_fuzz_driver: verify_kernel_fuzz_driver.c rollback_index_mock.c + $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) clean: rm -f firmware_image_tests \ + firmware_rollback_tests \ firmware_verify_benchmark \ kernel_image_tests \ kernel_verify_benchmark \ diff --git a/tests/firmware_image_tests.c b/tests/firmware_image_tests.c index 3df7cb0671..b252735dec 100644 --- a/tests/firmware_image_tests.c +++ b/tests/firmware_image_tests.c @@ -12,18 +12,18 @@ #include "firmware_image.h" #include "rsa_utility.h" #include "utility.h" +#include "rollback_index.h" /* ANSI Color coding sequences. */ #define COL_GREEN "\e[1;32m" -#define COL_RED "\e[0;31m]" +#define COL_RED "\e[0;31m" #define COL_STOP "\e[m" int TEST_EQ(int result, int expected_result, char* testname) { if (result == expected_result) { fprintf(stderr, "%s Test " COL_GREEN " PASSED\n" COL_STOP, testname); return 1; - } - else { + } else { fprintf(stderr, "%s Test " COL_RED " FAILED\n" COL_STOP, testname); return 0; } @@ -33,7 +33,9 @@ FirmwareImage* GenerateTestFirmwareImage(int algorithm, uint8_t* firmware_sign_key, int firmware_key_version, int firmware_version, - int firmware_len) { + int firmware_len, + const char* root_key_file, + const char* firmware_key_file) { FirmwareImage* image = FirmwareImageNew(); Memcpy(image->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE); @@ -58,6 +60,18 @@ FirmwareImage* GenerateTestFirmwareImage(int algorithm, image->firmware_data = Malloc(image->firmware_len); Memset(image->firmware_data, 'F', image->firmware_len); + /* Generate and populate signatures. */ + if (!AddFirmwareKeySignature(image, root_key_file)) { + fprintf(stderr, "Couldn't create key signature.\n"); + FirmwareImageFree(image); + return NULL; + } + + if (!AddFirmwareSignature(image, firmware_key_file)) { + fprintf(stderr, "Couldn't create firmware and preamble signature.\n"); + FirmwareImageFree(image); + return NULL; + } return image; } @@ -79,7 +93,6 @@ int VerifyFirmwareTest(uint8_t* firmware_blob, uint8_t* root_key_blob) { return success; } - /* Normal FirmwareImage Verification Tests. */ int VerifyFirmwareImageTest(FirmwareImage* image, RSAPublicKey* root_key) { @@ -142,14 +155,17 @@ int VerifyFirmwareImageTamperTest(FirmwareImage* image, int main(int argc, char* argv[]) { uint64_t len; + const char* root_key_file = NULL; + const char* firmware_key_file = NULL; uint8_t* firmware_sign_key_buf = NULL; uint8_t* root_key_blob = NULL; uint8_t* firmware_blob = NULL; uint64_t firmware_blob_len = 0; FirmwareImage* image = NULL; - RSAPublicKey* root_key = NULL; + RSAPublicKey* root_key_pub = NULL; int error_code = 0; - + int algorithm; + SetupTPM(); if(argc != 6) { fprintf(stderr, "Usage: %s " " \n", argv[0]); @@ -157,30 +173,24 @@ int main(int argc, char* argv[]) { } /* Read verification keys and create a test image. */ - root_key = RSAPublicKeyFromFile(argv[3]); + algorithm = atoi(argv[1]); + root_key_pub = RSAPublicKeyFromFile(argv[3]); root_key_blob = BufferFromFile(argv[3], &len); firmware_sign_key_buf = BufferFromFile(argv[5], &len); - image = GenerateTestFirmwareImage(atoi(argv[1]), firmware_sign_key_buf, 1, - 1, 1000); + root_key_file = argv[2]; + firmware_key_file = argv[4]; + image = GenerateTestFirmwareImage(algorithm, + firmware_sign_key_buf, + 1, /* Firmware Key Version. */ + 1, /* Firmware Version. */ + 1000, /* Firmware length. */ + root_key_file, + firmware_key_file); - if (!root_key || !firmware_sign_key_buf || !image) { + if (!root_key_pub || !firmware_sign_key_buf || !image) { error_code = 1; goto failure; } - - /* Generate and populate signatures. */ - if (!AddFirmwareKeySignature(image, argv[2])) { - fprintf(stderr, "Couldn't create key signature.\n"); - error_code = 1; - goto failure; - } - - if (!AddFirmwareSignature(image, argv[4])) { - fprintf(stderr, "Couldn't create firmware and preamble signature.\n"); - error_code = 1; - goto failure; - } - firmware_blob = GetFirmwareBlob(image, &firmware_blob_len); /* Test Firmware blob verify operations. */ @@ -188,9 +198,9 @@ int main(int argc, char* argv[]) { error_code = 255; /* Test FirmwareImage verify operations. */ - if (!VerifyFirmwareImageTest(image, root_key)) + if (!VerifyFirmwareImageTest(image, root_key_pub)) error_code = 255; - if (!VerifyFirmwareImageTamperTest(image, root_key)) + if (!VerifyFirmwareImageTamperTest(image, root_key_pub)) error_code = 255; failure: @@ -198,7 +208,7 @@ failure: FirmwareImageFree(image); Free(firmware_sign_key_buf); Free(root_key_blob); - RSAPublicKeyFree(root_key); + RSAPublicKeyFree(root_key_pub); return error_code; } diff --git a/tests/firmware_rollback_tests.c b/tests/firmware_rollback_tests.c new file mode 100644 index 0000000000..19ac850c4f --- /dev/null +++ b/tests/firmware_rollback_tests.c @@ -0,0 +1,145 @@ +/* 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 checking firmware rollback-prevention logic. + */ + +#include +#include + +#include "file_keys.h" +#include "firmware_image.h" +#include "rsa_utility.h" +#include "utility.h" +#include "rollback_index.h" +#include "test_common.h" + +/* Generates a test firmware image for rollback tests with a given + * [firmware_key_version] and [firmware_version]. If [is_corrupt] is 1, + * then the image has invalid signatures and will fail verification. */ +uint8_t* GenerateRollbackTestImage(int firmware_key_version, + int firmware_version, + int is_corrupt) { + FirmwareImage* image = NULL; + uint8_t* firmware_blob = NULL; + const char* firmare_sign_key_pub_file = "testkeys/key_rsa1024.keyb"; + uint8_t* firmware_sign_key = NULL; + const char* root_key_file = "testkeys/key_rsa8192.pem"; + const char* firmware_key_file = "testkeys/key_rsa1024.pem"; + uint64_t len; + firmware_sign_key = BufferFromFile(firmare_sign_key_pub_file, + &len); + if (!firmware_sign_key) + return NULL; + + image = FirmwareImageNew(); + if (!image) + return NULL; + + Memcpy(image->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE); + image->firmware_sign_algorithm = 0; /* RSA1024/SHA1 */ + image->firmware_sign_key = (uint8_t*) Malloc( + RSAProcessedKeySize(image->firmware_sign_algorithm)); + Memcpy(image->firmware_sign_key, firmware_sign_key, + RSAProcessedKeySize(image->firmware_sign_algorithm)); + image->firmware_key_version = firmware_key_version; + Free(firmware_sign_key); + + /* Update correct header length. */ + image->header_len = GetFirmwareHeaderLen(image); + + /* Calculate SHA-512 digest on header and populate header_checksum. */ + CalculateFirmwareHeaderChecksum(image, image->header_checksum); + + /* Populate firmware and preamble with dummy data. */ + image->firmware_version = firmware_version; + image->firmware_len = 1; + image->preamble_signature = image->firmware_signature = NULL; + Memset(image->preamble, 'P', FIRMWARE_PREAMBLE_SIZE); + image->firmware_data = Malloc(image->firmware_len); + Memset(image->firmware_data, 'F', image->firmware_len); + + /* Generate and populate signatures. */ + if (!AddFirmwareKeySignature(image, root_key_file)) { + fprintf(stderr, "Couldn't create key signature.\n"); + FirmwareImageFree(image); + return NULL; + } + + if (!AddFirmwareSignature(image, firmware_key_file)) { + fprintf(stderr, "Couldn't create firmware and preamble signature.\n"); + FirmwareImageFree(image); + return NULL; + } + if (is_corrupt) { + /* Invalidate image. */ + Memset(image->firmware_data, 'X', image->firmware_len); + } + + firmware_blob = GetFirmwareBlob(image, &len); + FirmwareImageFree(image); + return firmware_blob; +} + +/* Tests that check for correctness of the VerifyFirmwareDriver_f() logic + * and rollback prevention. */ +void VerifyFirmwareDriverTest(void) { + uint8_t* valid_firmwareA = NULL; + uint8_t* valid_firmwareB = NULL; + uint8_t* corrupt_firmwareA = NULL; + uint8_t* corrupt_firmwareB = NULL; + uint64_t len; + uint8_t* root_key_pub = BufferFromFile("testkeys/key_rsa8192.keyb", + &len); + + /* Initialize rollback index state. */ + g_firmware_key_version = 1; + g_firmware_version = 1; + + valid_firmwareA = GenerateRollbackTestImage(1, 1, 0); + valid_firmwareB = GenerateRollbackTestImage(1, 1, 0); + corrupt_firmwareA = GenerateRollbackTestImage(1, 1, 1); + corrupt_firmwareB = GenerateRollbackTestImage(1, 1, 1); + + TEST_EQ(VerifyFirmwareDriver_f(root_key_pub, + valid_firmwareA, valid_firmwareB), + BOOT_FIRMWARE_A_CONTINUE, + "Firmware A (Valid with current version), " + "Firmware B (Valid with current version)"); + TEST_EQ(VerifyFirmwareDriver_f(root_key_pub, + corrupt_firmwareA, valid_firmwareB), + BOOT_FIRMWARE_B_CONTINUE, + "Firmware A (Corrupt with current version), " + "FirmwareB (Valid with current version)"); + TEST_EQ(VerifyFirmwareDriver_f(root_key_pub, + valid_firmwareA, corrupt_firmwareB), + BOOT_FIRMWARE_A_CONTINUE, + "Firmware A (Valid with current version), " + "FirmwareB (Corrupt with current version)"); + TEST_EQ(VerifyFirmwareDriver_f(root_key_pub, + corrupt_firmwareA, corrupt_firmwareB), + BOOT_FIRMWARE_RECOVERY_CONTINUE, + "Firmware A (Corrupt with current version), " + "FirmwareB (Corrupt with current version"); + g_firmware_key_version = 2; + g_firmware_version = 2; + TEST_EQ(VerifyFirmwareDriver_f(root_key_pub, valid_firmwareA, valid_firmwareB), + BOOT_FIRMWARE_RECOVERY_CONTINUE, + "Firmware A (Valid with old version), " + "Old Firmware B (Valid with old version)"); + + Free(root_key_pub); + Free(valid_firmwareA); + Free(valid_firmwareB); + Free(corrupt_firmwareA); + Free(corrupt_firmwareB); +} + +int main(int argc, char* argv[]) { + int error_code = 0; + VerifyFirmwareDriverTest(); + if (!gTestSuccess) + error_code = 255; + return error_code; +} diff --git a/tests/rollback_index_mock.c b/tests/rollback_index_mock.c new file mode 100644 index 0000000000..047fb8ac05 --- /dev/null +++ b/tests/rollback_index_mock.c @@ -0,0 +1,61 @@ +/* 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. + * + * Mock rollback index library for testing. + */ + +#include "rollback_index.h" + +#include +#include + +uint16_t g_firmware_key_version = 0; +uint16_t g_firmware_version = 0; +uint16_t g_kernel_key_version = 0; +uint16_t g_kernel_version = 0; + +void SetupTPM(void) { + fprintf(stderr, "Rollback Index Library Mock: TPM initialized.\n"); +} + +uint16_t GetStoredVersion(int type) { + switch (type) { + case FIRMWARE_KEY_VERSION: + return g_firmware_key_version; + break; + case FIRMWARE_VERSION: + return g_firmware_version; + break; + case KERNEL_KEY_VERSION: + return g_kernel_key_version; + break; + case KERNEL_VERSION: + return g_kernel_version; + break; + } + return 0; +} + +int WriteStoredVersion(int type, uint16_t version) { + switch (type) { + case FIRMWARE_KEY_VERSION: + g_firmware_key_version = version; + break; + case FIRMWARE_VERSION: + g_firmware_version = version; + break; + case KERNEL_KEY_VERSION: + g_kernel_key_version = version; + break; + case KERNEL_VERSION: + g_kernel_version = version; + break; + } + fprintf(stderr, "Rollback Index Library Mock: Stored Version written.\n"); + return 1; +} + +void LockStoredVersion(int type) { + fprintf(stderr, "Rollback Index Library Mock: Version Locked.\n"); +} diff --git a/tests/test_common.c b/tests/test_common.c new file mode 100644 index 0000000000..4a92f7d898 --- /dev/null +++ b/tests/test_common.c @@ -0,0 +1,30 @@ +/* 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 used by tests. + */ + +#include "test_common.h" + +#include + +/* ANSI Color coding sequences. */ +#define COL_GREEN "\e[1;32m" +#define COL_RED "\e[0;31m]" +#define COL_STOP "\e[m" + +/* Global test success flag. */ +int gTestSuccess = 1; + +int TEST_EQ(int result, int expected_result, char* testname) { + if (result == expected_result) { + fprintf(stderr, "%s Test " COL_GREEN " PASSED\n" COL_STOP, testname); + return 1; + } + else { + fprintf(stderr, "%s Test " COL_RED " FAILED\n" COL_STOP, testname); + gTestSuccess = 0; + return 0; + } +} diff --git a/tests/test_common.h b/tests/test_common.h new file mode 100644 index 0000000000..9fa3eecfcc --- /dev/null +++ b/tests/test_common.h @@ -0,0 +1,13 @@ +/* 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. + * + */ + +#ifndef VBOOT_REFERENCE_TEST_COMMON_H_ +#define VBOOT_REFERENCE_TEST_COMMON_H_ + +int TEST_EQ(int result, int expected_result, char* testname); +extern int gTestSuccess; + +#endif /* VBOOT_REFERENCE_TEST_COMMON_H_ */ diff --git a/utils/Makefile b/utils/Makefile index 6d5546e127..597638e75b 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -8,12 +8,15 @@ CFLAGS ?= -Wall -DNDEBUG -O3 -Werror INCLUDES ?= -I../include/ TOP ?= ../ -LIBS = firmware_image.o kernel_image.o signature_digest.o file_keys.o +LIBS = firmware_image.o kernel_image.o signature_digest.o file_keys.o \ + rollback_index.o + FIRMWARELIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a all: dumpRSAPublicKey verify_data file_keys.o signature_digest.o \ firmware_image.o kernel_image.o signature_digest.o \ - signature_digest_utility firmware_utility kernel_utility + signature_digest_utility firmware_utility kernel_utility \ + rollback_index.o dumpRSAPublicKey: dumpRSAPublicKey.c $(CC) $(CFLAGS) $< -o $@ -lcrypto @@ -26,16 +29,15 @@ signature_digest_utility: signature_digest_utility.c $(LIBS) $(FIRMWARELIBS) firmware_utility: firmware_utility.cc $(LIBS) $(FIRMWARELIBS) $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \ - -o $@ $(FIRMWARELIBS) $(LIBS) -lcrypto + -o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \ + -lcrypto kernel_utility: kernel_utility.cc $(LIBS) $(FIRMWARELIBS) $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \ - -o $@ $(FIRMWARELIBS) $(LIBS) -lcrypto + -o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \ + -lcrypto -signature_digest.o: signature_digest.c - $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ - -file_keys.o: file_keys.c +.c.o: $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ firmware_image.o: firmware_image.c diff --git a/utils/firmware_image.c b/utils/firmware_image.c index ab41fec910..4098bb2330 100644 --- a/utils/firmware_image.c +++ b/utils/firmware_image.c @@ -16,6 +16,7 @@ #include "file_keys.h" #include "padding.h" +#include "rollback_index.h" #include "rsa_utility.h" #include "sha_utility.h" #include "signature_digest.h" @@ -97,10 +98,10 @@ FirmwareImage* ReadFirmwareImage(const char* input_file) { } /* Read pre-processed public half of the sign key. */ - image->firmware_sign_key = (uint8_t*) Malloc(firmware_sign_key_len); - StatefulMemcpy(&st, image->firmware_sign_key, firmware_sign_key_len); StatefulMemcpy(&st, &image->firmware_key_version, FIELD_LEN(firmware_key_version)); + image->firmware_sign_key = (uint8_t*) Malloc(firmware_sign_key_len); + StatefulMemcpy(&st, image->firmware_sign_key, firmware_sign_key_len); StatefulMemcpy(&st, image->header_checksum, FIELD_LEN(header_checksum)); /* Check whether the header checksum matches. */ @@ -155,10 +156,10 @@ void CalculateFirmwareHeaderChecksum(const FirmwareImage* image, sizeof(image->header_len)); DigestUpdate(&ctx, (uint8_t*) &image->firmware_sign_algorithm, sizeof(image->firmware_sign_algorithm)); - DigestUpdate(&ctx, image->firmware_sign_key, - RSAProcessedKeySize(image->firmware_sign_algorithm)); DigestUpdate(&ctx, (uint8_t*) &image->firmware_key_version, sizeof(image->firmware_key_version)); + DigestUpdate(&ctx, image->firmware_sign_key, + RSAProcessedKeySize(image->firmware_sign_algorithm)); checksum = DigestFinal(&ctx); Memcpy(header_checksum, checksum, FIELD_LEN(header_checksum)); Free(checksum); @@ -176,10 +177,10 @@ uint8_t* GetFirmwareHeaderBlob(const FirmwareImage* image) { StatefulMemcpy_r(&st, &image->header_len, FIELD_LEN(header_len)); StatefulMemcpy_r(&st, &image->firmware_sign_algorithm, FIELD_LEN(header_len)); - StatefulMemcpy_r(&st, image->firmware_sign_key, - RSAProcessedKeySize(image->firmware_sign_algorithm)); StatefulMemcpy_r(&st, &image->firmware_key_version, FIELD_LEN(firmware_key_version)); + StatefulMemcpy_r(&st, image->firmware_sign_key, + RSAProcessedKeySize(image->firmware_sign_algorithm)); StatefulMemcpy_r(&st, &image->header_checksum, FIELD_LEN(header_checksum)); if (st.remaining_len != 0) { /* Underrun or Overrun. */ @@ -314,6 +315,9 @@ char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX] = { "Preamble Signature Failed.", "Firmware Signature Failed.", "Wrong Firmware Magic.", + "Invalid Firmware Header Checksum.", + "Firmware Signing Key Rollback.", + "Firmware Version Rollback." }; int VerifyFirmwareHeader(const uint8_t* root_key_blob, @@ -343,7 +347,7 @@ int VerifyFirmwareHeader(const uint8_t* root_key_blob, *algorithm = (int) algo; firmware_sign_key_len = RSAProcessedKeySize(*algorithm); - /* Verify if header len is correct? */ + /* Verify that header len is correct. */ if (hlen != (base_header_checksum_offset + firmware_sign_key_len + FIELD_LEN(header_checksum))) @@ -360,19 +364,18 @@ int VerifyFirmwareHeader(const uint8_t* root_key_blob, firmware_sign_key_len), FIELD_LEN(header_checksum))) { Free(header_checksum); - return VERIFY_FIRMWARE_INVALID_IMAGE; + return VERIFY_FIRMWARE_WRONG_HEADER_CHECKSUM; } Free(header_checksum); - /* Verify root key signature unless we are in dev mode. */ - if (!dev_mode) { - if (!RSAVerifyBinary_f(root_key_blob, NULL, /* Key to use */ - header_blob, /* Data to verify */ - *header_len, /* Length of data */ - header_blob + *header_len, /* Expected Signature */ - ROOT_SIGNATURE_ALGORITHM)) - return VERIFY_FIRMWARE_ROOT_SIGNATURE_FAILED; - } + /* Root key signature on the firmware signing key is always checked + * irrespective of dev mode. */ + if (!RSAVerifyBinary_f(root_key_blob, NULL, /* Key to use */ + header_blob, /* Data to verify */ + *header_len, /* Length of data */ + header_blob + *header_len, /* Expected Signature */ + ROOT_SIGNATURE_ALGORITHM)) + return VERIFY_FIRMWARE_ROOT_SIGNATURE_FAILED; return 0; } @@ -382,6 +385,10 @@ int VerifyFirmwarePreamble(RSAPublicKey* firmware_sign_key, int* firmware_len) { uint32_t len; int preamble_len; + uint16_t firmware_version; + + Memcpy(&firmware_version, preamble_blob, sizeof(firmware_version)); + preamble_len = (FIELD_LEN(firmware_version) + FIELD_LEN(firmware_len) + FIELD_LEN(preamble)); @@ -442,7 +449,8 @@ int VerifyFirmware(const uint8_t* root_key_blob, * times. */ firmware_sign_key_len = RSAProcessedKeySize(algorithm); firmware_sign_key_ptr = header_ptr + (FIELD_LEN(header_len) + - FIELD_LEN(firmware_sign_algorithm)); + FIELD_LEN(firmware_sign_algorithm) + + FIELD_LEN(firmware_key_version)); firmware_sign_key = RSAPublicKeyFromBuf(firmware_sign_key_ptr, firmware_sign_key_len); signature_len = siglen_map[algorithm]; @@ -458,7 +466,7 @@ int VerifyFirmware(const uint8_t* root_key_blob, } /* Only continue if firmware data verification succeeds. */ firmware_ptr = (preamble_ptr + - GetFirmwarePreambleLen(NULL) + + GetFirmwarePreambleLen(NULL) + signature_len); if ((error_code = VerifyFirmwareData(firmware_sign_key, firmware_ptr, @@ -494,16 +502,21 @@ int VerifyFirmwareImage(const RSAPublicKey* root_key, * 1) verifying the header length is correct. * 2) header_checksum is correct. */ + /* TODO(gauravsh): The [dev_mode] switch is actually irrelevant + * for the firmware verification. + * Change this to always verify the root key signature and change + * test expectations appropriately. + */ if (!dev_mode) { DigestInit(&ctx, ROOT_SIGNATURE_ALGORITHM); DigestUpdate(&ctx, (uint8_t*) &image->header_len, FIELD_LEN(header_len)); DigestUpdate(&ctx, (uint8_t*) &image->firmware_sign_algorithm, FIELD_LEN(firmware_sign_algorithm)); - DigestUpdate(&ctx, image->firmware_sign_key, - RSAProcessedKeySize(image->firmware_sign_algorithm)); DigestUpdate(&ctx, (uint8_t*) &image->firmware_key_version, FIELD_LEN(firmware_key_version)); + DigestUpdate(&ctx, image->firmware_sign_key, + RSAProcessedKeySize(image->firmware_sign_algorithm)); DigestUpdate(&ctx, image->header_checksum, FIELD_LEN(header_checksum)); header_digest = DigestFinal(&ctx); @@ -613,3 +626,117 @@ int AddFirmwareSignature(FirmwareImage* image, const char* signing_key_file) { Free(firmware_signature); return 1; } + +uint32_t GetLogicalFirmwareVersion(uint8_t* firmware_blob) { + uint16_t firmware_key_version; + uint16_t firmware_version; + uint16_t firmware_sign_algorithm; + int firmware_sign_key_len; + Memcpy(&firmware_sign_algorithm, + firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */ + FIELD_LEN(header_len)), + sizeof(firmware_sign_algorithm)); + Memcpy(&firmware_key_version, + firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */ + FIELD_LEN(header_len) + + FIELD_LEN(firmware_sign_algorithm)), + sizeof(firmware_key_version)); + if (firmware_sign_algorithm >= kNumAlgorithms) + return 0; + firmware_sign_key_len = RSAProcessedKeySize(firmware_sign_algorithm); + Memcpy(&firmware_version, + firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */ + FIELD_LEN(header_len) + + FIELD_LEN(firmware_key_version) + + firmware_sign_key_len + + FIELD_LEN(header_checksum) + + FIELD_LEN(firmware_key_signature)), + sizeof(firmware_version)); + return CombineUint16Pair(firmware_key_version, firmware_version); +} + +int VerifyFirmwareDriver_f(uint8_t* root_key_blob, + uint8_t* firmwareA, + uint8_t* firmwareB) { + /* Contains the logical firmware version (32-bit) which is calculated as + * (firmware_key_version << 16 | firmware_version) where + * [firmware_key_version] [firmware_version] are both 16-bit. + */ + uint32_t firmwareA_lversion, firmwareB_lversion; + uint8_t firmwareA_is_verified = 0; /* Whether firmwareA verify succeeded. */ + uint32_t min_lversion; /* Minimum of firmware A and firmware lversion. */ + uint32_t stored_lversion; /* Stored logical version in the TPM. */ + + /* Initialize the TPM since we'll be reading the rollback indices. */ + SetupTPM(); + + /* We get the key versions by reading directly from the image blobs without + * any additional (expensive) sanity checking on the blob since it's faster to + * outright reject a firmware with an older firmware key version. A malformed + * or corrupted firmware blob will still fail when VerifyFirmware() is called + * on it. + */ + firmwareA_lversion = GetLogicalFirmwareVersion(firmwareA); + firmwareB_lversion = GetLogicalFirmwareVersion(firmwareB); + min_lversion = Min(firmwareA_lversion, firmwareB_lversion); + stored_lversion = CombineUint16Pair(GetStoredVersion(FIRMWARE_KEY_VERSION), + GetStoredVersion(FIRMWARE_VERSION)); + /* Always try FirmwareA first. */ + if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareA, + 0)) + firmwareA_is_verified = 1; + if (firmwareA_is_verified && (stored_lversion < firmwareA_lversion)) { + /* Stored version may need to be updated but only if FirmwareB + * is successfully verified and has a logical version greater than + * the stored logical version. */ + if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB, + 0)) { + if (stored_lversion < firmwareB_lversion) { + WriteStoredVersion(FIRMWARE_KEY_VERSION, + (uint16_t) (min_lversion >> 16)); + WriteStoredVersion(FIRMWARE_VERSION, + (uint16_t) (min_lversion & 0x00FFFF)); + stored_lversion = min_lversion; /* Update stored version as it's used + * later. */ + } + } + } + /* Lock Firmware TPM rollback indices from further writes. */ + /* TODO(gauravsh): Figure out if these can be combined into one + * 32-bit location since we seem to always use them together. This can help + * us minimize the number of NVRAM writes/locks (which are limited over flash + * memory lifetimes. + */ + LockStoredVersion(FIRMWARE_KEY_VERSION); + LockStoredVersion(FIRMWARE_VERSION); + + /* Determine which firmware (if any) to jump to. + * + * We always attempt to jump to FirmwareA first. If verification of FirmwareA + * fails, we try FirmwareB. In all cases, if the firmware successfully + * verified but is a rollback, we jump to recovery. + * + * Note: This means that if FirmwareA verified successfully and is a + * rollback, then no attempt is made to check FirmwareB. We still jump to + * recovery. FirmwareB is only used as a backup in case FirmwareA gets + * corrupted. Since newer firmware updates are always written to A, + * the case where firmware A is verified but a rollback should not occur in + * normal operation. + */ + if (firmwareA_is_verified) { + if (stored_lversion <= firmwareA_lversion) + return BOOT_FIRMWARE_A_CONTINUE; + } else { + /* If FirmwareA was not valid, then we skipped over the + * check to update the rollback indices and a Verify of FirmwareB wasn't + * attempted. + * If FirmwareB is not a rollback, then we attempt to do the verification. + */ + if (stored_lversion <= firmwareB_lversion && + (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB, + 0))) + return BOOT_FIRMWARE_B_CONTINUE; + } + /* D'oh: No bootable firmware. */ + return BOOT_FIRMWARE_RECOVERY_CONTINUE; +} diff --git a/utils/rollback_index.c b/utils/rollback_index.c new file mode 100644 index 0000000000..0b65bf490f --- /dev/null +++ b/utils/rollback_index.c @@ -0,0 +1,148 @@ +/* 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 querying, manipulating and locking rollback indices + * stored in the TPM NVRAM. + */ + +#include "rollback_index.h" + +#include +#include + +#include "tlcl.h" + +uint16_t g_firmware_key_version = 0; +uint16_t g_firmware_version = 0; +uint16_t g_kernel_key_version = 0; +uint16_t g_kernel_version = 0; + +static void InitializeSpaces(void) { + uint16_t zero = 0; + uint32_t perm = TPM_NV_PER_WRITE_STCLEAR | TPM_NV_PER_PPWRITE; + + printf("Initializing spaces\n"); + TlclSetNvLocked(); /* useful only the first time */ + + TlclDefineSpace(FIRMWARE_KEY_VERSION_NV_INDEX, perm, sizeof(uint16_t)); + TlclWrite(FIRMWARE_KEY_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); + + TlclDefineSpace(FIRMWARE_VERSION_NV_INDEX, perm, sizeof(uint16_t)); + TlclWrite(FIRMWARE_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); + + TlclDefineSpace(KERNEL_KEY_VERSION_NV_INDEX, perm, sizeof(uint16_t)); + TlclWrite(KERNEL_KEY_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); + + TlclDefineSpace(KERNEL_VERSION_NV_INDEX, perm, sizeof(uint16_t)); + TlclWrite(KERNEL_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); +} + +static void EnterRecovery(void) { + /* Temporary recovery stub. Currently just initalizes spaces. */ + InitializeSpaces(); +} + +static int GetTPMRollbackIndices(void) { + /* We just perform the reads, making sure they succeed. A failure means that + * the rollback index locations are some how messed up and we must jump to + * recovery */ + if (TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, + (uint8_t*) &g_firmware_key_version, + sizeof(g_firmware_key_version)) || + TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, + (uint8_t*) &g_firmware_key_version, + sizeof(g_firmware_key_version)) || + TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, + (uint8_t*) &g_firmware_key_version, + sizeof(g_firmware_key_version)) || + TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, + (uint8_t*) &g_firmware_key_version, + sizeof(g_firmware_key_version))) + return 0; + return 1; +} + + +void SetupTPM(void) { + TlclLibinit(); + TlclStartup(); + /* TODO(gauravsh): The call to self test should probably be deferred. + * As per semenzato@chromium.org - + * TlclStartup should be called before the firmware initializes the memory + * controller, so the selftest can run in parallel with that. Here we should + * just call TlclSelftestFull to make sure the self test has + * completed---unless we want to rely on the NVRAM operations being available + * before the selftest completes. */ + TlclSelftestfull(); + TlclAssertPhysicalPresence(); + if (!GetTPMRollbackIndices()) { + fprintf(stderr, "Ho Ho Ho! We must jump to recovery."); + EnterRecovery(); + } +} + + +uint16_t GetStoredVersion(int type) { + switch (type) { + case FIRMWARE_KEY_VERSION: + return g_firmware_key_version; + break; + case FIRMWARE_VERSION: + return g_firmware_version; + break; + case KERNEL_KEY_VERSION: + return g_kernel_key_version; + break; + case KERNEL_VERSION: + return g_kernel_version; + break; + } + return 0; +} + +int WriteStoredVersion(int type, uint16_t version) { + switch (type) { + case FIRMWARE_KEY_VERSION: + return (TPM_SUCCESS == TlclWrite(FIRMWARE_KEY_VERSION_NV_INDEX, + (uint8_t*) &version, + sizeof(uint16_t))); + break; + case FIRMWARE_VERSION: + return (TPM_SUCCESS == TlclWrite(FIRMWARE_VERSION_NV_INDEX, + (uint8_t*) &version, + sizeof(uint16_t))); + break; + case KERNEL_KEY_VERSION: + return (TPM_SUCCESS == TlclWrite(KERNEL_KEY_VERSION_NV_INDEX, + (uint8_t*) &version, + sizeof(uint16_t))); + break; + case KERNEL_VERSION: + return (TPM_SUCCESS == TlclWrite(KERNEL_VERSION_NV_INDEX, + (uint8_t*) &version, + sizeof(uint16_t))); + break; + } + return 0; +} + +void LockStoredVersion(int type) { + /* TODO(gauravsh): Add error checking here to make sure TlclWriteLock + * did not fail. We must jump to recovery in that case. + */ + switch (type) { + case FIRMWARE_KEY_VERSION: + TlclWriteLock(FIRMWARE_KEY_VERSION_NV_INDEX); + break; + case FIRMWARE_VERSION: + TlclWriteLock(FIRMWARE_VERSION_NV_INDEX); + break; + case KERNEL_KEY_VERSION: + TlclWriteLock(KERNEL_KEY_VERSION_NV_INDEX); + break; + case KERNEL_VERSION: + TlclWriteLock(KERNEL_VERSION_NV_INDEX); + break; + } +}