summaryrefslogtreecommitdiff
path: root/tests/bdb_sprw_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/bdb_sprw_test.c')
-rw-r--r--tests/bdb_sprw_test.c328
1 files changed, 314 insertions, 14 deletions
diff --git a/tests/bdb_sprw_test.c b/tests/bdb_sprw_test.c
index 4528751d..06739ae5 100644
--- a/tests/bdb_sprw_test.c
+++ b/tests/bdb_sprw_test.c
@@ -10,12 +10,14 @@
#include <string.h>
#include "2sha.h"
+#include "2hmac.h"
#include "bdb.h"
#include "bdb_api.h"
#include "bdb_struct.h"
#include "host.h"
#include "test_common.h"
#include "vboot_register.h"
+#include "secrets.h"
static struct bdb_header *bdb, *bdb0, *bdb1;
static uint32_t vboot_register;
@@ -24,6 +26,16 @@ static char slot_selected;
static uint8_t aprw_digest[BDB_SHA256_DIGEST_SIZE];
static uint8_t reset_count;
+/* NVM-RW image in storage (e.g. EEPROM) */
+static uint8_t nvmrw1[NVM_RW_MAX_STRUCT_SIZE];
+static uint8_t nvmrw2[NVM_RW_MAX_STRUCT_SIZE];
+
+struct bdb_ro_secrets secrets = {
+ .nvm_rw = {0x00, },
+};
+
+static int vbe_write_nvm_failure = 0;
+
static struct bdb_header *create_bdb(const char *key_dir,
struct bdb_hash *hash, int num_hashes)
{
@@ -215,12 +227,12 @@ static void test_verify_aprw(const char *key_dir)
slot_selected = 'X';
memcpy(aprw_digest, hash0.digest, 4);
vbe_reset();
- TEST_EQ_S(reset_count, 1);
- TEST_EQ_S(slot_selected, 'A');
+ TEST_EQ(reset_count, 1, NULL);
+ TEST_EQ(slot_selected, 'A', NULL);
TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY,
- "VBOOT_REGISTER_FAILED_RW_PRIMARY==false");
+ NULL);
TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY,
- "VBOOT_REGISTER_FAILED_RW_SECONDARY==false");
+ NULL);
/* (slotA, slotB) = (bad, good) */
reset_count = 0;
@@ -228,12 +240,12 @@ static void test_verify_aprw(const char *key_dir)
slot_selected = 'X';
memcpy(aprw_digest, hash1.digest, 4);
vbe_reset();
- TEST_EQ_S(reset_count, 3);
- TEST_EQ_S(slot_selected, 'B');
+ TEST_EQ(reset_count, 3, NULL);
+ TEST_EQ(slot_selected, 'B', NULL);
TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY,
- "VBOOT_REGISTER_FAILED_RW_PRIMARY==true");
+ NULL);
TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY,
- "VBOOT_REGISTER_FAILED_RW_SECONDARY==false");
+ NULL);
/* (slotA, slotB) = (bad, bad) */
reset_count = 0;
@@ -241,21 +253,306 @@ static void test_verify_aprw(const char *key_dir)
slot_selected = 'X';
memset(aprw_digest, 0, BDB_SHA256_DIGEST_SIZE);
vbe_reset();
- TEST_EQ_S(reset_count, 5);
- TEST_EQ_S(slot_selected, 'X');
+ TEST_EQ(reset_count, 5, NULL);
+ TEST_EQ(slot_selected, 'X', NULL);
TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY,
- "VBOOT_REGISTER_FAILED_RW_PRIMARY==true");
+ NULL);
TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY,
- "VBOOT_REGISTER_FAILED_RW_SECONDARY==true");
+ NULL);
TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_RECOVERY_REQUEST,
- "Recovery request");
+ NULL);
/* Clean up */
free(bdb0);
free(bdb1);
}
-/*****************************************************************************/
+int vbe_read_nvm(enum nvm_type type, uint8_t *buf, uint32_t size)
+{
+ /* Read NVM-RW contents (from EEPROM for example) */
+ switch (type) {
+ case NVM_TYPE_RW_PRIMARY:
+ if (sizeof(nvmrw1) < size)
+ return -1;
+ memcpy(buf, nvmrw1, size);
+ break;
+ case NVM_TYPE_RW_SECONDARY:
+ if (sizeof(nvmrw2) < size)
+ return -1;
+ memcpy(buf, nvmrw2, size);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int vbe_write_nvm(enum nvm_type type, void *buf, uint32_t size)
+{
+ if (vbe_write_nvm_failure > 0) {
+ fprintf(stderr, "Failed to write NVM (type=%d failure=%d)\n",
+ type, vbe_write_nvm_failure);
+ vbe_write_nvm_failure--;
+ return -1;
+ }
+
+ /* Write NVM-RW contents (to EEPROM for example) */
+ switch (type) {
+ case NVM_TYPE_RW_PRIMARY:
+ memcpy(nvmrw1, buf, size);
+ break;
+ case NVM_TYPE_RW_SECONDARY:
+ memcpy(nvmrw2, buf, size);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static void install_nvm(enum nvm_type type,
+ uint32_t min_kernel_data_key_version,
+ uint32_t min_kernel_version,
+ uint32_t update_count)
+{
+ struct nvmrw nvm = {
+ .struct_magic = NVM_RW_MAGIC,
+ .struct_major_version = NVM_HEADER_VERSION_MAJOR,
+ .struct_minor_version = NVM_HEADER_VERSION_MINOR,
+ .struct_size = sizeof(struct nvmrw),
+ .min_kernel_data_key_version = min_kernel_data_key_version,
+ .min_kernel_version = min_kernel_version,
+ .update_count = update_count,
+ };
+
+ /* Compute HMAC */
+ hmac(VB2_HASH_SHA256, secrets.nvm_rw, BDB_SECRET_SIZE,
+ &nvm, nvm.struct_size - sizeof(nvm.hmac),
+ nvm.hmac, sizeof(nvm.hmac));
+
+ /* Install NVM-RWs (in EEPROM for example) */
+ switch (type) {
+ case NVM_TYPE_RW_PRIMARY:
+ memset(nvmrw1, 0, sizeof(nvmrw1));
+ memcpy(nvmrw1, &nvm, sizeof(nvm));
+ break;
+ case NVM_TYPE_RW_SECONDARY:
+ memset(nvmrw2, 0, sizeof(nvmrw2));
+ memcpy(nvmrw2, &nvm, sizeof(nvm));
+ break;
+ default:
+ fprintf(stderr, "Unsupported NVM type (%d)\n", type);
+ exit(2);
+ return;
+ }
+}
+
+static void test_nvm_read(void)
+{
+ struct vba_context ctx = {
+ .bdb = NULL,
+ .ro_secrets = &secrets,
+ };
+ struct nvmrw *nvm;
+ uint8_t nvmrw1_copy[NVM_RW_MAX_STRUCT_SIZE];
+ uint8_t nvmrw2_copy[NVM_RW_MAX_STRUCT_SIZE];
+
+ install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
+ install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 0);
+ memcpy(nvmrw1_copy, nvmrw1, sizeof(nvmrw1));
+ memcpy(nvmrw2_copy, nvmrw2, sizeof(nvmrw2));
+
+ /* Test nvm_read: both good -> pick primary, no sync */
+ memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
+ TEST_SUCC(nvmrw_read(&ctx), NULL);
+ TEST_SUCC(memcmp(&ctx.nvmrw, nvmrw1, sizeof(*nvm)), NULL);
+ TEST_SUCC(memcmp(nvmrw1, nvmrw1_copy, sizeof(nvmrw1)), NULL);
+ TEST_SUCC(memcmp(nvmrw2, nvmrw2_copy, sizeof(nvmrw2)), NULL);
+
+ /* Test nvm_read: primary bad -> pick secondary */
+ install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
+ install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 0);
+ memcpy(nvmrw2_copy, nvmrw2, sizeof(*nvm));
+ nvm = (struct nvmrw *)nvmrw1;
+ nvm->hmac[0] ^= 0xff;
+ memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
+ TEST_SUCC(nvmrw_read(&ctx), NULL);
+ TEST_SUCC(memcmp(&ctx.nvmrw, nvmrw2, sizeof(*nvm)), NULL);
+ TEST_SUCC(memcmp(nvmrw1, nvmrw2_copy, sizeof(nvmrw2)), NULL);
+ TEST_SUCC(memcmp(nvmrw2, nvmrw2_copy, sizeof(nvmrw2)), NULL);
+
+ /* Test nvm_read: secondary bad -> pick primary */
+ install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
+ install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 0);
+ memcpy(nvmrw1_copy, nvmrw1, sizeof(*nvm));
+ nvm = (struct nvmrw *)nvmrw2;
+ nvm->hmac[0] ^= 0xff;
+ memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
+ TEST_SUCC(nvmrw_read(&ctx), NULL);
+ TEST_SUCC(memcmp(&ctx.nvmrw, nvmrw1, sizeof(*nvm)), NULL);
+ TEST_SUCC(memcmp(nvmrw1, nvmrw1_copy, sizeof(nvmrw1)), NULL);
+ TEST_SUCC(memcmp(nvmrw2, nvmrw1_copy, sizeof(nvmrw1)), NULL);
+
+ /* Test nvm_read: both bad */
+ nvm = (struct nvmrw *)nvmrw1;
+ nvm->hmac[0] ^= 0xff;
+ nvm = (struct nvmrw *)nvmrw2;
+ nvm->hmac[0] ^= 0xff;
+ memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
+ TEST_EQ(nvmrw_read(&ctx), BDB_ERROR_NVM_RW_INVALID_HMAC, NULL);
+
+ /* Test update count: secondary new -> pick secondary */
+ install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
+ install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 1);
+ memcpy(nvmrw2_copy, nvmrw2, sizeof(*nvm));
+ memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
+ TEST_SUCC(nvmrw_read(&ctx), NULL);
+ TEST_SUCC(memcmp(&ctx.nvmrw, nvmrw2, sizeof(*nvm)), NULL);
+ TEST_SUCC(memcmp(nvmrw1, nvmrw2_copy, sizeof(nvmrw1)), NULL);
+ TEST_SUCC(memcmp(nvmrw2, nvmrw2_copy, sizeof(nvmrw2)), NULL);
+
+ /* Test old reader -> minor version downgrade */
+ install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
+ install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 1);
+ memset(&ctx.nvmrw, 0, sizeof(ctx.nvmrw));
+ nvm = (struct nvmrw *)nvmrw1;
+ nvm->struct_minor_version++;
+ nvm->struct_size++;
+ TEST_SUCC(nvmrw_read(&ctx), NULL);
+ TEST_EQ(ctx.nvmrw.struct_minor_version, NVM_HEADER_VERSION_MINOR, NULL);
+ TEST_EQ(ctx.nvmrw.struct_size, sizeof(*nvm), NULL);
+}
+
+static void verify_nvm_write(struct vba_context *ctx,
+ int expected_result)
+{
+ struct nvmrw *nvmrw;
+ struct nvmrw *nvm = &ctx->nvmrw;
+
+ TEST_EQ(nvmrw_write(ctx, NVM_TYPE_RW_PRIMARY), expected_result, NULL);
+
+ if (expected_result != BDB_SUCCESS)
+ return;
+
+ nvmrw = (struct nvmrw *)nvmrw1;
+ TEST_EQ(nvmrw->min_kernel_data_key_version,
+ nvm->min_kernel_data_key_version, NULL);
+ TEST_EQ(nvmrw->min_kernel_version, nvm->min_kernel_version, NULL);
+ TEST_EQ(nvmrw->update_count, nvm->update_count, NULL);
+}
+
+static void test_nvm_write(void)
+{
+ struct vba_context ctx = {
+ .bdb = NULL,
+ .ro_secrets = &secrets,
+ };
+ struct nvmrw nvm = {
+ .struct_magic = NVM_RW_MAGIC,
+ .struct_major_version = NVM_HEADER_VERSION_MAJOR,
+ .struct_minor_version = NVM_HEADER_VERSION_MINOR,
+ .struct_size = sizeof(struct nvmrw),
+ .min_kernel_data_key_version = 1,
+ .min_kernel_version = 2,
+ .update_count = 3,
+ };
+
+ /* Test normal case */
+ memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
+ vbe_write_nvm_failure = 0;
+ verify_nvm_write(&ctx, BDB_SUCCESS);
+
+ /* Test write failure: once */
+ memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
+ vbe_write_nvm_failure = 1;
+ verify_nvm_write(&ctx, BDB_SUCCESS);
+
+ /* Test write failure: twice */
+ memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
+ vbe_write_nvm_failure = 2;
+ verify_nvm_write(&ctx, BDB_ERROR_NVM_WRITE);
+
+ /* Test invalid struct magic */
+ memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
+ ctx.nvmrw.struct_magic ^= 0xff;
+ verify_nvm_write(&ctx, BDB_ERROR_NVM_RW_MAGIC);
+
+ /* Test struct size too small */
+ memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
+ ctx.nvmrw.struct_size = NVM_RW_MIN_STRUCT_SIZE - 1;
+ verify_nvm_write(&ctx, BDB_ERROR_NVM_STRUCT_SIZE);
+
+ /* Test struct size too large */
+ memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
+ ctx.nvmrw.struct_size = NVM_RW_MAX_STRUCT_SIZE + 1;
+ verify_nvm_write(&ctx, BDB_ERROR_NVM_STRUCT_SIZE);
+
+ /* Test invalid struct version */
+ memcpy(&ctx.nvmrw, &nvm, sizeof(nvm));
+ ctx.nvmrw.struct_major_version = NVM_HEADER_VERSION_MAJOR - 1;
+ verify_nvm_write(&ctx, BDB_ERROR_NVM_STRUCT_VERSION);
+
+ vbe_write_nvm_failure = 0;
+}
+
+static void verify_kernel_version(uint32_t min_kernel_data_key_version,
+ uint32_t new_kernel_data_key_version,
+ uint32_t min_kernel_version,
+ uint32_t new_kernel_version,
+ int expected_result)
+{
+ struct vba_context ctx = {
+ .bdb = NULL,
+ .ro_secrets = &secrets,
+ };
+ struct nvmrw *nvm = (struct nvmrw *)nvmrw1;
+ uint32_t expected_kernel_data_key_version = min_kernel_data_key_version;
+ uint32_t expected_kernel_version = min_kernel_version;
+ int should_update = 0;
+
+ if (min_kernel_data_key_version < new_kernel_data_key_version) {
+ expected_kernel_data_key_version = new_kernel_data_key_version;
+ should_update = 1;
+ }
+ if (min_kernel_version < new_kernel_version) {
+ expected_kernel_version = new_kernel_version;
+ should_update = 1;
+ }
+
+ install_nvm(NVM_TYPE_RW_PRIMARY, min_kernel_data_key_version,
+ min_kernel_version, 0);
+ install_nvm(NVM_TYPE_RW_SECONDARY, 0, 0, 0);
+
+ TEST_EQ(vba_update_kernel_version(&ctx, new_kernel_data_key_version,
+ new_kernel_version),
+ expected_result, NULL);
+
+ if (expected_result != BDB_SUCCESS)
+ return;
+
+ /* Check data key version */
+ TEST_EQ(nvm->min_kernel_data_key_version,
+ expected_kernel_data_key_version, NULL);
+ /* Check kernel version */
+ TEST_EQ(nvm->min_kernel_version, expected_kernel_version, NULL);
+ /* Check update_count */
+ TEST_EQ(nvm->update_count, 0 + should_update, NULL);
+ /* Check sync if update is expected */
+ if (should_update)
+ TEST_SUCC(memcmp(nvmrw2, nvmrw1, sizeof(nvmrw1)), NULL);
+}
+
+static void test_update_kernel_version(void)
+{
+ /* Test update: data key version */
+ verify_kernel_version(0, 1, 0, 0, BDB_SUCCESS);
+ /* Test update: kernel version */
+ verify_kernel_version(0, 0, 0, 1, BDB_SUCCESS);
+ /* Test no update: data key version */
+ verify_kernel_version(1, 0, 0, 0, BDB_SUCCESS);
+ /* Test no update: kernel version */
+ verify_kernel_version(0, 0, 1, 0, BDB_SUCCESS);
+}
int main(int argc, char *argv[])
{
@@ -266,6 +563,9 @@ int main(int argc, char *argv[])
printf("Running BDB SP-RW tests...\n");
test_verify_aprw(argv[1]);
+ test_nvm_read();
+ test_nvm_write();
+ test_update_kernel_version();
return gTestSuccess ? 0 : 255;
}