Add --describe flag to {firmware|kernel}_utility.

This spews out useful information about a verified boot firmware/kernel image. Also adds a helper function to calculate header checksum. That code was being duplicated at multiple places.

Review URL: http://codereview.chromium.org/1088001
diff --git a/utils/firmware_image.c b/utils/firmware_image.c
index e7dcde9..ab41fec 100644
--- a/utils/firmware_image.c
+++ b/utils/firmware_image.c
@@ -53,6 +53,7 @@
   int firmware_sign_key_len;
   int signature_len;
   uint8_t* firmware_buf;
+  uint8_t header_checksum[FIELD_LEN(header_checksum)];
   MemcpyState st;
   FirmwareImage* image = FirmwareImageNew();
 
@@ -102,6 +103,15 @@
                  FIELD_LEN(firmware_key_version));
   StatefulMemcpy(&st, image->header_checksum, FIELD_LEN(header_checksum));
 
+  /* Check whether the header checksum matches. */
+  CalculateFirmwareHeaderChecksum(image, header_checksum);
+  if (SafeMemcmp(header_checksum, image->header_checksum,
+                 FIELD_LEN(header_checksum))) {
+    fprintf(stderr, "Invalid firmware header checksum!\n");
+    Free(firmware_buf);
+    return NULL;
+  }
+
   /* Read key signature. */
   StatefulMemcpy(&st, image->firmware_key_signature,
                  FIELD_LEN(firmware_key_signature));
@@ -136,6 +146,26 @@
           FIELD_LEN(firmware_key_version) + FIELD_LEN(header_checksum));
 }
 
+void CalculateFirmwareHeaderChecksum(const FirmwareImage* image,
+                                     uint8_t* header_checksum) {
+  uint8_t* checksum;
+  DigestContext ctx;
+  DigestInit(&ctx, SHA512_DIGEST_ALGORITHM);
+  DigestUpdate(&ctx, (uint8_t*) &image->header_len,
+               sizeof(image->header_len));
+  DigestUpdate(&ctx, (uint8_t*) &image->firmware_sign_algorithm,
+               sizeof(image->firmware_sign_algorithm));
+  DigestUpdate(&ctx, image->firmware_sign_key,
+               RSAProcessedKeySize(image->firmware_sign_algorithm));
+  DigestUpdate(&ctx, (uint8_t*) &image->firmware_key_version,
+               sizeof(image->firmware_key_version));
+  checksum = DigestFinal(&ctx);
+  Memcpy(header_checksum, checksum, FIELD_LEN(header_checksum));
+  Free(checksum);
+  return;
+}
+
+
 uint8_t* GetFirmwareHeaderBlob(const FirmwareImage* image) {
   uint8_t* header_blob = NULL;
   MemcpyState st;
@@ -262,11 +292,9 @@
 
   /* Print header. */
   printf("Header Length = %d\n"
-         "Algorithm Id = %d\n"
-         "Signature Algorithm = %s\n"
-         "Key Version = %d\n\n",
+         "Firmware Signature Algorithm = %s\n"
+         "Firmware Key Version = %d\n\n",
          image->header_len,
-         image->firmware_sign_algorithm,
          algo_strings[image->firmware_sign_algorithm],
          image->firmware_key_version);
   /* TODO(gauravsh): Output hash and key signature here? */
diff --git a/utils/firmware_utility.cc b/utils/firmware_utility.cc
index e61b216..f5a4de8 100644
--- a/utils/firmware_utility.cc
+++ b/utils/firmware_utility.cc
@@ -37,7 +37,8 @@
     firmware_key_version_(-1),
     firmware_sign_algorithm_(-1),
     is_generate_(false),
-    is_verify_(false) {
+    is_verify_(false),
+    is_describe_(false) {
 }
 
 FirmwareUtility::~FirmwareUtility() {
@@ -84,6 +85,7 @@
     {"out", 1, 0, 0},
     {"generate", 0, 0, 0},
     {"verify", 0, 0, 0},
+    {"describe", 0, 0, 0},
     {NULL, 0, 0, 0}
   };
   while (1) {
@@ -137,9 +139,12 @@
         case 9:  // generate
           is_generate_ = true;
           break;
-        case 10: // verify
+        case 10:  // verify
           is_verify_ = true;
           break;
+        case 11:  // describe
+          is_describe_ = true;
+          break;
       }
     }
   }
@@ -156,10 +161,16 @@
   }
 }
 
