VBoot Reference: Refactor Pass 1: Split {firmware|kernel}_image

This CL refactors verified boot firmware and kernel image functions into firmware and userland portions. Data Types and Functions that need to be a part of the final firmware implementation reside in files with "_fw" suffix - firmware_image_fw.{c|h} and kernel_image_fw.{c|h}.

Also some Makefile cleanups.

Review URL: http://codereview.chromium.org/1599001
diff --git a/utils/Makefile b/utils/Makefile
index 597638e..81de74f 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -8,43 +8,53 @@
 INCLUDES ?= -I../include/
 TOP ?= ../
 
-LIBS =  firmware_image.o kernel_image.o signature_digest.o file_keys.o \
-	rollback_index.o
+LIBS = file_keys.o \
+	firmware_image.o \
+	firmware_image_fw.o \
+	kernel_image.o \
+	kernel_image_fw.o \
+	rollback_index.o \
+	signature_digest.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 \
-	rollback_index.o
+TARGET_BINS = $(LIBS) \
+		dumpRSAPublicKey \
+		firmware_utility \
+		kernel_utility \
+		signature_digest_utility \
+		verify_data
+
+all: $(TARGET_BINS)
+
+.c.o:
+	$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
 
 dumpRSAPublicKey: dumpRSAPublicKey.c
 	$(CC) $(CFLAGS) $< -o $@ -lcrypto
 
