vboot2: Add vb2_unpack_key2() and unit tests

This unpacks new-style packed keys.

For now, it can also handle old-style packed keys by passing them to
the old unpacking function.  Once we've switched over to new-style
keys in the signing scripts, we'll remove the old format to save code
size.

Also added is a test library which converts from old to new struct
formats.  That should eventually get absorbed into futility, and the
test keys directory should have both old and new format packed keys in
it.

BUG=chromium:423882
BRANCH=none
TEST=VBOOT2=1 make runtests

Change-Id: I0fe31f124781d1ea1efedab65dcd6130bfca18dd
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/225490
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
diff --git a/Makefile b/Makefile
index c344b08..2a3585c 100644
--- a/Makefile
+++ b/Makefile
@@ -287,6 +287,7 @@
 	firmware/2lib/2misc.c \
 	firmware/2lib/2nvstorage.c \
 	firmware/2lib/2packed_key.c \
+	firmware/2lib/2packed_key2.c \
 	firmware/2lib/2rsa.c \
 	firmware/2lib/2secdata.c \
 	firmware/2lib/2sha1.c \
@@ -579,6 +580,11 @@
 	tests/timer_utils.c \
 	tests/crc32_test.c
 
+ifneq (${VBOOT2},)
+TESTLIB_SRCS += \
+	tests/vb2_convert_structs.c
+endif
+
 TESTLIB_OBJS = ${TESTLIB_SRCS:%.c=${BUILD}/%.o}
 TEST_OBJS += ${TESTLIB_OBJS}
 
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index 3986e1f..c36372b 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -127,7 +127,7 @@
 	 */
 	if (ctx->workbuf_size < sizeof(*sd))
 		return VB2_ERROR_INITCTX_WORKBUF_SMALL;
-	if (!vb_aligned(ctx->workbuf, VB2_WORKBUF_ALIGN))
+	if (!vb2_aligned(ctx->workbuf, VB2_WORKBUF_ALIGN))
 		return VB2_ERROR_INITCTX_WORKBUF_ALIGN;
 
 	/* Initialize the shared data at the start of the work buffer */
diff --git a/firmware/2lib/2packed_key.c b/firmware/2lib/2packed_key.c
index fe6bab5..098296e 100644
--- a/firmware/2lib/2packed_key.c
+++ b/firmware/2lib/2packed_key.c
@@ -59,7 +59,7 @@
 
 	/* Make sure source buffer is 32-bit aligned */
 	buf32 = (const uint32_t *)vb2_packed_key_data(packed_key);
-	if (!vb_aligned(buf32, sizeof(uint32_t)))
+	if (!vb2_aligned(buf32, sizeof(uint32_t)))
 		return VB2_ERROR_UNPACK_KEY_ALIGN;
 
 	/* Sanity check key array size */
