Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ownership] Add config version and secver reset fields #24607

Merged
merged 1 commit into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions sw/device/silicon_creator/lib/ownership/datatypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,12 @@ typedef struct owner_block {
uint32_t sram_exec_mode;
/** Ownership key algorithm (currently, only ECDSA is supported). */
uint32_t ownership_key_alg;
/** Configuraion version (monotonically increasing per owner) */
uint32_t config_version;
/** Set the minimum security version to this value (UINT32_MAX: no change) */
uint32_t min_security_version_bl0;
/** Reserved space for future use. */
uint32_t reserved[27];
uint32_t reserved[25];
/** Owner public key. */
owner_key_t owner_key;
/** Owner's Activate public key. */
Expand All @@ -126,7 +130,9 @@ OT_ASSERT_MEMBER_OFFSET(owner_block_t, header, 0);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, struct_version, 8);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, sram_exec_mode, 12);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, ownership_key_alg, 16);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, reserved, 20);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, config_version, 20);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, min_security_version_bl0, 24);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, reserved, 28);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, owner_key, 128);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, activate_key, 224);
OT_ASSERT_MEMBER_OFFSET(owner_block_t, unlock_key, 320);
Expand Down
26 changes: 18 additions & 8 deletions sw/device/silicon_creator/lib/ownership/ownership.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ static owner_page_status_t owner_page_validity_check(size_t page) {
return kOwnerPageStatusSigned;
}