+void FirmwareUtility::DescribeSignedImage(void) {
+  image_ = ReadFirmwareImage(in_file_.c_str());
+  if (!image_) {
+    cerr << "Couldn't read firmware image or malformed image.\n";
+  }
+  PrintFirmwareImage(image_);
+}
+
 bool FirmwareUtility::GenerateSignedImage(void) {
   uint64_t firmware_sign_key_pub_len;
-  uint8_t* header_checksum;
-  DigestContext ctx;
   image_ = FirmwareImageNew();
 
   Memcpy(image_->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE);
@@ -177,19 +188,7 @@
   image_->header_len = GetFirmwareHeaderLen(image_);
 
   // Calculate header checksum.
-  DigestInit(&ctx, SHA512_DIGEST_ALGORITHM);
-  DigestUpdate(&ctx, reinterpret_cast<uint8_t*>(&image_->header_len),
-               sizeof(image_->header_len));
-  DigestUpdate(&ctx,
-               reinterpret_cast<uint8_t*>(&image_->firmware_sign_algorithm),
-               sizeof(image_->firmware_sign_algorithm));
-  DigestUpdate(&ctx, image_->firmware_sign_key,
-               RSAProcessedKeySize(image_->firmware_sign_algorithm));
-  DigestUpdate(&ctx, reinterpret_cast<uint8_t*>(&image_->firmware_key_version),
-               sizeof(image_->firmware_key_version));
-  header_checksum = DigestFinal(&ctx);
-  Memcpy(image_->header_checksum, header_checksum, SHA512_DIGEST_SIZE);
-  Free(header_checksum);
+  CalculateFirmwareHeaderChecksum(image_, image_->header_checksum);
 
   image_->firmware_version = firmware_version_;
   image_->firmware_len = 0;
@@ -235,8 +234,12 @@
 }
 
 bool FirmwareUtility::CheckOptions(void) {
-  if (is_generate_ == is_verify_) {
-    cerr << "One of --generate or --verify must be specified.\n";
+  // Ensure that only one of --{describe|generate|verify} is set.
+  if (!((is_describe_ && !is_generate_ && !is_verify_) ||
+        (!is_describe_ && is_generate_ && !is_verify_) ||
+        (!is_describe_ && !is_generate_ && is_verify_))) {
+    cerr << "One (and only one) of --describe, --generate or --verify "
+         << "must be specified.\n";
     return false;
   }
   // Common required options.
@@ -293,6 +296,9 @@
     fu.PrintUsage();
     return -1;
   }
+  if (fu.is_describe()) {
+    fu.DescribeSignedImage();
+  }
   if (fu.is_generate()) {
     if (!fu.GenerateSignedImage())
       return -1;
diff --git a/utils/kernel_image.c b/utils/kernel_image.c
index 81aa06f..a6b02bc 100644
--- a/utils/kernel_image.c
+++ b/utils/kernel_image.c
@@ -56,6 +56,7 @@
   int kernel_sign_key_len;
   int kernel_signature_len;
   uint8_t* kernel_buf;
+  uint8_t header_checksum[FIELD_LEN(header_checksum)];
   MemcpyState st;
   KernelImage* image = KernelImageNew();
 
@@ -102,14 +103,7 @@
   kernel_signature_len = siglen_map[image->kernel_sign_algorithm];
 
   /* Check whether key header length is correct. */
-  header_len = (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_len +
-                FIELD_LEN(header_checksum));
-
+  header_len = GetKernelHeaderLen(image);
   if (header_len != image->header_len) {
     fprintf(stderr, "Header length mismatch. Got: %d, Expected: %d\n",
             image->header_len, header_len);
@@ -124,6 +118,15 @@
   StatefulMemcpy(&st, image->kernel_sign_key, kernel_sign_key_len);
   StatefulMemcpy(&st, image->header_checksum, FIELD_LEN(header_checksum));
 
+  /* Check whether the header checksum matches. */
+  CalculateKernelHeaderChecksum(image, header_checksum);
+  if (SafeMemcmp(header_checksum, image->header_checksum,
+                 FIELD_LEN(header_checksum))) {
+    fprintf(stderr, "Invalid kernel header checksum!\n");
+    Free(kernel_buf);
+    return NULL;
+  }
+
   /* Read key signature. */
   image->kernel_key_signature = (uint8_t*) Malloc(kernel_key_signature_len);
   StatefulMemcpy(&st, image->kernel_key_signature,
@@ -166,6 +169,29 @@
           FIELD_LEN(header_checksum));
 }
 
+void CalculateKernelHeaderChecksum(const KernelImage* image,
+                                   uint8_t* header_checksum) {
+  uint8_t* checksum;
+  DigestContext ctx;
+  DigestInit(&ctx, SHA512_DIGEST_ALGORITHM);
+  DigestUpdate(&ctx, (uint8_t*) &image->header_version,
+               sizeof(image->header_version));
+  DigestUpdate(&ctx, (uint8_t*) &image->header_len,
+               sizeof(image->header_len));
+  DigestUpdate(&ctx, (uint8_t*) &image->firmware_sign_algorithm,
+               sizeof(image->firmware_sign_algorithm));
+  DigestUpdate(&ctx, (uint8_t*) &image->kernel_sign_algorithm,
+               sizeof(image->kernel_sign_algorithm));
+  DigestUpdate(&ctx, (uint8_t*) &image->kernel_key_version,
+               sizeof(image->kernel_key_version));
+  DigestUpdate(&ctx, image->kernel_sign_key,
+               RSAProcessedKeySize(image->kernel_sign_algorithm));
+  checksum = DigestFinal(&ctx);
+  Memcpy(header_checksum, checksum, FIELD_LEN(header_checksum));
+  Free(checksum);
+  return;
+}
+
 uint8_t* GetKernelHeaderBlob(const KernelImage* image) {
   uint8_t* header_blob = NULL;
   MemcpyState st;
@@ -303,21 +329,21 @@
     return;
 
   /* Print header. */
-  printf("Header Length = %d\n"
-         "Firmware Signing key algorithm id = %d\n"
-         "Kernel Signing key algorithm id = %d\n"
+  printf("Header Version = %d\n"
+         "Header Length = %d\n"
+         "Kernel Key Signature Algorithm = %s\n"
          "Kernel Signature Algorithm = %s\n"
          "Kernel Key Version = %d\n\n",
+         image->header_version,
          image->header_len,
-         image->firmware_sign_algorithm,
-         image->kernel_sign_algorithm,
+         algo_strings[image->firmware_sign_algorithm],
          algo_strings[image->kernel_sign_algorithm],
          image->kernel_key_version);
   /* TODO(gauravsh): Output hash and key signature here? */
   /* Print preamble. */
   printf("Kernel Version = %d\n"
          "Kernel Config Version = %d.%d\n"
-         "Kernel Config command line = %s\n"
+         "Kernel Config command line = \"%s\"\n"
          "kernel Length = %" PRId64 "\n"
          "Kernel Load Address = %" PRId64 "\n"
          "Kernel Entry Address = %" PRId64 "\n\n",
diff --git a/utils/kernel_utility.cc b/utils/kernel_utility.cc
index 03d4037..9a4f34b 100644
--- a/utils/kernel_utility.cc
+++ b/utils/kernel_utility.cc
@@ -38,7 +38,8 @@
                                 kernel_key_version_(-1),
                                 kernel_version_(-1),
                                 is_generate_(false),
-                                is_verify_(false) {
+                                is_verify_(false),
+                                is_describe_(false){
   // Populate kernel config options with defaults.
   options_.version[0] = 1;
   options_.version[1] = 0;
@@ -54,8 +55,8 @@
 
 void KernelUtility::PrintUsage(void) {
   cerr <<
-      "Utility to generate/verify a verified boot kernel image\n\n"
-      "Usage: kernel_utility <--generate|--verify> [OPTIONS]\n\n"
+      "Utility to generate/verify/describe a verified boot kernel image\n\n"
+      "Usage: kernel_utility <--generate|--verify|--describe> [OPTIONS]\n\n"
       "For \"--verify\",  required OPTIONS are:\n"
       "--in <infile>\t\t\tVerified boot kernel image to verify.\n"
       "--firmware_key_pub <pubkeyfile>\tPre-processed public firmware key "
@@ -101,6 +102,7 @@
     {"config_version", 1, 0, 0},
     {"kernel_load_addr", 1, 0, 0},
     {"kernel_entry_addr", 1, 0, 0},
+    {"describe", 0, 0, 0},
     {NULL, 0, 0, 0}
   };
   while (1) {
@@ -180,10 +182,12 @@
           errno = 0;
           options_.kernel_entry_addr =
               strtol(optarg, reinterpret_cast<char**>(NULL), 10);
-
           if (errno)
             return false;
           break;
+        case 15:  // describe
+          is_describe_ = true;
+          break;
       }
     }
   }
@@ -199,10 +203,17 @@
   }
 }
 
+void KernelUtility::DescribeSignedImage(void) {
+  image_ = ReadKernelImage(in_file_.c_str());
+  if (!image_) {
+    cerr << "Couldn't read kernel image or malformed image.\n";
+    return;
+  }
+  PrintKernelImage(image_);
+}
+
 bool KernelUtility::GenerateSignedImage(void) {
   uint64_t kernel_key_pub_len;
-  uint8_t* header_checksum;
-  DigestContext ctx;
   image_ = KernelImageNew();
 
   Memcpy(image_->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE);
@@ -222,28 +233,13 @@
   image_->header_len = GetKernelHeaderLen(image_);
 
   // Calculate header checksum.
-  DigestInit(&ctx, SHA512_DIGEST_ALGORITHM);
-  DigestUpdate(&ctx, reinterpret_cast<uint8_t*>(&image_->header_version),
-               sizeof(image_->header_version));
-  DigestUpdate(&ctx, reinterpret_cast<uint8_t*>(&image_->header_len),
-               sizeof(image_->header_len));
-  DigestUpdate(&ctx,
-               reinterpret_cast<uint8_t*>(&image_->firmware_sign_algorithm),
-               sizeof(image_->firmware_sign_algorithm));
-  DigestUpdate(&ctx,
-               reinterpret_cast<uint8_t*>(&image_->kernel_sign_algorithm),
-               sizeof(image_->kernel_sign_algorithm));
-  DigestUpdate(&ctx, reinterpret_cast<uint8_t*>(&image_->kernel_key_version),
-               sizeof(image_->kernel_key_version));
-  DigestUpdate(&ctx, image_->kernel_sign_key,
-               RSAProcessedKeySize(image_->kernel_sign_algorithm));
-  header_checksum = DigestFinal(&ctx);
-  Memcpy(image_->header_checksum, header_checksum, SHA512_DIGEST_SIZE);
-  Free(header_checksum);
+  CalculateKernelHeaderChecksum(image_, image_->header_checksum);
 
   image_->kernel_version = kernel_version_;
   image_->options.version[0] = options_.version[0];
   image_->options.version[1] = options_.version[1];
+  // TODO(gauravsh): Add a command line option for this.
+  Memset(image_->options.cmd_line, 0, sizeof(image_->options.cmd_line));
   image_->options.kernel_load_addr = options_.kernel_load_addr;
   image_->options.kernel_entry_addr = options_.kernel_entry_addr;
   image_->kernel_data = BufferFromFile(in_file_.c_str(),
@@ -284,8 +280,12 @@
 }
 
 bool KernelUtility::CheckOptions(void) {
-  if (is_generate_ == is_verify_) {
-    cerr << "One of --generate or --verify must be specified.\n";
+  // Ensure that only one of --{describe|generate|verify} is set.
+  if (!((is_describe_ && !is_generate_ && !is_verify_) ||
+        (!is_describe_ && is_generate_ && !is_verify_) ||
+        (!is_describe_ && !is_generate_ && is_verify_))) {
+    cerr << "One (and only one) of --describe, --generate or --verify "
+         << "must be specified.\n";
     return false;
   }
   // Common required options.
@@ -341,19 +341,22 @@
 }  // namespace vboot_reference
 
 int main(int argc, char* argv[]) {
-  vboot_reference::KernelUtility fu;
-  if (!fu.ParseCmdLineOptions(argc, argv)) {
-    fu.PrintUsage();
+  vboot_reference::KernelUtility ku;
+  if (!ku.ParseCmdLineOptions(argc, argv)) {
+    ku.PrintUsage();
     return -1;
   }
-  if (fu.is_generate()) {
-    if (!fu.GenerateSignedImage())
-      return -1;
-    fu.OutputSignedImage();
+  if (ku.is_describe()) {
+    ku.DescribeSignedImage();
   }
-  if (fu.is_verify()) {
+  else if (ku.is_generate()) {
+    if (!ku.GenerateSignedImage())
+      return -1;
+    ku.OutputSignedImage();
+  }
+  else if (ku.is_verify()) {
     cerr << "Verification ";
-    if (fu.VerifySignedImage())
+    if (ku.VerifySignedImage())
       cerr << "SUCCESS.\n";
     else
       cerr << "FAILURE.\n";