summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2012-05-03 08:40:44 -0700
committerGerrit <chrome-bot@google.com>2012-05-04 12:16:45 -0700
commit2448d3b3bc8e80232e7943c16b41eaab19faa1a2 (patch)
tree602ed9451ec91f58fd60ab055ab9f531f50a921e
parentf47291926afce3235421f73811a04324195f3e13 (diff)
downloadvboot-2448d3b3bc8e80232e7943c16b41eaab19faa1a2.tar.gz
Create vbutil_ec tool for signing EC firmware.
This just adds the vbutil_ec tool (and a simple test of the library functions related to it). BUG=chrome-os-partner:7459, chromium-os:27142 TEST=manual make make runtests Change-Id: I2a2c4e7cfb8ac6ce2229c5de4252a5cc89321fa5 Reviewed-on: https://gerrit.chromium.org/gerrit/21868 Commit-Ready: Bill Richardson <wfrichar@chromium.org> Tested-by: Bill Richardson <wfrichar@chromium.org> Reviewed-by: Stefan Reinauer <reinauer@google.com> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--firmware/include/vboot_struct.h39
-rw-r--r--firmware/lib/include/vboot_common.h14
-rw-r--r--firmware/lib/vboot_common.c78
-rw-r--r--host/include/host_common.h12
-rw-r--r--host/include/host_signature.h7
-rw-r--r--host/lib/host_common.c48
-rw-r--r--host/lib/host_signature.c26
-rwxr-xr-xscripts/keygeneration/common.sh11
-rwxr-xr-xscripts/keygeneration/create_new_keys.sh7
-rw-r--r--tests/Makefile2
-rw-r--r--tests/devkeys/ec.keyblockbin0 -> 1720 bytes
-rw-r--r--tests/devkeys/ec_data_key.vbprivkbin0 -> 2355 bytes
-rw-r--r--tests/devkeys/ec_data_key.vbpubkbin0 -> 1064 bytes
-rw-r--r--tests/devkeys/ec_root_key.vbprivkbin0 -> 2356 bytes
-rw-r--r--tests/devkeys/ec_root_key.vbpubkbin0 -> 1064 bytes
-rwxr-xr-xtests/run_vboot_common_tests.sh2
-rwxr-xr-xtests/run_vboot_ec_tests.sh19
-rw-r--r--tests/vboot_ec_tests.c160
-rw-r--r--utility/Makefile4
-rw-r--r--utility/vbutil_ec.c327
20 files changed, 748 insertions, 8 deletions
diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h
index 48a4f6b1..0f6b98d9 100644
--- a/firmware/include/vboot_struct.h
+++ b/firmware/include/vboot_struct.h
@@ -76,6 +76,42 @@ typedef struct VbKeyBlockHeader {
#define EXPECTED_VBKEYBLOCKHEADER_SIZE 112
+/****************************************************************************/
+
+#define EC_PREAMBLE_HEADER_VERSION_MAJOR 1
+#define EC_PREAMBLE_HEADER_VERSION_MINOR 0
+
+/* Flags for VbECPreambleHeader.flags */
+
+/* Use the normal boot path from the read-only firmware, instead
+ * of verifying the body signature. */
+#define VB_EC_PREAMBLE_USE_RO_NORMAL 0x00000001
+
+/* Premable block for EC rewritable firmware, version 1.0 */
+typedef struct VbECPreambleHeader {
+ uint64_t preamble_size; /* Size of this preamble, including keys,
+ * signatures, and padding, in bytes */
+ VbSignature preamble_signature; /* Signature for this preamble
+ * (header + * body signature) */
+ uint32_t header_version_major; /* Version of this header format */
+ uint32_t header_version_minor; /* Version of this header format */
+
+ uint64_t firmware_version; /* Firmware version */
+ VbSignature body_digest; /* Digest for the firmware body */
+
+ uint32_t flags; /* Flags; see VB_EC_PREAMBLE_* */
+ char name[128]; /* Human-readable ASCII, null-padded */
+} __attribute__((packed)) VbECPreambleHeader;
+
+#define EXPECTED_VB_EC_PREAMBLE_HEADER1_0_SIZE 76
+
+/* The firmware preamble header should be followed by:
+ * 2) The signature data for the firmware body, pointed to by
+ * body_signature.sig_offset.
+ * 3) The signature data for (header + body signature data), pointed
+ * to by preamble_signature.sig_offset. */
+
+/****************************************************************************/
#define FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR 2
#define FIRMWARE_PREAMBLE_HEADER_VERSION_MINOR 1
@@ -134,6 +170,7 @@ typedef struct VbFirmwarePreambleHeader {
* 3) The signature data for (header + kernel_subkey data + body signature
* data), pointed to by preamble_signature.sig_offset. */
+/****************************************************************************/
#define KERNEL_PREAMBLE_HEADER_VERSION_MAJOR 2
#define KERNEL_PREAMBLE_HEADER_VERSION_MINOR 0
@@ -162,6 +199,8 @@ typedef struct VbKernelPreambleHeader {
#define EXPECTED_VBKERNELPREAMBLEHEADER_SIZE 96
+/****************************************************************************/
+
/* Constants and sub-structures for VbSharedDataHeader */
/* Magic number for recognizing VbSharedDataHeader ("VbSD") */
diff --git a/firmware/lib/include/vboot_common.h b/firmware/lib/include/vboot_common.h
index 32031068..7032def4 100644
--- a/firmware/lib/include/vboot_common.h
+++ b/firmware/lib/include/vboot_common.h
@@ -81,10 +81,14 @@ int VerifyData(const uint8_t* data, uint64_t size, const VbSignature* sig,
/* Verifies a secure hash digest from DigestBuf() or DigestFinal(),
- * using [key]. */
+ * using [key]. Returns 0 on success. */
int VerifyDigest(const uint8_t* digest, const VbSignature *sig,
const RSAPublicKey* key);
+/* Uses [key] algorithm to hash [data], then compares that to the expected
+ * [hash]. Returns 0 if they're equal, non-zero if error. */
+int EqualData(const uint8_t* data, uint64_t size, const VbSignature *hash,
+ const RSAPublicKey* key);
/* Checks the sanity of a key block of size [size] bytes, using public
* key [key]. If hash_only is non-zero, uses only the block checksum
@@ -94,6 +98,14 @@ int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size,
const VbPublicKey *key, int hash_only);
+/* Checks the sanity of an EC preamble of size [size] bytes,
+ * using public key [key].
+ *
+ * Returns VBOOT_SUCCESS if successful. */
+int VerifyECPreamble(const VbECPreambleHeader* preamble,
+ uint64_t size, const RSAPublicKey* key);
+
+
/* Checks the sanity of a firmware preamble of size [size] bytes,
* using public key [key].
*
diff --git a/firmware/lib/vboot_common.c b/firmware/lib/vboot_common.c
index 5622d13b..e074d617 100644
--- a/firmware/lib/vboot_common.c
+++ b/firmware/lib/vboot_common.c
@@ -173,6 +173,28 @@ int VerifyDigest(const uint8_t* digest, const VbSignature *sig,
}
+int EqualData(const uint8_t* data, uint64_t size, const VbSignature *hash,
+ const RSAPublicKey* key) {
+ uint8_t* digest = NULL;
+ int rv;
+
+ if (hash->sig_size != hash_size_map[key->algorithm]) {
+ VBDEBUG(("Wrong hash size for algorithm.\n"));
+ return 1;
+ }
+ if (hash->data_size > size) {
+ VBDEBUG(("Data buffer smaller than length of signed data.\n"));
+ return 1;
+ }
+
+ digest = DigestBuf(data, hash->data_size, key->algorithm);
+
+ rv = SafeMemcmp(digest, GetSignatureDataC(hash), hash->sig_size);
+ VbExFree(digest);
+ return rv;
+}
+
+
int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size,
const VbPublicKey *key, int hash_only) {
@@ -291,6 +313,62 @@ int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size,
}
+int VerifyECPreamble(const VbECPreambleHeader* preamble,
+ uint64_t size, const RSAPublicKey* key) {
+
+ const VbSignature* sig = &preamble->preamble_signature;
+
+ /* Sanity checks before attempting signature of data */
+ if(size < EXPECTED_VB_EC_PREAMBLE_HEADER1_0_SIZE) {
+ VBDEBUG(("Not enough data for EC preamble header.\n"));
+ return VBOOT_PREAMBLE_INVALID;
+ }
+ if (preamble->header_version_major !=
+ EC_PREAMBLE_HEADER_VERSION_MAJOR) {
+ VBDEBUG(("Incompatible EC preamble header version (%d, not %d).\n",
+ preamble->header_version_major,
+ EC_PREAMBLE_HEADER_VERSION_MAJOR));
+ return VBOOT_PREAMBLE_INVALID;
+ }
+ if (size < preamble->preamble_size) {
+ VBDEBUG(("Not enough data for EC preamble.\n"));
+ return VBOOT_PREAMBLE_INVALID;
+ }
+
+ /* Check signature */
+ if (VerifySignatureInside(preamble, preamble->preamble_size, sig)) {
+ VBDEBUG(("EC preamble signature off end of preamble\n"));
+ return VBOOT_PREAMBLE_INVALID;
+ }
+
+ /* Make sure advertised signature data sizes are sane. */
+ if (preamble->preamble_size < sig->data_size) {
+ VBDEBUG(("EC signature calculated past end of the block\n"));
+ return VBOOT_PREAMBLE_INVALID;
+ }
+
+ if (VerifyData((const uint8_t*)preamble, size, sig, key)) {
+ VBDEBUG(("EC preamble signature validation failed\n"));
+ return VBOOT_PREAMBLE_SIGNATURE;
+ }
+
+ /* Verify we signed enough data */
+ if (sig->data_size < sizeof(VbFirmwarePreambleHeader)) {
+ VBDEBUG(("Didn't sign enough data\n"));
+ return VBOOT_PREAMBLE_INVALID;
+ }
+
+ /* Verify body digest is inside the signed data */
+ if (VerifySignatureInside(preamble, sig->data_size,
+ &preamble->body_digest)) {
+ VBDEBUG(("EC body digest off end of preamble\n"));
+ return VBOOT_PREAMBLE_INVALID;
+ }
+
+ /* Success */
+ return VBOOT_SUCCESS;
+}
+
int VerifyFirmwarePreamble(const VbFirmwarePreambleHeader* preamble,
uint64_t size, const RSAPublicKey* key) {
diff --git a/host/include/host_common.h b/host/include/host_common.h
index 5d206301..6fa8b3e3 100644
--- a/host/include/host_common.h
+++ b/host/include/host_common.h
@@ -22,6 +22,18 @@
#include "vboot_struct.h"
+/* Creates an EC preamble, signed with [signing_key].
+ * Caller owns the returned pointer, and must free it with Free().
+ *
+ * Returns NULL if error. */
+VbECPreambleHeader* CreateECPreamble(
+ uint64_t firmware_version,
+ const VbSignature* body_signature,
+ const VbPrivateKey* signing_key,
+ uint32_t flags,
+ const char* name);
+
+
/* Creates a firmware preamble, signed with [signing_key].
* Caller owns the returned pointer, and must free it with Free().
*
diff --git a/host/include/host_signature.h b/host/include/host_signature.h
index f08547c8..fb03c6c5 100644
--- a/host/include/host_signature.h
+++ b/host/include/host_signature.h
@@ -36,6 +36,13 @@ int SignatureCopy(VbSignature* dest, const VbSignature* src);
VbSignature* CalculateChecksum(const uint8_t* data, uint64_t size);
+/* Calculates a hash of the data using the algorithm from the specified key.
+ * Caller owns the returned pointer, and must free it with Free().
+ *
+ * Returns NULL on error. */
+VbSignature* CalculateHash(const uint8_t* data, uint64_t size,
+ const VbPrivateKey* key);
+
/* Calculates a signature for the data using the specified key.
* Caller owns the returned pointer, and must free it with Free().
*
diff --git a/host/lib/host_common.c b/host/lib/host_common.c
index cb513922..3aceddd6 100644
--- a/host/lib/host_common.c
+++ b/host/lib/host_common.c
@@ -6,6 +6,7 @@
*/
/* TODO: change all 'return 0', 'return 1' into meaningful return codes */
+#include <string.h>
#include "host_common.h"
@@ -13,6 +14,53 @@
#include "utility.h"
#include "vboot_common.h"
+VbECPreambleHeader* CreateECPreamble(
+ uint64_t firmware_version,
+ const VbSignature* body_digest,
+ const VbPrivateKey* signing_key,
+ uint32_t flags,
+ const char* name) {
+
+ VbECPreambleHeader* h;
+ uint64_t signed_size = (sizeof(VbECPreambleHeader) + body_digest->sig_size);
+ uint64_t block_size = signed_size + siglen_map[signing_key->algorithm];
+ uint8_t* body_digest_dest;
+ uint8_t* block_sig_dest;
+ VbSignature *sigtmp;
+
+ /* Allocate key block */
+ h = (VbECPreambleHeader*)malloc(block_size);
+ if (!h)
+ return NULL;
+ Memset(h, 0, block_size);
+ body_digest_dest = (uint8_t*)(h + 1);
+ block_sig_dest = body_digest_dest + body_digest->sig_size;
+
+ h->header_version_major = EC_PREAMBLE_HEADER_VERSION_MAJOR;
+ h->header_version_minor = EC_PREAMBLE_HEADER_VERSION_MINOR;
+ h->preamble_size = block_size;
+ h->firmware_version = firmware_version;
+ h->flags = flags;
+ if (name)
+ strncpy(h->name, name, sizeof(h->name));
+
+ /* Copy body hash */
+ SignatureInit(&h->body_digest, body_digest_dest,
+ body_digest->sig_size, 0);
+ SignatureCopy(&h->body_digest, body_digest);
+
+ /* Set up signature struct so we can calculate the signature */
+ SignatureInit(&h->preamble_signature, block_sig_dest,
+ siglen_map[signing_key->algorithm], signed_size);
+
+ /* Calculate signature */
+ sigtmp = CalculateSignature((uint8_t*)h, signed_size, signing_key);
+ SignatureCopy(&h->preamble_signature, sigtmp);
+ free(sigtmp);
+
+ /* Return the header */
+ return h;
+}
VbFirmwarePreambleHeader* CreateFirmwarePreamble(
uint64_t firmware_version,
diff --git a/host/lib/host_signature.c b/host/lib/host_signature.c
index 4dbac49a..0ebbca68 100644
--- a/host/lib/host_signature.c
+++ b/host/lib/host_signature.c
@@ -78,6 +78,32 @@ VbSignature* CalculateChecksum(const uint8_t* data, uint64_t size) {
return sig;
}
+VbSignature* CalculateHash(const uint8_t* data, uint64_t size,
+ const VbPrivateKey* key) {
+ uint8_t* digest = NULL;
+ int digest_size = hash_size_map[key->algorithm];
+ VbSignature* sig = NULL;
+
+ /* Calculate the digest */
+ digest = DigestBuf(data, size, key->algorithm);
+ if (!digest)
+ return NULL;
+
+ /* Allocate output signature */
+ sig = SignatureAlloc(digest_size, size);
+ if (!sig) {
+ free(digest);
+ return NULL;
+ }
+
+ /* The digest itself is the signature data */
+ Memcpy(GetSignatureData(sig), digest, digest_size);
+ free(digest);
+
+ /* Return the signature */
+ return sig;
+}
+
VbSignature* CalculateSignature(const uint8_t* data, uint64_t size,
const VbPrivateKey* key) {
diff --git a/scripts/keygeneration/common.sh b/scripts/keygeneration/common.sh
index 1d08fdbe..b6e20c61 100755
--- a/scripts/keygeneration/common.sh
+++ b/scripts/keygeneration/common.sh
@@ -23,7 +23,10 @@ function alg_to_keylen {
echo $(( 1 << (10 + ($1 / 3)) ))
}
-# Default alrogithms.
+# Default algorithms.
+EC_ROOT_KEY_ALGOID=7
+EC_DATAKEY_ALGOID=7
+
ROOT_KEY_ALGOID=11
RECOVERY_KEY_ALGOID=11
@@ -37,13 +40,13 @@ KERNEL_DATAKEY_ALGOID=4
# Keyblock modes determine which boot modes a signing key is valid for use
# in verification.
-FIRMWARE_KEYBLOCK_MODE=7
+EC_KEYBLOCK_MODE=7 # Only allow RW EC firmware in non-recovery.
+FIRMWARE_KEYBLOCK_MODE=7 # Only allow RW firmware in non-recovery.
DEV_FIRMWARE_KEYBLOCK_MODE=6 # Only allow in dev mode.
-RECOVERY_KERNEL_KEYBLOCK_MODE=11
+RECOVERY_KERNEL_KEYBLOCK_MODE=11 # Only in recovery mode.
KERNEL_KEYBLOCK_MODE=7 # Only allow in non-recovery.
INSTALLER_KERNEL_KEYBLOCK_MODE=10 # Only allow in Dev + Recovery.
-
# Emit .vbpubk and .vbprivk using given basename and algorithm
# NOTE: This function also appears in ../../utility/dev_make_keypair. Making
# the two implementations the same would require some common.sh, which is more
diff --git a/scripts/keygeneration/create_new_keys.sh b/scripts/keygeneration/create_new_keys.sh
index 054a3edd..722d69d8 100755
--- a/scripts/keygeneration/create_new_keys.sh
+++ b/scripts/keygeneration/create_new_keys.sh
@@ -32,6 +32,7 @@ if [ ! -e "${VERSION_FILE}" ]; then
fi
# Get the key versions for normal keypairs
+ECKEY_VERSION=$(get_version "ec_key_version")
FKEY_VERSION=$(get_version "firmware_key_version")
# Firmware version is the kernel subkey version.
KSUBKEY_VERSION=$(get_version "firmware_version")
@@ -39,6 +40,8 @@ KSUBKEY_VERSION=$(get_version "firmware_version")
KDATAKEY_VERSION=$(get_version "kernel_key_version")
# Create the normal keypairs
+make_pair ec_root_key $EC_ROOT_KEY_ALGOID
+make_pair ec_data_key $EC_DATAKEY_ALGOID $ECKEY_VERSION
make_pair root_key $ROOT_KEY_ALGOID
make_pair firmware_data_key $FIRMWARE_DATAKEY_ALGOID $FKEY_VERSION
if [ -n "$DEV_KEYBLOCK_FLAG" ]; then
@@ -55,14 +58,14 @@ make_pair installer_kernel_data_key $INSTALLER_KERNEL_ALGOID
# Create the firmware keyblock for use only in Normal mode. This is redundant,
# since it's never even checked during Recovery mode.
make_keyblock firmware $FIRMWARE_KEYBLOCK_MODE firmware_data_key root_key
-
+# Ditto EC keyblock
+make_keyblock ec $EC_KEYBLOCK_MODE ec_data_key ec_root_key
if [ -n "$DEV_KEYBLOCK_FLAG" ]; then
# Create the dev firmware keyblock for use only in Developer mode.
make_keyblock dev_firmware $DEV_FIRMWARE_KEYBLOCK_MODE dev_firmware_data_key root_key
fi
-
# Create the recovery kernel keyblock for use only in Recovery mode.
make_keyblock recovery_kernel $RECOVERY_KERNEL_KEYBLOCK_MODE recovery_kernel_data_key recovery_key
diff --git a/tests/Makefile b/tests/Makefile
index f7f0cba6..9df9a696 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -40,6 +40,7 @@ TEST_NAMES = cgptlib_test \
vboot_common_tests \
vboot_common2_tests \
vboot_common3_tests \
+ vboot_ec_tests \
vboot_firmware_tests \
vboot_nvstorage_test \
vboot_api_devmode_tests \
@@ -150,6 +151,7 @@ runcryptotests:
${BUILD_ROOT}/rsa_utility_tests
${BUILD_ROOT}/sha_tests
./run_vboot_common_tests.sh
+ ./run_vboot_ec_tests.sh
# Run other misc tests
runmisctests:
diff --git a/tests/devkeys/ec.keyblock b/tests/devkeys/ec.keyblock
new file mode 100644
index 00000000..6b088f32
--- /dev/null
+++ b/tests/devkeys/ec.keyblock
Binary files differ
diff --git a/tests/devkeys/ec_data_key.vbprivk b/tests/devkeys/ec_data_key.vbprivk
new file mode 100644
index 00000000..9f194ce9
--- /dev/null
+++ b/tests/devkeys/ec_data_key.vbprivk
Binary files differ
diff --git a/tests/devkeys/ec_data_key.vbpubk b/tests/devkeys/ec_data_key.vbpubk
new file mode 100644
index 00000000..5804dfd6
--- /dev/null
+++ b/tests/devkeys/ec_data_key.vbpubk
Binary files differ
diff --git a/tests/devkeys/ec_root_key.vbprivk b/tests/devkeys/ec_root_key.vbprivk
new file mode 100644
index 00000000..49144720
--- /dev/null
+++ b/tests/devkeys/ec_root_key.vbprivk
Binary files differ
diff --git a/tests/devkeys/ec_root_key.vbpubk b/tests/devkeys/ec_root_key.vbpubk
new file mode 100644
index 00000000..76c26f94
--- /dev/null
+++ b/tests/devkeys/ec_root_key.vbpubk
Binary files differ
diff --git a/tests/run_vboot_common_tests.sh b/tests/run_vboot_common_tests.sh
index 6295f404..9d4373f7 100755
--- a/tests/run_vboot_common_tests.sh
+++ b/tests/run_vboot_common_tests.sh
@@ -4,7 +4,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# Run verified boot firmware and kernel verification tests.
+# Run verified boot firmware and kernel verification tests.
# Load common constants and variables.
. "$(dirname "$0")/common.sh"
diff --git a/tests/run_vboot_ec_tests.sh b/tests/run_vboot_ec_tests.sh
new file mode 100755
index 00000000..b5b0e7b1
--- /dev/null
+++ b/tests/run_vboot_ec_tests.sh
@@ -0,0 +1,19 @@
+#!/bin/bash -eu
+
+# 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.
+
+# Run verified boot firmware and kernel verification tests.
+
+# Load common constants and variables.
+. "$(dirname "$0")/common.sh"
+
+check_test_keys
+
+for priv in ${TESTKEY_DIR}/*.vbprivk; do
+ root=$(basename ${i%.vbprivk})
+ pub="${priv%.vbprivk}.vbpubk"
+ echo "Trying $root ..."
+ ${TEST_DIR}/vboot_ec_tests "$priv" "$pub"
+done
diff --git a/tests/vboot_ec_tests.c b/tests/vboot_ec_tests.c
new file mode 100644
index 00000000..831565fc
--- /dev/null
+++ b/tests/vboot_ec_tests.c
@@ -0,0 +1,160 @@
+/* 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.
+ *
+ * Tests for EC firmware vboot stuff.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cryptolib.h"
+#include "file_keys.h"
+#include "host_common.h"
+#include "test_common.h"
+#include "vboot_common.h"
+
+static void ReSignECPreamble(VbECPreambleHeader* h,
+ const VbPrivateKey* key) {
+ VbSignature *sig = CalculateSignature((const uint8_t*)h,
+ h->preamble_signature.data_size, key);
+
+ SignatureCopy(&h->preamble_signature, sig);
+ free(sig);
+}
+
+
+static void VerifyECPreambleTest(const VbPublicKey* public_key,
+ const VbPrivateKey* private_key) {
+ VbECPreambleHeader* hdr;
+ VbECPreambleHeader* h;
+ RSAPublicKey* rsa;
+ unsigned hsize;
+
+ /* Create a dummy signature */
+ VbSignature* body_sig = SignatureAlloc(56, 78);
+
+ rsa = PublicKeyToRSA(public_key);
+ hdr = CreateECPreamble(0x1234, body_sig, private_key,
+ 0x5678, "Foo bar");
+ TEST_NEQ(hdr && rsa, 0, "VerifyECPreamble() prerequisites");
+ if (!hdr)
+ return;
+
+ hsize = (unsigned) hdr->preamble_size;
+ h = (VbECPreambleHeader*)malloc(hsize + 16384);
+
+ TEST_EQ(VerifyECPreamble(hdr, hsize, rsa), 0,
+ "VerifyECPreamble() ok using key");
+ TEST_NEQ(VerifyECPreamble(hdr, hsize - 1, rsa), 0,
+ "VerifyECPreamble() size--");
+ TEST_EQ(VerifyECPreamble(hdr, hsize + 1, rsa), 0,
+ "VerifyECPreamble() size++");
+
+ TEST_EQ(hdr->firmware_version, 0x1234,
+ "VerifyECPreamble() firmware version");
+ TEST_EQ(hdr->flags, 0x5678,
+ "VerifyECPreamble() flags");
+ TEST_EQ(strncmp(hdr->name, "Foo bar", sizeof(hdr->name)), 0,
+ "VerifyECPreamble() name");
+
+ /* Care about major version but not minor */
+ Memcpy(h, hdr, hsize);
+ h->header_version_major++;
+ ReSignECPreamble(h, private_key);
+ TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() major++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_major--;
+ ReSignECPreamble(h, private_key);
+ TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() major--");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor++;
+ ReSignECPreamble(h, private_key);
+ TEST_EQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() minor++");
+
+ Memcpy(h, hdr, hsize);
+ h->header_version_minor--;
+ ReSignECPreamble(h, private_key);
+ TEST_EQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() minor--");
+
+ /* Check signature */
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.sig_offset = hsize;
+ ReSignECPreamble(h, private_key);
+ TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() sig off end");
+
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.sig_size--;
+ ReSignECPreamble(h, private_key);
+ TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() sig too small");
+
+ Memcpy(h, hdr, hsize);
+ GetSignatureData(&h->body_digest)[0] ^= 0x34;
+ TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() sig mismatch");
+
+ /* Check that we signed header and body sig */
+ Memcpy(h, hdr, hsize);
+ h->preamble_signature.data_size = 4;
+ h->body_digest.sig_offset = 0;
+ h->body_digest.sig_size = 0;
+ ReSignECPreamble(h, private_key);
+ TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() didn't sign header");
+
+ Memcpy(h, hdr, hsize);
+ h->body_digest.sig_offset = hsize;
+ ReSignECPreamble(h, private_key);
+ TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0,
+ "VerifyECPreamble() body sig off end");
+
+ /* TODO: verify with extra padding at end of header. */
+
+ free(h);
+ RSAPublicKeyFree(rsa);
+ free(hdr);
+}
+
+
+int main(int argc, char* argv[]) {
+ VbPrivateKey* signing_private_key = NULL;
+ VbPublicKey* signing_public_key = NULL;
+
+ int error_code = 0;
+
+ if(argc != 3) {
+ fprintf(stderr, "Usage: %s <signing privkey> <signing pubkey>", argv[0]);
+ return -1;
+ }
+
+ signing_private_key = PrivateKeyRead(argv[1]);
+ if (!signing_private_key) {
+ fprintf(stderr, "Error reading signing_private_key\n");
+ return 1;
+ }
+
+ signing_public_key = PublicKeyRead(argv[2]);
+ if (!signing_public_key) {
+ fprintf(stderr, "Error reading signing_public_key\n");
+ return 1;
+ }
+
+ VerifyECPreambleTest(signing_public_key, signing_private_key);
+
+
+ if (signing_public_key)
+ free(signing_public_key);
+ if (signing_private_key)
+ free(signing_private_key);
+
+ return error_code;
+}
diff --git a/utility/Makefile b/utility/Makefile
index 59d624a5..7f1e15e3 100644
--- a/utility/Makefile
+++ b/utility/Makefile
@@ -27,6 +27,7 @@ TARGET_NAMES = crossystem \
tlcl_generator \
tpm_init_temp_fix \
tpmc \
+ vbutil_ec \
vbutil_firmware \
vbutil_kernel \
vbutil_key \
@@ -120,6 +121,9 @@ ${BUILD_ROOT}/signature_digest_utility: signature_digest_utility.c $(LIBS)
${BUILD_ROOT}/tlcl_generator: tlcl_generator.c
$(HOSTCC) $(CFLAGS) -fpack-struct $< -o $@
+${BUILD_ROOT}/vbutil_ec: vbutil_ec.c $(LIBS)
+ $(CC) $(CFLAGS) $< -o $@ $(LIBS) -lcrypto
+
${BUILD_ROOT}/vbutil_firmware: vbutil_firmware.c $(LIBS)
$(CC) $(CFLAGS) $< -o $@ $(LIBS) -lcrypto
diff --git a/utility/vbutil_ec.c b/utility/vbutil_ec.c
new file mode 100644
index 00000000..86747c0a
--- /dev/null
+++ b/utility/vbutil_ec.c
@@ -0,0 +1,327 @@
+/* Copyright (c) 2012 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.
+ *
+ * Verified boot utility for EC firmware
+ */
+
+#include <getopt.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cryptolib.h"
+#include "host_common.h"
+#include "vboot_common.h"
+
+
+/* Command line options */
+enum {
+ OPT_MODE_VBLOCK = 1000,
+ OPT_MODE_VERIFY,
+ OPT_KEYBLOCK,
+ OPT_SIGNPUBKEY,
+ OPT_SIGNPRIVATE,
+ OPT_VERSION,
+ OPT_FV,
+ OPT_KERNELKEY,
+ OPT_FLAGS,
+ OPT_NAME,
+};
+
+static struct option long_opts[] = {
+ {"vblock", 1, 0, OPT_MODE_VBLOCK },
+ {"verify", 1, 0, OPT_MODE_VERIFY },
+ {"keyblock", 1, 0, OPT_KEYBLOCK },
+ {"signpubkey", 1, 0, OPT_SIGNPUBKEY },
+ {"signprivate", 1, 0, OPT_SIGNPRIVATE },
+ {"version", 1, 0, OPT_VERSION },
+ {"fv", 1, 0, OPT_FV },
+ {"flags", 1, 0, OPT_FLAGS },
+ {"name", 1, 0, OPT_NAME },
+ {NULL, 0, 0, 0}
+};
+
+
+/* Print help and return error */
+static int PrintHelp(void) {
+
+ puts("vbutil_ec - Verified boot signing utility for EC firmware\n"
+ "\n"
+ "Usage: vbutil_ec <--vblock|--verify> <file> [OPTIONS]\n"
+ "\n"
+ "For '--vblock <file>', required OPTIONS are:\n"
+ " --keyblock <file> Key block in .keyblock format\n"
+ " --signprivate <file> Signing private key in .vbprivk format\n"
+ " --version <number> Firmware version\n"
+ " --fv <file> Firmware volume to sign\n"
+ "optional OPTIONS are:\n"
+ " --flags <number> Preamble flags (defaults to 0)\n"
+ " --name <string> Human-readable description\n"
+ "\n"
+ "For '--verify <file>', required OPTIONS are:\n"
+ " --fv <file> Firmware volume to verify\n"
+ "optional OPTIONS are:\n"
+ " --signpubkey <file> Signing public key in .vbpubk format\n"
+ "\n");
+ return 1;
+}
+
+
+/* Create an EC firmware .vblock */
+static int Vblock(const char* outfile, const char* keyblock_file,
+ const char* signprivate, uint64_t version,
+ const char* fv_file, uint32_t preamble_flags,
+ const char* name) {
+ VbPrivateKey* signing_key;
+ VbSignature* body_digest;
+ VbECPreambleHeader* preamble;
+ VbKeyBlockHeader* key_block;
+ uint64_t key_block_size;
+ uint8_t* fv_data;
+ uint64_t fv_size;
+ FILE* f;
+ uint64_t i;
+
+ if (!outfile)
+ VbExError("Must specify output filename\n");
+
+ if (!keyblock_file || !signprivate)
+ VbExError("Must specify all keys\n");
+
+ if (!fv_file)
+ VbExError("Must specify firmware volume\n");
+
+ /* Read the key block and keys */
+ key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
+ if (!key_block)
+ VbExError("Error reading key block.\n");
+
+ signing_key = PrivateKeyRead(signprivate);
+ if (!signing_key)
+ VbExError("Error reading signing key.\n");
+
+ /* Read and sign the firmware volume */
+ fv_data = ReadFile(fv_file, &fv_size);
+ if (!fv_data)
+ return 1;
+ if (!fv_size)
+ VbExError("Empty firmware volume file\n");
+
+ if (name && strlen(name)+1 > sizeof(preamble->name))
+ VbExError("Name string is too long\n");
+
+ body_digest = CalculateHash(fv_data, fv_size, signing_key);
+ if (!body_digest)
+ VbExError("Error calculating body digest\n");
+ free(fv_data);
+
+ /* Create preamble */
+ preamble = CreateECPreamble(version, body_digest, signing_key,
+ preamble_flags, name);
+ if (!preamble)
+ VbExError("Error creating preamble.\n");
+
+ /* Write the output file */
+ f = fopen(outfile, "wb");
+ if (!f)
+ VbExError("Can't open output file %s\n", outfile);
+
+ i = ((1 != fwrite(key_block, key_block_size, 1, f)) ||
+ (1 != fwrite(preamble, preamble->preamble_size, 1, f)));
+ fclose(f);
+ if (i) {
+ unlink(outfile);
+ VbExError("Can't write output file %s\n", outfile);
+ }
+
+ /* Success */
+ return 0;
+}
+
+static int Verify(const char* infile,
+ const char* signpubkey,
+ const char* fv_file) {
+ VbKeyBlockHeader* key_block;
+ VbECPreambleHeader* preamble;
+ VbPublicKey* data_key;
+ VbPublicKey* sign_key = 0;
+ RSAPublicKey* rsa;
+ uint8_t* blob;
+ uint64_t blob_size;
+ uint8_t* fv_data;
+ uint64_t fv_size;
+ uint64_t now = 0;
+
+ if (!infile || !fv_file) {
+ VbExError("Must specify filename and fv\n");
+ return 1;
+ }
+
+ /* Read public signing key */
+ if (signpubkey) {
+ sign_key = PublicKeyRead(signpubkey);
+ if (!sign_key)
+ VbExError("Error reading signpubkey.\n");
+ } else {
+ printf("WARNING: No public key given - signature is not checked\n");
+ }
+
+ /* Read blob */
+ blob = ReadFile(infile, &blob_size);
+ if (!blob)
+ VbExError("Error reading input file\n");
+
+ /* Read firmware volume */
+ fv_data = ReadFile(fv_file, &fv_size);
+ if (!fv_data)
+ VbExError("Error reading firmware volume\n");
+
+ /* Verify key block */
+ key_block = (VbKeyBlockHeader*)blob;
+ if (0 != KeyBlockVerify(key_block, blob_size, sign_key, !signpubkey))
+ VbExError("Error verifying key block.\n");
+
+ if (sign_key)
+ free(sign_key);
+ now += key_block->key_block_size;
+
+ printf("Key block:\n");
+ data_key = &key_block->data_key;
+ printf(" Size: %" PRIu64 "\n", key_block->key_block_size);
+ printf(" Flags: %" PRIu64 " (ignored)\n",
+ key_block->key_block_flags);
+ printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
+ (data_key->algorithm < kNumAlgorithms ?
+ algo_strings[data_key->algorithm] : "(invalid)"));
+ printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
+ printf(" Data key sha1sum: ");
+ PrintPubKeySha1Sum(data_key);
+ printf("\n");
+
+ rsa = PublicKeyToRSA(&key_block->data_key);
+ if (!rsa)
+ VbExError("Error parsing data key.\n");
+
+ /* Verify preamble */
+ preamble = (VbECPreambleHeader*)(blob + now);
+ if (0 != VerifyECPreamble(preamble, blob_size - now, rsa))
+ VbExError("Error verifying preamble.\n");
+
+ now += preamble->preamble_size;
+
+ printf("Preamble:\n");
+ printf(" Size: %" PRIu64 "\n", preamble->preamble_size);
+ printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
+ preamble->header_version_major, preamble->header_version_minor);
+ printf(" Firmware version: %" PRIu64 "\n", preamble->firmware_version);
+ printf(" Firmware body size: %" PRIu64 "\n",
+ preamble->body_digest.data_size);
+ printf(" Preamble flags: %" PRIu32 "\n", preamble->flags);
+ printf(" Preamble name: %s\n", preamble->name);
+
+ /* TODO: verify body size same as signature size */
+
+ /* Verify body */
+ if (preamble->flags & VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL) {
+ printf("Preamble requests USE_RO_NORMAL; skipping body verification.\n");
+ } else {
+ if (0 != EqualData(fv_data, fv_size, &preamble->body_digest, rsa))
+ VbExError("Error verifying firmware body.\n");
+ printf("Body verification succeeded.\n");
+ }
+
+ return 0;
+}
+
+
+int main(int argc, char* argv[]) {
+
+ char* filename = NULL;
+ char* key_block_file = NULL;
+ char* signpubkey = NULL;
+ char* signprivate = NULL;
+ uint64_t version = 0;
+ int got_version = 0;
+ char* fv_file = NULL;
+ uint32_t preamble_flags = 0;
+ char *name = NULL;
+ int mode = 0;
+ int parse_error = 0;
+ 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_MODE_VBLOCK:
+ case OPT_MODE_VERIFY:
+ mode = i;
+ filename = optarg;
+ break;
+
+ case OPT_KEYBLOCK:
+ key_block_file = optarg;
+ break;
+
+ case OPT_SIGNPUBKEY:
+ signpubkey = optarg;
+ break;
+
+ case OPT_SIGNPRIVATE:
+ signprivate = optarg;
+ break;
+
+ case OPT_FV:
+ fv_file = optarg;
+ break;
+
+ case OPT_VERSION:
+ version = strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e)) {
+ printf("Invalid --version\n");
+ parse_error = 1;
+ }
+ got_version = 1;
+ break;
+
+ case OPT_FLAGS:
+ preamble_flags = strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e)) {
+ printf("Invalid --flags\n");
+ parse_error = 1;
+ }
+ break;
+
+ case OPT_NAME:
+ name = optarg;
+ break;
+ }
+ }
+
+ if (parse_error)
+ return PrintHelp();
+
+ switch(mode) {
+ case OPT_MODE_VBLOCK:
+ if (!got_version) {
+ printf("Must specify a version\n");
+ return PrintHelp();
+ }
+ return Vblock(filename, key_block_file, signprivate, version,
+ fv_file, preamble_flags, name);
+ case OPT_MODE_VERIFY:
+ return Verify(filename, signpubkey, fv_file);
+ default:
+ printf("Must specify a mode.\n");
+ return PrintHelp();
+ }
+}