diff options
Diffstat (limited to 'tests/bdb_test.c')
-rw-r--r-- | tests/bdb_test.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/tests/bdb_test.c b/tests/bdb_test.c new file mode 100644 index 00000000..6c9022dc --- /dev/null +++ b/tests/bdb_test.c @@ -0,0 +1,492 @@ +/* Copyright (c) 2015 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. + * + * Unit tests + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "bdb.h" +#include "host.h" + +#define TEST_EQ(got, want) test_eq(got, want, #got, #want, __LINE__) + +void test_eq(int got, int want, const char *gotstr, const char *wantstr, + int line) +{ + if (got == want) + return; + + fprintf(stderr, "Fail(%d): %s != %s\n" + "got: 0x%08x (%d)\n" + "wanted: 0x%08x (%d)\n", + line, gotstr, wantstr, got, got, want, want); + exit(1); +} + +void check_header_tests(void) +{ + struct bdb_header sgood = { + .struct_magic = BDB_HEADER_MAGIC, + .struct_major_version = BDB_HEADER_VERSION_MAJOR, + .struct_minor_version = BDB_HEADER_VERSION_MINOR, + .struct_size = sizeof(struct bdb_header), + .bdb_load_address = -1, + .bdb_size = 1024, + .signed_size = 512, + .oem_area_0_size = 256, + }; + const size_t ssize = sgood.struct_size; + struct bdb_header s; + + s = sgood; + TEST_EQ(bdb_check_header(&s, ssize), BDB_SUCCESS); + TEST_EQ(bdb_check_header(&s, ssize - 1), BDB_ERROR_BUF_SIZE); + + s = sgood; + s.struct_size++; + TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_BUF_SIZE); + + s = sgood; + s.struct_size--; + TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_SIZE); + + s = sgood; + s.struct_magic++; + TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_MAGIC); + + s = sgood; + s.struct_major_version++; + TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_VERSION); + + s = sgood; + s.oem_area_0_size++; + TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_OEM_AREA_SIZE); + + s = sgood; + s.bdb_size = ssize - 1; + TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_BDB_SIZE); +} + +void check_key_tests(void) +{ + struct bdb_key sgood = { + .struct_magic = BDB_KEY_MAGIC, + .struct_major_version = BDB_KEY_VERSION_MAJOR, + .struct_minor_version = BDB_KEY_VERSION_MINOR, + .struct_size = (sizeof(struct bdb_key) + + BDB_RSA4096_KEY_DATA_SIZE), + .hash_alg = BDB_HASH_ALG_SHA256, + .sig_alg = BDB_SIG_ALG_RSA4096, + .key_version = 1, + .description = "Test key", + }; + const size_t ssize = sgood.struct_size; + struct bdb_key s; + + s = sgood; + TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS); + TEST_EQ(bdb_check_key(&s, ssize - 1), BDB_ERROR_BUF_SIZE); + + s = sgood; + s.struct_size++; + TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_BUF_SIZE); + + s = sgood; + s.struct_size--; + TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_SIZE); + + s = sgood; + s.struct_magic++; + TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_MAGIC); + + s = sgood; + s.struct_major_version++; + TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_VERSION); + + /* Description must contain a null */ + s = sgood; + memset(s.description, 'x', sizeof(s.description)); + TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_DESCRIPTION); + + /* Data AFTER the null is explicitly allowed, though */ + s = sgood; + s.description[100] = 'x'; + TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS); + + /* Limited algorithm choices at present */ + s = sgood; + s.hash_alg = BDB_HASH_ALG_INVALID; + TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_HASH_ALG); + + /* This works because ECDSA521 signatures are smaller than RSA4096 */ + s = sgood; + s.sig_alg = BDB_SIG_ALG_ECSDSA521; + TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS); + + s = sgood; + s.sig_alg = BDB_SIG_ALG_INVALID; + TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_SIG_ALG); +} + +void check_sig_tests(void) +{ + struct bdb_sig sgood = { + .struct_magic = BDB_SIG_MAGIC, + .struct_major_version = BDB_SIG_VERSION_MAJOR, + .struct_minor_version = BDB_SIG_VERSION_MINOR, + .struct_size = sizeof(struct bdb_sig) + BDB_RSA4096_SIG_SIZE, + .hash_alg = BDB_HASH_ALG_SHA256, + .sig_alg = BDB_SIG_ALG_RSA4096, + .signed_size = 123, + .description = "Test sig", + }; + const size_t ssize = sgood.struct_size; + struct bdb_sig s; + + s = sgood; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS); + TEST_EQ(bdb_check_sig(&s, ssize - 1), BDB_ERROR_BUF_SIZE); + + s = sgood; + s.struct_size++; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_BUF_SIZE); + + s = sgood; + s.struct_size--; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_SIZE); + + s = sgood; + s.struct_magic++; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_MAGIC); + + s = sgood; + s.struct_major_version++; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_VERSION); + + /* Description must contain a null */ + s = sgood; + memset(s.description, 'x', sizeof(s.description)); + TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_DESCRIPTION); + + /* Data AFTER the null is explicitly allowed, though */ + s = sgood; + s.description[100] = 'x'; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS); + + /* Limited algorithm choices at present */ + s = sgood; + s.hash_alg = BDB_HASH_ALG_INVALID; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_HASH_ALG); + + /* This works because ECDSA521 signatures are smaller than RSA4096 */ + s = sgood; + s.sig_alg = BDB_SIG_ALG_ECSDSA521; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS); + + s = sgood; + s.sig_alg = BDB_SIG_ALG_INVALID; + TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_SIG_ALG); +} + +void check_data_tests(void) +{ + struct bdb_data sgood = { + .struct_magic = BDB_DATA_MAGIC, + .struct_major_version = BDB_DATA_VERSION_MAJOR, + .struct_minor_version = BDB_DATA_VERSION_MINOR, + .struct_size = sizeof(struct bdb_data), + .data_version = 1, + .oem_area_1_size = 256, + .num_hashes = 3, + .hash_entry_size = sizeof(struct bdb_hash), + .signed_size = 2048, + .description = "Test data", + }; + const size_t ssize = sgood.signed_size; + struct bdb_data s; + + s = sgood; + TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS); + TEST_EQ(bdb_check_data(&s, ssize - 1), BDB_ERROR_BUF_SIZE); + + s = sgood; + s.struct_size--; + TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_SIZE); + + s = sgood; + s.struct_magic++; + TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_MAGIC); + + s = sgood; + s.struct_major_version++; + TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_VERSION); + + /* Description must contain a null */ + s = sgood; + memset(s.description, 'x', sizeof(s.description)); + TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_DESCRIPTION); + + /* Data AFTER the null is explicitly allowed, though */ + s = sgood; + s.description[100] = 'x'; + TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS); + + s = sgood; + s.hash_entry_size--; + TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_HASH_ENTRY_SIZE); + + s = sgood; + s.oem_area_1_size++; + TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_OEM_AREA_SIZE); + + /* Check exact size needed */ + s = sgood; + s.signed_size = sizeof(s) + s.num_hashes * sizeof(struct bdb_hash) + + s.oem_area_1_size; + TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS); + s.signed_size--; + TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_SIGNED_SIZE); + + /* + * TODO: Verify wraparound check works. That can only be tested on a + * platform where size_t is uint32_t, because otherwise a 32-bit + * oem_area_1_size can't cause wraparound. + */ +} + +/** + * Test bdb_verify() and bdb_create() + */ +void check_bdb_verify(void) +{ + uint8_t oem_area_0[32] = "Some OEM area."; + uint8_t oem_area_1[64] = "Some other OEM area."; + + struct bdb_hash hash[2] = { + { + .offset = 0x10000, + .size = 0x18000, + .partition = 1, + .type = BDB_DATA_SP_RW, + .load_address = 0x100000, + .digest = {0x11, 0x11, 0x11, 0x10}, + }, + { + .offset = 0x28000, + .size = 0x20000, + .partition = 1, + .type = BDB_DATA_AP_RW, + .load_address = 0x200000, + .digest = {0x22, 0x22, 0x22, 0x20}, + }, + }; + + struct bdb_create_params p = { + .bdb_load_address = 0x11223344, + .oem_area_0 = oem_area_0, + .oem_area_0_size = sizeof(oem_area_0), + .oem_area_1 = oem_area_1, + .oem_area_1_size = sizeof(oem_area_1), + .header_sig_description = "The header sig", + .data_sig_description = "The data sig", + .data_description = "Test BDB data", + .data_version = 3, + .hash = hash, + .num_hashes = 2, + }; + + uint8_t bdbkey_digest[BDB_SHA256_DIGEST_SIZE]; + struct bdb_header *hgood, *h; + size_t hsize; + + /* Load keys */ + p.bdbkey = bdb_create_key("testkeys/bdbkey.keyb", 100, "BDB key"); + p.subkey = bdb_create_key("testkeys/subkey.keyb", 200, "Subkey"); + p.private_bdbkey = read_pem("testkeys/bdbkey.pem"); + p.private_subkey = read_pem("testkeys/subkey.pem"); + if (!p.bdbkey || !p.subkey || !p.private_bdbkey || !p.private_subkey) { + fprintf(stderr, "Unable to load test keys\n"); + exit(2); + } + + bdb_sha256(bdbkey_digest, p.bdbkey, p.bdbkey->struct_size); + + /* Create the test BDB */ + hgood = bdb_create(&p); + if (!hgood) { + fprintf(stderr, "Unable to create test BDB\n"); + exit(2); + } + hsize = hgood->bdb_size; + + /* Allocate a copy we can mangle */ + h = calloc(hsize, 1); + + /* As created, it should pass */ + memcpy(h, hgood, hsize); + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_SUCCESS); + + /* Mangle each component in turn */ + memcpy(h, hgood, hsize); + h->struct_magic++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER); + + memcpy(h, hgood, hsize); + ((struct bdb_key *)bdb_get_bdbkey(h))->struct_magic++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_BDBKEY); + + memcpy(h, hgood, hsize); + ((struct bdb_key *)bdb_get_bdbkey(h))->key_version++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_GOOD_OTHER_THAN_KEY); + + memcpy(h, hgood, hsize); + h->oem_area_0_size += hsize; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_OEM_AREA_0); + + memcpy(h, hgood, hsize); + ((struct bdb_key *)bdb_get_subkey(h))->struct_magic++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_SUBKEY); + + memcpy(h, hgood, hsize); + ((struct bdb_key *)bdb_get_subkey(h))->struct_size += 4; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_BDB_SIGNED_SIZE); + + memcpy(h, hgood, hsize); + ((struct bdb_sig *)bdb_get_header_sig(h))->struct_magic++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG); + + memcpy(h, hgood, hsize); + ((struct bdb_sig *)bdb_get_header_sig(h))->signed_size--; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG); + + memcpy(h, hgood, hsize); + ((struct bdb_sig *)bdb_get_header_sig(h))->sig_data[0] ^= 0x42; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG); + + /* Also make sure the header sig really covers all the fields */ + memcpy(h, hgood, hsize); + ((struct bdb_key *)bdb_get_subkey(h))->key_version++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG); + + memcpy(h, hgood, hsize); + ((uint8_t *)bdb_get_oem_area_0(h))[0] ^= 0x42; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG); + + memcpy(h, hgood, hsize); + ((uint8_t *)bdb_get_oem_area_0(h))[p.oem_area_0_size - 1] ^= 0x24; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG); + + /* Check data header */ + memcpy(h, hgood, hsize); + ((struct bdb_data *)bdb_get_data(h))->struct_magic++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA); + + memcpy(h, hgood, hsize); + ((struct bdb_sig *)bdb_get_data_sig(h))->struct_magic++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG); + + memcpy(h, hgood, hsize); + ((struct bdb_sig *)bdb_get_data_sig(h))->signed_size--; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG); + + memcpy(h, hgood, hsize); + ((struct bdb_sig *)bdb_get_data_sig(h))->sig_data[0] ^= 0x42; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG); + + /* Also make sure the data sig really covers all the fields */ + memcpy(h, hgood, hsize); + ((struct bdb_data *)bdb_get_data(h))->data_version--; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG); + + memcpy(h, hgood, hsize); + ((uint8_t *)bdb_get_oem_area_1(h))[0] ^= 0x42; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG); + + memcpy(h, hgood, hsize); + ((uint8_t *)bdb_get_oem_area_1(h))[p.oem_area_1_size - 1] ^= 0x24; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG); + + memcpy(h, hgood, hsize); + ((struct bdb_hash *)bdb_get_hash(h, BDB_DATA_SP_RW))->offset++; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG); + + memcpy(h, hgood, hsize); + ((struct bdb_hash *)bdb_get_hash(h, BDB_DATA_AP_RW))->digest[0] ^= 0x96; + TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG); + + /* + * This is also a convenient place to test that all the parameters we + * fed into bdb_create() also worked. That also tests all the + * bdb_get_*() functions. + */ + memcpy(h, hgood, hsize); + TEST_EQ(h->bdb_load_address, p.bdb_load_address); + + TEST_EQ(strcmp(bdb_get_bdbkey(h)->description, + p.bdbkey->description), 0); + TEST_EQ(bdb_get_bdbkey(h)->key_version, p.bdbkey->key_version); + + TEST_EQ(h->oem_area_0_size, p.oem_area_0_size); + TEST_EQ(memcmp(bdb_get_oem_area_0(h), oem_area_0, sizeof(oem_area_0)), + 0); + + TEST_EQ(strcmp(bdb_get_subkey(h)->description, p.subkey->description), + 0); + TEST_EQ(bdb_get_subkey(h)->key_version, p.subkey->key_version); + + TEST_EQ(strcmp(bdb_get_header_sig(h)->description, + p.header_sig_description), 0); + + TEST_EQ(strcmp(bdb_get_data(h)->description, p.data_description), 0); + TEST_EQ(bdb_get_data(h)->data_version, p.data_version); + TEST_EQ(bdb_get_data(h)->num_hashes, p.num_hashes); + + TEST_EQ(bdb_get_data(h)->oem_area_1_size, p.oem_area_1_size); + TEST_EQ(memcmp(bdb_get_oem_area_1(h), oem_area_1, sizeof(oem_area_1)), + 0); + + TEST_EQ(strcmp(bdb_get_data_sig(h)->description, + p.data_sig_description), 0); + + /* Test getting hash entries */ + memcpy(h, hgood, hsize); + TEST_EQ(bdb_get_hash(h, BDB_DATA_SP_RW)->offset, hash[0].offset); + TEST_EQ(bdb_get_hash(h, BDB_DATA_AP_RW)->offset, hash[1].offset); + /* And a non-existent one */ + TEST_EQ(bdb_get_hash(h, BDB_DATA_MCU)!=NULL, 0); + + /* + * TODO: Verify wraparound checks works. That can only be tested on a + * platform where size_t is uint32_t, because otherwise a 32-bit + * oem_area_1_size can't cause wraparound. + */ + + /* Free keys and buffers */ + free(p.bdbkey); + free(p.subkey); + RSA_free(p.private_bdbkey); + RSA_free(p.private_subkey); + free(hgood); + free(h); +} + +/*****************************************************************************/ + +int main(void) +{ + printf("Running tests...\n"); + + check_header_tests(); + check_key_tests(); + check_sig_tests(); + check_data_tests(); + check_bdb_verify(); + + printf("All tests passed!\n"); + + return 0; +} |