diff --git a/sw/device/silicon_creator/lib/ownership/BUILD b/sw/device/silicon_creator/lib/ownership/BUILD index 0c18c9d89f182..775328340ccb5 100644 --- a/sw/device/silicon_creator/lib/ownership/BUILD +++ b/sw/device/silicon_creator/lib/ownership/BUILD @@ -76,9 +76,11 @@ dual_cc_library( deps = dual_inputs( device = [ ":ecdsa", + "//sw/device/lib/base:memory", "//sw/device/lib/base:hardened_memory", "//sw/device/silicon_creator/lib/drivers:keymgr", "//sw/device/silicon_creator/lib/drivers:kmac", + "//sw/device/silicon_creator/lib/drivers:flash_ctrl", ], host = [ "//sw/device/lib/base:global_mock", @@ -88,6 +90,7 @@ dual_cc_library( shared = [ ":datatypes", "//sw/device/lib/base:hardened", + "//sw/device/silicon_creator/lib/drivers:hmac", "//sw/device/silicon_creator/lib:error", ], ), @@ -101,11 +104,13 @@ cc_library( ":datatypes", ":ecdsa", ":owner_block", + ":ownership_activate", ":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", + "//sw/device/silicon_creator/lib/drivers:lifecycle", ], ) diff --git a/sw/device/silicon_creator/lib/ownership/datatypes.h b/sw/device/silicon_creator/lib/ownership/datatypes.h index 0048d2b29fa14..d849f39ee97e4 100644 --- a/sw/device/silicon_creator/lib/ownership/datatypes.h +++ b/sw/device/silicon_creator/lib/ownership/datatypes.h @@ -72,8 +72,19 @@ typedef enum ownership_update_mode { * if the config_version is newer) */ kOwnershipUpdateModeNewVersion = 0x5657454e, + /** + * Update mode SelfVersion: `SELV` + * (unlock key only unlocks to UnlockedSelf; accept new owner configs from + * self-same owner if the config_version is newer) + */ + kOwnershipUpdateModeSelfVersion = 0x564c4553, } ownership_update_mode_t; +typedef enum lock_constraint { + /** No locking constraint: `~~~~`. */ + kLockConstraintNone = 0x7e7e7e7e, +} lock_constraint_t; + typedef enum tlv_tag { /** Owner struct: `OWNR`. */ kTlvTagOwner = 0x524e574f, @@ -130,8 +141,12 @@ typedef struct owner_block { uint32_t update_mode; /** Set the minimum security version to this value (UINT32_MAX: no change) */ uint32_t min_security_version_bl0; + /** The device ID locking constraint */ + uint32_t lock_constraint; + /** The device ID to which this config applies */ + uint32_t device_id[8]; /** Reserved space for future use. */ - uint32_t reserved[25]; + uint32_t reserved[16]; /** Owner public key. */ owner_key_t owner_key; /** Owner's Activate public key. */ @@ -152,7 +167,9 @@ 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, update_mode, 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, lock_constraint, 28); +OT_ASSERT_MEMBER_OFFSET(owner_block_t, device_id, 32); +OT_ASSERT_MEMBER_OFFSET(owner_block_t, reserved, 64); 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); diff --git a/sw/device/silicon_creator/lib/ownership/mock_ownership_key.cc b/sw/device/silicon_creator/lib/ownership/mock_ownership_key.cc index 39cce86b5bf0a..81e56a1c1bcea 100644 --- a/sw/device/silicon_creator/lib/ownership/mock_ownership_key.cc +++ b/sw/device/silicon_creator/lib/ownership/mock_ownership_key.cc @@ -26,5 +26,9 @@ rom_error_t ownership_seal_check(size_t page) { return MockOwnershipKey::Instance().seal_check(page); } +rom_error_t ownership_secret_new() { + return MockOwnershipKey::Instance().secret_new(); +} + } // extern "C" } // namespace rom_test diff --git a/sw/device/silicon_creator/lib/ownership/mock_ownership_key.h b/sw/device/silicon_creator/lib/ownership/mock_ownership_key.h index a6f936b2ce73c..6ffcf2ecc94d3 100644 --- a/sw/device/silicon_creator/lib/ownership/mock_ownership_key.h +++ b/sw/device/silicon_creator/lib/ownership/mock_ownership_key.h @@ -23,6 +23,7 @@ class MockOwnershipKey : public global_mock::GlobalMock { MOCK_METHOD(rom_error_t, seal_init, ()); MOCK_METHOD(rom_error_t, seal_page, (size_t)); MOCK_METHOD(rom_error_t, seal_check, (size_t)); + MOCK_METHOD(rom_error_t, secret_new, ()); }; } // namespace internal diff --git a/sw/device/silicon_creator/lib/ownership/owner_block.c b/sw/device/silicon_creator/lib/ownership/owner_block.c index e52676f2b392d..d2bc31608b148 100644 --- a/sw/device/silicon_creator/lib/ownership/owner_block.c +++ b/sw/device/silicon_creator/lib/ownership/owner_block.c @@ -23,6 +23,15 @@ enum { kFlashBankSize = FLASH_CTRL_PARAM_REG_PAGES_PER_BANK, }; +hardened_bool_t owner_block_newversion_mode(void) { + if (owner_page_valid[0] == kOwnerPageStatusSealed && + (owner_page[0].update_mode == kOwnershipUpdateModeNewVersion || + owner_page[0].update_mode == kOwnershipUpdateModeSelfVersion)) { + return kHardenedBoolTrue; + } + return kHardenedBoolFalse; +} + hardened_bool_t owner_block_page1_valid_for_transfer(boot_data_t *bootdata) { if (bootdata->ownership_state == kOwnershipStateLockedOwner && owner_page_valid[1] == kOwnerPageStatusSealed) { diff --git a/sw/device/silicon_creator/lib/ownership/owner_block.h b/sw/device/silicon_creator/lib/ownership/owner_block.h index c4b8b42167e71..59bb173bc75bb 100644 --- a/sw/device/silicon_creator/lib/ownership/owner_block.h +++ b/sw/device/silicon_creator/lib/ownership/owner_block.h @@ -58,6 +58,13 @@ typedef struct owner_application_keyring { const owner_application_key_t *key[16]; } owner_application_keyring_t; +/** + * Determine if the ownership update mode is one of the "newversion" modes. + * + * @return kHardenedBoolTrue if it is a newversion mode. + */ +hardened_bool_t owner_block_newversion_mode(void); + /** * Check if owner page 1 is valid for ownership transfer. * diff --git a/sw/device/silicon_creator/lib/ownership/ownership.c b/sw/device/silicon_creator/lib/ownership/ownership.c index 64aa152145e40..d805ac5db0d2c 100644 --- a/sw/device/silicon_creator/lib/ownership/ownership.c +++ b/sw/device/silicon_creator/lib/ownership/ownership.c @@ -12,21 +12,41 @@ #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/drivers/lifecycle.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_activate.h" #include "sw/device/silicon_creator/lib/ownership/ownership_key.h" static owner_page_status_t owner_page_validity_check(size_t page) { size_t sig_len = (uintptr_t)&owner_page[0].signature - (uintptr_t)&owner_page[0]; + // Any non-constrained words of the owner block device_id field are required + // to be `kLockConstraintNone`. We wipe the field and then fill in the + // relevant words from the lifecycle constroller. + // + // For node-locked owner configurations, this makes the device_id from the + // lifecycle controller relevant to the cryptographic verification. + memset(owner_page[page].device_id, 0, sizeof(owner_page[page].device_id)); + lifecycle_device_id_t id; + lifecycle_device_id_get(&id); + for (uint32_t i = 0; i < ARRAYSIZE(owner_page[0].device_id); ++i) { + if (owner_page[page].lock_constraint & (1u << i)) { + owner_page[page].device_id[i] = id.device_id[i]; + } else { + owner_page[page].device_id[i] = kLockConstraintNone; + } + } + rom_error_t sealed = ownership_seal_check(page); if (sealed == kErrorOk) { HARDENED_CHECK_EQ(sealed, kErrorOk); return kOwnerPageStatusSealed; } + hardened_bool_t result = ownership_key_validate(page, kOwnershipKeyOwner, &owner_page[page].signature, &owner_page[page], sig_len); @@ -49,39 +69,20 @@ static rom_error_t locked_owner_init(boot_data_t *bootdata, owner_application_keyring_t *keyring) { if (owner_page_valid[0] == kOwnerPageStatusSealed && owner_page_valid[1] == kOwnerPageStatusSigned && - owner_page[0].update_mode == kOwnershipUpdateModeNewVersion && + owner_block_newversion_mode() == kHardenedBoolTrue && + owner_page[1].config_version > owner_page[0].config_version && hardened_memeq(owner_page[0].owner_key.raw, owner_page[1].owner_key.raw, ARRAYSIZE(owner_page[0].owner_key.raw)) == kHardenedBoolTrue) { - // TODO(cfrantz): Consider refactoring this block along with the body - // of the `ownership_activate.c%activate` function into a common - // activation function. - owner_config_t tmpcfg; - owner_application_keyring_t tmpkey; - // Trial parse of the new page: it has to be valid to accept the update. - rom_error_t error = owner_block_parse(&owner_page[1], &tmpcfg, &tmpkey); - if (error == kErrorOk && - owner_page[1].config_version > owner_page[0].config_version) { - // Page 1 parses and has a newer version: seal it into flash. - ownership_seal_page(/*page=*/1); - owner_page_valid[1] = kOwnerPageStatusSealed; - 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])); - - // If the new owner page resets the BL0 min_security_version, perform - // the reset now. - if (owner_page[1].min_security_version_bl0 != UINT32_MAX) { - bootdata->min_security_version_bl0 = - owner_page[1].min_security_version_bl0; - HARDENED_RETURN_IF_ERROR(boot_data_write(bootdata)); - } + rom_error_t error = + ownership_activate(bootdata, /*write_both_pages=*/kHardenedBoolFalse); + if (error == kErrorOk) { + HARDENED_CHECK_EQ(error, kErrorOk); // Thunk the status of page 0 to Invalid so the next set of validity // checks will copy the new page 1 content over to page 0 and establish a // redundant backup of the new configuration. owner_page_valid[0] = kOwnerPageStatusInvalid; + owner_page_valid[1] = kOwnerPageStatusSealed; } else { // If the new page wasn't good, we'll do nothing here and let the next set // of validity checks copy page 0 over to page 1 and re-establish a @@ -184,6 +185,9 @@ rom_error_t ownership_init(boot_data_t *bootdata, owner_config_t *config, flash_ctrl_info_cfg_set(&kFlashCtrlInfoPageOwnerSlot0, cfg); flash_ctrl_info_perms_set(&kFlashCtrlInfoPageOwnerSlot1, perm); flash_ctrl_info_cfg_set(&kFlashCtrlInfoPageOwnerSlot1, cfg); + // Set up the OwnerSecret page for ECC & Scrambling. We won't + // turn on read/write/earse permissions until we need them. + flash_ctrl_info_cfg_set(&kFlashCtrlInfoPageOwnerSecret, cfg); ownership_seal_init(); // We don't want to abort ownership setup if we fail to @@ -276,6 +280,18 @@ rom_error_t ownership_flash_lockdown(boot_data_t *bootdata, } void ownership_pages_lockdown(boot_data_t *bootdata, hardened_bool_t rescue) { +#ifdef ROM_EXT_KLOBBER_ALLOWED + if (rescue == kHardenedBoolTrue) { + // If ROM_EXT_KLOBBER_ALLOWED and we're in rescue mode, we skip + // lockdown because the rescue protcol is permitted, on DEV chips + // only, to erase the ownership pages. This exists to simulate + // disaster scenarios and test the disaster recovery mechanisms. + // + // Under normal circumstances, the ROM_EXT is not built with this + // capability. + return; + } +#endif flash_ctrl_perms_t perm = { .read = kMultiBitBool4True, .write = kMultiBitBool4False, @@ -296,9 +312,7 @@ void ownership_pages_lockdown(boot_data_t *bootdata, hardened_bool_t rescue) { return; } if (bootdata->ownership_state == kOwnershipStateLockedOwner) { - if (owner_page[0].update_mode == kOwnershipUpdateModeNewVersion) { - HARDENED_CHECK_EQ(owner_page[0].update_mode, - kOwnershipUpdateModeNewVersion); + if (owner_block_newversion_mode() == kHardenedBoolTrue) { // Leave page 1 unlocked if we're in "NewVersion" update mode. } else { // Otherwise, make the page read-only. diff --git a/sw/device/silicon_creator/lib/ownership/ownership_activate.c b/sw/device/silicon_creator/lib/ownership/ownership_activate.c index 619b5fb9057f5..5b0b25f7aa6ba 100644 --- a/sw/device/silicon_creator/lib/ownership/ownership_activate.c +++ b/sw/device/silicon_creator/lib/ownership/ownership_activate.c @@ -14,7 +14,46 @@ #include "sw/device/silicon_creator/lib/ownership/owner_block.h" #include "sw/device/silicon_creator/lib/ownership/ownership_key.h" -static rom_error_t activate(boot_svc_msg_t *msg, boot_data_t *bootdata) { +rom_error_t ownership_activate(boot_data_t *bootdata, + hardened_bool_t write_both_pages) { + // Check if page1 parses correctly. + owner_config_t config; + owner_application_keyring_t keyring; + HARDENED_RETURN_IF_ERROR( + owner_block_parse(&owner_page[1], &config, &keyring)); + + // Seal page one to this chip. + ownership_seal_page(/*page=*/1); + + // 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; + } + + // TODO(cfrantz): Consider reading back the flash pages to check that the + // flash writes succeeded. + // + // Program the sealed page into slot 1. + 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])); + + if (write_both_pages == kHardenedBoolTrue) { + // Program the same data into slot 0. + HARDENED_RETURN_IF_ERROR(flash_ctrl_info_erase( + &kFlashCtrlInfoPageOwnerSlot0, kFlashCtrlEraseTypePage)); + HARDENED_RETURN_IF_ERROR(flash_ctrl_info_write( + &kFlashCtrlInfoPageOwnerSlot0, 0, + sizeof(owner_page[1]) / sizeof(uint32_t), &owner_page[1])); + } + + return kErrorOk; +} + +static rom_error_t activate_handler(boot_svc_msg_t *msg, + boot_data_t *bootdata) { // Check if page1 is in a valid state for a transfer (e.g. signature and owner // requirements are met). We do this first (rather than parse) because if the // signature requirements are not met, the RAM copy of the owner page will be @@ -23,12 +62,6 @@ static rom_error_t activate(boot_svc_msg_t *msg, boot_data_t *bootdata) { return kErrorOwnershipInvalidInfoPage; } - // Check if page1 parses correctly. - owner_config_t config; - owner_application_keyring_t keyring; - HARDENED_RETURN_IF_ERROR( - owner_block_parse(&owner_page[1], &config, &keyring)); - // Check the activation key and the nonce. size_t len = (uintptr_t)&msg->ownership_activate_req.signature - (uintptr_t)&msg->ownership_activate_req.primary_bl0_slot; @@ -50,25 +83,8 @@ static rom_error_t activate(boot_svc_msg_t *msg, boot_data_t *bootdata) { return kErrorOwnershipInvalidDin; } - // Seal page one to this chip. - ownership_seal_page(/*page=*/1); - - // TODO(cfrantz): Consider reading back the flash pages to check that the - // flash writes succeeded. - // - // Program the sealed page into slot 1. - 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])); - - // Program the same data into slot 0. - HARDENED_RETURN_IF_ERROR(flash_ctrl_info_erase(&kFlashCtrlInfoPageOwnerSlot0, - kFlashCtrlEraseTypePage)); - HARDENED_RETURN_IF_ERROR(flash_ctrl_info_write( - &kFlashCtrlInfoPageOwnerSlot0, 0, - sizeof(owner_page[1]) / sizeof(uint32_t), &owner_page[1])); + HARDENED_RETURN_IF_ERROR( + ownership_activate(bootdata, /*write_both_pages=*/kHardenedBoolTrue)); // The requested primary_bl0_slot is user input. Validate and clamp it to // legal values. @@ -78,15 +94,20 @@ 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; + if (bootdata->ownership_state == kOwnershipStateUnlockedSelf) { + // An activate from UnlockedSelf is not a transfer and should not + // regenerate the OwnerSecret page. + HARDENED_CHECK_EQ(bootdata->ownership_state, kOwnershipStateUnlockedSelf); + } else { + // All other activations are transfers and need to regenerate entropy stored + // in the OwnerSecret page. + HARDENED_RETURN_IF_ERROR(ownership_secret_new()); + bootdata->ownership_transfers += 1; } // Set the ownership state to LockedOwner. nonce_new(&bootdata->nonce); bootdata->ownership_state = kOwnershipStateLockedOwner; - bootdata->ownership_transfers += 1; memset(bootdata->next_owner, 0, sizeof(bootdata->next_owner)); return kErrorWriteBootdataThenReboot; } @@ -98,7 +119,7 @@ rom_error_t ownership_activate_handler(boot_svc_msg_t *msg, case kOwnershipStateUnlockedSelf: case kOwnershipStateUnlockedAny: case kOwnershipStateUnlockedEndorsed: - error = activate(msg, bootdata); + error = activate_handler(msg, bootdata); break; default: /* nothing */; diff --git a/sw/device/silicon_creator/lib/ownership/ownership_activate.h b/sw/device/silicon_creator/lib/ownership/ownership_activate.h index 89230f9300935..15d1f602e14ed 100644 --- a/sw/device/silicon_creator/lib/ownership/ownership_activate.h +++ b/sw/device/silicon_creator/lib/ownership/ownership_activate.h @@ -7,12 +7,24 @@ #include "sw/device/silicon_creator/lib/boot_data.h" #include "sw/device/silicon_creator/lib/boot_svc/boot_svc_msg.h" +#include "sw/device/silicon_creator/lib/drivers/lifecycle.h" #include "sw/device/silicon_creator/lib/error.h" #ifdef __cplusplus extern "C" { #endif // __cplusplus +/** + * Activate the owner configuration on owner page 1. + * + * @param bootdata A pointer to the current boot_data in RAM. + * @param write_both_pages Whether to write the activated page to both owner + * pages. + * @return rom_error_t + */ +rom_error_t ownership_activate(boot_data_t *bootdata, + hardened_bool_t write_both_pages); + /** * Process a boot_svc OwnershipActivate message. * @@ -20,6 +32,7 @@ extern "C" { * @param bootdata A pointer to the current boot_data in RAM. * @return rom_error_t */ + rom_error_t ownership_activate_handler(boot_svc_msg_t *msg, boot_data_t *bootdata); diff --git a/sw/device/silicon_creator/lib/ownership/ownership_activate_unittest.cc b/sw/device/silicon_creator/lib/ownership/ownership_activate_unittest.cc index 335e4e14fd817..867f7d58ee420 100644 --- a/sw/device/silicon_creator/lib/ownership/ownership_activate_unittest.cc +++ b/sw/device/silicon_creator/lib/ownership/ownership_activate_unittest.cc @@ -38,6 +38,8 @@ class OwnershipActivateTest : public rom_test::RomTest { owner_page[1].header.version = (struct_version_t){0, 0}; owner_page[1].config_version = 0; owner_page[1].min_security_version_bl0 = UINT32_MAX; + owner_page[1].lock_constraint = 0; + memset(owner_page[1].device_id, 0x7e, sizeof(owner_page[1].device_id)); memset(owner_page[1].data, 0x5a, sizeof(owner_page[1].data)); } @@ -123,12 +125,16 @@ INSTANTIATE_TEST_SUITE_P(AllCases, OwnershipActivateInvalidStateTest, testing::Values(kOwnershipStateLockedOwner, kOwnershipStateRecovery)); -// Tests that an owner block with an invalid signature fails. +// Tests that an owner block with an invalid version fails. TEST_P(OwnershipActivateValidStateTest, InvalidVersion) { bootdata_.ownership_state = static_cast(GetParam()); MakePage1Valid(true); owner_page[1].header.version.major = 5; + EXPECT_CALL(ownership_key_, validate(1, kOwnershipKeyActivate, _, _, _)) + .WillOnce(Return(kHardenedBoolTrue)); + EXPECT_CALL(lifecycle_, DeviceId(_)) + .WillOnce(SetArgPointee<0>((lifecycle_device_id_t){0})); EXPECT_CALL(hdr_, Finalize(_, _, _)); rom_error_t error = ownership_activate_handler(&message_, &bootdata_); @@ -149,7 +155,7 @@ TEST_P(OwnershipActivateValidStateTest, InvalidSignature) { EXPECT_EQ(error, kErrorOwnershipInvalidSignature); } -// Tests that an owner block with an invalid nonce fails. +// Tests that an ownership activate with an invalid nonce fails. TEST_P(OwnershipActivateValidStateTest, InvalidNonce) { bootdata_.ownership_state = static_cast(GetParam()); bootdata_.nonce = {3, 4}; @@ -164,8 +170,8 @@ TEST_P(OwnershipActivateValidStateTest, InvalidNonce) { EXPECT_EQ(error, kErrorOwnershipInvalidNonce); } -// Tests that an owner block with an invalid DIN fails. -TEST_P(OwnershipActivateValidStateTest, InvalidDin) { +// Tests that an ownership activate with an invalid DIN fails. +TEST_P(OwnershipActivateValidStateTest, InvalidActivateDin) { bootdata_.ownership_state = static_cast(GetParam()); // We want to pass the page 1 validity test to check the nonce of the // message. @@ -256,6 +262,10 @@ TEST_P(OwnershipActivateValidStateTest, OwnerPageValid) { &owner_page[1])) .WillOnce(Return(kErrorOk)); + if (state != kOwnershipStateUnlockedSelf) { + EXPECT_CALL(ownership_key_, secret_new()).WillOnce(Return(kErrorOk)); + } + // The nonce will be regenerated. EXPECT_CALL(rnd_, Uint32()).WillRepeatedly(Return(99)); // The boot_svc response will be finalized. @@ -321,6 +331,10 @@ TEST_P(OwnershipActivateValidStateTest, UpdateBootdataBl0) { &owner_page[1])) .WillOnce(Return(kErrorOk)); + if (state != kOwnershipStateUnlockedSelf) { + EXPECT_CALL(ownership_key_, secret_new()).WillOnce(Return(kErrorOk)); + } + // The nonce will be regenerated. EXPECT_CALL(rnd_, Uint32()).WillRepeatedly(Return(99)); // The boot_svc response will be finalized. diff --git a/sw/device/silicon_creator/lib/ownership/ownership_key.c b/sw/device/silicon_creator/lib/ownership/ownership_key.c index 373769ea52bca..0da48a5b4b597 100644 --- a/sw/device/silicon_creator/lib/ownership/ownership_key.c +++ b/sw/device/silicon_creator/lib/ownership/ownership_key.c @@ -6,6 +6,7 @@ #include "sw/device/lib/base/hardened.h" #include "sw/device/lib/base/hardened_memory.h" +#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h" #include "sw/device/silicon_creator/lib/drivers/keymgr.h" #include "sw/device/silicon_creator/lib/drivers/kmac.h" #include "sw/device/silicon_creator/lib/ownership/ecdsa.h" @@ -80,3 +81,96 @@ rom_error_t ownership_seal_check(size_t page) { } return kErrorOwnershipInvalidInfoPage; } + +static void reverse(void *buf, size_t len) { + char *x = (char *)buf; + char *y = x + len - 1; + for (; x < y; ++x, --y) { + char t = *x; + *x = *y; + *y = t; + } +} + +static void secret_page_enable(multi_bit_bool_t read, multi_bit_bool_t write) { + flash_ctrl_perms_t perm = { + .read = read, + .write = write, + .erase = write, + }; + flash_ctrl_info_perms_set(&kFlashCtrlInfoPageOwnerSecret, perm); +} + +rom_error_t ownership_secret_new(void) { + owner_secret_page_t secret; + + secret_page_enable(/*read=*/kMultiBitBool4True, /*write=*/kMultiBitBool4True); + rom_error_t error = + flash_ctrl_info_read(&kFlashCtrlInfoPageOwnerSecret, 0, + sizeof(secret) / sizeof(uint32_t), &secret); + if (error != kErrorOk) { + HARDENED_CHECK_NE(error, kErrorOk); + // This should only happen on the FPGA during the first ownership transfer. + // TODO: What should we do if we ever encounter this error on silicon? + // A successful erase and reprogram will "heal" the chip, but any + // ownership history will be lost. + error = flash_ctrl_info_erase(&kFlashCtrlInfoPageOwnerSecret, + kFlashCtrlEraseTypePage); + memset(&secret, 0xFF, sizeof(secret)); + } + if (error != kErrorOk) + goto exitproc; + + // Compute the ownership hash chain: + // owner_history = HASH(owner_history || new_owner_key) + hmac_sha256_init(); + hmac_sha256_update(&secret.owner_history, sizeof(secret.owner_history)); + size_t keysz = sizeof(owner_page[0].owner_key); + switch (owner_page[0].ownership_key_alg) { + case kOwnershipKeyAlgEcdsaP256: + keysz = sizeof(owner_page[0].owner_key.ecdsa); + break; + default:; + } + hmac_sha256_update(&owner_page[0].owner_key, keysz); + hmac_sha256_process(); + hmac_sha256_final(&secret.owner_history); + // TODO(cfrantz): when merging to master, use the big-endian form of + // the sha256 function to avoid the reversal operation at the end. + reverse(&secret.owner_history, sizeof(secret.owner_history)); + + // Generate a new owner secret seed. This will completely disconnect + // the keymgr state from the previous owner's keymgr state. + // We hash the previous owner entropy with the new owner key. + // owner_secret = HASH(owner_secret || new_owner_key) + // TODO: is this sufficient, or should we generate new entropy? + hmac_sha256_init(); + hmac_sha256_update(&secret.owner_secret, sizeof(secret.owner_secret)); + hmac_sha256_update(&owner_page[0].owner_key, keysz); + hmac_sha256_process(); + hmac_sha256_final(&secret.owner_secret); + + error = flash_ctrl_info_erase(&kFlashCtrlInfoPageOwnerSecret, + kFlashCtrlEraseTypePage); + if (error != kErrorOk) + goto exitproc; + error = flash_ctrl_info_write(&kFlashCtrlInfoPageOwnerSecret, 0, + sizeof(secret) / sizeof(uint32_t), &secret); + +exitproc: + secret_page_enable(/*read=*/kMultiBitBool4False, + /*write=*/kMultiBitBool4False); + return error; +} + +rom_error_t ownership_history_get(hmac_digest_t *history) { + secret_page_enable(/*read=*/kMultiBitBool4True, + /*write=*/kMultiBitBool4False); + rom_error_t error = + flash_ctrl_info_read(&kFlashCtrlInfoPageOwnerSecret, + offsetof(owner_secret_page_t, owner_history), + sizeof(*history) / sizeof(uint32_t), history); + secret_page_enable(/*read=*/kMultiBitBool4False, + /*write=*/kMultiBitBool4False); + return error; +} diff --git a/sw/device/silicon_creator/lib/ownership/ownership_key.h b/sw/device/silicon_creator/lib/ownership/ownership_key.h index fe013f2e2b49b..0f27edab87c12 100644 --- a/sw/device/silicon_creator/lib/ownership/ownership_key.h +++ b/sw/device/silicon_creator/lib/ownership/ownership_key.h @@ -6,6 +6,8 @@ #define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_OWNERSHIP_OWNERSHIP_KEY_H_ #include "sw/device/lib/base/hardened.h" +#include "sw/device/lib/base/memory.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/datatypes.h" @@ -30,6 +32,13 @@ typedef enum ownership_key { kOwnershipKeyRecovery = 0x8888, } ownership_key_t; +typedef struct owner_secret_page { + /** Owner entropy. */ + hmac_digest_t owner_secret; + /** Hash chain of previous owners. */ + hmac_digest_t owner_history; +} owner_secret_page_t; + /** * Validate that a message was signed with a given owner key. * If the message fails verification with the Activate or Unlock key, @@ -72,4 +81,19 @@ rom_error_t ownership_seal_page(size_t page); */ rom_error_t ownership_seal_check(size_t page); +/** + * Replace the owner secret with new entropy and update the ownership history. + * + * @return Success or error code. + */ +rom_error_t ownership_secret_new(void); + +/** + * Retrieve the owner history digest from the OwnerSecret page. + * + * @param history Digest of all previous owner keys. + * @return Success or error code. + */ +rom_error_t ownership_history_get(hmac_digest_t *history); + #endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_OWNERSHIP_OWNERSHIP_KEY_H_ diff --git a/sw/device/silicon_creator/lib/ownership/ownership_unlock.c b/sw/device/silicon_creator/lib/ownership/ownership_unlock.c index a7bf0a6d11b69..0484d36251759 100644 --- a/sw/device/silicon_creator/lib/ownership/ownership_unlock.c +++ b/sw/device/silicon_creator/lib/ownership/ownership_unlock.c @@ -67,6 +67,7 @@ static rom_error_t unlock(boot_svc_msg_t *msg, boot_data_t *bootdata) { // The NewVersion mode forbids all unlocks. return kErrorOwnershipUnlockDenied; case kOwnershipUpdateModeSelf: + case kOwnershipUpdateModeSelfVersion: default: // The `unlock` funciton services UnlockAny and UnlockEndorsed requests, // neither of which are valid for the `Self` mode. @@ -107,6 +108,7 @@ static rom_error_t unlock_update(boot_svc_msg_t *msg, boot_data_t *bootdata) { // The NewVersion mode forbids all unlocks. return kErrorOwnershipUnlockDenied; case kOwnershipUpdateModeSelf: + case kOwnershipUpdateModeSelfVersion: case kOwnershipUpdateModeOpen: default: // The `unlock_update` funciton services UnlockUpdate update requests, diff --git a/sw/device/silicon_creator/lib/ownership/ownership_unlock_unittest.cc b/sw/device/silicon_creator/lib/ownership/ownership_unlock_unittest.cc index 8a20c9167f0c5..b6086ab1f4b9a 100644 --- a/sw/device/silicon_creator/lib/ownership/ownership_unlock_unittest.cc +++ b/sw/device/silicon_creator/lib/ownership/ownership_unlock_unittest.cc @@ -394,6 +394,7 @@ TEST_P(OwnershipUnlockUpdateModesTest, UnlockAny) { expect = kErrorOwnershipUnlockDenied; break; case kOwnershipUpdateModeSelf: + case kOwnershipUpdateModeSelfVersion: expect = kErrorOwnershipInvalidMode; break; } @@ -413,6 +414,7 @@ TEST_P(OwnershipUnlockUpdateModesTest, UnlockUpdate) { switch (mode) { case kOwnershipUpdateModeOpen: case kOwnershipUpdateModeSelf: + case kOwnershipUpdateModeSelfVersion: EXPECT_CALL(ownership_key_, validate(0, static_cast(kOwnershipKeyUnlock), _, _, _)) @@ -435,6 +437,7 @@ TEST_P(OwnershipUnlockUpdateModesTest, UnlockUpdate) { INSTANTIATE_TEST_SUITE_P(AllCases, OwnershipUnlockUpdateModesTest, testing::Values(kOwnershipUpdateModeOpen, kOwnershipUpdateModeSelf, + kOwnershipUpdateModeSelfVersion, kOwnershipUpdateModeNewVersion)); } // namespace diff --git a/sw/device/silicon_creator/lib/ownership/test_owner.c b/sw/device/silicon_creator/lib/ownership/test_owner.c index a30819089dbb6..da8e643d07279 100644 --- a/sw/device/silicon_creator/lib/ownership/test_owner.c +++ b/sw/device/silicon_creator/lib/ownership/test_owner.c @@ -67,6 +67,9 @@ rom_error_t sku_creator_owner_init(boot_data_t *bootdata, owner_page[0].ownership_key_alg = kOwnershipKeyAlgEcdsaP256; owner_page[0].update_mode = TEST_OWNER_UPDATE_MODE; owner_page[0].min_security_version_bl0 = UINT32_MAX; + owner_page[0].lock_constraint = 0; + memset(owner_page[0].device_id, kLockConstraintNone, + sizeof(owner_page[0].device_id)); 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 diff --git a/sw/device/silicon_creator/rom_ext/doc/rescue.md b/sw/device/silicon_creator/rom_ext/doc/rescue.md index da75ad5f7064e..9e5756d75e084 100644 --- a/sw/device/silicon_creator/rom_ext/doc/rescue.md +++ b/sw/device/silicon_creator/rom_ext/doc/rescue.md @@ -217,3 +217,13 @@ The following errors and error responses can occur during Xmodem-CRC. - An invalid start-of-packet: the transfer is aborted. - An invalid block number: the transfer is cancelled. - An invalid CRC checksum: the frame is NAKed and the sender should retry the frame. + +## Security Considerations + +The ownership configuration can contain a `RescueConfig` structure that specifies which rescue commands are allowed. +When the `RescueConfig` is absent from the ownership configuration, all commands are allowed. +When the `RescueConfig` is present in the ownership configuration, all commands are subject to the `command_allow` list in the rescue configuration except for the following: + +- The reboot (`REBO`) command is always allowed. +- The disable automatic reboot (`WAIT`) command is always allowed. +- The change serial data rate (`BAUD`) command is always allowed. diff --git a/sw/device/silicon_creator/rom_ext/e2e/ownership/BUILD b/sw/device/silicon_creator/rom_ext/e2e/ownership/BUILD index e7274e2f2c855..f5ebedcc72554 100644 --- a/sw/device/silicon_creator/rom_ext/e2e/ownership/BUILD +++ b/sw/device/silicon_creator/rom_ext/e2e/ownership/BUILD @@ -286,6 +286,7 @@ ownership_transfer_test( # NOTE: We rotate the `fake` test owner's application key to the dummy key to test that # we can execute code with the new key. --next-application-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:app_prod_ecdsa_pub) + --non-transfer-update=true """, test_harness = "//sw/host/tests/ownership:transfer_test", ), @@ -447,6 +448,7 @@ ownership_transfer_test( # 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_ecdsa_pub) + --non-transfer-update=true """, test_harness = "//sw/host/tests/ownership:transfer_test", ), @@ -499,3 +501,122 @@ ownership_transfer_test( test_harness = "//sw/host/tests/ownership:newversion_test", ), ) + +ownership_transfer_test( + name = "newversion_nodelock_test", + ecdsa_key = { + "//sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_ecdsa": "app_prod", + }, + fpga = fpga_params( + changes_otp = True, + rom_ext = "//sw/device/silicon_creator/rom_ext:rom_ext_owner_update_newversion", + test_cmd = """ + --clear-bitstream + --bootstrap={firmware} + --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) + --next-application-key=$(location //sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_ecdsa_pub) + --config-version=2 + # A DIN of zero will cause the test program to use the DUT's current DIN. + --locked-to-din=0 + --expect "config_version = 2" + --expect "owner_key = 8e3dcb50" + """, + test_harness = "//sw/host/tests/ownership:newversion_test", + ), +) + +ownership_transfer_test( + name = "newversion_badlock_test", + ecdsa_key = { + "//sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_ecdsa": "app_prod", + }, + fpga = fpga_params( + changes_otp = True, + rom_ext = "//sw/device/silicon_creator/rom_ext:rom_ext_owner_update_newversion", + test_cmd = """ + --clear-bitstream + --bootstrap={firmware} + --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) + --next-application-key=$(location //sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_ecdsa_pub) + --config-version=2 + --locked-to-din=12345 + # We expect to see the version unchanged. + --expect "config_version = 1" + --expect "owner_key = 8e3dcb50" + """, + test_harness = "//sw/host/tests/ownership:newversion_test", + ), +) + +opentitan_binary( + name = "keymgr_test", + testonly = True, + srcs = ["//sw/device/silicon_creator/rom_ext/e2e/verified_boot:boot_test"], + defines = [ + "WITH_OWNERSHIP_INFO=1", + "WITH_KEYMGR=1", + ], + ecdsa_key = { + "//sw/device/silicon_creator/lib/ownership/keys/dummy:app_prod_ecdsa": "app_prod", + }, + exec_env = { + "//hw/top_earlgrey:fpga_cw310_rom_ext": None, + }, + deps = [ + "//hw/top_earlgrey/sw/autogen:top_earlgrey", + "//sw/device/lib/base:status", + "//sw/device/lib/dif:keymgr", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/silicon_creator/lib:boot_log", + "//sw/device/silicon_creator/lib/drivers:flash_ctrl", + "//sw/device/silicon_creator/lib/drivers:retention_sram", + "//sw/device/silicon_creator/lib/ownership:datatypes", + ], +) + +# rom_ext_e2e_testplan.hjson%rom_ext_e2e_transfer_any_test +ownership_transfer_test( + name = "transfer_keymgr_test", + srcs = ["//sw/device/silicon_creator/rom_ext/e2e/verified_boot:boot_test"], + defines = [ + "WITH_OWNERSHIP_INFO=1", + "WITH_KEYMGR=1", + ], + ecdsa_key = { + "//sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_ecdsa": "app_prod", + }, + fpga = fpga_params( + binaries = { + ":keymgr_test": "keymgr_test", + }, + changes_otp = True, + test_cmd = """ + --clear-bitstream + --bootstrap={firmware} + --pre-transfer-boot-check=true + --unlock-mode=Any + --unlock-key=$(location //sw/device/silicon_creator/lib/ownership/keys/fake:unlock_key) + --next-owner-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:owner_key) + --next-unlock-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:unlock_key) + --next-activate-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:activate_key) + --next-application-key=$(location //sw/device/silicon_creator/lib/ownership/keys/dummy:app_prod_ecdsa_pub) + --rescue-after-activate={keymgr_test} + --keygen-check=true + """, + test_harness = "//sw/host/tests/ownership:transfer_test", + ), + deps = [ + "//hw/top_earlgrey/sw/autogen:top_earlgrey", + "//sw/device/lib/base:status", + "//sw/device/lib/dif:keymgr", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/silicon_creator/lib:boot_log", + "//sw/device/silicon_creator/lib/drivers:flash_ctrl", + "//sw/device/silicon_creator/lib/drivers:retention_sram", + "//sw/device/silicon_creator/lib/ownership:datatypes", + ], +) diff --git a/sw/device/silicon_creator/rom_ext/e2e/verified_boot/boot_test.c b/sw/device/silicon_creator/rom_ext/e2e/verified_boot/boot_test.c index e177cf7eb5b78..ae72506282a95 100644 --- a/sw/device/silicon_creator/rom_ext/e2e/verified_boot/boot_test.c +++ b/sw/device/silicon_creator/rom_ext/e2e/verified_boot/boot_test.c @@ -30,6 +30,69 @@ status_t ownership_print(void) { status_t ownership_print(void) { return OK_STATUS(); } #endif +#ifdef WITH_KEYMGR +#include "sw/device/lib/dif/dif_keymgr.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" + +const char *keymgr_state(dif_keymgr_state_t s) { + switch (s) { + case kDifKeymgrStateReset: + return "Reset"; + case kDifKeymgrStateInitialized: + return "Initialized"; + case kDifKeymgrStateCreatorRootKey: + return "CreatorRootKey"; + case kDifKeymgrStateOwnerIntermediateKey: + return "OwnerIntermediateKey"; + case kDifKeymgrStateOwnerRootKey: + return "OwnerRootKey"; + case kDifKeymgrStateDisabled: + return "Disabled"; + case kDifKeymgrStateInvalid: + return "Invalid"; + default: + return "Unknown"; + } +} + +status_t keymgr_print(void) { + dif_keymgr_t km; + TRY(dif_keymgr_init(mmio_region_from_addr(TOP_EARLGREY_KEYMGR_BASE_ADDR), + &km)); + + dif_keymgr_state_t state; + TRY(dif_keymgr_get_state(&km, &state)); + LOG_INFO("keymgr state = %s", keymgr_state(state)); + + dif_keymgr_binding_value_t bind; + TRY(dif_keymgr_read_binding(&km, &bind)); + LOG_INFO("keymgr bind_sealing = %08x%08x%08x%08x%08x%08x%08x%08x", + bind.sealing[0], bind.sealing[1], bind.sealing[2], bind.sealing[3], + bind.sealing[4], bind.sealing[5], bind.sealing[6], bind.sealing[7]); + LOG_INFO("keymgr bind_attest = %08x%08x%08x%08x%08x%08x%08x%08x", + bind.attestation[0], bind.attestation[1], bind.attestation[2], + bind.attestation[3], bind.attestation[4], bind.attestation[5], + bind.attestation[6], bind.attestation[7]); + + dif_keymgr_versioned_key_params_t p = {kDifKeymgrVersionedKeyDestSw}; + TRY(dif_keymgr_generate_versioned_key(&km, p)); + + dif_keymgr_output_t out; + TRY(dif_keymgr_read_output(&km, &out)); + LOG_INFO("keymgr sw_key = %08x%08x%08x%08x%08x%08x%08x%08x", + out.value[0][0] ^ out.value[1][0], out.value[0][1] ^ out.value[1][1], + out.value[0][2] ^ out.value[1][2], out.value[0][3] ^ out.value[1][3], + out.value[0][4] ^ out.value[1][4], out.value[0][5] ^ out.value[1][5], + out.value[0][6] ^ out.value[1][6], + out.value[0][7] ^ out.value[1][7]); + + return OK_STATUS(); +} +#else +status_t keymgr_print(void) { return OK_STATUS(); } +#endif + OTTF_DEFINE_TEST_CONFIG(); status_t boot_log_print(boot_log_t *boot_log) { @@ -51,7 +114,9 @@ status_t boot_log_print(boot_log_t *boot_log) { LOG_INFO("boot_log rom_ext_min_sec_ver = %u", boot_log->rom_ext_min_sec_ver); LOG_INFO("boot_log bl0_min_sec_ver = %u", boot_log->bl0_min_sec_ver); LOG_INFO("boot_log primary_bl0_slot = %C", boot_log->primary_bl0_slot); - return ownership_print(); + TRY(ownership_print()); + TRY(keymgr_print()); + return OK_STATUS(); } bool test_main(void) { diff --git a/sw/device/silicon_creator/rom_ext/rescue.c b/sw/device/silicon_creator/rom_ext/rescue.c index 8ddd9a721394d..a91c51f957201 100644 --- a/sw/device/silicon_creator/rom_ext/rescue.c +++ b/sw/device/silicon_creator/rom_ext/rescue.c @@ -64,7 +64,7 @@ rom_error_t flash_owner_block(rescue_state_t *state, boot_data_t *bootdata) { bootdata->ownership_state == kOwnershipStateUnlockedSelf || bootdata->ownership_state == kOwnershipStateUnlockedEndorsed || (bootdata->ownership_state == kOwnershipStateLockedOwner && - owner_page[0].update_mode == kOwnershipUpdateModeNewVersion)) { + owner_block_newversion_mode() == kHardenedBoolTrue)) { HARDENED_RETURN_IF_ERROR(flash_ctrl_info_erase( &kFlashCtrlInfoPageOwnerSlot1, kFlashCtrlEraseTypePage)); HARDENED_RETURN_IF_ERROR(flash_ctrl_info_write( @@ -136,18 +136,33 @@ static void ownership_erase(void) { static void validate_mode(uint32_t mode, rescue_state_t *state, boot_data_t *bootdata) { dbg_printf("\r\nmode: %C\r\n", bitfield_byteswap32(mode)); - hardened_bool_t allow = owner_rescue_command_allowed(state->config, mode); + + // The following commands are always allowed and are not subject to + // the "command allowed" check. + switch (mode) { + case kRescueModeBaud: + change_speed(); + goto exitproc; + case kRescueModeReboot: + dbg_printf("ok: reboot\r\n"); + state->mode = (rescue_mode_t)mode; + goto exitproc; + case kRescueModeWait: + dbg_printf("ok: wait after upload\r\n"); + state->reboot = false; + goto exitproc; #ifdef ROM_EXT_KLOBBER_ALLOWED - if (mode == kRescueModeKlobber) { - ownership_erase(); - return; - } + case kRescueModeKlobber: + ownership_erase(); + goto exitproc; #endif + default: + /* do nothing */; + } + + hardened_bool_t allow = owner_rescue_command_allowed(state->config, mode); if (allow == kHardenedBoolTrue) { switch (mode) { - case kRescueModeBaud: - change_speed(); - return; case kRescueModeBootLog: dbg_printf("ok: receive boot_log via xmodem-crc\r\n"); break; @@ -162,7 +177,7 @@ static void validate_mode(uint32_t mode, rescue_state_t *state, bootdata->ownership_state == kOwnershipStateUnlockedSelf || bootdata->ownership_state == kOwnershipStateUnlockedEndorsed || (bootdata->ownership_state == kOwnershipStateLockedOwner && - owner_page[0].update_mode == kOwnershipUpdateModeNewVersion)) { + owner_block_newversion_mode() == kHardenedBoolTrue)) { dbg_printf("ok: send owner_block via xmodem-crc\r\n"); } else { dbg_printf("error: cannot accept owner_block in current state\r\n"); @@ -180,9 +195,6 @@ static void validate_mode(uint32_t mode, rescue_state_t *state, case kRescueModeOpenTitanID: dbg_printf("ok: receive device ID via xmodem-crc\r\n"); break; - case kRescueModeReboot: - dbg_printf("ok: reboot\r\n"); - break; default: // User input error. Do not change modes. dbg_printf("error: unrecognized mode\r\n"); @@ -192,6 +204,7 @@ static void validate_mode(uint32_t mode, rescue_state_t *state, } else { dbg_printf("error: mode not allowed\r\n"); } +exitproc: state->frame = 1; state->offset = 0; state->flash_offset = 0; diff --git a/sw/device/silicon_creator/rom_ext/rom_ext.c b/sw/device/silicon_creator/rom_ext/rom_ext.c index 90c68bb127fc9..28fd29733971e 100644 --- a/sw/device/silicon_creator/rom_ext/rom_ext.c +++ b/sw/device/silicon_creator/rom_ext/rom_ext.c @@ -44,6 +44,7 @@ #include "sw/device/silicon_creator/lib/otbn_boot_services.h" #include "sw/device/silicon_creator/lib/ownership/ownership.h" #include "sw/device/silicon_creator/lib/ownership/ownership_activate.h" +#include "sw/device/silicon_creator/lib/ownership/ownership_key.h" #include "sw/device/silicon_creator/lib/ownership/ownership_unlock.h" #include "sw/device/silicon_creator/lib/shutdown.h" #include "sw/device/silicon_creator/lib/sigverify/ecdsa_p256_key.h" diff --git a/sw/device/silicon_creator/rom_ext/sival/sival_owner.c b/sw/device/silicon_creator/rom_ext/sival/sival_owner.c index 84b3fd59c75fa..579dc9cabf19d 100644 --- a/sw/device/silicon_creator/rom_ext/sival/sival_owner.c +++ b/sw/device/silicon_creator/rom_ext/sival/sival_owner.c @@ -63,6 +63,9 @@ rom_error_t sku_creator_owner_init(boot_data_t *bootdata, owner_page[0].ownership_key_alg = kOwnershipKeyAlgEcdsaP256; owner_page[0].update_mode = kOwnershipUpdateModeOpen; owner_page[0].min_security_version_bl0 = UINT32_MAX; + owner_page[0].lock_constraint = 0; + memset(owner_page[0].device_id, kLockConstraintNone, + sizeof(owner_page[0].device_id)); 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 diff --git a/sw/host/opentitanlib/src/ownership/owner.rs b/sw/host/opentitanlib/src/ownership/owner.rs index 8b4d0a6aea7d2..142a23cb01285 100644 --- a/sw/host/opentitanlib/src/ownership/owner.rs +++ b/sw/host/opentitanlib/src/ownership/owner.rs @@ -29,6 +29,7 @@ with_unknown! { pub enum OwnershipUpdateMode: u32 [default = Self::Open] { Open = u32::from_le_bytes(*b"OPEN"), UnlockSelf = u32::from_le_bytes(*b"SELF"), + SelfVersion = u32::from_le_bytes(*b"SELV"), NewVersion = u32::from_le_bytes(*b"NEWV"), } } @@ -56,9 +57,19 @@ pub struct OwnerBlock { /// Set the minimum security version to this value. #[serde(default)] pub min_security_version_bl0: MinSecurityVersion, + /// The device ID locking constraint. + #[serde(default)] + pub lock_constraint: u32, + /// The device ID to which this config applies. + #[serde( + default = "OwnerBlock::default_constraint", + skip_serializing_if = "OwnerBlock::is_default_constraint" + )] + #[annotate(format=hex)] + pub device_id: [u32; 8], #[serde(default, skip_serializing_if = "GlobalFlags::not_debug")] #[annotate(format=hex)] - pub reserved: [u32; 25], + pub reserved: [u32; 16], /// The owner identity key. pub owner_key: KeyMaterial, /// The owner activation key. @@ -87,7 +98,9 @@ impl Default for OwnerBlock { ownership_key_alg: OwnershipKeyAlg::default(), update_mode: OwnershipUpdateMode::default(), min_security_version_bl0: MinSecurityVersion::default(), - reserved: [0u32; 25], + lock_constraint: 0, + device_id: Self::default_constraint(), + reserved: [0u32; 16], owner_key: KeyMaterial::default(), activate_key: KeyMaterial::default(), unlock_key: KeyMaterial::default(), @@ -104,6 +117,7 @@ impl OwnerBlock { const SIGNATURE_OFFSET: usize = 1952; // The not present value must be reflected in the TlvTag::NotPresent value. const NOT_PRESENT: u8 = 0x5a; + const NO_CONSTRAINT: u32 = 0x7e7e7e7e; pub fn default_header() -> TlvHeader { TlvHeader::new(TlvTag::Owner, 0, "0.0") @@ -128,6 +142,15 @@ impl OwnerBlock { dest.write_u32::(u32::from(self.ownership_key_alg))?; dest.write_u32::(u32::from(self.update_mode))?; dest.write_u32::(u32::from(self.min_security_version_bl0))?; + dest.write_u32::(self.lock_constraint)?; + + for (i, x) in self.device_id.iter().enumerate() { + if self.lock_constraint & (1u32 << i) == 0 { + dest.write_u32::(Self::NO_CONSTRAINT)?; + } else { + dest.write_u32::(*x)?; + } + } for x in &self.reserved { dest.write_u32::(*x)?; } @@ -155,7 +178,11 @@ impl OwnerBlock { let ownership_key_alg = OwnershipKeyAlg(src.read_u32::()?); let update_mode = OwnershipUpdateMode(src.read_u32::()?); let min_security_version_bl0 = MinSecurityVersion(src.read_u32::()?); - let mut reserved = [0u32; 25]; + let lock_constraint = src.read_u32::()?; + + let mut device_id = [0u32; 8]; + src.read_u32_into::(&mut device_id)?; + let mut reserved = [0u32; 16]; src.read_u32_into::(&mut reserved)?; let owner_key = KeyMaterial::read_length(src, ownership_key_alg, 96)?; let activate_key = KeyMaterial::read_length(src, ownership_key_alg, 96)?; @@ -180,6 +207,8 @@ impl OwnerBlock { ownership_key_alg, update_mode, min_security_version_bl0, + lock_constraint, + device_id, reserved, owner_key, activate_key, @@ -195,6 +224,14 @@ impl OwnerBlock { self.signature = key.digest_and_sign(&data[..Self::SIGNATURE_OFFSET])?; Ok(()) } + + pub fn is_default_constraint(d: &[u32; 8]) -> bool { + *d == [Self::NO_CONSTRAINT; 8] + } + + pub fn default_constraint() -> [u32; 8] { + [Self::NO_CONSTRAINT; 8] + } } #[derive(Debug, Serialize, Deserialize, Annotate)] @@ -268,8 +305,8 @@ mod test { const OWNER_BIN: &str = r#"00000000: 4f 57 4e 52 00 08 00 00 00 00 00 00 4c 4e 45 58 OWNR........LNEX 00000010: 50 32 35 36 4f 50 45 4e ff ff ff ff 00 00 00 00 P256OPEN........ -00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ -00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +00000020: 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e ~~~~~~~~~~~~~~~~ +00000030: 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e 7e ~~~~~~~~~~~~~~~~ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ @@ -402,6 +439,7 @@ r#"00000000: 4f 57 4e 52 00 08 00 00 00 00 00 00 4c 4e 45 58 OWNR........LNEX ownership_key_alg: "EcdsaP256", update_mode: "Open", min_security_version_bl0: "NoChange", + lock_constraint: 0, owner_key: { Ecdsa: { x: "1111111111111111111111111111111111111111111111111111111111111111", diff --git a/sw/host/tests/ownership/newversion_test.rs b/sw/host/tests/ownership/newversion_test.rs index c8a2cfae79e6f..789139a1897af 100644 --- a/sw/host/tests/ownership/newversion_test.rs +++ b/sw/host/tests/ownership/newversion_test.rs @@ -41,6 +41,12 @@ struct Opts { )] config_version: u32, + #[arg( + long, + help = "Lock the owner config to the device identification number" + )] + locked_to_din: Option, + #[arg( long, value_enum, @@ -60,6 +66,10 @@ fn newversion_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { let uart = transport.uart("console")?; let rescue = RescueSerial::new(Rc::clone(&uart)); + log::info!("###### Get Device Info ######"); + rescue.enter(transport, /*reset=*/ true)?; + let devid = rescue.get_device_id()?; + log::info!("###### Upload Owner Block ######"); transfer_lib::create_owner( transport, @@ -72,6 +82,15 @@ fn newversion_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { /*customize=*/ |owner| { owner.config_version = opts.config_version; + if let Some(din) = opts.locked_to_din { + let din = match din { + 0 => devid.din, + x => x, + }; + owner.lock_constraint = 0x6; + owner.device_id[1] = din as u32; + owner.device_id[2] = (din >> 32) as u32; + } }, )?; @@ -88,7 +107,10 @@ fn newversion_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { for exp in opts.expect.iter() { let erx = Regex::new(exp)?; - ensure!(erx.is_match(&capture[0]), "Did not find expected output {exp:?}"); + ensure!( + erx.is_match(&capture[0]), + "Did not find expected output {exp:?}" + ); } Ok(()) diff --git a/sw/host/tests/ownership/transfer_test.rs b/sw/host/tests/ownership/transfer_test.rs index 35cba4646fd46..9e0f97dfc42dc 100644 --- a/sw/host/tests/ownership/transfer_test.rs +++ b/sw/host/tests/ownership/transfer_test.rs @@ -60,8 +60,14 @@ struct Opts { )] rescue_after_activate: Option, + #[arg(long, default_value_t = false, action = clap::ArgAction::Set, help = "Check the firmware boots prior to ownership transfer")] + pre_transfer_boot_check: bool, #[arg(long, default_value_t = true, action = clap::ArgAction::Set, help = "Check the firmware boot in dual-owner mode")] dual_owner_boot_check: bool, + #[arg(long, default_value_t = false, action = clap::ArgAction::Set, help = "If true, this test is a non-transferring update")] + non_transfer_update: bool, + #[arg(long, default_value_t = false, action = clap::ArgAction::Set, help = "Check the sealing keys")] + keygen_check: bool, #[arg(long, default_value = "Any", help = "Mode of the unlock operation")] unlock_mode: UnlockMode, @@ -69,10 +75,37 @@ struct Opts { expected_error: Option, } +fn remember(haystack: &str, re: &str, memory: &mut Vec) -> bool { + let re = Regex::new(re).expect("regex"); + if let Some(cap) = re.captures(haystack) { + memory.push(cap[cap.len() - 1].into()); + true + } else { + false + } +} + fn transfer_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { let uart = transport.uart("console")?; let rescue = RescueSerial::new(Rc::clone(&uart)); + let mut keygen = Vec::new(); + + if opts.pre_transfer_boot_check { + log::info!("###### Pre-transfer Boot Check ######"); + let capture = UartConsole::wait_for( + &*uart, + r"(?msR)Starting.*ownership_state = (\w+)$.*PASS!$|BFV:([0-9A-Fa-f]{8})$", + opts.timeout, + )?; + if capture[0].starts_with("BFV") { + return RomError(u32::from_str_radix(&capture[2], 16)?).into(); + } + if opts.keygen_check && !remember(&capture[0], r"sw_key = (\w+)", &mut keygen) { + return Err(anyhow!("Failed to find sw_key")); + } + } + log::info!("###### Get Boot Log (1/2) ######"); let (data, devid) = transfer_lib::get_device_info(transport, &rescue)?; log::info!("###### Ownership Unlock ######"); @@ -113,7 +146,7 @@ fn transfer_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { transport.reset_target(Duration::from_millis(50), /*clear_uart=*/ true)?; let capture = UartConsole::wait_for( &*uart, - r"(?msR)ownership_state = (\w+)$.*ownership_transfers = (\d+)$.*PASS!$|BFV:([0-9A-Fa-f]{8})$", + r"(?msR)Starting.*ownership_state = (\w+)$.*ownership_transfers = (\d+)$.*PASS!$|BFV:([0-9A-Fa-f]{8})$", opts.timeout, )?; if capture[0].starts_with("BFV") { @@ -126,6 +159,9 @@ fn transfer_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { _ => return Err(anyhow!("Unexpected ownership state: {}", capture[1])), } transfers0 = capture[2].parse::()?; + if opts.keygen_check && !remember(&capture[0], r"sw_key = (\w+)", &mut keygen) { + return Err(anyhow!("Failed to find sw_key")); + } } log::info!("###### Get Boot Log (2/2) ######"); @@ -153,15 +189,34 @@ fn transfer_test(opts: &Opts, transport: &TransportWrapper) -> Result<()> { transport.reset_target(Duration::from_millis(50), /*clear_uart=*/ true)?; let capture = UartConsole::wait_for( &*uart, - r"(?msR)ownership_state = (\w+)$.*ownership_transfers = (\d+)$.*PASS!$|BFV:([0-9A-Fa-f]{8})$", + r"(?msR)Starting.*ownership_state = (\w+)$.*ownership_transfers = (\d+)$.*PASS!$|BFV:([0-9A-Fa-f]{8})$", opts.timeout, )?; if capture[0].starts_with("BFV") { return RomError(u32::from_str_radix(&capture[3], 16)?).into(); } + if opts.keygen_check && !remember(&capture[0], r"sw_key = (\w+)", &mut keygen) { + return Err(anyhow!("Failed to find sw_key")); + } assert_eq!(capture[1], "OWND"); let transfers1 = capture[2].parse::()?; - assert_eq!(transfers0 + 1, transfers1); + + if opts.non_transfer_update { + assert_eq!(transfers0, transfers1); + } else { + assert_eq!(transfers0 + 1, transfers1); + } + + if opts.keygen_check { + log::info!("###### Checking software keys ######"); + log::info!("sw_keys = {keygen:?}"); + for i in 0..keygen.len() { + for j in i + 1..keygen.len() { + log::info!("Checking that key[{i}] != key[{j}]"); + assert_ne!(keygen[i], keygen[j]); + } + } + } Ok(()) }