| /* Copyright 2019 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * Tests for GBB library. |
| */ |
| |
| #include "2common.h" |
| #include "2misc.h" |
| #include "common/tests.h" |
| |
| /* Mock data */ |
| static char gbb_data[4096 + sizeof(struct vb2_gbb_header)]; |
| static struct vb2_gbb_header *gbb = (struct vb2_gbb_header *)gbb_data; |
| static struct vb2_packed_key *rootkey; |
| static struct vb2_context *ctx; |
| static struct vb2_workbuf wb; |
| static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] |
| __attribute__((aligned(VB2_WORKBUF_ALIGN))); |
| |
| static void set_gbb_hwid(const char *hwid, size_t size) |
| { |
| memcpy(gbb_data + gbb->hwid_offset, hwid, size); |
| gbb->hwid_size = size; |
| } |
| |
| static void reset_common_data(void) |
| { |
| int gbb_used; |
| |
| memset(gbb_data, 0, sizeof(gbb_data)); |
| gbb->header_size = sizeof(*gbb); |
| gbb->major_version = VB2_GBB_MAJOR_VER; |
| gbb->minor_version = VB2_GBB_MINOR_VER; |
| gbb->flags = 0; |
| gbb_used = sizeof(struct vb2_gbb_header); |
| |
| gbb->recovery_key_offset = gbb_used; |
| gbb->recovery_key_size = 64; |
| gbb_used += gbb->recovery_key_size; |
| gbb->rootkey_offset = gbb_used; |
| gbb->rootkey_size = sizeof(struct vb2_packed_key); |
| gbb_used += gbb->rootkey_size; |
| |
| rootkey = ((void *)gbb + gbb->rootkey_offset); |
| rootkey->key_offset = sizeof(*rootkey); |
| |
| gbb->hwid_offset = gbb_used; |
| const char hwid_src[] = "Test HWID"; |
| set_gbb_hwid(hwid_src, sizeof(hwid_src)); |
| |
| TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), |
| "vb2api_init failed"); |
| vb2_workbuf_from_ctx(ctx, &wb); |
| } |
| |
| /* Mocks */ |
| struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) |
| { |
| return gbb; |
| } |
| |
| vb2_error_t vb2ex_read_resource(struct vb2_context *c, |
| enum vb2_resource_index index, uint32_t offset, |
| void *buf, uint32_t size) |
| { |
| uint8_t *rptr; |
| uint32_t rsize; |
| |
| switch(index) { |
| case VB2_RES_GBB: |
| rptr = (uint8_t *)&gbb_data; |
| rsize = sizeof(gbb_data); |
| break; |
| default: |
| return VB2_ERROR_EX_READ_RESOURCE_INDEX; |
| } |
| |
| if (offset + size >= rsize) |
| return VB2_ERROR_EX_READ_RESOURCE_SIZE; |
| |
| memcpy(buf, rptr + offset, size); |
| return VB2_SUCCESS; |
| } |
| |
| /* Tests */ |
| static void flag_tests(void) |
| { |
| reset_common_data(); |
| gbb->flags = 0xdeadbeef; |
| TEST_EQ(vb2api_gbb_get_flags(ctx), gbb->flags, |
| "retrieve GBB flags"); |
| } |
| |
| static void key_tests(void) |
| { |
| /* Assume that root key and recovery key are dealt with using the same |
| code in our GBB library functions. */ |
| struct vb2_packed_key *keyp; |
| struct vb2_workbuf wborig; |
| const char key_data[] = "HELLOWORLD"; |
| uint32_t size; |
| |
| /* gbb.offset < sizeof(vb2_gbb_header) */ |
| reset_common_data(); |
| wborig = wb; |
| gbb->rootkey_offset = sizeof(*gbb) - 1; |
| TEST_EQ(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| VB2_ERROR_GBB_INVALID, |
| "gbb.rootkey offset too small"); |
| TEST_TRUE(wb.buf == wborig.buf, |
| " workbuf restored on error"); |
| |
| /* gbb.offset > gbb_data */ |
| reset_common_data(); |
| wborig = wb; |
| gbb->rootkey_offset = sizeof(gbb_data) + 1; |
| TEST_EQ(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| VB2_ERROR_EX_READ_RESOURCE_SIZE, |
| "gbb.rootkey offset too large"); |
| TEST_TRUE(wb.buf == wborig.buf, |
| " workbuf restored on error"); |
| |
| /* gbb.size < sizeof(vb2_packed_key) */ |
| reset_common_data(); |
| wborig = wb; |
| gbb->rootkey_size = sizeof(*rootkey) - 1; |
| TEST_EQ(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| VB2_ERROR_GBB_INVALID, |
| "gbb.rootkey size too small"); |
| TEST_TRUE(wb.buf == wborig.buf, |
| " workbuf restored on error"); |
| |
| /* sizeof(vb2_packed_key) > workbuf.size */ |
| reset_common_data(); |
| wborig = wb; |
| wb.size = sizeof(*rootkey) - 1; |
| TEST_EQ(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| VB2_ERROR_GBB_WORKBUF, |
| "workbuf size too small for vb2_packed_key header"); |
| TEST_TRUE(wb.buf == wborig.buf, |
| " workbuf restored on error"); |
| |
| /* packed_key.offset < sizeof(vb2_packed_key) */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_size = 1; |
| rootkey->key_offset = sizeof(*rootkey) - 1; |
| TEST_EQ(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| VB2_ERROR_INSIDE_DATA_OVERLAP, |
| "rootkey offset too small"); |
| TEST_TRUE(wb.buf == wborig.buf, |
| " workbuf restored on error"); |
| |
| /* packed_key.offset > gbb_data */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_size = 1; |
| rootkey->key_offset = sizeof(gbb_data) + 1; |
| gbb->rootkey_size = rootkey->key_offset + rootkey->key_size; |
| TEST_EQ(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| VB2_ERROR_EX_READ_RESOURCE_SIZE, |
| "rootkey size too large"); |
| TEST_TRUE(wb.buf == wborig.buf, |
| " workbuf restored on error"); |
| |
| /* packed_key.size > workbuf.size */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_size = wb.size + 1; |
| gbb->rootkey_size = rootkey->key_offset + rootkey->key_size + 1; |
| TEST_EQ(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| VB2_ERROR_GBB_WORKBUF, |
| "workbuf size too small for vb2_packed_key contents"); |
| TEST_TRUE(wb.buf == wborig.buf, |
| " workbuf restored on error"); |
| |
| /* gbb.size < sizeof(vb2_packed_key) + packed_key.size */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_size = 2; |
| gbb->rootkey_size = rootkey->key_offset + rootkey->key_size - 1; |
| TEST_EQ(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| VB2_ERROR_INSIDE_DATA_OUTSIDE, |
| "rootkey size exceeds gbb.rootkey size"); |
| TEST_TRUE(wb.buf == wborig.buf, |
| " workbuf restored on error"); |
| |
| /* gbb.size == sizeof(vb2_packed_key) + packed_key.size */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_size = sizeof(key_data); |
| memcpy((void *)rootkey + rootkey->key_offset, |
| key_data, sizeof(key_data)); |
| gbb->rootkey_size = rootkey->key_offset + rootkey->key_size; |
| TEST_SUCC(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| "succeeds when gbb.rootkey and rootkey sizes agree"); |
| TEST_TRUE(wb.size < wborig.size, |
| " workbuf shrank on success"); |
| TEST_EQ(memcmp(rootkey, keyp, rootkey->key_offset + rootkey->key_size), |
| 0, " copied key data successfully"); |
| TEST_EQ(size, rootkey->key_offset + rootkey->key_size, |
| " correct size returned"); |
| |
| /* gbb.size > sizeof(vb2_packed_key) + packed_key.size |
| packed_key.offset = +0 */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_size = 1; |
| gbb->rootkey_size = rootkey->key_offset + rootkey->key_size + 1; |
| TEST_SUCC(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| "succeeds when gbb.rootkey is padded after key"); |
| TEST_TRUE(wb.size < wborig.size, |
| " workbuf shrank on success"); |
| TEST_EQ(size, rootkey->key_offset + rootkey->key_size, |
| " correct size returned"); |
| |
| /* gbb.size > sizeof(vb2_packed_key) + packed_key.size |
| packed_key.offset = +1 */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_offset = sizeof(*rootkey) + 1; |
| rootkey->key_size = 1; |
| gbb->rootkey_size = rootkey->key_offset + rootkey->key_size; |
| TEST_SUCC(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| "succeeds when gbb.rootkey is padded before key"); |
| TEST_TRUE(wb.size < wborig.size, |
| " workbuf shrank on success"); |
| TEST_EQ(size, rootkey->key_offset + rootkey->key_size, |
| " correct size returned"); |
| |
| /* packed_key.size = 0, packed_key.offset = +1 */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_offset = sizeof(*rootkey) + 1; |
| rootkey->key_size = 0; |
| gbb->rootkey_size = rootkey->key_offset + rootkey->key_size + 1; |
| TEST_SUCC(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| "succeeds when gbb.rootkey is padded; empty test key"); |
| TEST_TRUE(wb.size < wborig.size, |
| " workbuf shrank on success"); |
| TEST_EQ(size, rootkey->key_offset + rootkey->key_size, |
| " correct size returned"); |
| |
| /* packed_key.size = 0, packed_key.offset = -1 */ |
| reset_common_data(); |
| wborig = wb; |
| rootkey->key_offset = sizeof(*rootkey) - 1; |
| rootkey->key_size = 0; |
| gbb->rootkey_size = sizeof(*rootkey) + rootkey->key_size + 1; |
| TEST_SUCC(vb2_gbb_read_root_key(ctx, &keyp, &size, &wb), |
| "succeeds when gbb.rootkey is padded; empty test key"); |
| TEST_TRUE(wb.size < wborig.size, |
| " workbuf shrank on success"); |
| TEST_EQ(size, sizeof(*rootkey), " correct size returned"); |
| } |
| |
| static void hwid_tests(void) |
| { |
| char hwid[VB2_GBB_HWID_MAX_SIZE]; |
| uint32_t size; |
| |
| /* GBB HWID size = 0 */ |
| { |
| reset_common_data(); |
| gbb->hwid_size = 0; |
| size = VB2_GBB_HWID_MAX_SIZE; |
| TEST_EQ(vb2api_gbb_read_hwid(ctx, hwid, &size), |
| VB2_ERROR_GBB_INVALID, |
| "GBB HWID size invalid (HWID missing)"); |
| } |
| |
| /* GBB HWID offset > GBB size */ |
| { |
| reset_common_data(); |
| gbb->hwid_offset = sizeof(gbb_data) + 1; |
| size = VB2_GBB_HWID_MAX_SIZE; |
| TEST_EQ(vb2api_gbb_read_hwid(ctx, hwid, &size), |
| VB2_ERROR_EX_READ_RESOURCE_SIZE, |
| "GBB HWID offset invalid"); |
| } |
| |
| /* buffer size < HWID size */ |
| { |
| const char hwid_src[] = "Test HWID"; |
| reset_common_data(); |
| set_gbb_hwid(hwid_src, sizeof(hwid_src)); |
| size = sizeof(hwid_src) - 1; |
| TEST_EQ(vb2api_gbb_read_hwid(ctx, hwid, &size), |
| VB2_ERROR_INVALID_PARAMETER, |
| "HWID too large for buffer"); |
| } |
| |
| /* GBB HWID size < HWID size */ |
| { |
| const char hwid_src[] = "Test HWID"; |
| reset_common_data(); |
| set_gbb_hwid(hwid_src, sizeof(hwid_src) - 1); |
| size = sizeof(hwid_src); |
| TEST_EQ(vb2api_gbb_read_hwid(ctx, hwid, &size), |
| VB2_ERROR_INVALID_PARAMETER, |
| "HWID larger than GBB HWID size"); |
| } |
| |
| /* buffer size == HWID size */ |
| { |
| const char hwid_src[] = "Test HWID"; |
| reset_common_data(); |
| set_gbb_hwid(hwid_src, sizeof(hwid_src)); |
| size = sizeof(hwid_src); |
| TEST_SUCC(vb2api_gbb_read_hwid(ctx, hwid, &size), |
| "read normal HWID"); |
| TEST_EQ(strcmp(hwid, "Test HWID"), 0, " HWID correct"); |
| TEST_EQ(strlen(hwid) + 1, size, " HWID size consistent"); |
| TEST_EQ(strlen(hwid), strlen("Test HWID"), |
| " HWID size correct"); |
| } |
| |
| /* buffer size > HWID size */ |
| { |
| const char hwid_src[] = "Test HWID"; |
| reset_common_data(); |
| set_gbb_hwid(hwid_src, sizeof(hwid_src)); |
| size = sizeof(hwid_src) + 1; |
| TEST_SUCC(vb2api_gbb_read_hwid(ctx, hwid, &size), |
| "read normal HWID"); |
| TEST_EQ(strcmp(hwid, "Test HWID"), 0, " HWID correct"); |
| TEST_EQ(strlen(hwid) + 1, size, " HWID size consistent"); |
| TEST_EQ(strlen(hwid), strlen("Test HWID"), |
| " HWID size correct"); |
| } |
| |
| /* HWID with garbage */ |
| { |
| const char hwid_src[] = "Test HWID\0garbagegarbage"; |
| reset_common_data(); |
| set_gbb_hwid(hwid_src, sizeof(hwid_src)); |
| size = VB2_GBB_HWID_MAX_SIZE; |
| TEST_SUCC(vb2api_gbb_read_hwid(ctx, hwid, &size), |
| "read HWID with garbage"); |
| TEST_EQ(strcmp(hwid, "Test HWID"), 0, " HWID correct"); |
| TEST_EQ(strlen(hwid) + 1, size, " HWID size consistent"); |
| TEST_EQ(strlen(hwid), strlen("Test HWID"), |
| " HWID size correct"); |
| } |
| } |
| |
| int main(int argc, char* argv[]) |
| { |
| flag_tests(); |
| key_tests(); |
| hwid_tests(); |
| |
| return gTestSuccess ? 0 : 255; |
| } |