-verify_data: verify_data.c $(LIBS) $(FIRMWARELIBS)
-	$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
-
-signature_digest_utility: signature_digest_utility.c $(LIBS) $(FIRMWARELIBS)
-	$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
+firmware_image_fw.o: firmware_image_fw.c
+	$(CC) $(CFLAGS) -ansi $(INCLUDES) -c $^ -o $@
 
 firmware_utility: firmware_utility.cc $(LIBS) $(FIRMWARELIBS)
 	$(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \
 	-o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \
 	-lcrypto
 
+kernel_image_fw.o: kernel_image_fw.c
+	$(CC) $(CFLAGS) -ansi $(INCLUDES) -c $< -o $@
+
 kernel_utility: kernel_utility.cc $(LIBS) $(FIRMWARELIBS)
 	$(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \
 	-o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \
 	-lcrypto
 
-.c.o:
-	$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+signature_digest_utility: signature_digest_utility.c $(LIBS) $(FIRMWARELIBS)
+	$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
 
-firmware_image.o: firmware_image.c
-	$(CC) -ansi $(CFLAGS) $(INCLUDES) -c $< -o $@
+verify_data: verify_data.c $(LIBS) $(FIRMWARELIBS)
+	$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
 
-kernel_image.o: kernel_image.c
-	$(CC) -ansi $(CFLAGS) $(INCLUDES) -c $< -o $@
 clean:
-	rm -f dumpRSAPublicKey verify_data signature_digest firmware_utility \
-	kernel_utility signature_digest_utility $(LIBS)
+	rm -f $(TARGET_BINS) $(LIBS)
+
diff --git a/utils/dumpRSAPublicKey.c b/utils/dumpRSAPublicKey.c
index b6a5190..837303c 100644
--- a/utils/dumpRSAPublicKey.c
+++ b/utils/dumpRSAPublicKey.c
@@ -8,7 +8,7 @@
  * /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
  */
 
-#include <inttypes.h>
+#include <stdint.h>
 #include <openssl/bn.h>
 #include <openssl/evp.h>
 #include <openssl/pem.h>
diff --git a/utils/firmware_image.c b/utils/firmware_image.c
index 2e3f924..803ef89 100644
--- a/utils/firmware_image.c
+++ b/utils/firmware_image.c
@@ -9,14 +9,12 @@
 
 #include <fcntl.h>
 #include <limits.h>
-#include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include "file_keys.h"
 #include "padding.h"
-#include "rollback_index.h"
 #include "rsa_utility.h"
 #include "sha_utility.h"
 #include "signature_digest.h"
@@ -71,7 +69,7 @@
   /* Read and compare magic bytes. */
   StatefulMemcpy(&st, &image->magic, FIRMWARE_MAGIC_SIZE);
   if (SafeMemcmp(image->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE)) {
-    fprintf(stderr, "Wrong Firmware Magic.\n");
+    debug("Wrong Firmware Magic.\n");
     Free(firmware_buf);
     return NULL;
   }
@@ -92,8 +90,8 @@
   /* Check whether the header length is correct. */
   header_len = GetFirmwareHeaderLen(image);
   if (header_len != image->header_len) {
-    fprintf(stderr, "Header length mismatch. Got: %d Expected: %d\n",
-            image->header_len, header_len);
+    debug("Header length mismatch. Got: %d Expected: %d\n",
+          image->header_len, header_len);
     Free(firmware_buf);
     return NULL;
   }
@@ -109,7 +107,7 @@
   CalculateFirmwareHeaderChecksum(image, header_checksum);
   if (SafeMemcmp(header_checksum, image->header_checksum,
                  FIELD_LEN(header_checksum))) {
-    fprintf(stderr, "Invalid firmware header checksum!\n");
+    debug("Invalid firmware header checksum!\n");
     Free(firmware_buf);
     return NULL;
   }
@@ -271,17 +269,17 @@
   if (!image)
     return 0;
   if (-1 == (fd = creat(input_file, S_IRWXU))) {
-    fprintf(stderr, "Couldn't open file for writing.\n");
+    debug("Couldn't open file for writing.\n");
     return 0;
   }
 
   firmware_blob = GetFirmwareBlob(image, &blob_len);
   if (!firmware_blob) {
-    fprintf(stderr, "Couldn't create firmware blob from FirmwareImage.\n");
+    debug("Couldn't create firmware blob from FirmwareImage.\n");
     return 0;
   }
   if (blob_len != write(fd, firmware_blob, blob_len)) {
-    fprintf(stderr, "Couldn't write Firmware Image to file: %s\n", input_file);
+    debug("Couldn't write Firmware Image to file: %s\n", input_file);
     Free(firmware_blob);
     close(fd);
     return 0;
@@ -296,7 +294,7 @@
     return;
 
   /* Print header. */
-  printf("Header Length = %d\n"
+  debug("Header Length = %d\n"
          "Firmware Signature Algorithm = %s\n"
          "Firmware Key Version = %d\n\n",
          image->header_len,
@@ -304,201 +302,13 @@
          image->firmware_key_version);
   /* TODO(gauravsh): Output hash and key signature here? */
   /* Print preamble. */
-  printf("Firmware Version = %d\n"
+  debug("Firmware Version = %d\n"
          "Firmware Length = %" PRIu64 "\n\n",
          image->firmware_version,
          image->firmware_len);
   /* Output key signature here? */
 }
 
-char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX] = {
-  "Success.",
-  "Invalid Image.",
-  "Root Key Signature Failed.",
-  "Invalid Verification Algorithm.",
-  "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,
-                         const uint8_t* header_blob,
-                         int* algorithm,
-                         int* header_len) {
-  int firmware_sign_key_len;
-  int root_key_len;
-  uint16_t hlen, algo;
-  uint8_t* header_checksum = NULL;
-
-  /* Base Offset for the header_checksum field. Actual offset is
-   * this + firmware_sign_key_len. */
-  int base_header_checksum_offset = (FIELD_LEN(header_len) +
-                                     FIELD_LEN(firmware_sign_algorithm) +
-                                     FIELD_LEN(firmware_key_version));
-
-
-  root_key_len = RSAProcessedKeySize(ROOT_SIGNATURE_ALGORITHM);
-  Memcpy(&hlen, header_blob, sizeof(hlen));
-  Memcpy(&algo,
-         header_blob + FIELD_LEN(firmware_sign_algorithm),
-         sizeof(algo));
-  if (algo >= kNumAlgorithms)
-    return VERIFY_FIRMWARE_INVALID_ALGORITHM;
-  *algorithm = (int) algo;
-  firmware_sign_key_len = RSAProcessedKeySize(*algorithm);
-
-  /* Verify that header len is correct. */
-  if (hlen != (base_header_checksum_offset +
-               firmware_sign_key_len +
-               FIELD_LEN(header_checksum)))
-    return VERIFY_FIRMWARE_INVALID_IMAGE;
-
-  *header_len = (int) hlen;
-
-  /* Verify if the hash of the header is correct. */
-  header_checksum = DigestBuf(header_blob,
-                              *header_len - FIELD_LEN(header_checksum),
-                              SHA512_DIGEST_ALGORITHM);
-  if (SafeMemcmp(header_checksum,
-                  header_blob + (base_header_checksum_offset +
-                                 firmware_sign_key_len),
-                  FIELD_LEN(header_checksum))) {
-    Free(header_checksum);
-    return VERIFY_FIRMWARE_WRONG_HEADER_CHECKSUM;
-  }
-  Free(header_checksum);
-
-  /* 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;
-}
-
-int VerifyFirmwarePreamble(RSAPublicKey* firmware_sign_key,
-                           const uint8_t* preamble_blob,
-                           int algorithm,
-                           uint64_t* firmware_len) {
-  uint64_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));
-  if (!RSAVerifyBinary_f(NULL, firmware_sign_key,  /* Key to use */
-                         preamble_blob,  /* Data to verify */
-                         preamble_len,  /* Length of data */
-                         preamble_blob + preamble_len,  /* Expected Signature */
-                         algorithm))
-    return VERIFY_FIRMWARE_PREAMBLE_SIGNATURE_FAILED;
-
-  Memcpy(&len, preamble_blob + FIELD_LEN(firmware_version),
-         sizeof(len));
-  *firmware_len = len;
-  return 0;
-}
-
-int VerifyFirmwareData(RSAPublicKey* firmware_sign_key,
-                       const uint8_t* preamble_start,
-                       const uint8_t* firmware_data_start,
-                       uint64_t firmware_len,
-                       int algorithm) {
-  int signature_len = siglen_map[algorithm];
-  uint8_t* digest;
-  DigestContext ctx;
-
-  /* Since the firmware signature is over the preamble and the firmware data,
-   * which does not form a contiguous region of memory, we calculate the
-   * message digest ourselves. */
-  DigestInit(&ctx, algorithm);
-  DigestUpdate(&ctx, preamble_start, GetFirmwarePreambleLen());
-  DigestUpdate(&ctx, firmware_data_start + signature_len, firmware_len);
-  digest = DigestFinal(&ctx);
-  if (!RSAVerifyBinaryWithDigest_f(
-          NULL, firmware_sign_key,  /* Key to use. */
-          digest,  /* Digest of the data to verify. */
-          firmware_data_start,  /* Expected Signature */
-          algorithm)) {
-    Free(digest);
-    return VERIFY_FIRMWARE_SIGNATURE_FAILED;
-  }
-  Free(digest);
-  return 0;
-}
-
-int VerifyFirmware(const uint8_t* root_key_blob,
-                   const uint8_t* firmware_blob) {
-  int error_code = 0;
-  int algorithm;  /* Signing key algorithm. */
-  RSAPublicKey* firmware_sign_key = NULL;
-  int firmware_sign_key_len, signature_len, header_len;
-  uint64_t firmware_len;
-  const uint8_t* header_ptr = NULL;  /* Pointer to header. */
-  const uint8_t* firmware_sign_key_ptr = NULL;  /* Pointer to signing key. */
-  const uint8_t* preamble_ptr = NULL;  /* Pointer to preamble block. */
-  const uint8_t* firmware_ptr = NULL;  /* Pointer to firmware signature/data. */
-
-  /* Note: All the offset calculations are based on struct FirmwareImage which
-   * is defined in include/firmware_image.h. */
-
-  /* Compare magic bytes. */
-  if (SafeMemcmp(firmware_blob, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE))
-    return VERIFY_FIRMWARE_WRONG_MAGIC;
-  header_ptr = firmware_blob + FIRMWARE_MAGIC_SIZE;
-
-  /* Only continue if header verification succeeds. */
-  if ((error_code = VerifyFirmwareHeader(root_key_blob, header_ptr,
-                                         &algorithm, &header_len)))
-    return error_code;  /* AKA jump to revovery. */
-
-  /* Parse signing key into RSAPublicKey structure since it is required multiple
-   * 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_key_version));
-  firmware_sign_key = RSAPublicKeyFromBuf(firmware_sign_key_ptr,
-                                          firmware_sign_key_len);
-  signature_len = siglen_map[algorithm];
-
-  /* Only continue if preamble verification succeeds. */
-  preamble_ptr = (header_ptr + header_len +
-                  FIELD_LEN(firmware_key_signature));
-  if ((error_code = VerifyFirmwarePreamble(firmware_sign_key, preamble_ptr,
-                                           algorithm,
-                                           &firmware_len))) {
-    RSAPublicKeyFree(firmware_sign_key);
-    fprintf(stderr, "Couldn't verify Firmware preamble.\n");
-    return error_code;  /* AKA jump to recovery. */
-  }
-  /* Only continue if firmware data verification succeeds. */
-  firmware_ptr = (preamble_ptr +
-                  GetFirmwarePreambleLen() +
-                  signature_len);
-
-  if ((error_code = VerifyFirmwareData(firmware_sign_key, preamble_ptr,
-                                       firmware_ptr,
-                                       firmware_len,
-                                       algorithm))) {
-    RSAPublicKeyFree(firmware_sign_key);
-    fprintf(stderr, "Couldn't verify Firmware data.\n");
-    return error_code;  /* AKA jump to recovery. */
-  }
-
-  RSAPublicKeyFree(firmware_sign_key);
-  return 0;  /* Success! */
-}
-
 int VerifyFirmwareImage(const RSAPublicKey* root_key,
                         const FirmwareImage* image) {
   RSAPublicKey* firmware_sign_key = NULL;
@@ -662,114 +472,3 @@
   Free(preamble_blob);
   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))
