Skip to content

Commit

Permalink
[ownership] Initialize owner configuration
Browse files Browse the repository at this point in the history
Depending on the `ownership_state`, initialize the owner configuration
appropriately.
- Locked states initialize the chip with a single configuration and make
  sure the two owner pages are identical.
- Unlocked states initialize the chip with an A/B configuration based on
  the contents of each of the owner pages.
- The No-Owner state normally performs no initialization.  However, a
  weak function is introduced to allow FPGA builds to link a `testonly`
  module that supplies the default testing owner configuration.

Signed-off-by: Chris Frantz <[email protected]>
(cherry picked from commit 71c5cfb)
(cherry picked from commit e7bc525)
  • Loading branch information
cfrantz committed Oct 28, 2024
1 parent 547f978 commit d0c389a
Show file tree
Hide file tree
Showing 16 changed files with 703 additions and 55 deletions.
38 changes: 38 additions & 0 deletions sw/device/silicon_creator/lib/ownership/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ cc_library(
deps = [
":datatypes",
":ecdsa",
":owner_block",
":ownership_key",
"//sw/device/lib/base:hardened_memory",
"//sw/device/silicon_creator/lib:boot_data",
"//sw/device/silicon_creator/lib:dbg_print",
"//sw/device/silicon_creator/lib/drivers:flash_ctrl",
],
)
Expand Down Expand Up @@ -169,6 +173,39 @@ cc_test(
],
)

cc_library(
name = "ownership_activate",
srcs = ["ownership_activate.c"],
hdrs = ["ownership_activate.h"],
deps = [
":datatypes",
":ownership",
":ownership_key",
"//sw/device/lib/base:memory",
"//sw/device/silicon_creator/lib:boot_data",
"//sw/device/silicon_creator/lib:error",
"//sw/device/silicon_creator/lib/boot_svc:boot_svc_msg",
"//sw/device/silicon_creator/lib/drivers:flash_ctrl",
],
)

cc_test(
name = "ownership_activate_unittest",
srcs = [
"ownership_activate_unittest.cc",
],
deps = [
":datatypes",
":ownership_activate",
":ownership_key",
"//sw/device/lib/base:hardened",
"//sw/device/silicon_creator/lib:boot_data",
"//sw/device/silicon_creator/lib/boot_svc:boot_svc_header",
"//sw/device/silicon_creator/testing:rom_test",
"@googletest//:gtest_main",
],
)