diff --git a/firmware/2lib/2packed_key2.c b/firmware/2lib/2packed_key2.c
new file mode 100644
index 0000000..306206e
--- /dev/null
+++ b/firmware/2lib/2packed_key2.c
@@ -0,0 +1,109 @@
+/* Copyright (c) 2014 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.
+ *
+ * Key unpacking functions
+ */
+
+#include "2sysincludes.h"
+#include "2common.h"
+#include "2rsa.h"
+
+const uint8_t *vb2_packed_key2_data(const struct vb2_packed_key2 *key)
+{
+	return (const uint8_t *)key + key->key_offset;
+}
+
+int vb2_verify_packed_key2_inside(const void *parent,
+				  uint32_t parent_size,
+				  const struct vb2_packed_key2 *key)
+{
+	int rv;
+
+	rv = vb2_verify_member_inside(parent, parent_size,
+				      key, sizeof(*key),
+				      key->key_offset, key->key_size);
+	if (rv)
+		return rv;
+
+	return vb2_verify_common_header(parent, parent_size, &key->c);
+}
+
+int vb2_unpack_key2(struct vb2_public_key *key,
+		    const uint8_t *buf,
+		    uint32_t size)
+{
+	const struct vb2_packed_key2 *pkey =
+		(const struct vb2_packed_key2 *)buf;
+	const uint32_t *buf32;
+	uint32_t expected_key_size;
+	uint32_t sig_size;
+	int rv;
+
+	/*
+	 * Check magic number.
+	 *
+	 * If it doesn't match, pass through to the old packed key format.
+	 *
+	 * TODO: remove passthru when signing scripts have switched over to
+	 * use the new format.
+	 */
+	if (pkey->c.magic != VB2_MAGIC_PACKED_KEY2)
+		return vb2_unpack_key(key, buf, size);
+
+	/* Make sure passed buffer is big enough for the packed key */
+	rv = vb2_verify_packed_key2_inside(buf, size, pkey);
+	if (rv)
+		return rv;
+
+	/*
+	 * Check for compatible version.  No need to check minor version, since
+	 * that's compatible across readers matching the major version, and we
+	 * haven't added any new fields.
+	 */
+	if (pkey->c.struct_version_major != VB2_PACKED_KEY2_VERSION_MAJOR)
+		return VB2_ERROR_UNPACK_KEY_STRUCT_VERSION;
+
+	/* Copy key algorithms */
+	key->sig_alg = pkey->sig_algorithm;
+	sig_size = vb2_rsa_sig_size(key->sig_alg);
+	if (!sig_size)
+		return VB2_ERROR_UNPACK_KEY_SIG_ALGORITHM;
+
+	key->hash_alg = pkey->hash_algorithm;
+	if (!vb2_digest_size(key->hash_alg))
+		return VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM;
+
+	expected_key_size = vb2_packed_key_size(key->sig_alg);
+	if (!expected_key_size || expected_key_size != pkey->key_size) {
+		VB2_DEBUG("Wrong key size for algorithm\n");
+		return VB2_ERROR_UNPACK_KEY_SIZE;
+	}
+
+	/* Make sure source buffer is 32-bit aligned */
+	buf32 = (const uint32_t *)vb2_packed_key2_data(pkey);
+	if (!vb2_aligned(buf32, sizeof(uint32_t)))
+		return VB2_ERROR_UNPACK_KEY_ALIGN;
+
+	/* Sanity check key array size */
+	key->arrsize = buf32[0];
+	if (key->arrsize * sizeof(uint32_t) != sig_size)
+		return VB2_ERROR_UNPACK_KEY_ARRAY_SIZE;
+
+	key->n0inv = buf32[1];
+
+	/* Arrays point inside the key data */
+	key->n = buf32 + 2;
+	key->rr = buf32 + 2 + key->arrsize;
+
+	/* Key description */
+	if (pkey->c.desc_size)
+		key->desc = (const char *)&(pkey->c) + pkey->c.desc_offset;
+	else
+		key->desc = "";
+
+	key->version = pkey->key_version;
+	key->guid = &pkey->key_guid;
+
+	return VB2_SUCCESS;
+}
diff --git a/firmware/2lib/include/2common.h b/firmware/2lib/include/2common.h
index 8a00dd7..5ab145c 100644
--- a/firmware/2lib/include/2common.h
+++ b/firmware/2lib/include/2common.h
@@ -103,7 +103,7 @@
 void vb2_workbuf_free(struct vb2_workbuf *wb, uint32_t size);
 
 /* Check if a pointer is aligned on an align-byte boundary */
-#define vb_aligned(ptr, align) (!(((uintptr_t)(ptr)) & ((align) - 1)))
+#define vb2_aligned(ptr, align) (!(((uintptr_t)(ptr)) & ((align) - 1)))
 
 /**
  * Safer memcmp() for use in crypto.
@@ -233,6 +233,21 @@
 		   const uint8_t *buf,
 		   uint32_t size);
 
+/**
+ * Unpack a key for use in verification
+ *
+ * The elements of the unpacked key will point into the source buffer, so don't
+ * free the source buffer until you're done with the key.
+ *
+ * @param key		Destintion for unpacked key
+ * @param buf		Source buffer containing packed key
+ * @param size		Size of buffer in bytes
+ * @return VB2_SUCCESS, or non-zero error code if error.
+ */
+int vb2_unpack_key2(struct vb2_public_key *key,
+		    const uint8_t *buf,
+		    uint32_t size);
+
 /* Size of work buffer sufficient for vb2_rsa_verify_digest() worst case */
 #define VB2_VERIFY_DIGEST_WORKBUF_BYTES VB2_VERIFY_RSA_DIGEST_WORKBUF_BYTES
 
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index 333c29c..e75f422 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -176,6 +176,9 @@
 	/* Member data overlaps member header */
 	VB2_ERROR_INSIDE_DATA_OVERLAP,
 