-    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 (stored_lversion < firmwareB_lversion) {
-      if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)) {
-        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)))
-        return BOOT_FIRMWARE_B_CONTINUE;
-  }
-  /* D'oh: No bootable firmware. */
-  return BOOT_FIRMWARE_RECOVERY_CONTINUE;
-}
diff --git a/utils/firmware_image_fw.c b/utils/firmware_image_fw.c
new file mode 100644
index 0000000..f5c7d89
--- /dev/null
+++ b/utils/firmware_image_fw.c
@@ -0,0 +1,323 @@
+/* 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 verifying a verified boot firmware image.
+ * (Firmware Portion)
+ */
+
+#include "firmware_image_fw.h"
+
+#include "padding.h"
+#include "rollback_index.h"
+#include "rsa_utility.h"
+#include "sha_utility.h"
+#include "utility.h"
+
+/* Macro to determine the size of a field structure in the FirmwareImage
+ * structure. */
+#define FIELD_LEN(field) (sizeof(((FirmwareImage*)0)->field))
+
+char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX] = {
+  "Success.",
+  "Invalid Image.",
+  "Root Key Signature Failed.",
+  "Invalid Verification Algorithm.",
+  "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,
+                         const uint8_t* header_blob,
+                         int* algorithm,
+                         int* header_len) {
+  int firmware_sign_key_len;
+  int root_key_len;
+  uint16_t hlen, algo;
+  uint8_t* header_checksum = NULL;
+
+  /* Base Offset for the header_checksum field. Actual offset is
+   * this + firmware_sign_key_len. */
+  int base_header_checksum_offset = (FIELD_LEN(header_len) +
+                                     FIELD_LEN(firmware_sign_algorithm) +
+                                     FIELD_LEN(firmware_key_version));
+
+
+  root_key_len = RSAProcessedKeySize(ROOT_SIGNATURE_ALGORITHM);
+  Memcpy(&hlen, header_blob, sizeof(hlen));
+  Memcpy(&algo,
+         header_blob + FIELD_LEN(firmware_sign_algorithm),
+         sizeof(algo));
+  if (algo >= kNumAlgorithms)
+    return VERIFY_FIRMWARE_INVALID_ALGORITHM;
+  *algorithm = (int) algo;
+  firmware_sign_key_len = RSAProcessedKeySize(*algorithm);
+
+  /* Verify that header len is correct. */
+  if (hlen != (base_header_checksum_offset +
+               firmware_sign_key_len +
+               FIELD_LEN(header_checksum)))
+    return VERIFY_FIRMWARE_INVALID_IMAGE;
+
+  *header_len = (int) hlen;
+
+  /* Verify if the hash of the header is correct. */
+  header_checksum = DigestBuf(header_blob,
+                              *header_len - FIELD_LEN(header_checksum),
+                              SHA512_DIGEST_ALGORITHM);
+  if (SafeMemcmp(header_checksum,
+                  header_blob + (base_header_checksum_offset +
+                                 firmware_sign_key_len),
+                  FIELD_LEN(header_checksum))) {
+    Free(header_checksum);
+    return VERIFY_FIRMWARE_WRONG_HEADER_CHECKSUM;
+  }
+  Free(header_checksum);
+
+  /* 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;
+}
+
+int VerifyFirmwarePreamble(RSAPublicKey* firmware_sign_key,
+                           const uint8_t* preamble_blob,
+                           int algorithm,
+                           uint64_t* firmware_len) {
+  uint64_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));
+  if (!RSAVerifyBinary_f(NULL, firmware_sign_key,  /* Key to use */
+                         preamble_blob,  /* Data to verify */
+                         preamble_len,  /* Length of data */
+                         preamble_blob + preamble_len,  /* Expected Signature */
+                         algorithm))
+    return VERIFY_FIRMWARE_PREAMBLE_SIGNATURE_FAILED;
+
+  Memcpy(&len, preamble_blob + FIELD_LEN(firmware_version),
+         sizeof(len));
+  *firmware_len = len;
+  return 0;
+}
+
+int VerifyFirmwareData(RSAPublicKey* firmware_sign_key,
+                       const uint8_t* preamble_start,
+                       const uint8_t* firmware_data_start,
+                       uint64_t firmware_len,
+                       int algorithm) {
+  int signature_len = siglen_map[algorithm];
+  uint8_t* digest;
+  DigestContext ctx;
+
+  /* Since the firmware signature is over the preamble and the firmware data,
+   * which does not form a contiguous region of memory, we calculate the
+   * message digest ourselves. */
+  DigestInit(&ctx, algorithm);
+  DigestUpdate(&ctx, preamble_start,
+               (FIELD_LEN(firmware_version) +
+                FIELD_LEN(firmware_len) +
+                FIELD_LEN(preamble)));
+  DigestUpdate(&ctx, firmware_data_start + signature_len, firmware_len);
+  digest = DigestFinal(&ctx);
+  if (!RSAVerifyBinaryWithDigest_f(
+          NULL, firmware_sign_key,  /* Key to use. */
+          digest,  /* Digest of the data to verify. */
+          firmware_data_start,  /* Expected Signature */
+          algorithm)) {
+    Free(digest);
+    return VERIFY_FIRMWARE_SIGNATURE_FAILED;
+  }
+  Free(digest);
+  return 0;
+}
+
+int VerifyFirmware(const uint8_t* root_key_blob,
+                   const uint8_t* firmware_blob) {
+  int error_code = 0;
+  int algorithm;  /* Signing key algorithm. */
+  RSAPublicKey* firmware_sign_key = NULL;
+  int firmware_sign_key_len, signature_len, header_len;
+  uint64_t firmware_len;
+  const uint8_t* header_ptr = NULL;  /* Pointer to header. */
+  const uint8_t* firmware_sign_key_ptr = NULL;  /* Pointer to signing key. */
+  const uint8_t* preamble_ptr = NULL;  /* Pointer to preamble block. */
+  const uint8_t* firmware_ptr = NULL;  /* Pointer to firmware signature/data. */
+
+  /* Note: All the offset calculations are based on struct FirmwareImage which
+   * is defined in include/firmware_image.h. */
+
+  /* Compare magic bytes. */
+  if (SafeMemcmp(firmware_blob, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE))
+    return VERIFY_FIRMWARE_WRONG_MAGIC;
+  header_ptr = firmware_blob + FIRMWARE_MAGIC_SIZE;
+
+  /* Only continue if header verification succeeds. */
+  if ((error_code = VerifyFirmwareHeader(root_key_blob, header_ptr,
+                                         &algorithm, &header_len)))
+    return error_code;  /* AKA jump to revovery. */
+
+  /* Parse signing key into RSAPublicKey structure since it is required multiple
+   * 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_key_version));
+  firmware_sign_key = RSAPublicKeyFromBuf(firmware_sign_key_ptr,
+                                          firmware_sign_key_len);
+  signature_len = siglen_map[algorithm];
+
+  /* Only continue if preamble verification succeeds. */
+  preamble_ptr = (header_ptr + header_len +
+                  FIELD_LEN(firmware_key_signature));
+  if ((error_code = VerifyFirmwarePreamble(firmware_sign_key, preamble_ptr,
+                                           algorithm,
+                                           &firmware_len))) {
+    RSAPublicKeyFree(firmware_sign_key);
+    debug("Couldn't verify Firmware preamble.\n");
+    return error_code;  /* AKA jump to recovery. */
+  }
+  /* Only continue if firmware data verification succeeds. */
+  firmware_ptr = (preamble_ptr +
+                  (FIELD_LEN(firmware_version) +  /* Skip the preamble. */
+                   FIELD_LEN(firmware_len) +
+                   FIELD_LEN(preamble)) +
+                  signature_len);
+
+  if ((error_code = VerifyFirmwareData(firmware_sign_key, preamble_ptr,
+                                       firmware_ptr,
+                                       firmware_len,
+                                       algorithm))) {
+    RSAPublicKeyFree(firmware_sign_key);
+    debug("Couldn't verify Firmware data.\n");
+    return error_code;  /* AKA jump to recovery. */
+  }
+
+  RSAPublicKeyFree(firmware_sign_key);
+  return 0;  /* Success! */
+}
+
+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))
+    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 (stored_lversion < firmwareB_lversion) {
+      if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)) {
+        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)))
+        return BOOT_FIRMWARE_B_CONTINUE;
+  }
+  /* D'oh: No bootable firmware. */
+  return BOOT_FIRMWARE_RECOVERY_CONTINUE;
+}
diff --git a/utils/kernel_image.c b/utils/kernel_image.c
index 32e12a8..e66ce38 100644
--- a/utils/kernel_image.c
+++ b/utils/kernel_image.c
@@ -3,6 +3,7 @@
  * found in the LICENSE file.
  *
  * Functions for generating and manipulating a verified boot kernel image.