OT_WEAK rom_error_t test_owner_init(boot_data_t *bootdata,
owner_config_t *config,
owner_application_keyring_t *keyring) {
dbg_printf("error: no owner.\r\n");
return kErrorOwnershipNoOwner;
OT_WEAK rom_error_t
sku_creator_owner_init(boot_data_t *bootdata, owner_config_t *config,
owner_application_keyring_t *keyring) {
return kErrorOk;
}

static rom_error_t locked_owner_init(boot_data_t *bootdata,
Expand Down Expand Up @@ -186,6 +185,19 @@ rom_error_t ownership_init(boot_data_t *bootdata, owner_config_t *config,

dbg_printf("ownership: %C\r\n", bootdata->ownership_state);
owner_config_default(config);

// We give the weak `sku_creator_owner_init` function the opportunity to
// initialize ownership if it is uninitialized or needs updating.
//
// This is a temporary measure to get ownership configs installed on
// pre-existing silicon and to update the owner configuration automatically
// should we decide to update it during development.
//
// When we settle on a default ownership configuration, we'll remove this
// function and possibly relegate it to the `default` case below, only to
// be used should the chip enter the "no owner recovery" state.
HARDENED_RETURN_IF_ERROR(sku_creator_owner_init(bootdata, config, keyring));

rom_error_t error = kErrorOwnershipNoOwner;
// TODO(#22386): Harden this switch/case statement.
switch (bootdata->ownership_state) {
Expand All @@ -200,9 +212,7 @@ rom_error_t ownership_init(boot_data_t *bootdata, owner_config_t *config,
error = unlocked_init(bootdata, config, keyring);
break;
default:
// The `test_owner_init` function is weak and the default implementation
// does nothing.
error = test_owner_init(bootdata, config, keyring);
/* Do nothing. */;
}
return error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ static rom_error_t activate(boot_svc_msg_t *msg, boot_data_t *bootdata) {
bootdata->primary_bl0_slot = kBootSlotA;
}

// If requested, reset the mim security version of the application firmware.
if (owner_page[1].min_security_version_bl0 != UINT32_MAX) {
bootdata->min_security_version_bl0 = owner_page[1].min_security_version_bl0;
}

// Set the ownership state to LockedOwner.
nonce_new(&bootdata->nonce);
bootdata->ownership_state = kOwnershipStateLockedOwner;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class OwnershipActivateTest : public rom_test::RomTest {
owner_page[1].header.tag = kTlvTagOwner;
owner_page[1].header.length = sizeof(owner_page[1]);
owner_page[1].struct_version = 0;
owner_page[1].config_version = 0;
owner_page[1].min_security_version_bl0 = UINT32_MAX;
memset(owner_page[1].data, 0x5a, sizeof(owner_page[1].data));
}

Expand Down Expand Up @@ -192,7 +194,7 @@ TEST_P(OwnershipActivateValidStateTest, OwnerPageInvalid) {
}

// Tests that an owner block with a valid owner page with respect to the current
// update state succeeds.
// unlock state succeeds.
TEST_P(OwnershipActivateValidStateTest, OwnerPageValid) {
ownership_state_t state = GetParam();
bootdata_.ownership_state = static_cast<uint32_t>(state);
Expand Down Expand Up @@ -243,10 +245,77 @@ TEST_P(OwnershipActivateValidStateTest, OwnerPageValid) {
rom_error_t error = ownership_activate_handler(&message_, &bootdata_);
EXPECT_EQ(error, kErrorWriteBootdataThenReboot);
// After succeeding, the page should be sealed, the nonce changed and the
// ownership state set to LocedOwner.
// ownership state set to LockedOwner.
EXPECT_EQ(owner_page[1].seal[0], 0x5a5a5a5a);
EXPECT_FALSE(nonce_equal(&bootdata_.nonce, &kDefaultNonce));
EXPECT_EQ(bootdata_.ownership_state, kOwnershipStateLockedOwner);
// The default value of `owner_page.min_security_version_bl0` should perform
// no update to the bootdata's minimum version.
EXPECT_EQ(bootdata_.min_security_version_bl0, 0);
}

// Tests that an owner update with a non-default value of
// `min_security_version_bl0` updates bootdata's copy.
//
// TODO(cfrantz): Refactor this test as it is nearly a complete copy of the
// previous test except for the manipulation of the min_sec_ver.
TEST_P(OwnershipActivateValidStateTest, UpdateBootdataBl0) {
ownership_state_t state = GetParam();
bootdata_.ownership_state = static_cast<uint32_t>(state);

owner_page[0].owner_key = {{1}};
memset(bootdata_.next_owner, 0, sizeof(bootdata_.next_owner));
bootdata_.next_owner[0] = 12345;
MakePage1Valid(true);
owner_page[1].min_security_version_bl0 = 5;

EXPECT_CALL(ownership_key_, validate(1, kOwnershipKeyActivate, _, _, _))
.WillOnce(Return(kHardenedBoolTrue));

switch (state) {
case kOwnershipStateUnlockedSelf:
case kOwnershipStateUnlockedAny:
case kOwnershipStateUnlockedEndorsed:
// Test should pass.
break;
default:
FAIL() << "Invalid state for this test: " << state;
}
// Once the new owner page is determined to be valid, the page will be sealed.
EXPECT_CALL(hmac_, sha256(_, _, _))
.WillOnce(SetArgPointee<2>((hmac_digest_t){{0x5a5a5a5a}}));

// The sealed page will be written into flash owner slot 1 first.
EXPECT_CALL(flash_ctrl_,
InfoErase(&kFlashCtrlInfoPageOwnerSlot1, kFlashCtrlEraseTypePage))
.WillOnce(Return(kErrorOk));
EXPECT_CALL(flash_ctrl_, InfoWrite(&kFlashCtrlInfoPageOwnerSlot1, 0,
sizeof(owner_page[1]) / sizeof(uint32_t),
&owner_page[1]))
.WillOnce(Return(kErrorOk));
// The sealed page will be written into flash owner slot 0 second.
EXPECT_CALL(flash_ctrl_,
InfoErase(&kFlashCtrlInfoPageOwnerSlot0, kFlashCtrlEraseTypePage))
.WillOnce(Return(kErrorOk));
EXPECT_CALL(flash_ctrl_, InfoWrite(&kFlashCtrlInfoPageOwnerSlot0, 0,
sizeof(owner_page[1]) / sizeof(uint32_t),
&owner_page[1]))
.WillOnce(Return(kErrorOk));

// The nonce will be regenerated.
EXPECT_CALL(rnd_, Uint32()).WillRepeatedly(Return(99));
// The boot_svc response will be finalized.
EXPECT_CALL(hdr_, Finalize(_, _, _));

rom_error_t error = ownership_activate_handler(&message_, &bootdata_);
EXPECT_EQ(error, kErrorWriteBootdataThenReboot);
// After succeeding, the page should be sealed, the nonce changed and the
// ownership state set to LockedOwner.
EXPECT_EQ(owner_page[1].seal[0], 0x5a5a5a5a);
EXPECT_FALSE(nonce_equal(&bootdata_.nonce, &kDefaultNonce));
EXPECT_EQ(bootdata_.ownership_state, kOwnershipStateLockedOwner);
// Bootdata should receive the owner_block's minimum version upon activation.
EXPECT_EQ(bootdata_.min_security_version_bl0, 5);
}

INSTANTIATE_TEST_SUITE_P(AllCases, OwnershipActivateValidStateTest,
Expand Down
78 changes: 56 additions & 22 deletions sw/device/silicon_creator/lib/ownership/test_owner.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/lib/base/hardened_memory.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/silicon_creator/lib/dbg_print.h"
Expand All @@ -20,25 +21,50 @@
#include "sw/device/silicon_creator/lib/ownership/ownership.h"

/*
* This module overrides the weak `test_owner_init` symbol in ownership.c, thus
* allowing FPGA builds to boot in the `LockedOwner` state with a valid set of
* keys.
* This module overrides the weak `sku_creator_owner_init` symbol in
* ownership.c, thus allowing FPGA builds to boot in the `LockedOwner` state
* with a valid set of keys.
*/

rom_error_t test_owner_init(boot_data_t *bootdata, owner_config_t *config,
owner_application_keyring_t *keyring) {
bool flash_invalid = owner_page[0].header.tag == 0xFFFFFFFF &&
owner_page[1].header.tag == 0xFFFFFFFF;
// NOTE: if you update this version number, you must also update the version
// number in the test library `sw/host/tests/ownership/transfer_lib.rs`.
#define TEST_OWNER_CONFIG_VERSION 1

rom_error_t sku_creator_owner_init(boot_data_t *bootdata,
owner_config_t *config,
owner_application_keyring_t *keyring) {
owner_key_t owner = (owner_key_t){
// Although this is an ECDSA key, we initialize the `raw` member of the
// union to zero-initialize the unused space.
.raw = OWNER_ECDSA_P256};
ownership_state_t state = bootdata->ownership_state;

if (state == kOwnershipStateUnlockedSelf ||
state == kOwnershipStateUnlockedAny ||
state == kOwnershipStateUnlockedEndorsed) {
// Nothing to do when in an unlocked state.
return kErrorOk;
} else if (state == kOwnershipStateLockedOwner) {
if (hardened_memeq(owner.raw, owner_page[0].owner_key.raw,
ARRAYSIZE(owner.raw)) != kHardenedBoolTrue ||
TEST_OWNER_CONFIG_VERSION <= owner_page[0].config_version) {
// Different owner or already newest config version; nothing to do.
return kErrorOk;
}
} else {
// State is an unknown value, which is the same as kOwnershipStateRecovery.
// We'll not return, thus allowing the owner config below to be programmed
// into flash.
}

owner_page[0].header.tag = kTlvTagOwner;
owner_page[0].header.length = 2048;
owner_page[0].struct_version = 0;
owner_page[0].sram_exec_mode = kOwnerSramExecModeDisabledLocked;
owner_page[0].ownership_key_alg = kOwnershipKeyAlgEcdsaP256;
owner_page[0].owner_key = (owner_key_t){
// Although this is an ECDSA key, we initialize the `raw` member of the
// union to zero-initialize the unused space.
.raw = OWNER_ECDSA_P256};
owner_page[0].config_version = TEST_OWNER_CONFIG_VERSION;
owner_page[0].min_security_version_bl0 = UINT32_MAX;
owner_page[0].owner_key = owner;
owner_page[0].activate_key = (owner_key_t){
// Although this is an ECDSA key, we initialize the `raw` member of the
// union to zero-initialize the unused space.
Expand Down Expand Up @@ -141,6 +167,7 @@ rom_error_t test_owner_init(boot_data_t *bootdata, owner_config_t *config,
memset(app, 0x5a, len);

owner_block_page_seal(/*page=*/0);
memcpy(&owner_page[1], &owner_page[0], sizeof(owner_page[0]));

RETURN_IF_ERROR(owner_block_parse(&owner_page[0], config, keyring));
RETURN_IF_ERROR(owner_block_flash_apply(config->flash, kBootSlotA,
Expand All @@ -152,16 +179,23 @@ rom_error_t test_owner_init(boot_data_t *bootdata, owner_config_t *config,
// Since this module should only get linked in to FPGA builds, we can simply
// thunk the ownership state to LockedOwner.
bootdata->ownership_state = kOwnershipStateLockedOwner;
if (flash_invalid) {
OT_DISCARD(flash_ctrl_info_erase(&kFlashCtrlInfoPageOwnerSlot0,
kFlashCtrlEraseTypePage));
OT_DISCARD(flash_ctrl_info_write(&kFlashCtrlInfoPageOwnerSlot0, 0,
sizeof(owner_page[0]) / sizeof(uint32_t),
&owner_page[0]));
OT_DISCARD(boot_data_write(bootdata));
dbg_printf("test_owner_init: flash\r\n");
} else {
dbg_printf("test_owner_init: ram\r\n");
}

// Write the configuration to both owner pages.
OT_DISCARD(flash_ctrl_info_erase(&kFlashCtrlInfoPageOwnerSlot0,
kFlashCtrlEraseTypePage));
OT_DISCARD(flash_ctrl_info_write(&kFlashCtrlInfoPageOwnerSlot0, 0,
sizeof(owner_page[0]) / sizeof(uint32_t),
&owner_page[0]));
owner_page_valid[0] = kOwnerPageStatusSealed;

OT_DISCARD(flash_ctrl_info_erase(&kFlashCtrlInfoPageOwnerSlot1,
kFlashCtrlEraseTypePage));
OT_DISCARD(flash_ctrl_info_write(&kFlashCtrlInfoPageOwnerSlot1, 0,
sizeof(owner_page[0]) / sizeof(uint32_t),
&owner_page[0]));
owner_page_valid[1] = kOwnerPageStatusSealed;

OT_DISCARD(boot_data_write(bootdata));
dbg_printf("sku_creator_owner_init: saved to flash\r\n");
return kErrorOk;
}
27 changes: 27 additions & 0 deletions sw/device/silicon_creator/rom_ext/e2e/ownership/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,30 @@ ownership_transfer_test(
"//sw/device/lib/testing/test_framework:ottf_main",
],
)

# Test that the `sku_creator_owner_init` function responsible for installing the test owner on
# FPGA builds will automatically update from an older owner block.
ownership_transfer_test(
name = "fpga_owner_upgrade_test",
fpga = fpga_params(
changes_otp = True,
test_cmd = """
--clear-bitstream
--bootstrap={firmware}
--unlock-mode=Update
--unlock-key=$(location //sw/device/silicon_creator/lib/ownership/keys/fake:unlock_key)
--next-owner-key=$(location //sw/device/silicon_creator/lib/ownership/keys/fake:owner_key)
--next-unlock-key=$(location //sw/device/silicon_creator/lib/ownership/keys/fake:unlock_key)
--next-activate-key=$(location //sw/device/silicon_creator/lib/ownership/keys/fake:activate_key)
# `sku_creator_owner_init` has a newer version number than this and should self-update.
--config-version=0
# NOTE: Should sku_creator_owner_init fail to upgrade to its built-in owner block, then the
# dummy key will prevent the test program from executing and we'll get a failure from the test.
--next-application-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:app_prod_pub)
""",
test_harness = "//sw/host/tests/ownership:transfer_test",
),
rsa_key = {
"//sw/device/silicon_creator/lib/ownership/keys/fake:app_prod": "app_prod",
},
)
2 changes: 1 addition & 1 deletion sw/device/silicon_creator/rom_ext/e2e/ownership/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def ownership_transfer_test(
name,
srcs = ["//sw/device/silicon_creator/rom_ext/e2e/verified_boot:boot_test"],
exec_env = {
"//hw/top_earlgrey:fpga_cw310_rom_ext": None,
"//hw/top_earlgrey:fpga_hyper310_rom_ext": None,
},
rsa_key = {
"//sw/device/silicon_creator/lib/ownership/keys/dummy:app_prod": "app_prod",
Expand Down
2 changes: 1 addition & 1 deletion sw/device/silicon_creator/rom_ext/rom_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ static rom_error_t rom_ext_start(boot_data_t *boot_data, boot_log_t *boot_log) {
}
// TODO(cfrantz): evaluate permissible ownership init failure conditions
// and change this to HARDENED_RETURN_IF_ERROR.
if (error == kErrorOk) {
if (error != kErrorOk) {
dbg_printf("ownership_init: %x\r\n", error);
}

Expand Down
Loading
Loading