diff options
author | Randall Spangler <rspangler@chromium.org> | 2014-11-04 17:50:32 -0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-11-12 19:57:57 +0000 |
commit | 43e0a9ed6c0b332631442fcf581e7456d62e4532 (patch) | |
tree | c3bc2345e722a682a4667420e777d1079b8c4da8 | |
parent | 054c1147989d39a432923cc359d123f0d8e9390a (diff) | |
download | vboot-43e0a9ed6c0b332631442fcf581e7456d62e4532.tar.gz |
vboot2: Add code and tests for verifying vb2_fw_preamble2
This is the last low-level data structure verification code for the
new data structures. Subsequent changes are the next level up the
food chain.
BUG=chromium:423882
BRANCH=none
TEST=VBOOT2=1 make runtests
Change-Id: I2e45106c27447eb624c1ed562e40b98088249742
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/228360
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
-rw-r--r-- | firmware/2lib/2common2.c | 70 | ||||
-rw-r--r-- | firmware/2lib/include/2common.h | 10 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 6 | ||||
-rw-r--r-- | firmware/2lib/include/2struct.h | 8 | ||||
-rw-r--r-- | tests/vb2_common_tests.c | 194 |
5 files changed, 277 insertions, 11 deletions
diff --git a/firmware/2lib/2common2.c b/firmware/2lib/2common2.c index 1dcf53d1..099f7c1f 100644 --- a/firmware/2lib/2common2.c +++ b/firmware/2lib/2common2.c @@ -366,3 +366,73 @@ int vb2_verify_keyblock2(struct vb2_keyblock2 *block, /* If we're still here, no signature matched the key GUID */ return VB2_ERROR_KEYBLOCK_SIG_GUID; } + +int vb2_verify_fw_preamble2(struct vb2_fw_preamble2 *preamble, + uint32_t size, + const struct vb2_public_key *key, + const struct vb2_workbuf *wb) +{ + struct vb2_signature2 *sig; + uint32_t min_offset = 0, hash_offset; + int rv, i; + + /* Check magic number */ + if (preamble->c.magic != VB2_MAGIC_FW_PREAMBLE2) + return VB2_ERROR_PREAMBLE_MAGIC; + + /* Make sure common header is good */ + rv = vb2_verify_common_header(preamble, size); + 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 (preamble->c.struct_version_major != VB2_FW_PREAMBLE2_VERSION_MAJOR) + return VB2_ERROR_PREAMBLE_HEADER_VERSION; + + /* Make sure header is big enough */ + if (preamble->c.fixed_size < sizeof(*preamble)) + return VB2_ERROR_PREAMBLE_SIZE; + + /* Make sure all hash signatures are inside */ + hash_offset = preamble->hash_offset; + for (i = 0; i < preamble->hash_count; i++, hash_offset = min_offset) { + /* Make sure signature is inside preamble */ + rv = vb2_verify_common_subobject(preamble, &min_offset, + hash_offset); + if (rv) + return rv; + + sig = (struct vb2_signature2 *) + ((uint8_t *)preamble + hash_offset); + + /* Verify the signature integrity */ + rv = vb2_verify_signature2( + sig, preamble->c.total_size - hash_offset); + if (rv) + return rv; + + /* Hashes must all be unsigned */ + if (sig->sig_alg != VB2_SIG_NONE) + return VB2_ERROR_PREAMBLE_HASH_SIGNED; + } + + /* Make sure signature is inside preamble */ + rv = vb2_verify_common_subobject(preamble, &min_offset, + preamble->sig_offset); + if (rv) + return rv; + + /* Verify preamble signature */ + sig = (struct vb2_signature2 *)((uint8_t *)preamble + + preamble->sig_offset); + + rv = vb2_verify_data2(preamble, preamble->sig_offset, sig, key, wb); + if (rv) + return rv; + + return VB2_SUCCESS; +} diff --git a/firmware/2lib/include/2common.h b/firmware/2lib/include/2common.h index 0f7012cb..d26ccfce 100644 --- a/firmware/2lib/include/2common.h +++ b/firmware/2lib/include/2common.h @@ -410,7 +410,10 @@ int vb2_verify_keyblock2(struct vb2_keyblock2 *block, const struct vb2_public_key *key, const struct vb2_workbuf *wb); -/* Size of work buffer sufficient for vb2_verify_fw_preamble() worst case */ +/* + * Size of work buffer sufficient for vb2_verify_fw_preamble() or + * vb2_verify_fw_preamble2() worst case. + */ #define VB2_VERIFY_FIRMWARE_PREAMBLE_WORKBUF_BYTES VB2_VERIFY_DATA_WORKBUF_BYTES /** @@ -429,4 +432,9 @@ int vb2_verify_fw_preamble(struct vb2_fw_preamble *preamble, const struct vb2_public_key *key, const struct vb2_workbuf *wb); +int vb2_verify_fw_preamble2(struct vb2_fw_preamble2 *preamble, + uint32_t size, + const struct vb2_public_key *key, + const struct vb2_workbuf *wb); + #endif /* VBOOT_REFERENCE_VBOOT_2COMMON_H_ */ diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h index 054655ee..4ff84468 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -302,6 +302,12 @@ enum vb2_return_code { /* Kernel subkey outside preamble */ VB2_ERROR_PREAMBLE_KERNEL_SUBKEY_OUTSIDE, + /* Bad magic number */ + VB2_ERROR_PREAMBLE_MAGIC, + + /* Hash is signed */ + VB2_ERROR_PREAMBLE_HASH_SIGNED, + /********************************************************************** * Misc higher-level code errors */ diff --git a/firmware/2lib/include/2struct.h b/firmware/2lib/include/2struct.h index e988f3f9..3f208325 100644 --- a/firmware/2lib/include/2struct.h +++ b/firmware/2lib/include/2struct.h @@ -491,9 +491,9 @@ struct vb2_keyblock2 { #define EXPECTED_VB2_KEYBLOCK2_SIZE (EXPECTED_VB2_STRUCT_COMMON_SIZE + 16) -/* Current version of vb2_preamble2 struct */ -#define VB2_PREAMBLE2_VERSION_MAJOR 3 -#define VB2_PREAMBLE2_VERSION_MINOR 0 +/* Current version of vb2_fw_preamble2 struct */ +#define VB2_FW_PREAMBLE2_VERSION_MAJOR 3 +#define VB2_FW_PREAMBLE2_VERSION_MINOR 0 /* * Firmware preamble @@ -501,7 +501,7 @@ struct vb2_keyblock2 { * The preamble data must be arranged like this: * 1) vb2_fw_preamble2 header struct h * 2) Preamble description (pointed to by h.c.fixed_size) - * 3) Hash table (pointed to by h.hash_table_offset) + * 3) Hashes (pointed to by h.hash_offset) * 4) Signature (pointed to by h.sig_offset) * * The signature 4) must cover all the data from 1), 2), 3). diff --git a/tests/vb2_common_tests.c b/tests/vb2_common_tests.c index ad7635e0..d8a714bb 100644 --- a/tests/vb2_common_tests.c +++ b/tests/vb2_common_tests.c @@ -13,6 +13,8 @@ #include "test_common.h" +static const uint8_t test_data[] = "This is some test data to sign."; + /** * Test memory compare functions */ @@ -449,7 +451,6 @@ static void test_sig_size(void) */ static void test_verify_hash(void) { - static const uint8_t test_data[] = "This is some test data to sign."; struct vb2_signature2 *sig; struct vb2_public_key pubk = { .sig_alg = VB2_SIG_NONE, @@ -535,12 +536,12 @@ static void test_verify_keyblock(void) */ kb.c.total_size = kb.sig_offset; - sig = vb2_create_hash_sig((const uint8_t *)desc, sizeof(desc), + sig = vb2_create_hash_sig(test_data, sizeof(test_data), VB2_HASH_SHA256); kb.c.total_size += sig->c.total_size; free(sig); - sig = vb2_create_hash_sig((const uint8_t *)desc, sizeof(desc), + sig = vb2_create_hash_sig(test_data, sizeof(test_data), VB2_HASH_SHA512); kb.c.total_size += sig->c.total_size; free(sig); @@ -587,19 +588,19 @@ static void test_verify_keyblock(void) memcpy(buf, buf2, buf_size); kbuf->c.magic = VB2_MAGIC_PACKED_KEY2; - TEST_EQ(vb2_verify_keyblock2(kbuf, buf_size, &pubk_not_present, &wb), + TEST_EQ(vb2_verify_keyblock2(kbuf, buf_size, &pubk, &wb), VB2_ERROR_KEYBLOCK_MAGIC, "vb2_verify_keyblock2() magic"); memcpy(buf, buf2, buf_size); kbuf->c.fixed_size++; - TEST_EQ(vb2_verify_keyblock2(kbuf, buf_size, &pubk_not_present, &wb), + TEST_EQ(vb2_verify_keyblock2(kbuf, buf_size, &pubk, &wb), VB2_ERROR_COMMON_FIXED_UNALIGNED, "vb2_verify_keyblock2() header"); memcpy(buf, buf2, buf_size); kbuf->c.struct_version_major++; - TEST_EQ(vb2_verify_keyblock2(kbuf, buf_size, &pubk_not_present, &wb), + TEST_EQ(vb2_verify_keyblock2(kbuf, buf_size, &pubk, &wb), VB2_ERROR_KEYBLOCK_HEADER_VERSION, "vb2_verify_keyblock2() major version"); @@ -656,6 +657,186 @@ static void test_verify_keyblock(void) free(buf2); } +/** + * Verify firmware preamble + */ +static void test_verify_fw_preamble(void) +{ + const char desc[16] = "test preamble"; + struct vb2_signature2 *sig; + struct vb2_fw_preamble2 *pre; + uint32_t buf_size; + uint8_t *buf, *buf2, *bnext; + + uint8_t workbuf[VB2_VERIFY_FIRMWARE_PREAMBLE_WORKBUF_BYTES]; + struct vb2_workbuf wb; + + /* + * Preambles will usually be signed with a real key not a bare hash, + * but the call to vb2_verify_data2() inside the preamble check is the + * same (and its functionality is verified separately), and using a + * bare hash here saves us from needing to have a private key to do + * this test. + */ + const struct vb2_public_key pubk = { + .sig_alg = VB2_SIG_NONE, + .hash_alg = VB2_HASH_SHA256, + .guid = vb2_hash_guid(VB2_HASH_SHA256) + }; + + struct vb2_fw_preamble2 fp = { + .c.magic = VB2_MAGIC_FW_PREAMBLE2, + .c.struct_version_major = VB2_FW_PREAMBLE2_VERSION_MAJOR, + .c.struct_version_minor = VB2_FW_PREAMBLE2_VERSION_MAJOR, + .c.fixed_size = sizeof(fp), + .c.desc_size = sizeof(desc), + .flags = 0, + .hash_count = 3, + }; + + fp.hash_offset = fp.c.fixed_size + fp.c.desc_size; + + /* Create some hashes so we can calculate their sizes */ + fp.c.total_size = fp.hash_offset; + + sig = vb2_create_hash_sig(test_data, sizeof(test_data), + VB2_HASH_SHA512); + fp.c.total_size += sig->c.total_size; + free(sig); + + sig = vb2_create_hash_sig(test_data, sizeof(test_data), + VB2_HASH_SHA256); + fp.c.total_size += 2 * sig->c.total_size; + + /* Preamble signature goes after that */ + fp.sig_offset = fp.c.total_size; + fp.c.total_size += sig->c.total_size; + free(sig); + + /* Now that the total size is known, create the real preamble */ + buf_size = fp.c.total_size; + buf = malloc(buf_size); + memset(buf, 0, buf_size); + memcpy(buf, &fp, sizeof(fp)); + memcpy(buf + fp.c.fixed_size, desc, sizeof(desc)); + + /* And copy in the component hashes (use parts of test data) */ + bnext = buf + fp.hash_offset; + + sig = vb2_create_hash_sig(test_data, sizeof(test_data), + VB2_HASH_SHA256); + memset(&sig->guid, 0x01, sizeof(sig->guid)); + memcpy(bnext, sig, sig->c.total_size); + bnext += sig->c.total_size; + free(sig); + + sig = vb2_create_hash_sig(test_data, sizeof(test_data), + VB2_HASH_SHA512); + memset(&sig->guid, 0x03, sizeof(sig->guid)); + memcpy(bnext, sig, sig->c.total_size); + bnext += sig->c.total_size; + free(sig); + + sig = vb2_create_hash_sig(test_data, sizeof(test_data) - 4, + VB2_HASH_SHA256); + memset(&sig->guid, 0x02, sizeof(sig->guid)); + memcpy(bnext, sig, sig->c.total_size); + bnext += sig->c.total_size; + free(sig); + + /* Now sign the preamble */ + sig = vb2_create_hash_sig(buf, fp.sig_offset, VB2_HASH_SHA256); + memcpy(buf + fp.sig_offset, sig, sig->c.total_size); + free(sig); + + /* Make a copy of the buffer, so we can mangle it for tests */ + buf2 = malloc(buf_size); + memcpy(buf2, buf, buf_size); + + vb2_workbuf_init(&wb, workbuf, sizeof(workbuf)); + pre = (struct vb2_fw_preamble2 *)buf; + + TEST_SUCC(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + "vb2_verify_fw_preamble2()"); + + memcpy(buf, buf2, buf_size); + pre->c.magic = VB2_MAGIC_PACKED_KEY2; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_PREAMBLE_MAGIC, + "vb2_verify_fw_preamble2() magic"); + + memcpy(buf, buf2, buf_size); + pre->c.fixed_size++; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_COMMON_FIXED_UNALIGNED, + "vb2_verify_fw_preamble2() header"); + + memcpy(buf, buf2, buf_size); + pre->c.struct_version_major++; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_PREAMBLE_HEADER_VERSION, + "vb2_verify_fw_preamble2() major version"); + + memcpy(buf, buf2, buf_size); + pre->c.struct_version_minor++; + /* That changes the signature, so resign the fw_preamble */ + sig = vb2_create_hash_sig(buf, fp.sig_offset, VB2_HASH_SHA256); + memcpy(buf + pre->sig_offset, sig, sig->c.total_size); + free(sig); + TEST_SUCC(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + "vb2_verify_fw_preamble2() minor version"); + + memcpy(buf, buf2, buf_size); + pre->c.fixed_size -= 4; + pre->c.desc_size += 4; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_PREAMBLE_SIZE, + "vb2_verify_fw_preamble2() header size"); + + memcpy(buf, buf2, buf_size); + sig = (struct vb2_signature2 *)(buf + fp.hash_offset); + sig->c.total_size += fp.c.total_size; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_COMMON_TOTAL_SIZE, + "vb2_verify_fw_preamble2() hash size"); + + memcpy(buf, buf2, buf_size); + sig = (struct vb2_signature2 *)(buf + fp.hash_offset); + sig->sig_size /= 2; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_SIG_SIZE, + "vb2_verify_fw_preamble2() hash integrity"); + + memcpy(buf, buf2, buf_size); + pre->hash_count++; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_COMMON_MEMBER_OVERLAP, + "vb2_verify_fw_preamble2() hash count"); + + memcpy(buf, buf2, buf_size); + sig = (struct vb2_signature2 *)(buf + fp.sig_offset); + sig->c.total_size += 4; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_COMMON_TOTAL_SIZE, + "vb2_verify_fw_preamble2() sig inside"); + + memcpy(buf, buf2, buf_size); + sig = (struct vb2_signature2 *)(buf + fp.sig_offset); + buf[fp.sig_offset + sig->sig_offset]++; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_VDATA_VERIFY_DIGEST, + "vb2_verify_fw_preamble2() sig corrupt"); + + memcpy(buf, buf2, buf_size); + pre->flags++; + TEST_EQ(vb2_verify_fw_preamble2(pre, buf_size, &pubk, &wb), + VB2_ERROR_VDATA_VERIFY_DIGEST, + "vb2_verify_fw_preamble2() preamble corrupt"); + + free(buf); + free(buf2); +} + int main(int argc, char* argv[]) { test_memcmp(); @@ -667,6 +848,7 @@ int main(int argc, char* argv[]) test_sig_size(); test_verify_hash(); test_verify_keyblock(); + test_verify_fw_preamble(); return gTestSuccess ? 0 : 255; } |