+ * (Userland portion)
  */
 
 #include "kernel_image.h"
@@ -75,7 +76,7 @@
   StatefulMemcpy(&st, &image->magic, KERNEL_MAGIC_SIZE);
 
   if (SafeMemcmp(image->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE)) {
-    fprintf(stderr, "Wrong Kernel Magic.\n");
+    debug("Wrong Kernel Magic.\n");
     Free(kernel_buf);
     return NULL;
   }
@@ -107,7 +108,7 @@
   /* Check whether key header length is correct. */
   header_len = GetKernelHeaderLen(image);
   if (header_len != image->header_len) {
-    fprintf(stderr, "Header length mismatch. Got: %d, Expected: %d\n",
+    debug("Header length mismatch. Got: %d, Expected: %d\n",
             image->header_len, header_len);
     Free(kernel_buf);
     return NULL;
@@ -124,7 +125,7 @@
   CalculateKernelHeaderChecksum(image, header_checksum);
   if (SafeMemcmp(header_checksum, image->header_checksum,
                  FIELD_LEN(header_checksum))) {
-    fprintf(stderr, "Invalid kernel header checksum!\n");
+    debug("Invalid kernel header checksum!\n");
     Free(kernel_buf);
     return NULL;
   }
@@ -307,17 +308,17 @@
   if (!image)
     return 0;
   if (-1 == (fd = creat(input_file, S_IRWXU))) {
-    fprintf(stderr, "Couldn't open file for writing kernel image: %s\n",
+    debug("Couldn't open file for writing kernel image: %s\n",
             input_file);
     return 0;
   }
   kernel_blob = GetKernelBlob(image, &blob_len);
   if (!kernel_blob) {
-    fprintf(stderr, "Couldn't create kernel blob from KernelImage.\n");
+    debug("Couldn't create kernel blob from KernelImage.\n");
     return 0;
   }
   if (blob_len != write(fd, kernel_blob, blob_len)) {
-    fprintf(stderr, "Couldn't write Kernel Image to file: %s\n",
+    debug("Couldn't write Kernel Image to file: %s\n",
             input_file);
 
     Free(kernel_blob);
@@ -361,212 +362,6 @@
   /* TODO(gauravsh): Output kernel signature here? */
 }
 
-char* kVerifyKernelErrors[VERIFY_KERNEL_MAX] = {
-  "Success.",
-  "Invalid Image.",
-  "Kernel Key Signature Failed.",
-  "Invalid Kernel Verification Algorithm.",
-  "Config Signature Failed.",
-  "Kernel Signature Failed.",
-  "Wrong Kernel Magic.",
-};
-
-int VerifyKernelHeader(const uint8_t* firmware_key_blob,
-                       const uint8_t* header_blob,
-                       const int dev_mode,
-                       int* firmware_algorithm,
-                       int* kernel_algorithm,
-                       int* kernel_header_len) {
-  int kernel_sign_key_len;
-  int firmware_sign_key_len;
-  uint16_t header_version, header_len;
-  uint16_t firmware_sign_algorithm, kernel_sign_algorithm;
-  uint8_t* header_checksum = NULL;
-
-  /* Base Offset for the header_checksum field. Actual offset is
-   * this + kernel_sign_key_len. */
-  int base_header_checksum_offset = (FIELD_LEN(header_version) +
-                                     FIELD_LEN(header_len) +
-                                     FIELD_LEN(firmware_sign_algorithm) +
-                                     FIELD_LEN(kernel_sign_algorithm) +
-                                     FIELD_LEN(kernel_key_version));
-
-  Memcpy(&header_version, header_blob, sizeof(header_version));
-  Memcpy(&header_len, header_blob + FIELD_LEN(header_version),
-         sizeof(header_len));
-  Memcpy(&firmware_sign_algorithm,
-         header_blob + (FIELD_LEN(header_version) +
-                        FIELD_LEN(header_len)),
-         sizeof(firmware_sign_algorithm));
-  Memcpy(&kernel_sign_algorithm,
-         header_blob + (FIELD_LEN(header_version) +
-                        FIELD_LEN(header_len) +
-                        FIELD_LEN(firmware_sign_algorithm)),
-         sizeof(kernel_sign_algorithm));
-
-  /* TODO(gauravsh): Make this return two different error types depending
-   * on whether the firmware or kernel signing algorithm is invalid. */
-  if (firmware_sign_algorithm >= kNumAlgorithms)
-    return VERIFY_KERNEL_INVALID_ALGORITHM;
-  if (kernel_sign_algorithm >= kNumAlgorithms)
-    return VERIFY_KERNEL_INVALID_ALGORITHM;
-
-  *firmware_algorithm = (int) firmware_sign_algorithm;
-  *kernel_algorithm = (int) kernel_sign_algorithm;
-  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
-  firmware_sign_key_len = RSAProcessedKeySize(firmware_sign_algorithm);
-
-
-  /* Verify if header len is correct? */
-  if (header_len != (base_header_checksum_offset +
-                     kernel_sign_key_len +
-                     FIELD_LEN(header_checksum))) {
-    fprintf(stderr, "VerifyKernelHeader: Header length mismatch\n");
-    return VERIFY_KERNEL_INVALID_IMAGE;
-  }
-  *kernel_header_len = (int) header_len;
-
-  /* Verify if the hash of the header is correct. */
-  header_checksum = DigestBuf(header_blob,
-                              header_len - FIELD_LEN(header_checksum),
-                              SHA512_DIGEST_ALGORITHM);
-  if (SafeMemcmp(header_checksum,
-                 header_blob + (base_header_checksum_offset +
-                                kernel_sign_key_len),
-                 FIELD_LEN(header_checksum))) {
-    Free(header_checksum);
-    fprintf(stderr, "VerifyKernelHeader: Invalid header hash\n");
-    return VERIFY_KERNEL_INVALID_IMAGE;
-  }
-  Free(header_checksum);
-
-  /* Verify kernel key signature unless we are in dev mode. */
-  if (!dev_mode) {
-    if (!RSAVerifyBinary_f(firmware_key_blob, NULL,  /* Key to use */
-                           header_blob,  /* Data to verify */
-                           header_len, /* Length of data */
-                           header_blob + header_len,  /* Expected Signature */
-                           firmware_sign_algorithm))
-      return VERIFY_KERNEL_KEY_SIGNATURE_FAILED;
-  }
-  return 0;
-}
-
-int VerifyKernelConfig(RSAPublicKey* kernel_sign_key,
-                       const uint8_t* config_blob,
-                       int algorithm,
-                       uint64_t* kernel_len) {
-  uint64_t len;
-  int config_len;
-  config_len = GetKernelConfigLen(NULL);
-  if (!RSAVerifyBinary_f(NULL, kernel_sign_key,  /* Key to use */
-                         config_blob,  /* Data to verify */
-                         config_len,  /* Length of data */
-                         config_blob + config_len,  /* Expected Signature */
-                         algorithm))
-    return VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED;
-
-  Memcpy(&len,
-         config_blob + (FIELD_LEN(kernel_version) + FIELD_LEN(options.version) +
-                              FIELD_LEN(options.cmd_line)),
-         sizeof(len));
-  *kernel_len = len;
-  return 0;
-}
-
-int VerifyKernelData(RSAPublicKey* kernel_sign_key,
-                     const uint8_t* kernel_config_start,
-                     const uint8_t* kernel_data_start,
-                     uint64_t kernel_len,
-                     int algorithm) {
-  int signature_len = siglen_map[algorithm];
-  uint8_t* digest;
-  DigestContext ctx;
-
-  /* Since the kernel signature is computed over the kernel version, options
-   * and data, which does not form a contiguous region of memory, we calculate
-   * the message digest ourselves. */
-  DigestInit(&ctx, algorithm);
-  DigestUpdate(&ctx, kernel_config_start, GetKernelConfigLen());
-  DigestUpdate(&ctx, kernel_data_start + signature_len, kernel_len);
-  digest = DigestFinal(&ctx);
-  if (!RSAVerifyBinaryWithDigest_f(
-          NULL, kernel_sign_key,  /* Key to use. */
-          digest, /* Digest of the data to verify. */
-          kernel_data_start,  /* Expected Signature */
-          algorithm)) {
-    Free(digest);
-    return VERIFY_KERNEL_SIGNATURE_FAILED;
-  }
-  Free(digest);
-  return 0;
-}
-
-int VerifyKernel(const uint8_t* firmware_key_blob,
-                 const uint8_t* kernel_blob,
-                 const int dev_mode) {
-  int error_code;
-  int firmware_sign_algorithm;  /* Firmware signing key algorithm. */
-  int kernel_sign_algorithm;  /* Kernel Signing key algorithm. */
-  RSAPublicKey* kernel_sign_key;
-  int kernel_sign_key_len, kernel_key_signature_len, kernel_signature_len,
-      header_len;
-  uint64_t kernel_len;
-  const uint8_t* header_ptr;  /* Pointer to header. */
-  const uint8_t* kernel_sign_key_ptr;  /* Pointer to signing key. */
-  const uint8_t* config_ptr;  /* Pointer to kernel config block. */
-  const uint8_t* kernel_ptr;  /* Pointer to kernel signature/data. */
-
-  /* Note: All the offset calculations are based on struct FirmwareImage which
-   * is defined in include/firmware_image.h. */
-
-  /* Compare magic bytes. */
-  if (SafeMemcmp(kernel_blob, KERNEL_MAGIC, KERNEL_MAGIC_SIZE))
-    return VERIFY_KERNEL_WRONG_MAGIC;
-  header_ptr = kernel_blob + KERNEL_MAGIC_SIZE;
-
-  /* Only continue if header verification succeeds. */
-  if ((error_code = VerifyKernelHeader(firmware_key_blob, header_ptr, dev_mode,
-                                       &firmware_sign_algorithm,
-                                       &kernel_sign_algorithm, &header_len))) {
-    fprintf(stderr, "VerifyKernel: Kernel header verification failed.\n");
-    return error_code;  /* AKA jump to recovery. */
-  }
-  /* Parse signing key into RSAPublicKey structure since it is required multiple
-   * times. */
-  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
-  kernel_sign_key_ptr = header_ptr + (FIELD_LEN(header_version) +
-                                      FIELD_LEN(header_len) +
-                                      FIELD_LEN(firmware_sign_algorithm) +
-                                      FIELD_LEN(kernel_sign_algorithm) +
-                                      FIELD_LEN(kernel_key_version));
-  kernel_sign_key = RSAPublicKeyFromBuf(kernel_sign_key_ptr,
-                                        kernel_sign_key_len);
-  kernel_signature_len = siglen_map[kernel_sign_algorithm];
-  kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
-
-  /* Only continue if config verification succeeds. */
-  config_ptr = (header_ptr + header_len + kernel_key_signature_len);
-  if ((error_code = VerifyKernelConfig(kernel_sign_key, config_ptr,
-                                       kernel_sign_algorithm,
-                                       &kernel_len))) {
-    RSAPublicKeyFree(kernel_sign_key);
-    return error_code;  /* AKA jump to recovery. */
-  }
-  /* Only continue if kernel data verification succeeds. */
-  kernel_ptr = (config_ptr +
-                GetKernelConfigLen() +  /* Skip config block/signature. */
-                kernel_signature_len);
-
-  if ((error_code = VerifyKernelData(kernel_sign_key, config_ptr, kernel_ptr,
-                                     kernel_len,
-                                     kernel_sign_algorithm))) {
-    RSAPublicKeyFree(kernel_sign_key);
-    return error_code;  /* AKA jump to recovery. */
-  }
-  RSAPublicKeyFree(kernel_sign_key);
-  return 0;  /* Success! */
-}
 
 int VerifyKernelImage(const RSAPublicKey* firmware_key,
                       const KernelImage* image,
@@ -617,7 +412,7 @@
                     siglen_map[image->firmware_sign_algorithm],
                     image->firmware_sign_algorithm,
                     header_digest)) {
-      fprintf(stderr, "VerifyKernelImage(): Key signature check failed.\n");
+      debug("VerifyKernelImage(): Key signature check failed.\n");
       error_code =  VERIFY_KERNEL_KEY_SIGNATURE_FAILED;
       goto verify_failure;
     }
@@ -723,7 +518,7 @@
                                         GetKernelConfigLen(),
                                         kernel_signing_key_file,
                                         image->kernel_sign_algorithm))) {
-    fprintf(stderr, "Could not compute signature on the kernel config.\n");
+    debug("Could not compute signature on the kernel config.\n");
     Free(config_blob);
     return 0;
   }
