Add tests for vboot_firmware.c

Yaay, LoadFirmware() finally has unit tests!

Fix minor memory leak in LoadFirmware().

BUG=chromium-os:17564
TEST=make && make runtests

Change-Id: I7eabc14484271f488b77f286e846781ccc22b8f2
(cherry picked from commit 2b7c5635d7069c55a1d96d11b99d02291b7e308b)
Reviewed-on: http://gerrit.chromium.org/gerrit/7052
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Tested-by: Randall Spangler <rspangler@chromium.org>
diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c
index a293639..2bc6652 100644
--- a/firmware/lib/vboot_firmware.c
+++ b/firmware/lib/vboot_firmware.c
@@ -204,8 +204,10 @@
     /* 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)
+    if (-1 != good_index) {
+      RSAPublicKeyFree(data_key);
       continue;
+    }
 
     /* Handle preamble flag for using the RO normal/dev code path */
     if (VbGetFirmwarePreambleFlags(preamble) &
@@ -251,7 +253,8 @@
       /* Verify firmware data */
       VBPERFSTART("VB_VFD");
       body_digest = DigestFinal(&lfi->body_digest_context);
-      if (0 != VerifyDigest(body_digest, &preamble->body_signature, data_key)) {
+      if (0 != VerifyDigest(body_digest, &preamble->body_signature,
+                            data_key)) {
         VBDEBUG(("Firmware body verification failed.\n"));
         *check_result = VBSD_LF_CHECK_VERIFY_BODY;
         RSAPublicKeyFree(data_key);
diff --git a/tests/Makefile b/tests/Makefile
index 6330329..ca21aef 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -24,6 +24,7 @@
 	     vboot_common_tests \
 	     vboot_common2_tests \
 	     vboot_common3_tests \
+	     vboot_firmware_tests \
 	     vboot_nvstorage_test
 
 TEST_BINS = $(addprefix ${BUILD_ROOT}/,$(TEST_NAMES))
@@ -114,6 +115,7 @@
 	${BUILD_ROOT}/tpm_bootmode_tests
 	${BUILD_ROOT}/utility_string_tests
 	${BUILD_ROOT}/utility_tests
+	${BUILD_ROOT}/vboot_firmware_tests
 
 #This will exercise vbutil_kernel and vbutil_firmware
 runfuzztests:
diff --git a/tests/test_common.c b/tests/test_common.c
index 20d8131..48ee7fd 100644
--- a/tests/test_common.c
+++ b/tests/test_common.c
@@ -17,7 +17,7 @@
 /* Global test success flag. */
 int gTestSuccess = 1;
 
-int TEST_EQ(int result, int expected_result, char* testname) {
+int TEST_EQ(int result, int expected_result, const char* testname) {
   if (result == expected_result) {
     fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname);
     return 1;
@@ -29,7 +29,7 @@
   }
 }
 
-int TEST_NEQ(int result, int not_expected_result, char* testname) {
+int TEST_NEQ(int result, int not_expected_result, const char* testname) {
   if (result != not_expected_result) {
     fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname);
     return 1;
@@ -42,7 +42,7 @@
 }
 
 int TEST_PTR_EQ(const void* result, const void* expected_result,
-                char* testname) {
+                const char* testname) {
   if (result == expected_result) {
     fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname);
     return 1;
@@ -56,7 +56,7 @@
 }
 
 int TEST_PTR_NEQ(const void* result, const void* not_expected_result,
-                char* testname) {
+                const char* testname) {
   if (result != not_expected_result) {
     fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname);
     return 1;
@@ -70,7 +70,7 @@
 }
 
 int TEST_STR_EQ(const char* result, const char* expected_result,
-                char* testname) {
+                const char* testname) {
 
   if (!result || !expected_result) {
     fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname);
diff --git a/tests/test_common.h b/tests/test_common.h
index 7a64ed3..6f594f9 100644
--- a/tests/test_common.h
+++ b/tests/test_common.h
@@ -11,28 +11,28 @@
 
 /* Return 1 if result is equal to expected_result, else return 0.
  * Also update the global gTestSuccess flag if test fails. */
-int TEST_EQ(int result, int expected_result, char* testname);
+int TEST_EQ(int result, int expected_result, const char* testname);
 
 /* Return 0 if result is equal to not_expected_result, else return 1.
  * Also update the global gTestSuccess flag if test fails. */
-int TEST_NEQ(int result, int not_expected_result, char* testname);
+int TEST_NEQ(int result, int not_expected_result, const char* testname);
 
 /* Return 1 if result pointer is equal to expected_result pointer,
  * else return 0.  Does not check pointer contents, only the pointer
  * itself.  Also update the global gTestSuccess flag if test fails. */
 int TEST_PTR_EQ(const void* result, const void* expected_result,
-                char* testname);
+                const char* testname);
 
 /* Return 1 if result pointer is not equal to expected_result pointer,
  * else return 0.  Does not check pointer contents, only the pointer
  * itself.  Also update the global gTestSuccess flag if test fails. */
 int TEST_PTR_NEQ(const void* result, const void* expected_result,
-                char* testname);
+                 const char* testname);
 
 /* Return 1 if result string is equal to expected_result string,
  * else return 0.  Also update the global gTestSuccess flag if test fails. */
 int TEST_STR_EQ(const char* result, const char* expected_result,
-                char* testname);
+                const char* testname);
 
 /* ANSI Color coding sequences.
  *
diff --git a/tests/vboot_firmware_tests.c b/tests/vboot_firmware_tests.c
new file mode 100644
index 0000000..f5925f0
--- /dev/null
+++ b/tests/vboot_firmware_tests.c
@@ -0,0 +1,427 @@
+/* Copyright (c) 2011 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 vboot_firmware library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "gbb_header.h"
+#include "host_common.h"
+#include "load_firmware_fw.h"
+#include "test_common.h"
+#include "vboot_common.h"
+#include "vboot_nvstorage.h"
+#include "vboot_struct.h"
+
+/* Mock data */
+static VbCommonParams cparams;
+static VbSelectFirmwareParams fparams;
+static VbKeyBlockHeader vblock[2];
+static VbFirmwarePreambleHeader mpreamble[2];
+static VbNvContext vnc;
+static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE];
+static VbSharedDataHeader* shared = (VbSharedDataHeader*)shared_data;
+static uint8_t gbb_data[sizeof(GoogleBinaryBlockHeader) + 2048];
+static GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)gbb_data;
+static RSAPublicKey data_key;
+static uint32_t digest_size;
+static uint8_t* digest_returned;
+static uint8_t* digest_expect_ptr;
+static int hash_fw_index;
+
+/* Reset mock data (for use before each test) */
+static void ResetMocks(void) {
+  int i;
+
+  Memset(&cparams, 0, sizeof(cparams));
+  cparams.shared_data_blob = shared_data;
+  cparams.gbb_data = gbb_data;
+
+  Memset(&fparams, 0, sizeof(fparams));
+  fparams.verification_block_A = vblock;
+  fparams.verification_size_A = sizeof(VbKeyBlockHeader);
+  fparams.verification_block_B = vblock + 1;
+  fparams.verification_size_B = sizeof(VbKeyBlockHeader);
+
+  Memset(vblock, 0, sizeof(vblock));
+  Memset(mpreamble, 0, sizeof(mpreamble));
+  for (i = 0; i < 2; i++) {
+    /* Default verification blocks to working in all modes */
+    vblock[i].key_block_flags = 0x0F;
+    vblock[i].data_key.key_version = 2;
+    /* Fix up offsets to preambles */
+    vblock[i].key_block_size =
+        (uint8_t*)(mpreamble + i) - (uint8_t*)(vblock + i);
+
+    mpreamble[i].header_version_minor = 1;  /* Supports preamble flags */
+    mpreamble[i].firmware_version = 4;
+    /* Point kernel subkey to some data following the key header */
+    PublicKeyInit(&mpreamble[i].kernel_subkey,
+                  (uint8_t*)&mpreamble[i].body_signature, 20);
+    mpreamble[i].kernel_subkey.algorithm = 7 + i;
+    mpreamble[i].body_signature.data_size = 20000 + 1000 * i;
+  }
+
+  Memset(&vnc, 0, sizeof(vnc));
+  VbNvSetup(&vnc);
+
+  Memset(&shared_data, 0, sizeof(shared_data));
+  VbSharedDataInit(shared, sizeof(shared_data));
+  shared->fw_version_tpm = 0x00020004;
+
+  Memset(&gbb_data, 0, sizeof(gbb_data));
+  gbb->rootkey_offset = sizeof(GoogleBinaryBlockHeader);
+
+  Memset(&data_key, 0, sizeof(data_key));
+
+  digest_size = 1234;
+  digest_returned = NULL;
+  digest_expect_ptr = NULL;
+  hash_fw_index = -1;
+}
+
+/****************************************************************************/
+/* Mocked verification functions */
+
+int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size,
+                   const VbPublicKey *key, int hash_only) {
+
+  TEST_EQ(hash_only, 0, "  Don't verify firmware with hash");
+  TEST_PTR_EQ(key, gbb_data + gbb->rootkey_offset, "  Verify with root key");
+  TEST_NEQ(block==vblock || block==vblock+1, 0, "  Verify a valid key block");
+
+  /* Mock uses header_version_major to hold return value */
+  return block->header_version_major;
+}
+
+int VerifyFirmwarePreamble(const VbFirmwarePreambleHeader* preamble,
+                           uint64_t size, const RSAPublicKey* key) {
+  TEST_PTR_EQ(key, &data_key, "  Verify preamble data key");
+  TEST_NEQ(preamble==mpreamble || preamble==mpreamble+1, 0,
+           "  Verify a valid preamble");
+
+  /* Mock uses header_version_major to hold return value */
+  return preamble->header_version_major;
+}
+
+RSAPublicKey* PublicKeyToRSA(const VbPublicKey* key) {
+  /* Mock uses algorithm!0 to mean invalid key */
+  if (key->algorithm)
+    return NULL;
+  /* Mock uses data key len to hold number of alloc'd keys */
+  data_key.len++;
+  return &data_key;
+}
+
+void RSAPublicKeyFree(RSAPublicKey* key) {
+  TEST_PTR_EQ(key, &data_key, "  RSA data key");
+  data_key.len--;
+}
+
+void DigestInit(DigestContext* ctx, int sig_algorithm) {
+  digest_size = 0;
+}
+
+void DigestUpdate(DigestContext* ctx, const uint8_t* data, uint32_t len) {
+  TEST_PTR_EQ(data, digest_expect_ptr, "  Digesting expected data");
+  digest_size += len;
+}
+
+uint8_t* DigestFinal(DigestContext* ctx) {
+  digest_returned = (uint8_t*)VbExMalloc(4);
+  return digest_returned;
+}
+
+VbError_t VbExHashFirmwareBody(VbCommonParams* cparams,
+                               uint32_t firmware_index) {
+  if (VB_SELECT_FIRMWARE_A == firmware_index)
+    hash_fw_index = 0;
+  else if (VB_SELECT_FIRMWARE_B == firmware_index)
+    hash_fw_index = 1;
+  else
+    return VBERROR_INVALID_PARAMETER;
+
+  digest_expect_ptr = (uint8_t*)(vblock + hash_fw_index) + 5;
+  VbUpdateFirmwareBodyHash(
+      cparams, digest_expect_ptr,
+      mpreamble[hash_fw_index].body_signature.data_size - 1024);
+  VbUpdateFirmwareBodyHash(cparams, digest_expect_ptr, 1024);
+
+  /* If signature offset is 42, hash the wrong amount and return success */
+  if (42 == mpreamble[hash_fw_index].body_signature.sig_offset) {
+    VbUpdateFirmwareBodyHash(cparams, digest_expect_ptr, 4);
+    return VBERROR_SUCCESS;
+  }
+
+  /* Otherwise, mocked function uses body signature offset as returned value */
+  return mpreamble[hash_fw_index].body_signature.sig_offset;
+}
+
+int VerifyDigest(const uint8_t* digest, const VbSignature *sig,
+                 const RSAPublicKey* key) {
+  TEST_PTR_EQ(digest, digest_returned, "Verifying expected digest");
+  TEST_PTR_EQ(key, &data_key, "Verifying using data key");
+  TEST_PTR_EQ(sig, &mpreamble[hash_fw_index].body_signature, "Verifying sig");
+  /* Mocked function uses sig size as return value for verifying digest */
+  return sig->sig_size;
+}
+
+/****************************************************************************/
+/* Test LoadFirmware() and check expected return value and recovery reason */
+static void TestLoadFirmware(VbError_t expected_retval,
+                             uint8_t expected_recovery, const char* desc) {
+  uint32_t rr = 256;
+
+  TEST_EQ(LoadFirmware(&cparams, &fparams, &vnc), expected_retval, desc);
+  VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &rr);
+  TEST_EQ(rr, expected_recovery, "  recovery reason");
+  TEST_EQ(data_key.len, 0, "  Data key free must be paired with alloc");
+}
+
+/****************************************************************************/
+
+static void LoadFirmwareTest(void) {
+  uint32_t u;
+
+  /* Test error should cause abort */
+  ResetMocks();
+  VbNvSet(&vnc, VBNV_TEST_ERROR_FUNC, VBNV_TEST_ERROR_LOAD_FIRMWARE);
+  VbNvSet(&vnc, VBNV_TEST_ERROR_NUM, 1);
+  TestLoadFirmware(VBERROR_SIMULATED, VBNV_RECOVERY_RO_TEST_LF,
+                   "Simulated error");
+  TEST_EQ(shared->firmware_index, 0xFF, "Error means no firmware index");
+
+  /* Require GBB */
+  ResetMocks();
+  cparams.gbb_data = NULL;
+  TestLoadFirmware(VBERROR_INVALID_GBB, VBNV_RECOVERY_RO_UNSPECIFIED,
+                   "No GBB");
+
+  /* Key block flags must match */
+  /* Normal boot */
+  ResetMocks();
+  vblock[0].key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_1;
+  vblock[1].key_block_flags =
+      KEY_BLOCK_FLAG_DEVELOPER_0 | KEY_BLOCK_FLAG_RECOVERY_1;
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_REC_MISMATCH),
+                   "Flags mismatch dev=0");
+  TEST_EQ(shared->flags & VBSD_LF_DEV_SWITCH_ON, 0,
+          "Dev flag in shared.flags dev=0");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_DEV_MISMATCH,
+          "Key block flag mismatch for dev=0");
+  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_REC_MISMATCH,
+          "Key block flag mismatch for rec=0");
+  /* Developer boot */
+  ResetMocks();
+  shared->flags |= VBSD_BOOT_DEV_SWITCH_ON;
+  vblock[0].key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_0;
+  vblock[1].key_block_flags =
+      KEY_BLOCK_FLAG_DEVELOPER_1 | KEY_BLOCK_FLAG_RECOVERY_1;
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_REC_MISMATCH),
+                   "Flags mismatch dev=1");
+  TEST_NEQ(shared->flags & VBSD_LF_DEV_SWITCH_ON, 0,
+          "Dev flag in shared.flags dev=1");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_DEV_MISMATCH,
+          "Key block flag mismatch for dev=1");
+  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_REC_MISMATCH,
+          "Key block flag mismatch for rec=1");
+
+  /* Test key block verification with A and key version rollback with B */
+  ResetMocks();
+  vblock[0].header_version_major = 1;  /* Simulate failure */
+  vblock[1].data_key.key_version = 1;  /* Simulate rollback */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_KEY_ROLLBACK),
+                   "Key block invalid / key version rollback");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VERIFY_KEYBLOCK,
+          "Key block invalid");
+  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_KEY_ROLLBACK,
+          "Key version rollback ");
+  TEST_EQ(shared->fw_version_lowest, 0, "Lowest valid version");
+  TEST_EQ(shared->fw_version_tpm, 0x20004, "TPM version");
+
+  /* Test invalid key version with A and bad data key with B */
+  ResetMocks();
+  vblock[0].data_key.key_version = 0x10003;  /* Version > 0xFFFF is invalid */
+  vblock[1].data_key.algorithm = 1;  /* Simulate invalid data key */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_DATA_KEY_PARSE),
+                   "Key version overflow / invalid data key");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_KEY_ROLLBACK,
+          "Key version overflow");
+  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_DATA_KEY_PARSE,
+          "Data key invalid");
+
+  /* Test invalid preamble with A */
+  ResetMocks();
+  mpreamble[0].header_version_major = 1;  /* Simulate failure */
+  vblock[1].key_block_flags = 0;  /* Invalid */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_VERIFY_PREAMBLE),
+                   "Preamble invalid");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VERIFY_PREAMBLE,
+          "Preamble invalid A");
+
+  /* Test invalid firmware versions */
+  ResetMocks();
+  mpreamble[0].firmware_version = 3;  /* Simulate rollback */
+  mpreamble[1].firmware_version = 0x10001;  /* Check overflow */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_FW_ROLLBACK),
+                   "Firmware version check");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_FW_ROLLBACK,
+          "Firmware version rollback");
+  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_FW_ROLLBACK,
+          "Firmware version overflow");
+
+  /* Test RO normal with A */
+  ResetMocks();
+  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+  vblock[1].key_block_flags = 0;  /* Invalid */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_NO_RO_NORMAL),
+                   "Preamble asked for RO normal but fw doesn't support it");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_NO_RO_NORMAL,
+          "No RO normal A");
+
+  /* If RO normal is supported, don't need to verify the firmware body */
+  ResetMocks();
+  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+  /* Mock bad sig, to ensure we didn't use it */
+  mpreamble[0].body_signature.sig_size = 1;
+  shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
+  vblock[1].key_block_flags = 0;  /* Invalid */
+  TestLoadFirmware(VBERROR_SUCCESS, 0, "RO normal A");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID, "RO normal A valid");
+  TEST_EQ(shared->firmware_index, 0, "Boot A shared index");
+  TEST_EQ(shared->fw_keyblock_flags, vblock[0].key_block_flags,
+          "Copy key block flags");
+  TEST_EQ(shared->kernel_subkey.algorithm, 7, "Copy kernel subkey");
+
+  /* If both A and B are valid and same version as TPM, A is selected
+   * and B isn't attempted. */
+  ResetMocks();
+  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+  mpreamble[1].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+  shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
+  TestLoadFirmware(VBERROR_SUCCESS, 0, "Check A then B");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID, "RO normal A valid");
+  TEST_EQ(shared->check_fw_b_result, 0, "RO normal B not checked ");
+  TEST_EQ(shared->firmware_index, 0, "Boot A");
+  TEST_EQ(shared->flags & VBSD_FWB_TRIED, 0, "Didn't try firmware B");
+  TEST_EQ(shared->kernel_subkey.algorithm, 7, "Copy kernel subkey");
+  /* But if try B flag is set, B is selected and A not attempted */
+  ResetMocks();
+  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+  mpreamble[1].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+  shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
+  VbNvSet(&vnc, VBNV_TRY_B_COUNT, 5);
+  TestLoadFirmware(VBERROR_SUCCESS, 0, "Check B then A");
+  TEST_EQ(shared->check_fw_a_result, 0, "RO normal A not checked ");
+  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_VALID, "RO normal B valid");
+  TEST_EQ(shared->firmware_index, 1, "Boot B");
+  TEST_NEQ(shared->flags & VBSD_FWB_TRIED, 0, "Tried firmware B");
+  TEST_EQ(shared->kernel_subkey.algorithm, 8, "Copy kernel subkey");
+  VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
+  TEST_EQ(u, 4, "Used up a try");
+
+  /* If both A and B are valid and grater version than TPM, A is
+   * selected and B preamble (but not body) is verified. */
+  ResetMocks();
+  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+  mpreamble[1].flags = 0;
+  mpreamble[0].firmware_version = 5;
+  mpreamble[1].firmware_version = 6;
+  shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
+  TestLoadFirmware(VBERROR_SUCCESS, 0, "Check A then B advancing version");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID, "RO normal A valid");
+  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_HEADER_VALID,
+          "RO normal B header valid");
+  TEST_EQ(shared->firmware_index, 0, "Boot A");
+  TEST_EQ(shared->fw_keyblock_flags, vblock[0].key_block_flags, "Key block A");
+  TEST_EQ(shared->kernel_subkey.algorithm, 7, "Copy kernel subkey");
+  TEST_EQ(shared->fw_version_lowest, 0x20005, "Lowest valid version");
+  TEST_EQ(shared->fw_version_tpm, 0x20005, "TPM version advanced");
+
+  /* Verify firmware data */
+  ResetMocks();
+  vblock[1].key_block_flags = 0;  /* Invalid */
+  TestLoadFirmware(VBERROR_SUCCESS, 0, "Verify firmware body");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID,
+          "Firmware body A valid");
+  TEST_EQ(shared->firmware_index, 0, "Boot A shared index");
+  TEST_EQ(hash_fw_index, 0, "Hash firmware data A");
+  TEST_EQ(digest_size, mpreamble[0].body_signature.data_size,
+          "Verified all data expected");
+
+  /* Test error getting firmware body */
+  ResetMocks();
+  mpreamble[0].body_signature.sig_offset = VBERROR_UNKNOWN;
+  vblock[1].key_block_flags = 0;  /* Invalid */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_GET_FW_BODY),
+                   "Error getting firmware body");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_GET_FW_BODY,
+          "Firmware body A");
+
+  /* Test digesting the wrong amount */
+  ResetMocks();
+  mpreamble[0].body_signature.sig_offset = 42;  /* Mock hashing wrong amount */
+  vblock[1].key_block_flags = 0;  /* Invalid */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_HASH_WRONG_SIZE),
+                   "Hash wrong size");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_HASH_WRONG_SIZE,
+          "Firmware hash wrong size A");
+
+  /* Test bad signature */
+  ResetMocks();
+  mpreamble[0].body_signature.sig_size = 1;  /* Mock bad sig */
+  vblock[1].key_block_flags = 0;  /* Invalid */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_VERIFY_BODY),
+                   "Bad signature");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VERIFY_BODY,
+          "Bad signature A");
+
+  /* Test unable to store kernel data key */
+  ResetMocks();
+  mpreamble[0].kernel_subkey.key_size = VB_SHARED_DATA_MIN_SIZE + 1;
+  vblock[1].key_block_flags = 0;  /* Invalid */
+  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
+                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
+                    VBSD_LF_CHECK_VALID),
+                   "Kernel key too big");
+  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID,
+          "Kernel key too big");
+}
+
+
+/* disable MSVC warnings on unused arguments */
+__pragma(warning (disable: 4100))
+
+int main(int argc, char* argv[]) {
+  int error_code = 0;
+
+  LoadFirmwareTest();
+
+  if (!gTestSuccess)
+    error_code = 255;
+
+  return error_code;
+}