Vboot reference: A basic user-land verified boot firmware signing and verification utility.
This is a first cut at what I envision as a utility we can use to manage our firmware/kernel signing needs. Currently, it implements firmware signing (given a binary image, create a verified boot header) and verification (given a verified boot image, verify it using the given public root key).
This CL also fixes the ReadFirmwareImage function from firmware_image to make it more consistent and fixes some bugs.
Review URL: http://codereview.chromium.org/652216
diff --git a/utils/Makefile b/utils/Makefile
index c0de74e..5005645 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -3,6 +3,7 @@
# found in the LICENSE file.
CC ?= gcc
+CXX ?= g++
CFLAGS = -Wall -DNDEBUG
INCLUDES ?= -I../include/
TOP ?= ../
@@ -22,8 +23,8 @@
signature_digest: signature_digest.c
$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(FIRMWARELIBS)
-firmware_utility: firmware_utility.c firmware_image.o file_keys.c
- $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(FIRMWARELIBS)
+firmware_utility: firmware_utility.cc firmware_image.o file_keys.o
+ $(CXX) $(CFLAGS) $(INCLUDES) -ggdb $^ -o $@ $(FIRMWARELIBS)
# Used by tests.
file_keys.o: file_keys.c
@@ -34,5 +35,5 @@
clean:
rm -f dumpRSAPublicKey verify_data signature_digest firmware_image.o \
- file_keys.o
+ file_keys.o firmware_utility
diff --git a/utils/file_keys.c b/utils/file_keys.c
index bcba749..2401a15 100644
--- a/utils/file_keys.c
+++ b/utils/file_keys.c
@@ -19,7 +19,7 @@
#include "rsa_utility.h"
#include "utility.h"
-uint8_t* BufferFromFile(char* input_file, int* len) {
+uint8_t* BufferFromFile(const char* input_file, uint32_t* len) {
int fd;
struct stat stat_fd;
uint8_t* buf = NULL;
@@ -49,15 +49,18 @@
return buf;
}
-RSAPublicKey* RSAPublicKeyFromFile(char* input_file) {
- int len;
+RSAPublicKey* RSAPublicKeyFromFile(const char* input_file) {
+ uint32_t len;
+ RSAPublicKey* key;
uint8_t* buf = BufferFromFile(input_file, &len);
- RSAPublicKey* key = RSAPublicKeyFromBuf(buf, len);
+ if (buf)
+ key = RSAPublicKeyFromBuf(buf, len);
Free(buf);
return key;
}
-uint8_t* SignatureFile(char* input_file, char* key_file, int algorithm) {
+uint8_t* SignatureFile(const char* input_file, const char* key_file,
+ int algorithm) {
char* sign_utility = "./sign_data.sh";
char* cmd; /* Command line to invoke. */
int cmd_len;
diff --git a/utils/firmware_image.c b/utils/firmware_image.c
index 07aa8bd..714d5dd 100644
--- a/utils/firmware_image.c
+++ b/utils/firmware_image.c
@@ -25,54 +25,40 @@
#define FIELD_LEN(field) (sizeof(((FirmwareImage*)0)->field))
FirmwareImage* FirmwareImageNew(void) {
- FirmwareImage* fw = (FirmwareImage*) Malloc(sizeof(FirmwareImage));
- return fw;
+ FirmwareImage* image = (FirmwareImage*) Malloc(sizeof(FirmwareImage));
+ if (image) {
+ image->sign_key = NULL;
+ image->preamble_signature = NULL;
+ image->firmware_signature = NULL;
+ image->firmware_data = NULL;
+ }
+ return image;
}
void FirmwareImageFree(FirmwareImage* image) {
- Free(image->sign_key);
- Free(image->key_signature);
- Free(image->preamble_signature);
- Free(image->firmware_signature);
- Free(image->firmware_data);
+ if (image) {
+ Free(image->sign_key);
+ Free(image->preamble_signature);
+ Free(image->firmware_signature);
+ Free(image->firmware_data);
+ }
}
-FirmwareImage* ReadFirmwareImage(const char* input_file,
- FirmwareImage* image) {
- int fd;
- struct stat fd_stat;
-
+FirmwareImage* ReadFirmwareImage(const char* input_file) {
+ uint32_t file_size;
int image_len = 0; /* Total size of the firmware image. */
int header_len = 0;
int sign_key_len;
int signature_len;
uint8_t* firmware_buf;
MemcpyState st;
+ FirmwareImage* image = FirmwareImageNew();
if (!image)
return NULL;
- if (-1 == (fd = open(input_file, O_RDONLY))) {
- fprintf(stderr, "Couldn't open file for reading.\n");
- return NULL;
- }
-
- if (-1 == fstat(fd, &fd_stat)) {
- fprintf(stderr, "Couldn't stat file.\n");
- close(fd);
- return NULL;
- }
-
- firmware_buf = (uint8_t*) Malloc(fd_stat.st_size);
- image_len = fd_stat.st_size;
-
- /* Read entire file into a buffer. */
- if (image_len != read(fd, firmware_buf, image_len)) {
- fprintf(stderr, "Couldn't read file data.\n");
- close(fd);
- return NULL;
- }
- close(fd);
+ firmware_buf = BufferFromFile(input_file, &file_size);
+ image_len = file_size;
st.remaining_len = image_len;
st.remaining_buf = firmware_buf;
@@ -86,41 +72,43 @@
goto parse_failure;
}
- StatefulMemcpy(&st, &image->header_len, sizeof(image->header_len));
- StatefulMemcpy(&st, &image->sign_algorithm, sizeof(image->sign_algorithm));
+ StatefulMemcpy(&st, &image->header_len, FIELD_LEN(header_len));
+ StatefulMemcpy(&st, &image->sign_algorithm, FIELD_LEN(sign_algorithm));
/* Valid Algorithm? */
- if (image->sign_algorithm > kNumAlgorithms)
+ if (image->sign_algorithm >= kNumAlgorithms)
goto parse_failure;
/* Compute size of pre-processed RSA public key and signature. */
- sign_key_len = (2*siglen_map[image->sign_algorithm]*sizeof(uint32_t)
- + sizeof(uint32_t) + sizeof(int));
+ sign_key_len = RSAProcessedKeySize(image->sign_algorithm);
signature_len = siglen_map[image->sign_algorithm] * sizeof(uint32_t);
/* Check whether the header length is correct. */
- header_len = (sizeof(image->header_len) + sizeof(image->sign_algorithm) +
- sizeof(image->key_version) +
- sizeof(image->header_checksum));
+ header_len = (FIELD_LEN(header_len) +
+ FIELD_LEN(sign_algorithm) +
+ sign_key_len +
+ FIELD_LEN(key_version) +
+ FIELD_LEN(header_checksum));
if (header_len != image->header_len) {
- fprintf(stderr, "Header length mismatch.");
+ fprintf(stderr, "Header length mismatch. Got: %d Expected: %d\n",
+ image->header_len, header_len);
goto parse_failure;
}
/* Read pre-processed public half of the sign key. */
image->sign_key = (uint8_t*) Malloc(sign_key_len);
StatefulMemcpy(&st, image->sign_key, sign_key_len);
- StatefulMemcpy(&st, &image->key_version, sizeof(image->key_version));
- StatefulMemcpy(&st, image->header_checksum, sizeof(image->header_checksum));
+ StatefulMemcpy(&st, &image->key_version, FIELD_LEN(key_version));
+ StatefulMemcpy(&st, image->header_checksum, FIELD_LEN(header_checksum));
/* Read key signature. */
- StatefulMemcpy(&st, image->key_signature, sizeof(image->key_signature));
+ StatefulMemcpy(&st, image->key_signature, FIELD_LEN(key_signature));
/* Read the firmware preamble. */
- StatefulMemcpy(&st,&image->firmware_version, sizeof(image->firmware_version));
- StatefulMemcpy(&st, &image->firmware_len, sizeof(image->firmware_len));
- StatefulMemcpy(&st, image->preamble, sizeof(image->preamble));
+ StatefulMemcpy(&st,&image->firmware_version, FIELD_LEN(firmware_version));
+ StatefulMemcpy(&st, &image->firmware_len, FIELD_LEN(firmware_len));
+ StatefulMemcpy(&st, image->preamble, FIELD_LEN(preamble));
/* Read firmware preamble signature. */
image->preamble_signature = (uint8_t*) Malloc(signature_len);
@@ -145,22 +133,22 @@
void WriteFirmwareHeader(int fd, FirmwareImage* image) {
int sign_key_len;
- write(fd, &image->header_len, sizeof(image->header_len));
- write(fd, &image->sign_algorithm, sizeof(image->header_len));
- sign_key_len = (image->header_len - sizeof(image->header_len) -
- sizeof(image->sign_algorithm) -
- sizeof(image->key_version) -
- sizeof(image->header_checksum));
+ write(fd, &image->header_len, FIELD_LEN(header_len));
+ write(fd, &image->sign_algorithm, FIELD_LEN(header_len));
+ sign_key_len = (image->header_len - FIELD_LEN(header_len) -
+ FIELD_LEN(sign_algorithm) -
+ FIELD_LEN(key_version) -
+ FIELD_LEN(header_checksum));
write(fd, image->sign_key, sign_key_len);
- write(fd, &image->key_version, sizeof(image->key_version));
- write(fd, &image->header_checksum, sizeof(image->header_checksum));
+ write(fd, &image->key_version, FIELD_LEN(key_version));
+ write(fd, &image->header_checksum, FIELD_LEN(header_checksum));
}
void WriteFirmwarePreamble(int fd, FirmwareImage* image) {
write(fd, &image->firmware_version,
- sizeof(image->firmware_version));
- write(fd, &image->firmware_len, sizeof(image->firmware_len));
- write(fd, image->preamble, sizeof(image->preamble));
+ FIELD_LEN(firmware_version));
+ write(fd, &image->firmware_len, FIELD_LEN(firmware_len));
+ write(fd, image->preamble, FIELD_LEN(preamble));
}
FirmwareImage* WriteFirmwareImage(const char* input_file,
@@ -175,9 +163,9 @@
return NULL;
}
- write(fd, image->magic, sizeof(image->magic));
+ write(fd, image->magic, FIELD_LEN(magic));
WriteFirmwareHeader(fd, image);
- write(fd, image->key_signature, sizeof(image->key_signature));
+ write(fd, image->key_signature, FIELD_LEN(key_signature));
signature_len = siglen_map[image->sign_algorithm] * sizeof(uint32_t);
WriteFirmwarePreamble(fd, image);
write(fd, image->preamble_signature, signature_len);
@@ -188,7 +176,7 @@
return image;
}
-void PrintFirmware(const FirmwareImage* image) {
+void PrintFirmwareImage(const FirmwareImage* image) {
if (!image)
return;
@@ -395,18 +383,18 @@
if (!dev_mode) {
DigestInit(&ctx, ROOT_SIGNATURE_ALGORITHM);
DigestUpdate(&ctx, (uint8_t*) &image->header_len,
- sizeof(image->header_len));
+ FIELD_LEN(header_len));
DigestUpdate(&ctx, (uint8_t*) &image->sign_algorithm,
- sizeof(image->sign_algorithm));
+ FIELD_LEN(sign_algorithm));
DigestUpdate(&ctx, image->sign_key,
RSAProcessedKeySize(image->sign_algorithm));
DigestUpdate(&ctx, (uint8_t*) &image->key_version,
- sizeof(image->key_version));
+ FIELD_LEN(key_version));
DigestUpdate(&ctx, image->header_checksum,
- sizeof(image->header_checksum));
+ FIELD_LEN(header_checksum));
header_digest = DigestFinal(&ctx);
if (!RSA_verify(root_key, image->key_signature,
- sizeof(image->key_signature),
+ FIELD_LEN(key_signature),
ROOT_SIGNATURE_ALGORITHM,
header_digest)) {
error_code = VERIFY_FIRMWARE_ROOT_SIGNATURE_FAILED;
@@ -426,11 +414,11 @@
/* Verify firmware preamble signature. */
DigestInit(&ctx, image->sign_algorithm);
DigestUpdate(&ctx, (uint8_t*) &image->firmware_version,
- sizeof(image->firmware_version));
+ FIELD_LEN(firmware_version));
DigestUpdate(&ctx, (uint8_t*) &image->firmware_len,
- sizeof(image->firmware_len));
+ FIELD_LEN(firmware_len));
DigestUpdate(&ctx, (uint8_t*) &image->preamble,
- sizeof(image->preamble));
+ FIELD_LEN(preamble));
preamble_digest = DigestFinal(&ctx);
if (!RSA_verify(sign_key, image->preamble_signature,
signature_size, image->sign_algorithm,
@@ -457,8 +445,11 @@
return error_code;
}
+const char* VerifyFirmwareErrorString(int error) {
+ return kVerifyFirmwareErrors[error];
+}
-int AddKeySignature(FirmwareImage* image, char* root_key_file) {
+int AddFirmwareKeySignature(FirmwareImage* image, const char* root_key_file) {
int tmp_hdr_fd;
char* tmp_hdr_file = ".tmpHdrFile";
uint8_t* signature;
@@ -478,7 +469,7 @@
return 1;
}
-int AddFirmwareSignature(FirmwareImage* image, char* signing_key_file,
+int AddFirmwareSignature(FirmwareImage* image, const char* signing_key_file,
int algorithm) {
int tmp_preamble_fd;
char* tmp_preamble_file = ".tmpPreambleFile";
diff --git a/utils/firmware_utility.c b/utils/firmware_utility.c
deleted file mode 100644
index 1d94e21..0000000
--- a/utils/firmware_utility.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/* 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.
- *
- * Utility for manipulating verified boot firmware images.
- */
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "firmware_image.h"
-#include "rsa_utility.h"
-#include "sha_utility.h"
-#include "utility.h"
-
-int main(int argc, char* argv[]) {
- /* TODO(gauravsh): TO BE IMPLEMENTED. */
- return 0;
-}
diff --git a/utils/firmware_utility.cc b/utils/firmware_utility.cc
new file mode 100644
index 0000000..fa207fb
--- /dev/null
+++ b/utils/firmware_utility.cc
@@ -0,0 +1,305 @@
+// 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.
+//
+// Utility for manipulating verified boot firmware images.
+//
+
+#include "firmware_utility.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <iostream>
+
+extern "C" {
+#include "file_keys.h"
+#include "firmware_image.h"
+#include "padding.h"
+#include "rsa_utility.h"
+#include "sha_utility.h"
+#include "utility.h"
+}
+
+extern int errno;
+using std::cerr;
+
+namespace vboot_reference {
+
+FirmwareUtility::FirmwareUtility():
+ image_(NULL),
+ root_key_pub_(NULL),
+ firmware_version_(-1),
+ key_version_(-1),
+ sign_algorithm_(-1),
+ is_generate_(false),
+ is_verify_(false) {
+}
+
+FirmwareUtility::~FirmwareUtility() {
+ RSAPublicKeyFree(root_key_pub_);
+ FirmwareImageFree(image_);
+}
+
+void FirmwareUtility::PrintUsage(void) {
+ cerr <<
+ "Utility to generate/verify a verified boot firmware image\n\n"
+ "Usage: firmware_utility <--generate|--verify> [OPTIONS]\n\n"
+ "For \"--verify\", required OPTIONS are:\n"
+ "--in <infile>\t\t\tVerified boot firmware image to verify.\n"
+ "--root_key_pub <pubkeyfile>\tPre-processed public root key "
+ "to use for verification.\n\n"
+ "For \"--generate\", required OPTIONS are:\n"
+ "--root_key <privkeyfile>\tPrivate root key file\n"
+ "--sign_key <privkeyfile>\tPrivate signing key file\n"
+ "--sign_key_pub <pubkeyfile>\tPre-processed public signing"
+ " key\n"
+ "--sign_algorithm <algoid>\tSigning algorithm to use\n"
+ "--key_version <version#>\tSigning Key Version#\n"
+ "--firmware_version <version#>\tFirmware Version#\n"
+ "--in <infile>\t\t\tFirmware Image to sign\n"
+ "--out <outfile>\t\t\tOutput file for verified boot firmware image\n\n"
+ "<algoid> (for --sign-algorithm) is one of the following:\n";
+ for (int i = 0; i < kNumAlgorithms; i++) {
+ cerr << i << " for " << algo_strings[i] << "\n";
+ }
+ cerr << "\n\n";
+}
+
+bool FirmwareUtility::ParseCmdLineOptions(int argc, char* argv[]) {
+ int option_index;
+ static struct option long_options[] = {
+ {"root_key", 1, 0, 0},
+ {"root_key_pub", 1, 0, 0},
+ {"sign_key", 1, 0, 0},
+ {"sign_key_pub", 1, 0, 0},
+ {"sign_algorithm", 1, 0, 0},
+ {"key_version", 1, 0, 0},
+ {"firmware_version", 1, 0, 0},
+ {"in", 1, 0, 0},
+ {"out", 1, 0, 0},
+ {"generate", 0, 0, 0},
+ {"verify", 0, 0, 0},
+ {NULL, 0, 0, 0}
+ };
+ while (1) {
+ int i = getopt_long(argc, argv, "", long_options, &option_index);
+ if (-1 == i) // Done with option processing.
+ break;
+ if ('?' == i) // Invalid option found.
+ return false;
+
+ if (0 == i) {
+ switch(option_index) {
+ case 0: // root_key
+ root_key_file_ = optarg;
+ break;
+ case 1: // root_key_pub
+ root_key_pub_file_ = optarg;
+ break;
+ case 2: // sign_key
+ sign_key_file_ = optarg;
+ break;
+ case 3: // sign_key_pub
+ sign_key_pub_file_ = optarg;
+ break;
+ case 4: // sign_algorithm
+ errno = 0; // strtol() returns an error via errno
+ sign_algorithm_ = strtol(optarg, (char**) NULL, 10);
+ if (errno)
+ return false;
+ break;
+ case 5: // key_version
+ errno = 0;
+ key_version_ = strtol(optarg, (char**) NULL, 10);
+ if (errno)
+ return false;
+ break;
+ case 6: // firmware_version
+ errno = 0;
+ firmware_version_ = strtol(optarg, (char**) NULL, 10);
+ if (errno)
+ return false;
+ break;
+ case 7: // in
+ in_file_ = optarg;
+ break;
+ case 8: // out
+ out_file_ = optarg;
+ break;
+ case 9: // generate
+ is_generate_ = true;
+ break;
+ case 10: // verify
+ is_verify_ = true;
+ break;
+ }
+ }
+ }
+ return CheckOptions();
+}
+
+
+void FirmwareUtility::OutputSignedImage(void) {
+ if (image_) {
+ if (!WriteFirmwareImage(out_file_.c_str(), image_)) {
+ cerr << "Couldn't write verified boot image to file "
+ << out_file_ <<".\n";
+ }
+ }
+}
+
+bool FirmwareUtility::GenerateSignedImage(void) {
+ uint32_t sign_key_pub_len;
+ uint8_t* header_checksum;
+ DigestContext ctx;
+ image_ = FirmwareImageNew();
+
+ Memcpy(image_->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE);
+
+ // Copy pre-processed public signing key.
+ image_->sign_algorithm = (uint16_t) sign_algorithm_;
+ image_->sign_key = BufferFromFile(sign_key_pub_file_.c_str(),
+ &sign_key_pub_len);
+ if (!image_->sign_key)
+ return false;
+ image_->key_version = key_version_;
+
+ // Update header length.
+ image_->header_len = (sizeof(image_->header_len) +
+ sizeof(image_->sign_algorithm) +
+ sign_key_pub_len +
+ sizeof(image_->key_version) +
+ sizeof(image_->header_checksum));
+
+ // Calculate header checksum.
+ DigestInit(&ctx, SHA512_DIGEST_ALGORITHM);
+ DigestUpdate(&ctx, (uint8_t*) &image_->header_len,
+ sizeof(image_->header_len));
+ DigestUpdate(&ctx, (uint8_t*) &image_->sign_algorithm,
+ sizeof(image_->sign_algorithm));
+ DigestUpdate(&ctx, image_->sign_key,
+ RSAProcessedKeySize(image_->sign_algorithm));
+ DigestUpdate(&ctx, (uint8_t*) &image_->key_version,
+ sizeof(image_->key_version));
+ header_checksum = DigestFinal(&ctx);
+ Memcpy(image_->header_checksum, header_checksum, SHA512_DIGEST_SIZE);
+ Free(header_checksum);
+
+ image_->firmware_version = firmware_version_;
+ image_->firmware_len = 0;
+ // TODO(gauravsh): Populate this with the right bytes once we decide
+ // what goes into the preamble.
+ Memset(image_->preamble, 'P', FIRMWARE_PREAMBLE_SIZE);
+ image_->firmware_data = BufferFromFile(in_file_.c_str(),
+ &image_->firmware_len);
+ if (!image_)
+ return false;
+ // Generate and add the signatures.
+ if(!AddFirmwareKeySignature(image_, root_key_file_.c_str())) {
+ cerr << "Couldn't write key signature to verified boot image.\n";
+ return false;
+ }
+
+ if(!AddFirmwareSignature(image_, sign_key_file_.c_str(),
+ image_->sign_algorithm)) {
+ cerr << "Couldn't write firmware signature to verified boot image.\n";
+ return false;
+ }
+ return true;
+}
+
+bool FirmwareUtility::VerifySignedImage(void) {
+ int error;
+ root_key_pub_ = RSAPublicKeyFromFile(root_key_pub_file_.c_str());
+ image_ = ReadFirmwareImage(in_file_.c_str());
+
+ if (!root_key_pub_) {
+ cerr << "Couldn't read pre-processed public root key.\n";
+ return false;
+ }
+
+ if (!image_) {
+ cerr << "Couldn't read firmware image or malformed image.\n";
+ return false;
+ }
+ if(!(error = VerifyFirmwareImage(root_key_pub_, image_, 0))) // Trusted Mode.
+ return true;
+ cerr << VerifyFirmwareErrorString(error) << "\n";
+ return false;;
+}
+
+bool FirmwareUtility::CheckOptions(void) {
+ if (is_generate_ == is_verify_) {
+ cerr << "One of --generate or --verify must be specified.\n";
+ return false;
+ }
+ // Common required options.
+ if (in_file_.empty()) {
+ cerr << "No input file specified." << "\n";
+ return false;
+ }
+ // Required options for --verify.
+ if (is_verify_ && root_key_pub_file_.empty()) {
+ cerr << "No pre-processed public root key file specified." << "\n";
+ return false;
+ }
+ // Required options for --generate.
+ if (is_generate_) {
+ if (root_key_file_.empty()) {
+ cerr << "No root key file specified." << "\n";
+ return false;
+ }
+ if (firmware_version_ <= 0 || firmware_version_ > 0xFFFF) {
+ cerr << "Invalid or no firmware version specified." << "\n";
+ return false;
+ }
+ if (sign_key_file_.empty()) {
+ cerr << "No signing key file specified." << "\n";
+ return false;
+ }
+ if (sign_key_pub_file_.empty()) {
+ cerr << "No pre-processed public signing key file specified." << "\n";
+ return false;
+ }
+ if (key_version_ <= 0 || key_version_ > 0xFFFF) {
+ cerr << "Invalid or no key version specified." << "\n";
+ return false;
+ }
+ if (sign_algorithm_ < 0 || sign_algorithm_ >= kNumAlgorithms) {
+ cerr << "Invalid or no signing key algorithm specified." << "\n";
+ return false;
+ }
+ if (out_file_.empty()) {
+ cerr <<"No output file specified." << "\n";
+ return false;
+ }
+ }
+ return true;
+}
+
+
+} // namespace vboot_reference
+
+int main(int argc, char* argv[]) {
+ vboot_reference::FirmwareUtility fu;
+ if (!fu.ParseCmdLineOptions(argc, argv)) {
+ fu.PrintUsage();
+ return -1;
+ }
+ if (fu.is_generate()) {
+ if (!fu.GenerateSignedImage())
+ return -1;
+ fu.OutputSignedImage();
+ }
+ if (fu.is_verify()) {
+ cerr << "Verification ";
+ if(fu.VerifySignedImage())
+ cerr << "SUCCESS.\n";
+ else
+ cerr << "FAILURE.\n";
+ }
+ return 0;
+}