summaryrefslogtreecommitdiff
path: root/utility
diff options
context:
space:
mode:
authorGaurav Shah <gauravsh@chromium.org>2010-03-31 13:26:55 -0700
committerGaurav Shah <gauravsh@chromium.org>2010-03-31 13:26:55 -0700
commitfc70d72aaab4d558e39ec43832375267603bfd93 (patch)
tree2b71e90cdb26c079a76ab82607e9bf7a7c97e6ff /utility
parent5411c7a9f03f91bf2c1cd1cf852db9d4585a05c9 (diff)
downloadvboot-fc70d72aaab4d558e39ec43832375267603bfd93.tar.gz
VBoot Reference: Refactoring Part 3
Refactor and restructure reference code into individual self-contain modules. I have revamped the way the code is structured to make it easy to determine which parts belong in the firmware and which are used by userland tools. common/ - common utilities and stub functions (Firmware) cryptolib/ - crypto library (Firmware) misclibs/ - miscellaneous userland libraries (Userland) sctips/ - Miscellaenous scripts (Userland) tests/ - Tests (Userland) vfirmware/ - Verified Firmware Implementation vfirmware/firmware_image_fw.c (Firmware) vfirmware/firmware_image.c (Userland) vkernel/ - Verified Kernel Implementation vkernel/kernel_image_fw.c (Firmware) vkernel/kernel_image.c (Userland) Review URL: http://codereview.chromium.org/1581005
Diffstat (limited to 'utility')
-rw-r--r--utility/Makefile52
-rw-r--r--utility/dumpRSAPublicKey.c181
-rw-r--r--utility/firmware_utility.cc313
-rw-r--r--utility/include/firmware_utility.h71
-rw-r--r--utility/include/kernel_utility.h79
-rw-r--r--utility/include/verify_data.h20
-rw-r--r--utility/kernel_utility.cc363
-rw-r--r--utility/signature_digest_utility.c54
-rw-r--r--utility/verify_data.c95
9 files changed, 1228 insertions, 0 deletions
diff --git a/utility/Makefile b/utility/Makefile
new file mode 100644
index 00000000..a5f6f584
--- /dev/null
+++ b/utility/Makefile
@@ -0,0 +1,52 @@
+# 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.
+
+TOP ?= ../
+CC ?= gcc
+CXX ?= g++
+INCLUDES += -I./include \
+ -I../cryptolib/include \
+ -I../common/include \
+ -I../misclibs/include \
+ -I../vfirmware/include\
+ -I../vkernel/include
+CFLAGS ?= -Wall -DNDEBUG -O3 -Werror $(INCLUDES)
+LIBS = $(TOP)/misclibs/file_keys.o \
+ $(TOP)/misclibs/signature_digest.o \
+ $(TOP)/vfirmware/firmware_image.o \
+ $(TOP)/vfirmware/firmware_image_fw.o \
+ $(TOP)/vkernel/kernel_image.o \
+ $(TOP)/vkernel/kernel_image_fw.o
+FIRMWARELIBS = $(TOP)/cryptolib/libcrypto.a $(TOP)/common/libcommon.a
+
+TARGET_BINS = dumpRSAPublicKey \
+ firmware_utility \
+ kernel_utility \
+ signature_digest_utility \
+ verify_data
+
+all: $(TARGET_BINS)
+
+dumpRSAPublicKey: dumpRSAPublicKey.c
+ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ -lcrypto
+
+firmware_utility: firmware_utility.cc $(LIBS) $(FIRMWARELIBS)
+ $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \
+ -o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \
+ -lcrypto
+
+kernel_utility: kernel_utility.cc $(LIBS) $(FIRMWARELIBS)
+ $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \
+ -o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \
+ -lcrypto
+
+signature_digest_utility: signature_digest_utility.c $(LIBS) $(FIRMWARELIBS)
+ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
+
+verify_data: verify_data.c $(LIBS) $(FIRMWARELIBS)
+ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
+
+clean:
+ rm -f $(TARGET_BINS)
+
diff --git a/utility/dumpRSAPublicKey.c b/utility/dumpRSAPublicKey.c
new file mode 100644
index 00000000..837303cb
--- /dev/null
+++ b/utility/dumpRSAPublicKey.c
@@ -0,0 +1,181 @@
+/* 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.
+ */
+
+/* C port of DumpPublicKey.java from the Android Open source project with
+ * support for additional RSA key sizes. (platform/system/core,git/libmincrypt
+ * /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
+ */
+
+#include <stdint.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Command line tool to extract RSA public keys from X.509 certificates
+ * and output a pre-processed version of keys for use by RSA verification
+ * routines.
+ */
+
+int check(RSA* key) {
+ int public_exponent = BN_get_word(key->e);
+ int modulus = BN_num_bits(key->n);
+
+ if (public_exponent != 65537) {
+ fprintf(stderr, "WARNING: Public exponent should be 65537 (but is %d).\n",
+ public_exponent);
+ }
+
+ if (modulus != 1024 && modulus != 2048 && modulus != 4096
+ && modulus != 8192) {
+ fprintf(stderr, "ERROR: Unknown modulus length = %d.\n", modulus);
+ return 0;
+ }
+ return 1;
+}
+
+/* Pre-processes and outputs RSA public key to standard out.
+ */
+void output(RSA* key) {
+ int i, nwords;
+ BIGNUM *N = key->n;
+ BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL;
+ BIGNUM *B = NULL;
+ BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL, *NnumBits = NULL;
+ BIGNUM *n = NULL, *rr = NULL;
+ BN_CTX *bn_ctx = BN_CTX_new();
+ uint32_t n0invout;
+
+ N = key->n;
+ /* Output size of RSA key in 32-bit words */
+ nwords = BN_num_bits(N) / 32;
+ if (-1 == write(1, &nwords, sizeof(nwords)))
+ goto failure;
+
+
+ /* Initialize BIGNUMs */
+ Big1 = BN_new();
+ Big2 = BN_new();
+ Big32 = BN_new();
+ BigMinus1 = BN_new();
+ N0inv= BN_new();
+ R = BN_new();
+ RR = BN_new();
+ RRTemp = BN_new();
+ NnumBits = BN_new();
+ n = BN_new();
+ rr = BN_new();
+
+
+ BN_set_word(Big1, 1L);
+ BN_set_word(Big2, 2L);
+ BN_set_word(Big32, 32L);
+ BN_sub(BigMinus1, Big1, Big2);
+
+ B = BN_new();
+ BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */
+
+ /* Calculate and output N0inv = -1 / N[0] mod 2^32 */
+ BN_mod_inverse(N0inv, N, B, bn_ctx);
+ BN_sub(N0inv, B, N0inv);
+ n0invout = BN_get_word(N0inv);
+ if (-1 == write(1, &n0invout, sizeof(n0invout)))
+ goto failure;
+
+ /* Calculate R = 2^(# of key bits) */
+ BN_set_word(NnumBits, BN_num_bits(N));
+ BN_exp(R, Big2, NnumBits, bn_ctx);
+
+ /* Calculate RR = R^2 mod N */
+ BN_copy(RR, R);
+ BN_mul(RRTemp, RR, R, bn_ctx);
+ BN_mod(RR, RRTemp, N, bn_ctx);
+
+
+ /* Write out modulus as little endian array of integers. */
+ for (i = 0; i < nwords; ++i) {
+ uint32_t nout;
+
+ BN_mod(n, N, B, bn_ctx); /* n = N mod B */
+ nout = BN_get_word(n);
+ if (-1 == write(1, &nout, sizeof(nout)))
+ goto failure;
+
+ BN_rshift(N, N, 32); /* N = N/B */
+ }
+
+ /* Write R^2 as little endian array of integers. */
+ for (i = 0; i < nwords; ++i) {
+ uint32_t rrout;
+
+ BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */
+ rrout = BN_get_word(rr);
+ if (-1 == write(1, &rrout, sizeof(rrout)))
+ goto failure;
+
+ BN_rshift(RR, RR, 32); /* RR = RR/B */
+ }
+
+failure:
+ /* Free BIGNUMs. */
+ BN_free(Big1);
+ BN_free(Big2);
+ BN_free(Big32);
+ BN_free(BigMinus1);
+ BN_free(N0inv);
+ BN_free(R);
+ BN_free(RRTemp);
+ BN_free(NnumBits);
+ BN_free(n);
+ BN_free(rr);
+
+}
+
+int main(int argc, char* argv[]) {
+ FILE* fp;
+ X509* cert = NULL;
+ RSA* pubkey = NULL;
+ EVP_PKEY* key;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <certfile>\n", argv[0]);
+ return -1;
+ }
+
+ fp = fopen(argv[1], "r");
+
+ if (!fp) {
+ fprintf(stderr, "Couldn't open certificate file!\n");
+ return -1;
+ }
+
+ /* Read the certificate */
+ if (!PEM_read_X509(fp, &cert, NULL, NULL)) {
+ fprintf(stderr, "Couldn't read certificate.\n");
+ goto fail;
+ }
+
+ /* Get the public key from the certificate. */
+ key = X509_get_pubkey(cert);
+
+ /* Convert to a RSA_style key. */
+ if (!(pubkey = EVP_PKEY_get1_RSA(key))) {
+ fprintf(stderr, "Couldn't convert to a RSA style key.\n");
+ goto fail;
+ }
+
+ if (check(pubkey)) {
+ output (pubkey);
+ }
+
+fail:
+ X509_free(cert);
+ RSA_free(pubkey);
+ fclose(fp);
+
+ return 0;
+}
diff --git a/utility/firmware_utility.cc b/utility/firmware_utility.cc
new file mode 100644
index 00000000..85275e73
--- /dev/null
+++ b/utility/firmware_utility.cc
@@ -0,0 +1,313 @@
+// 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 <stdio.h>
+#include <stdint.h> // Needed for UINT16_MAX.
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iostream>
+
+extern "C" {
+#include "cryptolib.h"
+#include "file_keys.h"
+#include "firmware_image.h"
+#include "utility.h"
+}
+
+extern int errno;
+using std::cerr;
+
+namespace vboot_reference {
+
+FirmwareUtility::FirmwareUtility():
+ image_(NULL),
+ root_key_pub_(NULL),
+ firmware_version_(-1),
+ firmware_key_version_(-1),
+ firmware_sign_algorithm_(-1),
+ is_generate_(false),
+ is_verify_(false),
+ is_describe_(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"
+ "--firmware_sign_key <privkeyfile>\tPrivate signing key file\n"
+ "--firmware_sign_key_pub <pubkeyfile>\tPre-processed public signing"
+ " key\n"
+ "--firmware_sign_algorithm <algoid>\tSigning algorithm to use\n"
+ "--firmware_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},
+ {"firmware_sign_key", 1, 0, 0},
+ {"firmware_sign_key_pub", 1, 0, 0},
+ {"firmware_sign_algorithm", 1, 0, 0},
+ {"firmware_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},
+ {"describe", 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: // firmware_sign_key
+ firmware_key_file_ = optarg;
+ break;
+ case 3: // firmware_sign_key_pub
+ firmware_key_pub_file_ = optarg;
+ break;
+ case 4: // firmware_sign_algorithm
+ errno = 0; // strtol() returns an error via errno
+ firmware_sign_algorithm_ = strtol(optarg,
+ reinterpret_cast<char**>(NULL), 10);
+ if (errno)
+ return false;
+ break;
+ case 5: // firmware_key_version
+ errno = 0;
+ firmware_key_version_ = strtol(optarg,
+ reinterpret_cast<char**>(NULL), 10);
+ if (errno)
+ return false;
+ break;
+ case 6: // firmware_version
+ errno = 0;
+ firmware_version_ = strtol(optarg,
+ reinterpret_cast<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;
+ case 11: // describe
+ is_describe_ = 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";
+ }
+ }
+}
+
+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;
+ image_ = FirmwareImageNew();
+
+ Memcpy(image_->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE);
+
+ // Copy pre-processed public signing key.
+ image_->firmware_sign_algorithm = (uint16_t) firmware_sign_algorithm_;
+ image_->firmware_sign_key = BufferFromFile(
+ firmware_key_pub_file_.c_str(),
+ &firmware_sign_key_pub_len);
+ if (!image_->firmware_sign_key)
+ return false;
+ image_->firmware_key_version = firmware_key_version_;
+
+ // Update header length.
+ image_->header_len = GetFirmwareHeaderLen(image_);
+
+ // Calculate header checksum.
+ CalculateFirmwareHeaderChecksum(image_, image_->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_, firmware_key_file_.c_str())) {
+ 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 (VERIFY_FIRMWARE_SUCCESS ==
+ (error = VerifyFirmwareImage(root_key_pub_, image_)))
+ return true;
+ cerr << VerifyFirmwareErrorString(error) << "\n";
+ return false;;
+}
+
+bool FirmwareUtility::CheckOptions(void) {
+ // 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.
+ 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_ > UINT16_MAX) {
+ cerr << "Invalid or no firmware version specified." << "\n";
+ return false;
+ }
+ if (firmware_key_file_.empty()) {
+ cerr << "No signing key file specified." << "\n";
+ return false;
+ }
+ if (firmware_key_pub_file_.empty()) {
+ cerr << "No pre-processed public signing key file specified." << "\n";
+ return false;
+ }
+ if (firmware_key_version_ <= 0 || firmware_key_version_ > UINT16_MAX) {
+ cerr << "Invalid or no key version specified." << "\n";
+ return false;
+ }
+ if (firmware_sign_algorithm_ < 0 ||
+ firmware_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_describe()) {
+ fu.DescribeSignedImage();
+ }
+ 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;
+}
diff --git a/utility/include/firmware_utility.h b/utility/include/firmware_utility.h
new file mode 100644
index 00000000..6c2b735a
--- /dev/null
+++ b/utility/include/firmware_utility.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef VBOOT_REFERENCE_FIRMWARE_UTILITY_H_
+#define VBOOT_REFERENCE_FIRMWARE_UTILITY_H_
+
+#include <string>
+
+class FirmwareImage;
+struct RSAPublicKey;
+
+namespace vboot_reference {
+
+// A class for handling verified boot firmware images.
+class FirmwareUtility {
+ public:
+ FirmwareUtility();
+ ~FirmwareUtility();
+
+ // Print usage to stderr.
+ void PrintUsage(void);
+
+ // Parse command line options and populate data members.
+ // Return true on success, false on failure.
+ bool ParseCmdLineOptions(int argc, char* argv[]);
+
+ // Print descriptio of verified boot firmware image.
+ void DescribeSignedImage();
+
+ // Generate a verified boot image by reading firmware data from in_file_.
+ // Return true on success, false on failure.
+ bool GenerateSignedImage();
+
+ // Verify a previously generated signed firmware image using the root key read
+ // from [root_key_pub_file_].
+ bool VerifySignedImage();
+
+ // Output the verified boot image to out_file_.
+ void OutputSignedImage();
+
+
+ bool is_generate() { return is_generate_; }
+ bool is_verify() { return is_verify_; }
+ bool is_describe() { return is_describe_; }
+
+ private:
+
+ // Check if all options were specified and sane.
+ // Return true on success, false on failure.
+ bool CheckOptions();
+
+ FirmwareImage* image_;
+ RSAPublicKey* root_key_pub_;
+ std::string root_key_file_;
+ std::string root_key_pub_file_;
+ int firmware_version_;
+ std::string firmware_key_file_;
+ std::string firmware_key_pub_file_;
+ int firmware_key_version_;
+ int firmware_sign_algorithm_;
+ std::string in_file_;
+ std::string out_file_;
+ bool is_generate_; // Are we generating a new image?
+ bool is_verify_; // Are we just verifying an already signed image?
+ bool is_describe_; // Should we print out description of the image?
+};
+
+} // namespace vboot_reference
+
+#endif // VBOOT_REFERENCE_FIRMWARE_UTILITY_H_
diff --git a/utility/include/kernel_utility.h b/utility/include/kernel_utility.h
new file mode 100644
index 00000000..1cb7f1ae
--- /dev/null
+++ b/utility/include/kernel_utility.h
@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef VBOOT_REFERENCE_KERNEL_UTILITY_H_
+#define VBOOT_REFERENCE_KERNEL_UTILITY_H_
+
+#include <string>
+
+extern "C" {
+#include "kernel_image.h"
+}
+
+struct RSAPublicKey;
+
+namespace vboot_reference {
+
+// A class for handling verified boot kernel images.
+class KernelUtility {
+ public:
+ KernelUtility();
+ ~KernelUtility();
+
+ // Print usage to stderr.
+ void PrintUsage(void);
+
+ // Parse command line options and populate data members.
+ // Return true on success, false on failure.
+ bool ParseCmdLineOptions(int argc, char* argv[]);
+
+ // Print description of a verified boot kernel image.
+ void DescribeSignedImage();
+
+ // Generate a verified boot image by reading kernel data from in_file_.
+ // Return true on success, false on failure.
+ bool GenerateSignedImage();
+
+ // Verify a previously generated signed firmware image using the key read
+ // from [firmware_key_pub_file_].
+ bool VerifySignedImage();
+
+ // Output the verified boot kernel image to out_file_.
+ void OutputSignedImage();
+
+ bool is_generate() { return is_generate_; }
+ bool is_verify() { return is_verify_; }
+ bool is_describe() { return is_describe_; }
+
+ private:
+
+ // Check if all options were specified and sane.
+ // Return true on success, false on failure.
+ bool CheckOptions();
+
+ KernelImage* image_;
+ RSAPublicKey* firmware_key_pub_; // Root key used for verification.
+ std::string firmware_key_file_; // Private key for signing the kernel key.
+ std::string firmware_key_pub_file_;
+ std::string kernel_key_file_; // Private key for signing the kernel.
+ std::string kernel_key_pub_file_;
+
+ // Fields of a KernelImage. (read from the command line).
+ int header_version_;
+ int firmware_sign_algorithm_;
+ int kernel_sign_algorithm_;
+ int kernel_key_version_;
+ int kernel_version_;
+ kconfig_options options_;
+
+ std::string in_file_;
+ std::string out_file_;
+ bool is_generate_; // Are we generating a new image?
+ bool is_verify_; // Are we just verifying an already signed image?
+ bool is_describe_; // Should we print out description of the image?
+};
+
+} // namespace vboot_reference
+
+#endif // VBOOT_REFERENCE_FIRMWARE_UTILITY_H_
diff --git a/utility/include/verify_data.h b/utility/include/verify_data.h
new file mode 100644
index 00000000..51b9fd6d
--- /dev/null
+++ b/utility/include/verify_data.h
@@ -0,0 +1,20 @@
+/* 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.
+ */
+
+#ifndef VBOOT_REFERENCE_VERIFY_DATA_H_
+#define VBOOT_REFERENCE_VERIFY_DATA_H_
+
+/* Reads a pre-processed key from [input_file] and
+ * returns it in a RSAPublicKey structure.
+ * Caller owns the returned key and must free it.
+ */
+RSAPublicKey* read_RSAkey(char *input_file);
+
+/* Return a signature of [len] bytes read from [input_file].
+ * Caller owns the returned signature and must free it.
+ */
+uint8_t* read_signature(char *input_file, int len);
+
+#endif /* VBOOT_REFERENCE_VERIFY_DATA_H_ */
diff --git a/utility/kernel_utility.cc b/utility/kernel_utility.cc
new file mode 100644
index 00000000..9fedeb5f
--- /dev/null
+++ b/utility/kernel_utility.cc
@@ -0,0 +1,363 @@
+// 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 kernel images.
+//
+
+#include "kernel_utility.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h> // Needed for UINT16_MAX.
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iostream>
+
+extern "C" {
+#include "cryptolib.h"
+#include "file_keys.h"
+#include "kernel_image.h"
+#include "utility.h"
+}
+
+extern int errno;
+using std::cerr;
+
+namespace vboot_reference {
+
+KernelUtility::KernelUtility(): image_(NULL),
+ firmware_key_pub_(NULL),
+ header_version_(1),
+ firmware_sign_algorithm_(-1),
+ kernel_sign_algorithm_(-1),
+ kernel_key_version_(-1),
+ kernel_version_(-1),
+ is_generate_(false),
+ is_verify_(false),
+ is_describe_(false){
+ // Populate kernel config options with defaults.
+ options_.version[0] = 1;
+ options_.version[1] = 0;
+ options_.kernel_len = 0;
+ options_.kernel_load_addr = 0;
+ options_.kernel_entry_addr = 0;
+}
+
+KernelUtility::~KernelUtility() {
+ RSAPublicKeyFree(firmware_key_pub_);
+ KernelImageFree(image_);
+}
+
+void KernelUtility::PrintUsage(void) {
+ cerr <<
+ "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 "
+ "to use for verification.\n\n"
+ "For \"--generate\", required OPTIONS are:\n"
+ "--firmware_key <privkeyfile>\tPrivate firmware signing key file\n"
+ "--kernel_key <privkeyfile>\tPrivate kernel signing key file\n"
+ "--kernel_key_pub <pubkeyfile>\tPre-processed public kernel signing"
+ " key\n"
+ "--firmware_sign_algorithm <algoid>\tSigning algorithm used by "
+ "the firmware\n"
+ "--kernel_sign_algorithm <algoid>\tSigning algorithm to use for kernel\n"
+ "--kernel_key_version <version#>\tKernel signing Key Version#\n"
+ "--kernel_version <version#>\tKernel Version#\n"
+ "--in <infile>\t\tKernel Image to sign\n"
+ "--out <outfile>\t\tOutput file for verified boot Kernel image\n\n"
+ "Optional arguments for \"--generate\" include:\n"
+ "--config_version <version>\n"
+ "--kernel_load_addr <addr>\n"
+ "--kernel_entry_addr <addr>\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 KernelUtility::ParseCmdLineOptions(int argc, char* argv[]) {
+ int option_index;
+ static struct option long_options[] = {
+ {"firmware_key", 1, 0, 0},
+ {"firmware_key_pub", 1, 0, 0},
+ {"kernel_key", 1, 0, 0},
+ {"kernel_key_pub", 1, 0, 0},
+ {"firmware_sign_algorithm", 1, 0, 0},
+ {"kernel_sign_algorithm", 1, 0, 0},
+ {"kernel_key_version", 1, 0, 0},
+ {"kernel_version", 1, 0, 0},
+ {"in", 1, 0, 0},
+ {"out", 1, 0, 0},
+ {"generate", 0, 0, 0},
+ {"verify", 0, 0, 0},
+ {"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) {
+ 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: // firmware_key
+ firmware_key_file_ = optarg;
+ break;
+ case 1: // firmware_key_pub
+ firmware_key_pub_file_ = optarg;
+ break;
+ case 2: // kernel_key
+ kernel_key_file_ = optarg;
+ break;
+ case 3: // kernel_key_pub
+ kernel_key_pub_file_ = optarg;
+ break;
+ case 4: // firmware_sign_algorithm
+ errno = 0; // strtol() returns an error via errno
+ firmware_sign_algorithm_ = strtol(optarg,
+ reinterpret_cast<char**>(NULL), 10);
+ if (errno)
+ return false;
+ break;
+ case 5: // kernel_sign_algorithm
+ errno = 0;
+ kernel_sign_algorithm_ = strtol(optarg,
+ reinterpret_cast<char**>(NULL), 10);
+ if (errno)
+ return false;
+ break;
+ case 6: // kernel_key_version
+ errno = 0;
+ kernel_key_version_ = strtol(optarg,
+ reinterpret_cast<char**>(NULL), 10);
+ if (errno)
+ return false;
+ break;
+ case 7: // kernel_version
+ errno = 0;
+ kernel_version_ = strtol(optarg,
+ reinterpret_cast<char**>(NULL), 10);
+ if (errno)
+ return false;
+ break;
+ case 8: // in
+ in_file_ = optarg;
+ break;
+ case 9: // out
+ out_file_ = optarg;
+ break;
+ case 10: // generate
+ is_generate_ = true;
+ break;
+ case 11: // verify
+ is_verify_ = true;
+ break;
+ case 12: // config_version
+ if (2 != sscanf(optarg, "%d.%d", &options_.version[0],
+ &options_.version[1]))
+ return false;
+ break;
+ case 13: // kernel_load_addr
+ errno = 0;
+ options_.kernel_load_addr =
+ strtol(optarg, reinterpret_cast<char**>(NULL), 10);
+ if (errno)
+ return false;
+ break;
+ case 14: // kernel_entry_addr
+ 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;
+ }
+ }
+ }
+ return CheckOptions();
+}
+
+void KernelUtility::OutputSignedImage(void) {
+ if (image_) {
+ if (!WriteKernelImage(out_file_.c_str(), image_)) {
+ cerr << "Couldn't write verified boot kernel image to file "
+ << out_file_ <<".\n";
+ }
+ }
+}
+
+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;
+ image_ = KernelImageNew();
+
+ Memcpy(image_->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE);
+
+ // TODO(gauravsh): make this a command line option.
+ image_->header_version = 1;
+ image_->firmware_sign_algorithm = (uint16_t) firmware_sign_algorithm_;
+ // Copy pre-processed public signing key.
+ image_->kernel_sign_algorithm = (uint16_t) kernel_sign_algorithm_;
+ image_->kernel_sign_key = BufferFromFile(kernel_key_pub_file_.c_str(),
+ &kernel_key_pub_len);
+ if (!image_->kernel_sign_key)
+ return false;
+ image_->kernel_key_version = kernel_key_version_;
+
+ // Update header length.
+ image_->header_len = GetKernelHeaderLen(image_);
+
+ // Calculate 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(),
+ &image_->options.kernel_len);
+ if (!image_)
+ return false;
+ // Generate and add the signatures.
+ if (!AddKernelKeySignature(image_, firmware_key_file_.c_str())) {
+ cerr << "Couldn't write key signature to verified boot kernel image.\n";
+ return false;
+ }
+
+ if (!AddKernelSignature(image_, kernel_key_file_.c_str())) {
+ cerr << "Couldn't write firmware signature to verified boot kernel image.\n";
+ return false;
+ }
+ return true;
+}
+
+bool KernelUtility::VerifySignedImage(void) {
+ int error;
+ firmware_key_pub_ = RSAPublicKeyFromFile(firmware_key_pub_file_.c_str());
+ image_ = ReadKernelImage(in_file_.c_str());
+
+ if (!firmware_key_pub_) {
+ cerr << "Couldn't read pre-processed public root key.\n";
+ return false;
+ }
+
+ if (!image_) {
+ cerr << "Couldn't read kernel image or malformed image.\n";
+ return false;
+ }
+ if (!(error = VerifyKernelImage(firmware_key_pub_, image_, 0)))
+ return true;
+ cerr << VerifyKernelErrorString(error) << "\n";
+ return false;
+}
+
+bool KernelUtility::CheckOptions(void) {
+ // 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.
+ if (in_file_.empty()) {
+ cerr << "No input file specified.\n";
+ return false;
+ }
+ // Required options for --verify.
+ if (is_verify_ && firmware_key_pub_file_.empty()) {
+ cerr << "No pre-processed public firmware key file specified.\n";
+ return false;
+ }
+ // Required options for --generate.
+ if (is_generate_) {
+ if (firmware_key_file_.empty()) {
+ cerr << "No firmware key file specified.\n";
+ return false;
+ }
+ if (kernel_key_file_.empty()) {
+ cerr << "No kernel key file specified.\n";
+ return false;
+ }
+ if (kernel_key_pub_file_.empty()) {
+ cerr << "No pre-processed public kernel key file specified\n";
+ return false;
+ }
+ if (kernel_key_version_ <= 0 || kernel_key_version_ > UINT16_MAX) {
+ cerr << "Invalid or no kernel key version specified.\n";
+ return false;
+ }
+ if (firmware_sign_algorithm_ < 0 ||
+ firmware_sign_algorithm_ >= kNumAlgorithms) {
+ cerr << "Invalid or no firmware signing key algorithm specified.\n";
+ return false;
+ }
+ if (kernel_sign_algorithm_ < 0 ||
+ kernel_sign_algorithm_ >= kNumAlgorithms) {
+ cerr << "Invalid or no kernel signing key algorithm specified.\n";
+ return false;
+ }
+ if (kernel_version_ <=0 || kernel_version_ > UINT16_MAX) {
+ cerr << "Invalid or no kernel version 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::KernelUtility ku;
+ if (!ku.ParseCmdLineOptions(argc, argv)) {
+ ku.PrintUsage();
+ return -1;
+ }
+ if (ku.is_describe()) {
+ ku.DescribeSignedImage();
+ }
+ else if (ku.is_generate()) {
+ if (!ku.GenerateSignedImage())
+ return -1;
+ ku.OutputSignedImage();
+ }
+ else if (ku.is_verify()) {
+ cerr << "Verification ";
+ if (ku.VerifySignedImage())
+ cerr << "SUCCESS.\n";
+ else
+ cerr << "FAILURE.\n";
+ }
+ return 0;
+}
diff --git a/utility/signature_digest_utility.c b/utility/signature_digest_utility.c
new file mode 100644
index 00000000..6c8891b9
--- /dev/null
+++ b/utility/signature_digest_utility.c
@@ -0,0 +1,54 @@
+/* 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 that outputs the cryptographic digest of a contents of a
+ * file in a format that can be directly used to generate PKCS#1 v1.5
+ * signatures via the "openssl" command line utility.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "file_keys.h"
+#include "padding.h"
+#include "signature_digest.h"
+#include "utility.h"
+
+int main(int argc, char* argv[]) {
+ int algorithm = -1;
+ int error_code = 0;
+ uint8_t* buf = NULL;
+ uint8_t* signature_digest = NULL;
+ uint64_t len;
+ uint32_t signature_digest_len;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s <algoid> <file>", argv[0]);
+ return -1;
+ }
+ algorithm = atoi(argv[1]);
+ if (algorithm < 0 || algorithm >= kNumAlgorithms) {
+ fprintf(stderr, "Invalid Algorithm!\n");
+ return -1;
+ }
+
+ buf = BufferFromFile(argv[2], &len);
+ if (!buf) {
+ fprintf(stderr, "Could read file: %s\n", argv[2]);
+ return -1;
+ }
+
+ signature_digest = SignatureDigest(buf, len, algorithm);
+ signature_digest_len = (hash_size_map[algorithm] +
+ digestinfo_size_map[algorithm]);
+ if (!signature_digest)
+ error_code = -1;
+ if(signature_digest &&
+ 1 != fwrite(signature_digest, signature_digest_len, 1, stdout))
+ error_code = -1;
+ Free(signature_digest);
+ Free(buf);
+ return error_code;
+}
diff --git a/utility/verify_data.c b/utility/verify_data.c
new file mode 100644
index 00000000..c9da2401
--- /dev/null
+++ b/utility/verify_data.c
@@ -0,0 +1,95 @@
+/* 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.
+ */
+
+/* Routines for verifying a file's signature. Useful in testing the core
+ * RSA verification implementation.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "cryptolib.h"
+#include "file_keys.h"
+#include "verify_data.h"
+
+/* ANSI Color coding sequences. */
+#define COL_GREEN "\e[1;32m"
+#define COL_RED "\e[0;31m"
+#define COL_STOP "\e[m"
+
+uint8_t* read_signature(char* input_file, int len) {
+ int i, sigfd;
+ uint8_t* signature = NULL;
+ if ((sigfd = open(input_file, O_RDONLY)) == -1) {
+ fprintf(stderr, "Couldn't open signature file\n");
+ return NULL;
+ }
+
+ /* Read the signature into a buffer*/
+ signature = (uint8_t*) malloc(len);
+ if (!signature)
+ return NULL;
+
+ if( (i = read(sigfd, signature, len)) != len ) {
+ fprintf(stderr, "Wrong signature length - Expected = %d, Received = %d\n",
+ len, i);
+ close(sigfd);
+ return NULL;
+ }
+
+ close(sigfd);
+ return signature;
+}
+
+int main(int argc, char* argv[]) {
+ int i, algorithm, sig_len;
+ int return_code = 1; /* Default to error. */
+ uint8_t* digest = NULL;
+ uint8_t* signature = NULL;
+ RSAPublicKey* key = NULL;
+
+ if (argc!=5) {
+ fprintf(stderr, "Usage: %s <algorithm> <key file> <signature file>"
+ " <input file>\n\n", argv[0]);
+ fprintf(stderr, "where <algorithm> depends on the signature algorithm"
+ " used:\n");
+ for(i = 0; i<kNumAlgorithms; i++)
+ fprintf(stderr, "\t%d for %s\n", i, algo_strings[i]);
+ return -1;
+ }
+
+ algorithm = atoi(argv[1]);
+ if (algorithm >= kNumAlgorithms) {
+ fprintf(stderr, "Invalid Algorithm!\n");
+ return 0;
+ }
+ /* Length of the RSA Signature/RSA Key */
+ sig_len = siglen_map[algorithm];
+ if ((key = RSAPublicKeyFromFile(argv[2])) &&
+ (signature = read_signature(argv[3], sig_len)) &&
+ (digest = DigestFile(argv[4], algorithm))) {
+ if (RSAVerify(key, signature, sig_len, algorithm, digest)) {
+ return_code = 0;
+ fprintf(stderr, "Signature Verification "
+ COL_GREEN "SUCCEEDED" COL_STOP "\n");
+ } else {
+ fprintf(stderr, "Signature Verification "
+ COL_RED "FAILED" COL_STOP "\n");
+ }
+ }
+ else
+ return_code = -1;
+
+ free(key);
+ free(signature);
+ free(digest);
+
+ return return_code;
+}