VBoot Reference: Add version checking to for preventing rollbacks.
This CL adds a new function VerifyFirmwareDriver_f() means to be a part of the RO firmware which determine which copy of the firmware to boot from. It is meant to ensure that a particular firmware is only booted if 1) it verifies successfully, 2) its version is newer or equal to current stored version. In addition, the driver function also updates the stored version if needed.
Currently I am using the TLCL API with stub calls, (in fact, most of the TPM interaction is done in rollback_index.c which implements the actual version query/update API) used by the firmware.
Review URL: http://codereview.chromium.org/1241002
diff --git a/tests/Makefile b/tests/Makefile
index 6617a41..931e3f5 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -7,11 +7,13 @@
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 @@
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 3df7cb0..b252735 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 @@
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 @@
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 @@
return success;
}
-
/* Normal FirmwareImage Verification Tests. */
int VerifyFirmwareImageTest(FirmwareImage* image,
RSAPublicKey* root_key) {
@@ -142,14 +155,17 @@
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 <algorithm> <root key> <processed root pubkey>"
" <signing key> <processed signing key>\n", argv[0]);
@@ -157,30 +173,24 @@
}
/* 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 @@
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 @@
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 0000000..19ac850
--- /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 <stdio.h>
+#include <stdlib.h>
+
+#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 0000000..047fb8a
--- /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 <stdio.h>
+#include <stdint.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;
+
+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 0000000..4a92f7d
--- /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 <stdio.h>
+
+/* 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 0000000..9fa3eec
--- /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_ */