+	/* Unsupported packed key struct version */
+	VB2_ERROR_UNPACK_KEY_STRUCT_VERSION,
+
         /**********************************************************************
 	 * Keyblock verification errors (all in vb2_verify_keyblock())
 	 */
diff --git a/firmware/2lib/include/2rsa.h b/firmware/2lib/include/2rsa.h
index 8e21cd4..3d591a5 100644
--- a/firmware/2lib/include/2rsa.h
+++ b/firmware/2lib/include/2rsa.h
@@ -19,6 +19,9 @@
 	const uint32_t *rr;  /* R^2 as little endian array */
 	enum vb2_signature_algorithm sig_alg;  /* Signature algorithm */
 	enum vb2_hash_algorithm hash_alg;      /* Hash algorithm */
+	const char *desc;            /* Description */
+	uint32_t version;            /* Key version */
+	const struct vb2_guid *guid; /* Key GUID */
 };
 
 /**
diff --git a/tests/vb2_common2_tests.c b/tests/vb2_common2_tests.c
index 4b3a34e..f58d2a3 100644
--- a/tests/vb2_common2_tests.c
+++ b/tests/vb2_common2_tests.c
@@ -11,6 +11,7 @@
 
 #include "file_keys.h"
 #include "host_common.h"
+#include "vb2_convert_structs.h"
 #include "vboot_common.h"
 #include "test_common.h"
 
@@ -75,6 +76,113 @@
 	free(key);
 }
 
+static void test_unpack_key2(const VbPublicKey *orig_key)
+{
+	/* vb2_packed_key and VbPublicKey are bit-identical */
+	const struct vb2_packed_key *key1 =
+		(const struct vb2_packed_key *)orig_key;
+
+	struct vb2_public_key pubk;
+	struct vb2_packed_key2 *key2;
+	uint32_t size;
+
+	/* Should be able to handle a vboot1-style key binary as well */
+	TEST_SUCC(vb2_unpack_key2(&pubk, (uint8_t *)key1,
+				  key1->key_offset + key1->key_size),
+		  "vb2_unpack_key2() passthru");
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	TEST_SUCC(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		  "vb2_unpack_key2() ok");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->key_offset += 4;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_INSIDE_DATA_OUTSIDE,
+		"vb2_unpack_key2() buffer too small");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->c.desc_offset += size;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_INSIDE_DATA_OUTSIDE,
+		"vb2_unpack_key2() buffer too small for desc");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->c.desc_size = 0;
+	key2->c.desc_offset = 0;
+	TEST_SUCC(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		  "vb2_unpack_key2() no desc");
+	TEST_EQ(strcmp(pubk.desc, ""), 0, "  empty desc string");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->c.magic++;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_INSIDE_DATA_OUTSIDE,
+		"vb2_unpack_key2() bad magic");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->c.struct_version_major++;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_UNPACK_KEY_STRUCT_VERSION,
+		"vb2_unpack_key2() bad major version");
+	free(key2);
+
+	/*
+	 * Minor version changes are ok.  Note that this test assumes that the
+	 * source key struct version is the highest actually known to the
+	 * reader.  If the reader does know about minor version + 1 and that
+	 * adds fields, this test will likely fail.  But at that point, we
+	 * should have already added a test for minor version compatibility to
+	 * handle both old and new struct versions, so someone will have
+	 * noticed this comment.
+	 */
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->c.struct_version_minor++;
+	TEST_SUCC(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		  "vb2_unpack_key2() minor version change ok");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->sig_algorithm = VB2_SIG_INVALID;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_UNPACK_KEY_SIG_ALGORITHM,
+		"vb2_unpack_key2() bad sig algorithm");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->hash_algorithm = VB2_HASH_INVALID;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM,
+		"vb2_unpack_key2() bad hash algorithm");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->key_size--;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_UNPACK_KEY_SIZE,
+		"vb2_unpack_key2() invalid size");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	key2->key_offset--;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_UNPACK_KEY_ALIGN,
+		"vb2_unpack_key2() unaligned data");
+	free(key2);
+
+	key2 = vb2_convert_packed_key2(key1, "Test key", &size);
+	*(uint32_t *)((uint8_t *)key2 + key2->key_offset) /= 2;
+	TEST_EQ(vb2_unpack_key2(&pubk, (uint8_t *)key2, size),
+		VB2_ERROR_UNPACK_KEY_ARRAY_SIZE,
+		"vb2_unpack_key2() invalid key array size");
+	free(key2);
+}
+
 static void test_verify_data(const VbPublicKey *public_key,
 			     const VbPrivateKey *private_key)
 {
@@ -173,6 +281,7 @@
 	}
 
 	test_unpack_key(public_key);