cc_library(
name = "test_owner",
testonly = True,
Expand All @@ -178,6 +215,7 @@ cc_library(
":owner_block",
":ownership",
"//sw/device/silicon_creator/lib:boot_data",
"//sw/device/silicon_creator/lib/drivers:flash_ctrl",
"//sw/device/silicon_creator/lib/ownership/keys/fake:includes",
],
alwayslink = True,
Expand Down
2 changes: 1 addition & 1 deletion sw/device/silicon_creator/lib/ownership/datatypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ typedef enum ownership_state {
/* Unlocked Endorsed: `UEND`. */
kOwnershipStateUnlockedEndorsed = 0x444e4555,
/* Locked None: any bit pattern not listed above. */
kOwnershipStateLockedNone = 0,
kOwnershipStateRecovery = 0,
} ownership_state_t;

typedef enum ownership_key_alg {
Expand Down
14 changes: 9 additions & 5 deletions sw/device/silicon_creator/lib/ownership/owner_block.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ enum {
kFlashBankSize = FLASH_CTRL_PARAM_REG_PAGES_PER_BANK,
};

void owner_config_default(owner_config_t *config) {
// Use a bogus pointer value to avoid the all-zeros pattern of NULL.
config->flash = (const owner_flash_config_t *)kHardenedBoolFalse;
config->info = (const owner_flash_info_config_t *)kHardenedBoolFalse;
config->rescue = (const owner_rescue_config_t *)kHardenedBoolFalse;
config->sram_exec = kOwnerSramExecModeDisabledLocked;
}

rom_error_t owner_block_parse(const owner_block_t *block,
owner_config_t *config,
owner_application_keyring_t *keyring) {
Expand All @@ -27,13 +35,9 @@ rom_error_t owner_block_parse(const owner_block_t *block,
if (block->header.length != sizeof(owner_block_t))
return kErrorOwnershipInvalidTagLength;

owner_config_default(config);
config->sram_exec = block->sram_exec_mode;

// Use a bogus pointer value to avoid the all-zeros pattern of NULL.
config->flash = (const owner_flash_config_t *)kHardenedBoolFalse;
config->info = (const owner_flash_info_config_t *)kHardenedBoolFalse;
config->rescue = (const owner_rescue_config_t *)kHardenedBoolFalse;

uint32_t remain = sizeof(block->data);
uint32_t offset = 0;
while (remain) {
Expand Down
10 changes: 10 additions & 0 deletions sw/device/silicon_creator/lib/ownership/owner_block.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ typedef struct owner_application_keyring {
const owner_application_key_t *key[16];
} owner_application_keyring_t;

/**
* Initialize the owner config with default values.
*
* The sram_exec mode is set to DisabledLocked and the three configuration
* pointers are set to kHardenedBoolFalse.
*
* @param config A pointer to a config struct holding pointers to config items.
*/
void owner_config_default(owner_config_t *config);

/**
* Parse an owner block, extracting pointers to keys and configuration items.
*
Expand Down
228 changes: 200 additions & 28 deletions sw/device/silicon_creator/lib/ownership/ownership.c
Original file line number Diff line number Diff line change
@@ -1,21 +1,177 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/silicon_creator/lib/ownership/ownership.h"

#include "sw/device/lib/base/hardened.h"
#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/boot_data.h"
#include "sw/device/silicon_creator/lib/dbg_print.h"
#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
#include "sw/device/silicon_creator/lib/drivers/hmac.h"
#include "sw/device/silicon_creator/lib/error.h"
#include "sw/device/silicon_creator/lib/ownership/ecdsa.h"
#include "sw/device/silicon_creator/lib/ownership/owner_block.h"
#include "sw/device/silicon_creator/lib/ownership/ownership.h"
#include "sw/device/silicon_creator/lib/ownership/ownership_key.h"

// RAM copy of the owner INFO pages from flash.
owner_block_t owner_page[2];
owner_page_status_t owner_page_valid[2];

static owner_page_status_t owner_page_validity_check(size_t page) {
size_t seal_len = (uintptr_t)&owner_page[0].seal - (uintptr_t)&owner_page[0];
size_t sig_len =
(uintptr_t)&owner_page[0].signature - (uintptr_t)&owner_page[0];

// TODO(cfrantz): validate owner pages.
// For now, we consider the seal to be just the sha256 over the page.
// This really needs to be the KMAC over the page with a keymgr-created
// sealing secret.
hmac_digest_t digest;
hmac_sha256(&owner_page[page], seal_len, &digest);
if (hardened_memeq(owner_page[page].seal, digest.digest,
ARRAYSIZE(digest.digest)) == kHardenedBoolTrue) {
return kOwnerPageStatusSealed;
}
hardened_bool_t result = ownership_key_validate(page, kOwnershipKeyOwner,
&owner_page[page].signature,
&owner_page[page], sig_len);
if (result == kHardenedBoolFalse) {
// If the page is bad, destroy the RAM copy.
memset(&owner_page[page], 0x5a, sizeof(owner_page[0]));
return kOwnerPageStatusInvalid;
}
return kOwnerPageStatusSigned;
}

rom_error_t ownership_init(void) {
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;
}

static rom_error_t locked_owner_init(boot_data_t *bootdata,
owner_config_t *config,
owner_application_keyring_t *keyring) {
if (owner_page_valid[0] == kOwnerPageStatusInvalid &&
owner_page_valid[1] == kOwnerPageStatusSealed) {
// Page 0 bad, Page 1 good: copy page 1 to page 0.
memcpy(&owner_page[0], &owner_page[1], sizeof(owner_page[0]));
HARDENED_RETURN_IF_ERROR(flash_ctrl_info_erase(
&kFlashCtrlInfoPageOwnerSlot0, kFlashCtrlEraseTypePage));
HARDENED_RETURN_IF_ERROR(flash_ctrl_info_write(
&kFlashCtrlInfoPageOwnerSlot0, 0,
sizeof(owner_page[0]) / sizeof(uint32_t), &owner_page[0]));
owner_page_valid[0] = owner_page_valid[1];
} else if (owner_page_valid[1] == kOwnerPageStatusInvalid &&
owner_page_valid[0] == kOwnerPageStatusSealed) {
// Page 1 bad, Page 0 good: copy page 0 to page 1.
memcpy(&owner_page[1], &owner_page[0], sizeof(owner_page[0]));
HARDENED_RETURN_IF_ERROR(flash_ctrl_info_erase(
&kFlashCtrlInfoPageOwnerSlot1, kFlashCtrlEraseTypePage));
HARDENED_RETURN_IF_ERROR(flash_ctrl_info_write(
&kFlashCtrlInfoPageOwnerSlot1, 0,
sizeof(owner_page[1]) / sizeof(uint32_t), &owner_page[1]));
owner_page_valid[1] = owner_page_valid[0];
} else if (owner_page_valid[0] != kOwnerPageStatusSealed &&
owner_page_valid[1] != kOwnerPageStatusSealed) {
// Neither page is valid; go to the Recovery state.
dbg_printf("error: both owner pages invalid.\r\n");
bootdata->ownership_state = kOwnershipStateRecovery;
nonce_new(&bootdata->nonce);
HARDENED_RETURN_IF_ERROR(boot_data_write(bootdata));
return kErrorOwnershipBadInfoPage;
}
HARDENED_RETURN_IF_ERROR(owner_block_parse(&owner_page[0], config, keyring));
HARDENED_RETURN_IF_ERROR(owner_block_flash_apply(config->flash, kBootSlotA,
bootdata->primary_bl0_slot));
HARDENED_RETURN_IF_ERROR(owner_block_flash_apply(config->flash, kBootSlotB,
bootdata->primary_bl0_slot));
HARDENED_RETURN_IF_ERROR(owner_block_info_apply(config->info));
// TODO: apply SRAM exec config
// TODO: apply rescue config
return kErrorOk;
}

hardened_bool_t ownership_page1_valid_for_transfer(boot_data_t *bootdata) {
if (bootdata->ownership_state == kOwnershipStateLockedOwner &&
owner_page_valid[1] == kOwnerPageStatusSealed) {
return kHardenedBoolTrue;
}
if (owner_page_valid[1] == kOwnerPageStatusSigned) {
hmac_digest_t digest;
switch (bootdata->ownership_state) {
case kOwnershipStateUnlockedAny:
// In UnlockedAny, any valid (signed) Owner Page 1 is acceptable.
return kHardenedBoolTrue;
case kOwnershipStateLockedUpdate:
// In LockedUpdate, the owner key must be the same. If not,
// skip parsing of Owner Page 1.
if (hardened_memeq(
owner_page[0].owner_key.key, owner_page[1].owner_key.key,
ARRAYSIZE(owner_page[0].owner_key.key)) == kHardenedBoolTrue) {
return kHardenedBoolTrue;
}
break;
case kOwnershipStateUnlockedEndorsed:
// In UnlockedEndorsed, the owner key must match the key endorsed by the
// next_owner field in bootdata. If not, skip parsing owner page 1.
hmac_sha256(owner_page[1].owner_key.key,
sizeof(owner_page[1].owner_key.key), &digest);
if (hardened_memeq(bootdata->next_owner, digest.digest,
ARRAYSIZE(digest.digest)) == kHardenedBoolTrue) {
return kHardenedBoolTrue;
}
break;
default:
/* nothing */;
}
}
return kHardenedBoolFalse;
}

static rom_error_t unlocked_init(boot_data_t *bootdata, owner_config_t *config,
owner_application_keyring_t *keyring) {
uint32_t secondary =
bootdata->primary_bl0_slot == kBootSlotA ? kBootSlotB : kBootSlotA;
if (bootdata->ownership_state == kOwnershipStateLockedUpdate &&
owner_page_valid[0] != kOwnerPageStatusSealed) {
// Owner Page 0 must be sealed in the "LockedUpdate" state. If not,
// go to the Recovery state.
bootdata->ownership_state = kOwnershipStateRecovery;
nonce_new(&bootdata->nonce);
HARDENED_RETURN_IF_ERROR(boot_data_write(bootdata));
return kErrorOwnershipBadInfoPage;
}

if (owner_page_valid[0] == kOwnerPageStatusSealed) {
// Configure the primary half of the flash as Owner Page 0 requests.
HARDENED_RETURN_IF_ERROR(
owner_block_parse(&owner_page[0], config, keyring));
HARDENED_RETURN_IF_ERROR(owner_block_flash_apply(
config->flash, bootdata->primary_bl0_slot, bootdata->primary_bl0_slot));
}

if (ownership_page1_valid_for_transfer(bootdata) == kHardenedBoolTrue) {
// If we passed the validity test for Owner Page 1, parse the configuration
// and add its keys to the keyring.
HARDENED_RETURN_IF_ERROR(
owner_block_parse(&owner_page[1], config, keyring));
}
HARDENED_RETURN_IF_ERROR(owner_block_flash_apply(config->flash, secondary,
bootdata->primary_bl0_slot));
HARDENED_RETURN_IF_ERROR(owner_block_info_apply(config->info));
// TODO: apply SRAM exec config
// TODO: apply rescue config
return kErrorOk;
}

rom_error_t ownership_init(boot_data_t *bootdata, owner_config_t *config,
owner_application_keyring_t *keyring) {
flash_ctrl_perms_t perm = {
.read = kMultiBitBool4True,
.write = kMultiBitBool4True,
Expand All @@ -31,33 +187,23 @@ rom_error_t ownership_init(void) {
flash_ctrl_info_cfg_set(&kFlashCtrlInfoPageOwnerSlot1, cfg);
// We don't want to abort ownership setup if we fail to
// read the INFO pages, so we discard the error result.
OT_DISCARD(flash_ctrl_info_read(&kFlashCtrlInfoPageOwnerSlot0, 0,
sizeof(owner_page[0]) / sizeof(uint32_t),
&owner_page[0]));
OT_DISCARD(flash_ctrl_info_read(&kFlashCtrlInfoPageOwnerSlot1, 0,
sizeof(owner_page[1]) / sizeof(uint32_t),
&owner_page[1]));

// TODO(cfrantz): validate owner pages.
// For now, just validate the signature on the page
size_t len = (uintptr_t)&owner_page[0].signature - (uintptr_t)&owner_page[0];
hardened_bool_t result;
result =
ownership_key_validate(/*page=*/0, kOwnershipKeyOwner,
&owner_page[0].signature, &owner_page[0], len);
if (result == kHardenedBoolFalse) {
// If the page is bad, destroy the RAM copy.
memset(&owner_page[0], 0xFF, sizeof(owner_page[0]));
if (flash_ctrl_info_read(&kFlashCtrlInfoPageOwnerSlot0, 0,
sizeof(owner_page[0]) / sizeof(uint32_t),
&owner_page[0]) == kErrorOk) {
owner_page_valid[0] = owner_page_validity_check(0);
} else {
owner_page_valid[0] = kOwnerPageStatusInvalid;
memset(&owner_page[0], 0xff, sizeof(owner_page[0]));
}
result =
ownership_key_validate(/*page=*/0, kOwnershipKeyOwner,
&owner_page[0].signature, &owner_page[0], len);
if (result == kHardenedBoolFalse) {
// If the page is bad, destroy the RAM copy;.
memset(&owner_page[1], 0xFF, sizeof(owner_page[1]));
if (flash_ctrl_info_read(&kFlashCtrlInfoPageOwnerSlot1, 0,
sizeof(owner_page[1]) / sizeof(uint32_t),
&owner_page[1]) == kErrorOk) {
owner_page_valid[1] = owner_page_validity_check(1);
} else {
owner_page_valid[1] = kOwnerPageStatusInvalid;
memset(&owner_page[1], 0xff, sizeof(owner_page[1]));
}

// TOOD(cfrantz):
// Depending on ownership state:
// - kOwnershipStateLockedOwner:
// - Make sure page0 and page1 are identical and fix if not.
Expand All @@ -75,9 +221,35 @@ rom_error_t ownership_init(void) {
// - Allow the pages to be different.
// - Set up flash config: primary from page0, secondary from page 1.
// - Enumerate application keys from both pages.
// - kOwnershipStateLockedNone: Allow the pages to be different.
// - kOwnershipStateRecovery: Allow the pages to be different.
// - Disaster state. Do nothing and wait for remediation via
// the recovery key.

return kErrorOk;
dbg_printf("ownership: %C\r\n", bootdata->ownership_state);
owner_config_default(config);
rom_error_t error = kErrorOwnershipNoOwner;
switch (bootdata->ownership_state) {
case kOwnershipStateLockedOwner:
error = locked_owner_init(bootdata, config, keyring);
break;
case kOwnershipStateLockedUpdate:
OT_FALLTHROUGH_INTENDED;
case kOwnershipStateUnlockedAny:
OT_FALLTHROUGH_INTENDED;
case kOwnershipStateUnlockedEndorsed:
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);
}
return error;
}

void ownership_page_seal(size_t page) {
size_t seal_len = (uintptr_t)&owner_page[0].seal - (uintptr_t)&owner_page[0];
hmac_digest_t digest;
hmac_sha256(&owner_page[page], seal_len, &digest);
memcpy(&owner_page[page].seal, digest.digest, sizeof(digest.digest));
}
Loading

0 comments on commit d0c389a

Please sign in to comment.