Skip to content

Commit

Permalink
[ownership] Add config version and secver reset fields
Browse files Browse the repository at this point in the history
1. Add a `config_version` field to permit monotonically increasing
   updates of the ownership configuration.
2. Add a `min_security_version_bl0` field to allow the owner to reset
   the minimum security version with a new ownership configuration.
3. Refactor the `test_owner_init` function into `sku_creator_owner_init`
   to allow versioned updates of built-in ownership configs.
4. Add tests for `min_security_version_bl0` and the built-in self update
   behavior.

Signed-off-by: Chris Frantz <[email protected]>
  • Loading branch information
cfrantz committed Sep 20, 2024
1 parent 94c31ea commit 46cad50
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 43 deletions.
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
5 changes: 5 additions & 0 deletions sw/device/silicon_creator/lib/ownership/ownership_activate.c
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/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

0 comments on commit 46cad50

Please sign in to comment.