+	test_unpack_key2(public_key);
 	test_verify_data(public_key, private_key);
 
 	if (public_key)
diff --git a/tests/vb2_convert_structs.c b/tests/vb2_convert_structs.c
new file mode 100644
index 0000000..fe74f85
--- /dev/null
+++ b/tests/vb2_convert_structs.c
@@ -0,0 +1,57 @@
+/* Copyright (c) 2014 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.
+ *
+ * Convert structs from vboot1 data format to new vboot2 structs
+ */
+
+#include "2sysincludes.h"
+#include "2common.h"
+#include "2rsa.h"
+#include "vb2_convert_structs.h"
+#include "vboot_struct.h"  /* For old struct sizes */
+
+#include "test_common.h"
+
+struct vb2_packed_key2 *vb2_convert_packed_key2(
+				const struct vb2_packed_key *key,
+				const char *desc, uint32_t *out_size)
+{
+	struct vb2_packed_key2 k2 = {
+		.c.magic = VB2_MAGIC_PACKED_KEY2,
+		.c.struct_version_major = VB2_PACKED_KEY2_VERSION_MAJOR,
+		.c.struct_version_minor = VB2_PACKED_KEY2_VERSION_MINOR,
+	};
+	uint8_t *kbuf;
+
+	/* Calculate description size */
+	k2.c.desc_offset = sizeof(k2);
+	k2.c.desc_size = roundup32(strlen(desc) + 1);
+
+	/* Copy/initialize fields */
+	k2.key_offset = k2.c.desc_offset + k2.c.desc_size;
+	k2.key_size = key->key_size;
+	k2.key_version = key->key_version;
+	k2.sig_algorithm = vb2_crypto_to_signature(key->algorithm);
+	k2.hash_algorithm = vb2_crypto_to_hash(key->algorithm);
+	/* TODO: fill in a non-zero GUID */
+
+	/* Allocate the new buffer */
+	*out_size = k2.key_offset + k2.key_size;
+	kbuf = malloc(*out_size);
+	memset(kbuf, 0, *out_size);
+
+	/* Copy data into the buffer */
+	memcpy(kbuf, &k2, sizeof(k2));
+
+	/* strcpy() is safe because we allocated above based on strlen() */
+	strcpy((char *)(kbuf + k2.c.desc_offset), desc);
+	kbuf[k2.c.desc_offset + k2.c.desc_size - 1] = 0;
+
+	memcpy(kbuf + k2.key_offset,
+	       (const uint8_t *)key + key->key_offset,
+	       key->key_size);
+
+	/* Return the newly allocated buffer */
+	return (struct vb2_packed_key2 *)kbuf;
+}
diff --git a/tests/vb2_convert_structs.h b/tests/vb2_convert_structs.h
new file mode 100644
index 0000000..dcf1583
--- /dev/null
+++ b/tests/vb2_convert_structs.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2014 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_VB2_CONVERT_STRUCTS_H_
+#define VBOOT_REFERENCE_VB2_CONVERT_STRUCTS_H_
+
+#include "2struct.h"
+
+/**
+ * Round up a size to a multiple of 32 bits (4 bytes).
+ */
+static __inline const uint32_t roundup32(uint32_t v)
+{
+	return (v + 3) & ~3;
+}
+
+/**
+ * Convert a packed key from vboot data format to vboot2 data format.
+ *
+ * Intended for use by unit tests.  Does NOT validate the original struct
+ * contents, just copies them.
+ *
+ * @param key		Packed key in vboot1 format
+ * @param desc		Description of packed key
+ * @param out_size	Size of the newly allocated buffer
+ * @return a newly allocated buffer with the converted key.  Caller is
+ * responsible for freeing this buffer.
+ */
+struct vb2_packed_key2 *vb2_convert_packed_key2(
+				const struct vb2_packed_key *key,
+				const char *desc, uint32_t *out_size);
+
+#endif  /* VBOOT_REFERENCE_VB2_CONVERT_STRUCTS_H_ */