@@ -745,7 +540,7 @@
                                         image->kernel_sign_algorithm))) {
     Free(config_blob);
     Free(kernel_buf);
-    fprintf(stderr, "Could not compute signature on the kernel.\n");
+    debug("Could not compute signature on the kernel.\n");
     return 0;
   }
   image->kernel_signature = (uint8_t*) Malloc(signature_len);
@@ -756,146 +551,8 @@
   return 1;
 }
 
-uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob) {
-  uint8_t* kernel_ptr;
-  uint16_t kernel_key_version;
-  uint16_t kernel_version;
-  uint16_t firmware_sign_algorithm;
-  uint16_t kernel_sign_algorithm;
-  int kernel_key_signature_len;
-  int kernel_sign_key_len;
-  kernel_ptr = kernel_blob + (FIELD_LEN(magic) +
-                              FIELD_LEN(header_version) +
-                              FIELD_LEN(header_len));
-  Memcpy(&firmware_sign_algorithm, kernel_ptr, sizeof(firmware_sign_algorithm));
-  kernel_ptr += FIELD_LEN(firmware_sign_algorithm);
-  Memcpy(&kernel_sign_algorithm, kernel_ptr, sizeof(kernel_sign_algorithm));
-  kernel_ptr += FIELD_LEN(kernel_sign_algorithm);
-  Memcpy(&kernel_key_version, kernel_ptr, sizeof(kernel_key_version));
-
-  if (firmware_sign_algorithm >= kNumAlgorithms)
-    return 0;
-  if (kernel_sign_algorithm >= kNumAlgorithms)
-    return 0;
-  kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
-  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
-  kernel_ptr += (FIELD_LEN(kernel_key_version) +
-                 kernel_sign_key_len +
-                 FIELD_LEN(header_checksum) +
-                 kernel_key_signature_len);
-  Memcpy(&kernel_version, kernel_ptr, sizeof(kernel_version));
-  return CombineUint16Pair(kernel_key_version, kernel_version);
-}
-
 void PrintKernelEntry(kernel_entry* entry) {
-  fprintf(stderr, "Boot Priority = %d\n", entry->boot_priority);
-  fprintf(stderr, "Boot Tries Remaining = %d\n", entry->boot_tries_remaining);
-  fprintf(stderr, "Boot Success Flag = %d\n", entry->boot_success_flag);
-}
-
-int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
-                         kernel_entry* kernelA,
-                         kernel_entry* kernelB,
-                         int dev_mode) {
-  int i;
-  /* Contains the logical kernel version (32-bit) which is calculated as
-   * (kernel_key_version << 16 | kernel_version) where
-   * [kernel_key_version], [firmware_version] are both 16-bit.
-   */
-  uint32_t kernelA_lversion, kernelB_lversion;
-  uint32_t min_lversion;  /* Minimum of kernel A and kernel B lversion. */
-  uint32_t stored_lversion;  /* Stored logical version in the TPM. */
-  kernel_entry* try_kernel[2];  /* Kernel in try order. */
-  int try_kernel_which[2];  /* Which corresponding kernel in the try order */
-  uint32_t try_kernel_lversion[2];  /* Their logical versions. */
-
-  /* [kernel_to_boot] will eventually contain the boot path to follow
-   * and is returned to the caller. Initially, we set it to recovery. If
-   * a valid bootable kernel is found, it will be set to that. */
-  int kernel_to_boot = BOOT_KERNEL_RECOVERY_CONTINUE;
-
-
-  /* The TPM must already have be initialized, so no need to call 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 kernel with an older kernel key version. A malformed
-   * or corrupted kernel blob will still fail when VerifyKernel() is called
-   * on it.
-   */
-  kernelA_lversion = GetLogicalKernelVersion(kernelA->kernel_blob);
-  kernelB_lversion = GetLogicalKernelVersion(kernelB->kernel_blob);
-  min_lversion  = Min(kernelA_lversion, kernelB_lversion);
-  stored_lversion = CombineUint16Pair(GetStoredVersion(KERNEL_KEY_VERSION),
-                                      GetStoredVersion(KERNEL_VERSION));
-
-  /* TODO(gauravsh): The kernel entries kernelA and kernelB come from the
-   * partition table - verify its signature/checksum before proceeding
-   * further. */
-
-  /* The logic for deciding which kernel to boot from is taken from the
-   * the Chromium OS Drive Map design document.
-   *
-   * We went to consider the kernels in their according to their boot
-   * priority attribute value.
-   */
-
-  if (kernelA->boot_priority >= kernelB->boot_priority) {
-    try_kernel[0] = kernelA;
-    try_kernel_which[0] = BOOT_KERNEL_A_CONTINUE;
-    try_kernel_lversion[0] = kernelA_lversion;
-    try_kernel[1] = kernelB;
-    try_kernel_which[1] = BOOT_KERNEL_B_CONTINUE;
-    try_kernel_lversion[1] = kernelB_lversion;
-  } else {
-    try_kernel[0] = kernelB;
-    try_kernel_which[0] = BOOT_KERNEL_B_CONTINUE;
-    try_kernel_lversion[0] = kernelB_lversion;
-    try_kernel[1] = kernelA;
-    try_kernel_which[1] = BOOT_KERNEL_A_CONTINUE;
-    try_kernel_lversion[1] = kernelA_lversion;
-  }
-
-  /* TODO(gauravsh): Changes to boot_tries_remaining and boot_priority
-   * below should be propagated to partition table. This will be added
-   * once the firmware parition table parsing code is in. */
-  for (i = 0; i < 2; i++) {
-    if ((try_kernel[i]->boot_success_flag ||
-         try_kernel[i]->boot_tries_remaining) &&
-        (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
-                                               try_kernel[i]->kernel_blob,
-                                               dev_mode))) {
-      if (try_kernel[i]->boot_tries_remaining > 0)
-        try_kernel[i]->boot_tries_remaining--;
-      if (stored_lversion > try_kernel_lversion[i])
-        continue;  /* Rollback: I am afraid I can't let you do that Dave. */
-      if (i == 0 && (stored_lversion < try_kernel_lversion[1])) {
-        /* The higher priority kernel is valid and bootable, See if we
-         * need to update the stored version for rollback prevention. */
-        if (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
-                                                  try_kernel[1]->kernel_blob,
-                                                  dev_mode)) {
-          WriteStoredVersion(KERNEL_KEY_VERSION,
-                             (uint16_t) (min_lversion >> 16));
-          WriteStoredVersion(KERNEL_VERSION,
-                             (uint16_t) (min_lversion & 0xFFFF));
-          stored_lversion = min_lversion;  /* Update stored version as it's
-                                            * used later. */
-        }
-      }
-      kernel_to_boot = try_kernel_which[i];
-      break;  /* We found a valid kernel. */
-    }
-    try_kernel[i]->boot_priority = 0;
-    }  /* for loop. */
-
-  /* Lock Kernel 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(KERNEL_KEY_VERSION);
-  LockStoredVersion(KERNEL_VERSION);
-  return kernel_to_boot;
+  debug("Boot Priority = %d\n", entry->boot_priority);
+  debug("Boot Tries Remaining = %d\n", entry->boot_tries_remaining);
+  debug("Boot Success Flag = %d\n", entry->boot_success_flag);
 }
diff --git a/utils/kernel_image_fw.c b/utils/kernel_image_fw.c
new file mode 100644
index 0000000..466d34a
--- /dev/null
+++ b/utils/kernel_image_fw.c
@@ -0,0 +1,368 @@
+/* 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 verifying a verified boot kernel image.
+ * (Firmware portion)
+ */
+
+#include "kernel_image_fw.h"
+
+#include "padding.h"
+#include "rollback_index.h"
+#include "rsa_utility.h"
+#include "sha_utility.h"
+#include "utility.h"
+
+/* Macro to determine the size of a field structure in the KernelImage
+ * structure. */
+#define FIELD_LEN(field) (sizeof(((KernelImage*)0)->field))
+#define KERNEL_CONFIG_FIELD_LEN (FIELD_LEN(kernel_version) + FIELD_LEN(options.version) + \
+                                 FIELD_LEN(options.cmd_line) + \
+                                 FIELD_LEN(options.kernel_len) + \
+                                 FIELD_LEN(options.kernel_load_addr) + \
+                                 FIELD_LEN(options.kernel_entry_addr))
+
+char* kVerifyKernelErrors[VERIFY_KERNEL_MAX] = {
+  "Success.",
+  "Invalid Image.",
+  "Kernel Key Signature Failed.",
+  "Invalid Kernel Verification Algorithm.",
+  "Config Signature Failed.",
+  "Kernel Signature Failed.",
+  "Wrong Kernel Magic.",
+};
+
+int VerifyKernelHeader(const uint8_t* firmware_key_blob,
+                       const uint8_t* header_blob,
+                       const int dev_mode,
+                       int* firmware_algorithm,
+                       int* kernel_algorithm,
+                       int* kernel_header_len) {
+  int kernel_sign_key_len;
+  int firmware_sign_key_len;
+  uint16_t header_version, header_len;
+  uint16_t firmware_sign_algorithm, kernel_sign_algorithm;
+  uint8_t* header_checksum = NULL;
+
+  /* Base Offset for the header_checksum field. Actual offset is
+   * this + kernel_sign_key_len. */
+  int base_header_checksum_offset = (FIELD_LEN(header_version) +
+                                     FIELD_LEN(header_len) +
+                                     FIELD_LEN(firmware_sign_algorithm) +
+                                     FIELD_LEN(kernel_sign_algorithm) +
+                                     FIELD_LEN(kernel_key_version));
+
+  Memcpy(&header_version, header_blob, sizeof(header_version));
+  Memcpy(&header_len, header_blob + FIELD_LEN(header_version),
+         sizeof(header_len));
+  Memcpy(&firmware_sign_algorithm,
+         header_blob + (FIELD_LEN(header_version) +
+                        FIELD_LEN(header_len)),
+         sizeof(firmware_sign_algorithm));
+  Memcpy(&kernel_sign_algorithm,
+         header_blob + (FIELD_LEN(header_version) +
+                        FIELD_LEN(header_len) +
+                        FIELD_LEN(firmware_sign_algorithm)),
+         sizeof(kernel_sign_algorithm));
+
+  /* TODO(gauravsh): Make this return two different error types depending
+   * on whether the firmware or kernel signing algorithm is invalid. */
+  if (firmware_sign_algorithm >= kNumAlgorithms)
+    return VERIFY_KERNEL_INVALID_ALGORITHM;
+  if (kernel_sign_algorithm >= kNumAlgorithms)
+    return VERIFY_KERNEL_INVALID_ALGORITHM;
+
+  *firmware_algorithm = (int) firmware_sign_algorithm;
+  *kernel_algorithm = (int) kernel_sign_algorithm;
+  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
+  firmware_sign_key_len = RSAProcessedKeySize(firmware_sign_algorithm);
+
+
+  /* Verify if header len is correct? */
+  if (header_len != (base_header_checksum_offset +
+                     kernel_sign_key_len +
+                     FIELD_LEN(header_checksum))) {
+    debug("VerifyKernelHeader: Header length mismatch\n");
+    return VERIFY_KERNEL_INVALID_IMAGE;
+  }
+  *kernel_header_len = (int) header_len;
+
+  /* Verify if the hash of the header is correct. */
+  header_checksum = DigestBuf(header_blob,
+                              header_len - FIELD_LEN(header_checksum),
+                              SHA512_DIGEST_ALGORITHM);
+  if (SafeMemcmp(header_checksum,
+                 header_blob + (base_header_checksum_offset +
+                                kernel_sign_key_len),
+                 FIELD_LEN(header_checksum))) {
+    Free(header_checksum);
+    debug("VerifyKernelHeader: Invalid header hash\n");
+    return VERIFY_KERNEL_INVALID_IMAGE;
+  }
+  Free(header_checksum);
+
+  /* Verify kernel key signature unless we are in dev mode. */
+  if (!dev_mode) {
+    if (!RSAVerifyBinary_f(firmware_key_blob, NULL,  /* Key to use */
+                           header_blob,  /* Data to verify */
+                           header_len, /* Length of data */
+                           header_blob + header_len,  /* Expected Signature */
+                           firmware_sign_algorithm))
+      return VERIFY_KERNEL_KEY_SIGNATURE_FAILED;
+  }
+  return 0;
+}
+
+int VerifyKernelConfig(RSAPublicKey* kernel_sign_key,
+                       const uint8_t* config_blob,
+                       int algorithm,
+                       uint64_t* kernel_len) {
+  uint64_t len;
+  if (!RSAVerifyBinary_f(NULL, kernel_sign_key,  /* Key to use */
+                         config_blob,  /* Data to verify */
+                         KERNEL_CONFIG_FIELD_LEN,  /* Length of data */
+                         config_blob + KERNEL_CONFIG_FIELD_LEN,  /* Expected
+                                                                  * Signature */
+                         algorithm))
+    return VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED;
+
+  Memcpy(&len,
+         config_blob + (FIELD_LEN(kernel_version) + FIELD_LEN(options.version) +
+                              FIELD_LEN(options.cmd_line)),
+         sizeof(len));
+  *kernel_len = len;
+  return 0;
+}
+
+int VerifyKernelData(RSAPublicKey* kernel_sign_key,
+                     const uint8_t* kernel_config_start,
+                     const uint8_t* kernel_data_start,
+                     uint64_t kernel_len,
+                     int algorithm) {
+  int signature_len = siglen_map[algorithm];
+  uint8_t* digest;
+  DigestContext ctx;
+
+  /* Since the kernel signature is computed over the kernel version, options
+   * and data, which does not form a contiguous region of memory, we calculate
+   * the message digest ourselves. */
+  DigestInit(&ctx, algorithm);
+  DigestUpdate(&ctx, kernel_config_start, KERNEL_CONFIG_FIELD_LEN);
+  DigestUpdate(&ctx, kernel_data_start + signature_len, kernel_len);
+  digest = DigestFinal(&ctx);
+  if (!RSAVerifyBinaryWithDigest_f(
+          NULL, kernel_sign_key,  /* Key to use. */
+          digest, /* Digest of the data to verify. */
+          kernel_data_start,  /* Expected Signature */
+          algorithm)) {
+    Free(digest);
+    return VERIFY_KERNEL_SIGNATURE_FAILED;
+  }
+  Free(digest);
+  return 0;
+}
+
+int VerifyKernel(const uint8_t* firmware_key_blob,
+                 const uint8_t* kernel_blob,
+                 const int dev_mode) {
+  int error_code;
+  int firmware_sign_algorithm;  /* Firmware signing key algorithm. */
+  int kernel_sign_algorithm;  /* Kernel Signing key algorithm. */
+  RSAPublicKey* kernel_sign_key;
+  int kernel_sign_key_len, kernel_key_signature_len, kernel_signature_len,
+      header_len;
+  uint64_t kernel_len;
+  const uint8_t* header_ptr;  /* Pointer to header. */
+  const uint8_t* kernel_sign_key_ptr;  /* Pointer to signing key. */
+  const uint8_t* config_ptr;  /* Pointer to kernel config block. */
+  const uint8_t* kernel_ptr;  /* Pointer to kernel signature/data. */
+
+  /* Note: All the offset calculations are based on struct FirmwareImage which
+   * is defined in include/firmware_image.h. */
+
+  /* Compare magic bytes. */
+  if (SafeMemcmp(kernel_blob, KERNEL_MAGIC, KERNEL_MAGIC_SIZE))
+    return VERIFY_KERNEL_WRONG_MAGIC;
+  header_ptr = kernel_blob + KERNEL_MAGIC_SIZE;
+
+  /* Only continue if header verification succeeds. */
+  if ((error_code = VerifyKernelHeader(firmware_key_blob, header_ptr, dev_mode,
+                                       &firmware_sign_algorithm,
+                                       &kernel_sign_algorithm, &header_len))) {
+    debug("VerifyKernel: Kernel header verification failed.\n");
+    return error_code;  /* AKA jump to recovery. */
+  }
+  /* Parse signing key into RSAPublicKey structure since it is required multiple
+   * times. */
+  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
+  kernel_sign_key_ptr = header_ptr + (FIELD_LEN(header_version) +
+                                      FIELD_LEN(header_len) +
+                                      FIELD_LEN(firmware_sign_algorithm) +
+                                      FIELD_LEN(kernel_sign_algorithm) +
+                                      FIELD_LEN(kernel_key_version));
+  kernel_sign_key = RSAPublicKeyFromBuf(kernel_sign_key_ptr,
+                                        kernel_sign_key_len);
+  kernel_signature_len = siglen_map[kernel_sign_algorithm];
+  kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
+
+  /* Only continue if config verification succeeds. */
+  config_ptr = (header_ptr + header_len + kernel_key_signature_len);
+  if ((error_code = VerifyKernelConfig(kernel_sign_key, config_ptr,
+                                       kernel_sign_algorithm,
+                                       &kernel_len))) {
+    RSAPublicKeyFree(kernel_sign_key);
+    return error_code;  /* AKA jump to recovery. */
+  }
+  /* Only continue if kernel data verification succeeds. */
+  kernel_ptr = (config_ptr +
+                KERNEL_CONFIG_FIELD_LEN +  /* Skip config block/signature. */
+                kernel_signature_len);
+
+  if ((error_code = VerifyKernelData(kernel_sign_key, config_ptr, kernel_ptr,
+                                     kernel_len,
+                                     kernel_sign_algorithm))) {
+    RSAPublicKeyFree(kernel_sign_key);
+    return error_code;  /* AKA jump to recovery. */
+  }
+  RSAPublicKeyFree(kernel_sign_key);
+  return 0;  /* Success! */
+}
+
+uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob) {
+  uint8_t* kernel_ptr;
+  uint16_t kernel_key_version;
+  uint16_t kernel_version;
+  uint16_t firmware_sign_algorithm;
+  uint16_t kernel_sign_algorithm;
+  int kernel_key_signature_len;
+  int kernel_sign_key_len;
+  kernel_ptr = kernel_blob + (FIELD_LEN(magic) +
+                              FIELD_LEN(header_version) +
+                              FIELD_LEN(header_len));
+  Memcpy(&firmware_sign_algorithm, kernel_ptr, sizeof(firmware_sign_algorithm));
+  kernel_ptr += FIELD_LEN(firmware_sign_algorithm);
+  Memcpy(&kernel_sign_algorithm, kernel_ptr, sizeof(kernel_sign_algorithm));
+  kernel_ptr += FIELD_LEN(kernel_sign_algorithm);
+  Memcpy(&kernel_key_version, kernel_ptr, sizeof(kernel_key_version));
+
+  if (firmware_sign_algorithm >= kNumAlgorithms)
+    return 0;
+  if (kernel_sign_algorithm >= kNumAlgorithms)
+    return 0;
+  kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
+  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
+  kernel_ptr += (FIELD_LEN(kernel_key_version) +
+                 kernel_sign_key_len +
+                 FIELD_LEN(header_checksum) +
+                 kernel_key_signature_len);
+  Memcpy(&kernel_version, kernel_ptr, sizeof(kernel_version));
+  return CombineUint16Pair(kernel_key_version, kernel_version);
+}
+
+int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
+                         kernel_entry* kernelA,
+                         kernel_entry* kernelB,
+                         int dev_mode) {
+  int i;
+  /* Contains the logical kernel version (32-bit) which is calculated as
+   * (kernel_key_version << 16 | kernel_version) where
+   * [kernel_key_version], [firmware_version] are both 16-bit.
+   */
+  uint32_t kernelA_lversion, kernelB_lversion;
+  uint32_t min_lversion;  /* Minimum of kernel A and kernel B lversion. */
+  uint32_t stored_lversion;  /* Stored logical version in the TPM. */
+  kernel_entry* try_kernel[2];  /* Kernel in try order. */
+  int try_kernel_which[2];  /* Which corresponding kernel in the try order */
+  uint32_t try_kernel_lversion[2];  /* Their logical versions. */
+
+  /* [kernel_to_boot] will eventually contain the boot path to follow
+   * and is returned to the caller. Initially, we set it to recovery. If
+   * a valid bootable kernel is found, it will be set to that. */
+  int kernel_to_boot = BOOT_KERNEL_RECOVERY_CONTINUE;
+
+
+  /* The TPM must already have be initialized, so no need to call 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 kernel with an older kernel key version. A malformed
+   * or corrupted kernel blob will still fail when VerifyKernel() is called
+   * on it.
+   */
+  kernelA_lversion = GetLogicalKernelVersion(kernelA->kernel_blob);
+  kernelB_lversion = GetLogicalKernelVersion(kernelB->kernel_blob);
+  min_lversion  = Min(kernelA_lversion, kernelB_lversion);
+  stored_lversion = CombineUint16Pair(GetStoredVersion(KERNEL_KEY_VERSION),
+                                      GetStoredVersion(KERNEL_VERSION));
+
+  /* TODO(gauravsh): The kernel entries kernelA and kernelB come from the
+   * partition table - verify its signature/checksum before proceeding
+   * further. */
+
+  /* The logic for deciding which kernel to boot from is taken from the
+   * the Chromium OS Drive Map design document.
+   *
+   * We went to consider the kernels in their according to their boot
+   * priority attribute value.
+   */
+
+  if (kernelA->boot_priority >= kernelB->boot_priority) {
+    try_kernel[0] = kernelA;
+    try_kernel_which[0] = BOOT_KERNEL_A_CONTINUE;
+    try_kernel_lversion[0] = kernelA_lversion;
+    try_kernel[1] = kernelB;
+    try_kernel_which[1] = BOOT_KERNEL_B_CONTINUE;
+    try_kernel_lversion[1] = kernelB_lversion;
+  } else {
+    try_kernel[0] = kernelB;
+    try_kernel_which[0] = BOOT_KERNEL_B_CONTINUE;
+    try_kernel_lversion[0] = kernelB_lversion;
+    try_kernel[1] = kernelA;
+    try_kernel_which[1] = BOOT_KERNEL_A_CONTINUE;
+    try_kernel_lversion[1] = kernelA_lversion;
+  }
+
+  /* TODO(gauravsh): Changes to boot_tries_remaining and boot_priority
+   * below should be propagated to partition table. This will be added
+   * once the firmware parition table parsing code is in. */
+  for (i = 0; i < 2; i++) {
+    if ((try_kernel[i]->boot_success_flag ||
+         try_kernel[i]->boot_tries_remaining) &&
+        (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
+                                               try_kernel[i]->kernel_blob,
+                                               dev_mode))) {
+      if (try_kernel[i]->boot_tries_remaining > 0)
+        try_kernel[i]->boot_tries_remaining--;
+      if (stored_lversion > try_kernel_lversion[i])
+        continue;  /* Rollback: I am afraid I can't let you do that Dave. */
+      if (i == 0 && (stored_lversion < try_kernel_lversion[1])) {
+        /* The higher priority kernel is valid and bootable, See if we
+         * need to update the stored version for rollback prevention. */
+        if (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
+                                                  try_kernel[1]->kernel_blob,
+                                                  dev_mode)) {
+          WriteStoredVersion(KERNEL_KEY_VERSION,
+                             (uint16_t) (min_lversion >> 16));
+          WriteStoredVersion(KERNEL_VERSION,
+                             (uint16_t) (min_lversion & 0xFFFF));
+          stored_lversion = min_lversion;  /* Update stored version as it's
+                                            * used later. */
+        }
+      }
+      kernel_to_boot = try_kernel_which[i];
+      break;  /* We found a valid kernel. */
+    }
+    try_kernel[i]->boot_priority = 0;
+    }  /* for loop. */
+
+  /* Lock Kernel 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(KERNEL_KEY_VERSION);
+  LockStoredVersion(KERNEL_VERSION);
+  return kernel_to_boot;
+}