Add --minversion option to vbutil_kernel to check for rollback.

BUG=chrome-os-partner:3309
TEST=manual:

1. Extract a kernel partition from an image, for example, using
unpack_partitions.sh.  Or if running this on a device, use /dev/sda2
or /dev/sda4 for the kernel filename.

2. vbutil_kernel --verify part_2

3. Note the data key version and kernel version printed.  For example,

  Data key version:    1
  Kernel version:      3

4. Test specifying the same version.  This should succeed.

   vbutil_kernel --verify part_2 --minversion 0x00010003

5. Test specifying a higher data key version.  This should fail with a
data key version error.

   vbutil_kernel --verify part_2 --minversion 0x00020003

6. Test specifying a higher kernel version.  This should fail with a
kernel version error.

   vbutil_kernel --verify part_2 --minversion 0x00010004

Change-Id: I7b69041cf41527fc59ad29995135f30d9f496fac
Reviewed-on: http://gerrit.chromium.org/gerrit/792
Reviewed-by: Gaurav Shah <gauravsh@chromium.org>
Tested-by: Randall Spangler <rspangler@chromium.org>
diff --git a/utility/vbutil_kernel.c b/utility/vbutil_kernel.c
index 85f8a51..4904610 100644
--- a/utility/vbutil_kernel.c
+++ b/utility/vbutil_kernel.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+/* 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.
  *
@@ -46,6 +46,7 @@
   OPT_VBLOCKONLY,
   OPT_PAD,
   OPT_VERBOSE,
+  OPT_MINVERSION,
 };
 
 enum {
@@ -64,6 +65,7 @@
   {"signpubkey", 1, 0,                OPT_SIGNPUBKEY              },
   {"signprivate", 1, 0,               OPT_SIGNPRIVATE             },
   {"version", 1, 0,                   OPT_VERSION                 },
+  {"minversion", 1, 0,                OPT_MINVERSION              },
   {"vmlinuz", 1, 0,                   OPT_VMLINUZ                 },
   {"bootloader", 1, 0,                OPT_BOOTLOADER              },
   {"config", 1, 0,                    OPT_CONFIG                  },
@@ -127,6 +129,8 @@
           "    --keyblock <file>"
           "         Outputs the verified key block, in .keyblock format\n"
           "    --kloadaddr <address>     Assign kernel body load address\n"
+          "    --minversion <number>     Minimum combined kernel key version\n"
+          "                              and kernel version\n"
           "\n",
           progname);
   return 1;
@@ -660,7 +664,7 @@
 
 static int Verify(const char* infile, const char* signpubkey, int verbose,
                   const char* key_block_file,
-                  uint64_t kernel_body_load_address) {
+                  uint64_t kernel_body_load_address, uint64_t min_version) {
 
   VbKeyBlockHeader* key_block;
   VbKernelPreambleHeader* preamble;
@@ -738,6 +742,12 @@
   PrintPubKeySha1Sum(data_key);
   printf("\n");
 
+  if (data_key->key_version < (min_version >> 16)) {
+    error("Data key version %" PRIu64 " is lower than minimum %" PRIu64".\n",
+          data_key->key_version, (min_version >> 16));
+    goto verify_exit;
+  }
+
   rsa = PublicKeyToRSA(&key_block->data_key);
   if (!rsa) {
     error("Error parsing data key.\n");
@@ -765,6 +775,12 @@
          preamble->bootloader_address);
   printf("  Bootloader size:     0x%" PRIx64 "\n", preamble->bootloader_size);
 
+  if (preamble->kernel_version < (min_version & 0xFFFF)) {
+    error("Kernel version %" PRIu64 " is lower than minimum %" PRIu64 ".\n",
+          preamble->kernel_version, (min_version & 0xFFFF));
+    goto verify_exit;
+  }
+
   /* Verify body */
   if (0 != VerifyData(bp->blob, bp->blob_size, &preamble->body_signature,
                       rsa)) {
@@ -804,6 +820,7 @@
   uint64_t pad = DEFAULT_PADDING;
   int mode = 0;
   int parse_error = 0;
+  uint64_t min_version = 0;
   char* e;
   int i,r;
   blob_t *bp;
@@ -899,6 +916,14 @@
         }
         break;
 
+      case OPT_MINVERSION:
+        min_version = strtoul(optarg, &e, 0);
+        if (!*optarg || (e && *e)) {
+          fprintf(stderr, "Invalid --minversion\n");
+          parse_error = 1;
+        }
+        break;
+
       case OPT_PAD:
         pad = strtoul(optarg, &e, 0);
         if (!*optarg || (e && *e)) {
@@ -951,7 +976,7 @@
 
     case OPT_MODE_VERIFY:
       return Verify(filename, signpubkey, verbose, key_block_file,
-          kernel_body_load_address);
+                    kernel_body_load_address, min_version);
 
     default:
       fprintf(stderr,