diff --git a/host/Makefile b/host/Makefile index 6c320eb9f1..fd3e77e77e 100644 --- a/host/Makefile +++ b/host/Makefile @@ -21,13 +21,14 @@ INCLUDES += \ LIB_SRCS = \ ./lib/host_common.c \ ./lib/host_key.c \ + ./lib/host_misc.c \ ./lib/host_signature.c LIB_OBJS = $(LIB_SRCS:%.c=%.o) test : $(LIBNAME) $(CC) $(CFLAGS) $(INCLUDES) -o $(TESTDIR)/a.out $(TESTDIR)/main.c \ - $(LIBNAME) $(FWLIB) $(TOP)/misclibs/file_keys.o -lcrypto + $(LIBNAME) $(FWLIB) -lcrypto $(LIBNAME) : $(LIB_OBJS) $(STUB_OBJS) rm -f $@ diff --git a/host/include/host_common.h b/host/include/host_common.h index cd7c06318e..ad71ab521b 100644 --- a/host/include/host_common.h +++ b/host/include/host_common.h @@ -12,6 +12,7 @@ #include "cryptolib.h" #include "host_key.h" +#include "host_misc.h" #include "host_signature.h" #include "utility.h" #include "vboot_struct.h" @@ -46,6 +47,7 @@ VbKernelPreambleHeader* CreateKernelPreamble( uint64_t bootloader_address, uint64_t bootloader_size, const VbSignature* body_signature, + uint64_t desired_size, const VbPrivateKey* signing_key); #endif /* VBOOT_REFERENCE_HOST_COMMON_H_ */ diff --git a/host/include/host_key.h b/host/include/host_key.h index b71db8f88d..6d964a0f3e 100644 --- a/host/include/host_key.h +++ b/host/include/host_key.h @@ -48,12 +48,23 @@ VbPublicKey* PublicKeyAlloc(uint64_t key_size, uint64_t algorithm, int PublicKeyCopy(VbPublicKey* dest, const VbPublicKey* src); -/* Read a public key from a file. Caller owns the returned pointer, - * and must free it with Free(). +/* Read a public key from a .vbpubk file. Caller owns the returned + * pointer, and must free it with Free(). * * Returns NULL if error. */ -/* TODO: should really store public keys in files as VbPublicKey */ -VbPublicKey* PublicKeyRead(const char* filename, uint64_t algorithm, - uint64_t version); +VbPublicKey* PublicKeyRead(const char* filename); + + +/* Read a public key from a .keyb file. Caller owns the returned + * pointer, and must free it with Free(). + * + * Returns NULL if error. */ +VbPublicKey* PublicKeyReadKeyb(const char* filename, uint64_t algorithm, + uint64_t version); + + +/* Write a public key to a file in .vbpubk format. */ +int PublicKeyWrite(const char* filename, const VbPublicKey* key); + #endif /* VBOOT_REFERENCE_HOST_KEY_H_ */ diff --git a/host/include/host_misc.h b/host/include/host_misc.h new file mode 100644 index 0000000000..7e66c73cf8 --- /dev/null +++ b/host/include/host_misc.h @@ -0,0 +1,25 @@ +/* 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. + * + * Host-side misc functions for verified boot. + */ + +#ifndef VBOOT_REFERENCE_HOST_MISC_H_ +#define VBOOT_REFERENCE_HOST_MISC_H_ + +#include + +#include "host_misc.h" +#include "utility.h" +#include "vboot_struct.h" + + +/* Read data from [filename]. Store the size of returned data in [size]. + * + * Returns the data buffer, which the caller must Free(), or NULL if + * error. */ +uint8_t* ReadFile(const char* filename, uint64_t* size); + + +#endif /* VBOOT_REFERENCE_HOST_MISC_H_ */ diff --git a/host/lib/host_common.c b/host/lib/host_common.c index 6c4c438710..03efd2a419 100644 --- a/host/lib/host_common.c +++ b/host/lib/host_common.c @@ -7,18 +7,6 @@ /* TODO: change all 'return 0', 'return 1' into meaningful return codes */ -#if 0 -#define OPENSSL_NO_SHA -#include -#include -#include - -#include -#include -#include -#include "file_keys.h" -#endif - #include "host_common.h" #include "cryptolib.h" @@ -141,6 +129,7 @@ VbKernelPreambleHeader* CreateKernelPreamble( uint64_t bootloader_address, uint64_t bootloader_size, const VbSignature* body_signature, + uint64_t desired_size, const VbPrivateKey* signing_key) { VbKernelPreambleHeader* h; @@ -151,6 +140,10 @@ VbKernelPreambleHeader* CreateKernelPreamble( uint8_t* block_sig_dest; VbSignature *sigtmp; + /* If the block size is smaller than the desired size, pad it */ + if (block_size < desired_size) + block_size = desired_size; + /* Allocate key block */ h = (VbKernelPreambleHeader*)Malloc(block_size); if (!h) diff --git a/host/lib/host_key.c b/host/lib/host_key.c index 00770db3b6..388a2d4e83 100644 --- a/host/lib/host_key.c +++ b/host/lib/host_key.c @@ -19,7 +19,7 @@ #include "host_key.h" #include "cryptolib.h" -#include "file_keys.h" +#include "host_misc.h" #include "utility.h" #include "vboot_common.h" @@ -80,6 +80,7 @@ void PublicKeyInit(VbPublicKey* key, uint8_t* key_data, uint64_t key_size) { } +/* Allocate a new public key with space for a [key_size] byte key. */ VbPublicKey* PublicKeyAlloc(uint64_t key_size, uint64_t algorithm, uint64_t version) { VbPublicKey* key = (VbPublicKey*)Malloc(sizeof(VbPublicKey) + key_size); @@ -94,6 +95,9 @@ VbPublicKey* PublicKeyAlloc(uint64_t key_size, uint64_t algorithm, } +/* Copy a public key from [src] to [dest]. + * + * Returns zero if success, non-zero if error. */ int PublicKeyCopy(VbPublicKey* dest, const VbPublicKey* src) { if (dest->key_size < src->key_size) return 1; @@ -106,28 +110,31 @@ int PublicKeyCopy(VbPublicKey* dest, const VbPublicKey* src) { } -VbPublicKey* PublicKeyRead(const char* filename, uint64_t algorithm, - uint64_t version) { - +VbPublicKey* PublicKeyReadKeyb(const char* filename, uint64_t algorithm, + uint64_t version) { VbPublicKey* key; uint8_t* key_data; uint64_t key_size; if (algorithm >= kNumAlgorithms) { - debug("PublicKeyRead() called with invalid algorithm!\n"); + debug("PublicKeyReadKeyb() called with invalid algorithm!\n"); return NULL; } if (version > 0xFFFF) { /* Currently, TPM only supports 16-bit version */ - debug("PublicKeyRead() called with invalid version!\n"); + debug("PublicKeyReadKeyb() called with invalid version!\n"); return NULL; } - key_data = BufferFromFile(filename, &key_size); + key_data = ReadFile(filename, &key_size); if (!key_data) return NULL; - /* TODO: sanity-check key length based on algorithm */ + if (RSAProcessedKeySize(algorithm) != key_size) { + debug("PublicKeyReadKeyb() wrong key size for algorithm\n"); + Free(key_data); + return NULL; + } key = PublicKeyAlloc(key_size, algorithm, version); if (!key) { @@ -139,3 +146,78 @@ VbPublicKey* PublicKeyRead(const char* filename, uint64_t algorithm, Free(key_data); return key; } + + +VbPublicKey* PublicKeyRead(const char* filename) { + VbPublicKey* key; + uint64_t file_size; + + key = (VbPublicKey*)ReadFile(filename, &file_size); + if (!key) + return NULL; + + do { + /* Sanity-check key data */ + if (0 != VerifyPublicKeyInside(key, file_size, key)) { + debug("PublicKeyRead() not a VbPublicKey\n"); + break; + } + if (key->algorithm >= kNumAlgorithms) { + debug("PublicKeyRead() invalid algorithm\n"); + break; + } + if (key->key_version > 0xFFFF) { + debug("PublicKeyRead() invalid version\n"); + break; /* Currently, TPM only supports 16-bit version */ + } + if (RSAProcessedKeySize(key->algorithm) != key->key_size) { + debug("PublicKeyRead() wrong key size for algorithm\n"); + break; + } + + /* Success */ + return key; + + } while(0); + + /* Error */ + Free(key); + return NULL; +} + + +int PublicKeyWrite(const char* filename, const VbPublicKey* key) { + VbPublicKey* kcopy = NULL; + FILE* f = NULL; + int rv = 1; + + do { + f = fopen(filename, "wb"); + if (!f) { + debug("PublicKeyWrite() unable to open file %s\n", filename); + break; + } + + /* Copy the key, so its data is contiguous with the header */ + kcopy = PublicKeyAlloc(key->key_size, 0, 0); + if (!kcopy || 0 != PublicKeyCopy(kcopy, key)) + break; + + if (1 != fwrite(kcopy, kcopy->key_offset + kcopy->key_size, 1, f)) + break; + + /* Success */ + rv = 0; + + } while(0); + + if (kcopy) + Free(kcopy); + if (f) + fclose(f); + + if (0 != rv) + unlink(filename); /* Delete any partial file */ + + return rv; +} diff --git a/host/lib/host_misc.c b/host/lib/host_misc.c new file mode 100644 index 0000000000..f24bf40dc0 --- /dev/null +++ b/host/lib/host_misc.c @@ -0,0 +1,49 @@ +/* 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. + * + * Host functions for verified boot. + */ + +/* TODO: change all 'return 0', 'return 1' into meaningful return codes */ + +#include +#include + +#include "host_common.h" + +#include "cryptolib.h" +#include "utility.h" +#include "vboot_common.h" + + +uint8_t* ReadFile(const char* filename, uint64_t* size) { + FILE* f; + uint8_t* buf; + + f = fopen(filename, "rb"); + if (!f) { + debug("Unable to open file %s\n", filename); + return NULL; + } + + fseek(f, 0, SEEK_END); + *size = ftell(f); + rewind(f); + + buf = Malloc(*size); + if (!buf) { + fclose(f); + return NULL; + } + + if(1 != fread(buf, *size, 1, f)) { + debug("Unable to read from file %s\n", filename); + fclose(f); + Free(buf); + return NULL; + } + + fclose(f); + return buf; +} diff --git a/host/linktest/main.c b/host/linktest/main.c index 91fc5aff66..767a902575 100644 --- a/host/linktest/main.c +++ b/host/linktest/main.c @@ -10,7 +10,12 @@ int main(void) PublicKeyInit(0, 0, 0); PublicKeyAlloc(0, 0, 0); PublicKeyCopy(0, 0); - PublicKeyRead(0, 0, 0); + PublicKeyRead(0); + PublicKeyReadKeyb(0, 0, 0); + PublicKeyWrite(0, 0); + + /* host_misc.h */ + ReadFile(0, 0); /* host_signature.h */ SignatureInit(0, 0, 0, 0); @@ -22,7 +27,7 @@ int main(void) /* host_common.h */ CreateKeyBlock(0, 0, 0); CreateFirmwarePreamble(0, 0, 0, 0); - CreateKernelPreamble(0, 0, 0, 0, 0, 0); + CreateKernelPreamble(0, 0, 0, 0, 0, 0, 0); return 0; } diff --git a/tests/vboot_common2_tests.c b/tests/vboot_common2_tests.c index 361de83e93..1b7d4a1936 100644 --- a/tests/vboot_common2_tests.c +++ b/tests/vboot_common2_tests.c @@ -91,7 +91,7 @@ static void VerifyKernelPreambleTest(const VbPublicKey* public_key, rsa = PublicKeyToRSA(public_key); hdr = CreateKernelPreamble(0x1234, 0x100000, 0x300000, 0x4000, body_sig, - private_key); + 0, private_key); TEST_NEQ(hdr && rsa, 0, "VerifyKernelPreamble2() prerequisites"); if (!hdr) return; @@ -100,8 +100,10 @@ static void VerifyKernelPreambleTest(const VbPublicKey* public_key, TEST_EQ(VerifyKernelPreamble2(hdr, hsize, rsa), 0, "VerifyKernelPreamble2() ok using key"); - TEST_NEQ(VerifyKernelPreamble2(hdr, hsize-1, rsa), 0, - "VerifyKernelPreamble2() size"); + TEST_NEQ(VerifyKernelPreamble2(hdr, hsize - 1, rsa), 0, + "VerifyKernelPreamble2() size--"); + TEST_EQ(VerifyKernelPreamble2(hdr, hsize + 1, rsa), 0, + "VerifyKernelPreamble2() size++"); /* Care about major version but not minor */ Memcpy(h, hdr, hsize); @@ -191,7 +193,7 @@ int main(int argc, char* argv[]) { return 1; } - public_key = PublicKeyRead(argv[3], key_algorithm, 1); + public_key = PublicKeyReadKeyb(argv[3], key_algorithm, 1); if (!public_key) { fprintf(stderr, "Error reading public_key"); return 1; diff --git a/tests/vboot_common3_tests.c b/tests/vboot_common3_tests.c index a4e69fd19a..1cfdd5190d 100644 --- a/tests/vboot_common3_tests.c +++ b/tests/vboot_common3_tests.c @@ -46,7 +46,8 @@ static void VerifyKeyBlockTest(const VbPublicKey* public_key, TEST_EQ(VerifyKeyBlock(hdr, hsize, public_key), 0, "VerifyKeyBlock() ok using key"); - TEST_NEQ(VerifyKeyBlock(hdr, hsize-1, NULL), 0, "VerifyKeyBlock() size"); + TEST_NEQ(VerifyKeyBlock(hdr, hsize - 1, NULL), 0, "VerifyKeyBlock() size--"); + TEST_EQ(VerifyKeyBlock(hdr, hsize + 1, NULL), 0, "VerifyKeyBlock() size++"); Memcpy(h, hdr, hsize); h->magic[0] &= 0x12; @@ -164,8 +165,10 @@ static void VerifyFirmwarePreambleTest(const VbPublicKey* public_key, TEST_EQ(VerifyFirmwarePreamble2(hdr, hsize, rsa), 0, "VerifyFirmwarePreamble2() ok using key"); - TEST_NEQ(VerifyFirmwarePreamble2(hdr, hsize-1, rsa), 0, - "VerifyFirmwarePreamble2() size"); + TEST_NEQ(VerifyFirmwarePreamble2(hdr, hsize - 1, rsa), 0, + "VerifyFirmwarePreamble2() size--"); + TEST_EQ(VerifyFirmwarePreamble2(hdr, hsize + 1, rsa), 0, + "VerifyFirmwarePreamble2() size++"); /* Care about major version but not minor */ Memcpy(h, hdr, hsize); @@ -268,13 +271,13 @@ int main(int argc, char* argv[]) { return 1; } - signing_public_key = PublicKeyRead(argv[4], signing_key_algorithm, 1); + signing_public_key = PublicKeyReadKeyb(argv[4], signing_key_algorithm, 1); if (!signing_public_key) { fprintf(stderr, "Error reading signing_public_key"); return 1; } - data_public_key = PublicKeyRead(argv[6], data_key_algorithm, 1); + data_public_key = PublicKeyReadKeyb(argv[6], data_key_algorithm, 1); if (!data_public_key) { fprintf(stderr, "Error reading data_public_key"); return 1; diff --git a/utility/Makefile b/utility/Makefile index 1f50aebd83..e6b1bc0e69 100644 --- a/utility/Makefile +++ b/utility/Makefile @@ -20,8 +20,8 @@ LIBS = $(TOP)/misclibs/file_keys.o \ $(TOP)/misclibs/signature_digest.o \ $(TOP)/vfirmware/firmware_image.o \ $(TOP)/vkernel/kernel_image.o \ - $(FWLIB) \ - $(HOSTLIB) + $(HOSTLIB) \ + $(FWLIB) SUBDIRS = cgpt DESTDIR ?= /opt/bin @@ -32,6 +32,7 @@ TARGET_BINS = dumpRSAPublicKey \ kernel_utility \ load_kernel_test \ signature_digest_utility \ + vbutil_key \ verify_data all: $(TARGET_BINS) subdirs @@ -66,6 +67,9 @@ load_kernel_test: load_kernel_test.c $(LIBS) signature_digest_utility: signature_digest_utility.c $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto +vbutil_key: vbutil_key.c $(LIBS) + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto + verify_data: verify_data.c $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto diff --git a/utility/load_kernel_test.c b/utility/load_kernel_test.c index 7beb414ef7..6d3d81c8ef 100644 --- a/utility/load_kernel_test.c +++ b/utility/load_kernel_test.c @@ -15,6 +15,7 @@ #include "load_kernel_fw.h" #include "boot_device.h" +#include "host_common.h" #include "rollback_index.h" #include "utility.h" @@ -95,25 +96,12 @@ int main(int argc, char* argv[]) { /* Read header signing key blob */ { - FILE* f; - int key_size; - printf("Reading key from: %s\n", keyfile_name); - f = fopen(keyfile_name, "rb"); - if (!f) { - fprintf(stderr, "Unable to open key file %s\n", keyfile_name); + uint64_t key_size; + lkp.header_sign_key_blob = ReadFile(keyfile_name, &key_size); + if (!lkp.header_sign_key_blob) + fprintf(stderr, "Unable to read key file %s\n", keyfile_name); return 1; } - fseek(f, 0, SEEK_END); - key_size = ftell(f); - rewind(f); - lkp.header_sign_key_blob = Malloc(key_size); - printf("Reading %d bytes of key\n", key_size); - if (fread(lkp.header_sign_key_blob, key_size, 1, f) != 1) { - fprintf(stderr, "Unable to read key data\n"); - return 1; - } - fclose(f); - } /* Get image size */ printf("Reading from image: %s\n", image_name); diff --git a/utility/vbutil_key.c b/utility/vbutil_key.c new file mode 100644 index 0000000000..d770026ecd --- /dev/null +++ b/utility/vbutil_key.c @@ -0,0 +1,175 @@ +#include +#include /* For PRIu64 */ +#include +#include + +#include "cryptolib.h" +#include "host_common.h" +#include "vboot_common.h" + + +/* Command line options */ +enum { + OPT_IN = 1000, + OPT_OUT, + OPT_KEY_VERSION, + OPT_ALGORITHM, + OPT_MODE_PACK, + OPT_MODE_UNPACK, +}; + +static struct option long_opts[] = { + {"in", 1, 0, OPT_IN }, + {"out", 1, 0, OPT_OUT }, + {"version", 1, 0, OPT_KEY_VERSION }, + {"algorithm", 1, 0, OPT_ALGORITHM }, + {"pack", 0, 0, OPT_MODE_PACK }, + {"unpack", 0, 0, OPT_MODE_UNPACK }, + {NULL, 0, 0, 0} +}; + + +/* Print help and return error */ +static int PrintHelp(void) { + int i; + + puts("vbutil_key - Verified boot key utility\n" + "\n" + "Usage: vbutil_key <--pack|--unpack> [OPTIONS]\n" + "\n" + "For '--pack', required OPTIONS are:\n" + " --in Input key in .keyb format\n" + " --out Output file for .vbpubk format\n" + " --version Key version number\n" + " --algorithm Signing algorithm for key, one of:"); + + for (i = 0; i < kNumAlgorithms; i++) + printf(" %d (%s)\n", i, algo_strings[i]); + + puts("\n" + "For '--unpack', required OPTIONS are:\n" + " --in Input key in .vbpubk format\n" + "Optional OPTIONS are:\n" + " --out Output file for .keyb format\n" + ""); + return 1; +} + + +/* Pack a .keyb file into a .vbpubk */ +static int Pack(const char *infile, const char *outfile, uint64_t algorithm, + uint64_t version) { + VbPublicKey* key; + + if (!infile || !outfile) { + fprintf(stderr, "vbutil_key: Must specify --in and --out\n"); + return 1; + } + + key = PublicKeyReadKeyb(infile, algorithm, version); + if (!key) { + fprintf(stderr, "vbutil_key: Error reading key.\n"); + return 1; + } + + if (0 != PublicKeyWrite(outfile, key)) { + fprintf(stderr, "vbutil_key: Error writing key.\n"); + return 1; + } + + Free(key); + return 0; +} + + +/* Unpack a .vbpubk */ +static int Unpack(const char *infile, const char *outfile) { + VbPublicKey* key; + + if (!infile) { + fprintf(stderr, "vbutil_key: Must specify --in\n"); + return 1; + } + + key = PublicKeyRead(infile); + if (!key) { + fprintf(stderr, "vbutil_key: Error reading key.\n"); + return 1; + } + + printf("Key file: %s\n", infile); + printf("Algorithm: %" PRIu64 " %s\n", key->algorithm, + (key->algorithm < kNumAlgorithms ? + algo_strings[key->algorithm] : "(invalid)")); + printf("Version: %" PRIu64 "\n", key->key_version); + + /* TODO: write key data, if any */ + + Free(key); + return 0; +} + + +int main(int argc, char* argv[]) { + + char *infile = NULL; + char *outfile = NULL; + int mode = 0; + int parse_error = 0; + uint64_t version = 1; + uint64_t algorithm = kNumAlgorithms; + char* e; + int i; + + while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { + switch (i) { + case '?': + /* Unhandled option */ + printf("Unknown option\n"); + parse_error = 1; + break; + + case OPT_IN: + infile = optarg; + break; + + case OPT_OUT: + outfile = optarg; + break; + + case OPT_KEY_VERSION: + version = strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + printf("Invalid --version\n"); + parse_error = 1; + } + break; + + case OPT_ALGORITHM: + algorithm = strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + printf("Invalid --algorithm\n"); + parse_error = 1; + } + break; + + case OPT_MODE_PACK: + case OPT_MODE_UNPACK: + mode = i; + break; + } + } + + if (parse_error) + return PrintHelp(); + + switch(mode) { + case OPT_MODE_PACK: + return Pack(infile, outfile, algorithm, version); + case OPT_MODE_UNPACK: + return Unpack(infile, outfile); + default: + printf("Must specify a mode.\n"); + return PrintHelp(); + } +} diff --git a/vboot_firmware/lib/include/vboot_struct.h b/vboot_firmware/lib/include/vboot_struct.h index 9957e462b9..c77fa4bc74 100644 --- a/vboot_firmware/lib/include/vboot_struct.h +++ b/vboot_firmware/lib/include/vboot_struct.h @@ -26,7 +26,7 @@ typedef struct VbPublicKey { typedef struct VbSignature { uint64_t sig_offset; /* Offset of signature data from start of this * struct */ - uint64_t sig_size; /* Size of signature data from start of this struct */ + uint64_t sig_size; /* Size of signature data in bytes */ uint64_t data_size; /* Size of the data block which was signed in bytes */ } VbSignature;