diff --git a/sw/device/silicon_creator/lib/cert/cert.h b/sw/device/silicon_creator/lib/cert/cert.h index 20fb01c10d86b..4baff633151f9 100644 --- a/sw/device/silicon_creator/lib/cert/cert.h +++ b/sw/device/silicon_creator/lib/cert/cert.h @@ -82,6 +82,11 @@ typedef struct cert_flash_info_layout { * extensions. */ bool used; + /** + * Boolean to indicate if a hash digest needs to be placed at the end of the + * page. + */ + bool need_digest; /** * A name string for the group of certificates (e.g., "DICE"). */ diff --git a/sw/device/silicon_creator/lib/cert/dice_chain.c b/sw/device/silicon_creator/lib/cert/dice_chain.c index 38fa54f3ebbf6..64793dcaf8c5a 100644 --- a/sw/device/silicon_creator/lib/cert/dice_chain.c +++ b/sw/device/silicon_creator/lib/cert/dice_chain.c @@ -25,11 +25,7 @@ #include "hw/top/flash_ctrl_regs.h" // Generated. enum { - /** - * The size of the scratch buffer that is large enough for constructing the - * CDI certs. - */ - kScratchCertSizeBytes = FLASH_CTRL_PARAM_BYTES_PER_PAGE, + kFlashPageSize = FLASH_CTRL_PARAM_BYTES_PER_PAGE, }; /** @@ -42,20 +38,20 @@ typedef struct dice_chain { /** * RAM buffer that mirrors the DICE cert chain in a flash page. */ - uint8_t data[FLASH_CTRL_PARAM_BYTES_PER_PAGE]; + dice_page_t page; /** - * Indicate whether `data` needs to be written back to flash. + * Indicate whether `page` needs to be written back to flash. */ hardened_bool_t data_dirty; /** - * The amount of bytes in `data` that has been processed. + * The amount of bytes in `page.data` that has been processed. */ size_t tail_offset; /** - * Indicate the info page currently buffered in `data`. + * Indicate the info page currently buffered in `page`. * This is used to skip unnecessary read ops. */ const flash_ctrl_info_page_t *info_page; @@ -83,7 +79,7 @@ typedef struct dice_chain { /** * Scratch buffer for constructing CDI certs. */ - uint8_t scratch_cert[kScratchCertSizeBytes]; + uint8_t scratch_cert[kDicePageDataSize]; /** * The current tlv cert the builder is processing. @@ -108,14 +104,14 @@ cert_key_id_pair_t dice_chain_cdi_0_key_ids = (cert_key_id_pair_t){ OT_WARN_UNUSED_RESULT OT_NOINLINE static size_t dice_chain_get_tail_size(void) { - HARDENED_CHECK_GE(sizeof(dice_chain.data), dice_chain.tail_offset); - return sizeof(dice_chain.data) - dice_chain.tail_offset; + HARDENED_CHECK_GE(sizeof(dice_chain.page.data), dice_chain.tail_offset); + return sizeof(dice_chain.page.data) - dice_chain.tail_offset; } // Get the pointer to the remaining tail space that is not processed yet. OT_WARN_UNUSED_RESULT static uint8_t *dice_chain_get_tail_buffer(void) { - return &dice_chain.data[dice_chain.tail_offset]; + return &dice_chain.page.data[dice_chain.tail_offset]; } // Cleanup stale `cert_obj` data and mark it as invalid. @@ -141,7 +137,7 @@ static void dice_chain_next_cert_obj(void) { dice_chain.tail_offset += cert_size; // Post-check for the buffer boundary. - HARDENED_CHECK_LE(dice_chain.tail_offset, sizeof(dice_chain.data)); + HARDENED_CHECK_LE(dice_chain.tail_offset, sizeof(dice_chain.page.data)); dice_chain_reset_cert_obj(); } @@ -168,18 +164,13 @@ static rom_error_t dice_chain_load_cert_obj(const char *name, if (err != kErrorOk) { // Cleanup the stale value if error. dice_chain_reset_cert_obj(); - } - if (err == kErrorPersoTlvCertObjNotFound) { - // If the cert is not found it is because we are running on a sim or FPGA - // platform, or the device has not yet been provisioned. Continue, and let - // the ROM_EXT generate an identity certificate for the current DICE stage. - // The error is not fatal, and the cert obj has been marked as invalid. + // If the cert is not found or corrupted, continue and allow the ROM_EXT + // to generate an identity certificate for the current DICE stage. The + // error is not fatal, and the cert obj has been marked as invalid. return kErrorOk; } - RETURN_IF_ERROR(err); - // Check if this cert is what we are looking for. The name and type (X.509 vs // CWT) should match. const perso_tlv_object_type_t kExpectedCertType = @@ -225,12 +216,11 @@ static rom_error_t dice_chain_load_flash( RETURN_IF_ERROR(dice_chain_flush_flash()); // Read in a DICE certificate(s) page. - static_assert(sizeof(dice_chain.data) == FLASH_CTRL_PARAM_BYTES_PER_PAGE, + static_assert(sizeof(dice_chain.page) == kFlashPageSize, "Invalid dice_chain buffer size"); RETURN_IF_ERROR(flash_ctrl_info_read_zeros_on_read_error( info_page, /*offset=*/0, - /*word_count=*/FLASH_CTRL_PARAM_BYTES_PER_PAGE / sizeof(uint32_t), - dice_chain.data)); + /*word_count=*/kFlashPageSize / sizeof(uint32_t), &dice_chain.page)); // Resets the flash page status. dice_chain.data_dirty = kHardenedBoolFalse; @@ -241,6 +231,18 @@ static rom_error_t dice_chain_load_flash( return kErrorOk; } +// Add the hash digest to the last of the page. +static rom_error_t dice_chain_seal_page(void) { + // Hash the entire page before the digest. + hmac_sha256(dice_chain.page.data, sizeof(dice_chain.page.data), + &dice_chain.page.digest); + + // The page is going to be updated. + dice_chain.data_dirty = kHardenedBoolTrue; + + return kErrorOk; +} + // Push the certificate to the tail with TLV header. OT_WARN_UNUSED_RESULT static rom_error_t dice_chain_push_cert(const char *name, const uint8_t *cert, @@ -322,9 +324,6 @@ rom_error_t dice_chain_attestation_creator( // Switch page for the device generated CDI_0. RETURN_IF_ERROR(dice_chain_load_flash(&kFlashCtrlInfoPageDiceCerts)); - // Seek to skip previous objects. - RETURN_IF_ERROR(dice_chain_skip_cert_obj("UDS", /*name_size=*/4)); - // Check if the current CDI_0 cert is valid. dice_chain.subject_pubkey_id = static_dice_cdi_0.cdi_0_pubkey_id; dice_chain.subject_pubkey = static_dice_cdi_0.cdi_0_pubkey; @@ -381,9 +380,6 @@ static rom_error_t dice_chain_attestation_check_cdi_0(void) { // Switch page for the device CDI chain. RETURN_IF_ERROR(dice_chain_load_flash(&kFlashCtrlInfoPageDiceCerts)); - // Seek to skip previous objects. - RETURN_IF_ERROR(dice_chain_skip_cert_obj("UDS", /*name_size=*/4)); - // Set the endorsement key for the next cert. dice_chain.endorsement_pubkey_id = static_dice_cdi_0.cdi_0_pubkey_id; @@ -397,14 +393,56 @@ static rom_error_t dice_chain_attestation_check_cdi_0(void) { } } +// Check the hash digest at the last of the page. +static rom_error_t dice_chain_seal_page_check( + const flash_ctrl_info_page_t *info_page) { + RETURN_IF_ERROR(dice_chain_load_flash(info_page)); + // Hash the entire page before the digest. + hmac_digest_t expected_digest; + hmac_sha256(dice_chain.page.data, sizeof(dice_chain.page.data), + &expected_digest); + + // Compare with the digest stored at the end of the page. + if (memcmp(&dice_chain.page.digest, &expected_digest, + sizeof(hmac_digest_t)) != 0) { + return kErrorDicePageCorrupted; + } + + return kErrorOk; +} + +rom_error_t dice_chain_rom_ext_check(void) { + if (dice_chain_seal_page_check(&kFlashCtrlInfoPageFactoryCerts) != kErrorOk) { + dbg_puts("warning: corrupted FactoryCerts page\r\n"); + } + + // Retry if the current cache is corrupted. + rom_error_t error = dice_chain_seal_page_check(&kFlashCtrlInfoPageDiceCerts); + if (error == kErrorDicePageCorrupted) { + dbg_puts("warning: corrupted DiceCerts page\r\n"); + // Clear the corrupted page and reboot. + dice_chain.data_dirty = kHardenedBoolTrue; + memset(&dice_chain.page, 0, sizeof(dice_chain.page)); + RETURN_IF_ERROR(dice_chain_flush_flash()); + + if (static_dice_cdi_0.cert_size > 0) { + // Continue since CDI_0 has been generated + error = kErrorOk; + } + } + RETURN_IF_ERROR(error); + + // Handles the certificates from the immutable rom_ext. + RETURN_IF_ERROR(dice_chain_attestation_check_uds()); + RETURN_IF_ERROR(dice_chain_attestation_check_cdi_0()); + + return kErrorOk; +} + rom_error_t dice_chain_attestation_owner( const manifest_t *owner_manifest, keymgr_binding_value_t *bl0_measurement, hmac_digest_t *owner_measurement, hmac_digest_t *owner_history_hash, keymgr_binding_value_t *sealing_binding, owner_app_domain_t key_domain) { - // Handles the certificates from the immutable rom_ext first. - RETURN_IF_ERROR(dice_chain_attestation_check_uds()); - RETURN_IF_ERROR(dice_chain_attestation_check_cdi_0()); - // Generate CDI_1 attestation keys and (potentially) update certificate. SEC_MMIO_WRITE_INCREMENT(kScKeymgrSecMmioSwBindingSet + kScKeymgrSecMmioOwnerIntMaxVerSet); @@ -435,7 +473,7 @@ rom_error_t dice_chain_attestation_owner( if (dice_chain.cert_valid == kHardenedBoolFalse) { dbg_puts("warning: CDI_1 certificate not valid; updating\r\n"); // Update the cert page buffer. - size_t updated_cert_size = kScratchCertSizeBytes; + size_t updated_cert_size = kDicePageDataSize; // TODO(#19596): add owner configuration block measurement to CDI_1 cert. HARDENED_RETURN_IF_ERROR(dice_cdi_1_cert_build( (hmac_digest_t *)bl0_measurement, owner_measurement, owner_history_hash, @@ -464,15 +502,17 @@ rom_error_t dice_chain_attestation_owner( rom_error_t dice_chain_flush_flash(void) { if (dice_chain.data_dirty == kHardenedBoolTrue && dice_chain.info_page != NULL) { + RETURN_IF_ERROR(dice_chain_seal_page()); + RETURN_IF_ERROR( flash_ctrl_info_erase(dice_chain.info_page, kFlashCtrlEraseTypePage)); - static_assert(sizeof(dice_chain.data) == FLASH_CTRL_PARAM_BYTES_PER_PAGE, + static_assert(sizeof(dice_chain.page) == kFlashPageSize, "Invalid dice_chain buffer size"); RETURN_IF_ERROR(flash_ctrl_info_write( dice_chain.info_page, /*offset=*/0, /*word_count=*/FLASH_CTRL_PARAM_BYTES_PER_PAGE / sizeof(uint32_t), - dice_chain.data)); + &dice_chain.page)); dice_chain.data_dirty = kHardenedBoolFalse; } return kErrorOk; diff --git a/sw/device/silicon_creator/lib/cert/dice_chain.h b/sw/device/silicon_creator/lib/cert/dice_chain.h index b94a54f653076..e6dcfd20d2a24 100644 --- a/sw/device/silicon_creator/lib/cert/dice_chain.h +++ b/sw/device/silicon_creator/lib/cert/dice_chain.h @@ -12,10 +12,27 @@ #include "sw/device/silicon_creator/lib/manifest.h" #include "sw/device/silicon_creator/lib/ownership/datatypes.h" +#include "hw/top/flash_ctrl_regs.h" // Generated. + #ifdef __cplusplus extern "C" { #endif +enum { + kDicePageDataSize = FLASH_CTRL_PARAM_BYTES_PER_PAGE - sizeof(hmac_digest_t), +}; + +/** + * The flash page schema for holding DICE certificates. + */ +typedef struct dice_page { + uint8_t data[kDicePageDataSize]; + hmac_digest_t digest; +} dice_page_t; + +static_assert(sizeof(dice_page_t) == FLASH_CTRL_PARAM_BYTES_PER_PAGE, + "Invalid dice page size"); + /** * Initialize the dice chain builder with data from the flash pages. * @@ -69,6 +86,17 @@ rom_error_t dice_chain_attestation_owner( OT_WARN_UNUSED_RESULT rom_error_t dice_chain_flush_flash(void); +/** + * Checks that the factory-provisioned certificates in flash are valid and + * updates device-generated certificates if they have been invalidated. + * + * This function needs to be called after `dice_chain_init()`. + * + * @return errors encountered during the operation. + */ +OT_WARN_UNUSED_RESULT +rom_error_t dice_chain_rom_ext_check(void); + #ifdef __cplusplus } #endif diff --git a/sw/device/silicon_creator/lib/error.h b/sw/device/silicon_creator/lib/error.h index bdf14188cda89..07e5824deef98 100644 --- a/sw/device/silicon_creator/lib/error.h +++ b/sw/device/silicon_creator/lib/error.h @@ -260,6 +260,7 @@ enum module_ { X(kErrorDiceCwtCoseKeyNotFound, ERROR_(1, kModuleDice, kNotFound)), \ X(kErrorDiceCwtCoseKeyBadSize, ERROR_(1, kModuleDice, kInternal)), \ X(kErrorDiceCwtKeyCoordsNotFound, ERROR_(2, kModuleDice, kNotFound)), \ + X(kErrorDicePageCorrupted, ERROR_(3, kModuleDice, kInvalidArgument)), \ \ X(kErrorPwrmgrUnknownRequestSource, ERROR_(1, kModulePwrmgr, kInvalidArgument)), \ X(kErrorPwrmgrInvalidRequestType, ERROR_(2, kModulePwrmgr, kInvalidArgument)), \ diff --git a/sw/device/silicon_creator/manuf/base/BUILD b/sw/device/silicon_creator/manuf/base/BUILD index b61c3fba1fbdb..dda2016abfaaa 100644 --- a/sw/device/silicon_creator/manuf/base/BUILD +++ b/sw/device/silicon_creator/manuf/base/BUILD @@ -342,6 +342,7 @@ manifest(d = { "//sw/device/silicon_creator/lib/cert", "//sw/device/silicon_creator/lib/cert:cdi_0_template_library", "//sw/device/silicon_creator/lib/cert:cdi_1_template_library", + "//sw/device/silicon_creator/lib/cert:dice_chain", "//sw/device/silicon_creator/lib/cert:uds_template_library", "//sw/device/silicon_creator/lib/drivers:flash_ctrl", "//sw/device/silicon_creator/lib/drivers:hmac", diff --git a/sw/device/silicon_creator/manuf/base/ft_personalize.c b/sw/device/silicon_creator/manuf/base/ft_personalize.c index cd8760a4742c1..73f076df792f6 100644 --- a/sw/device/silicon_creator/manuf/base/ft_personalize.c +++ b/sw/device/silicon_creator/manuf/base/ft_personalize.c @@ -35,6 +35,7 @@ #include "sw/device/silicon_creator/lib/cert/cdi_1.h" // Generated. #include "sw/device/silicon_creator/lib/cert/cert.h" #include "sw/device/silicon_creator/lib/cert/dice.h" +#include "sw/device/silicon_creator/lib/cert/dice_chain.h" #include "sw/device/silicon_creator/lib/cert/uds.h" // Generated. #include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h" #include "sw/device/silicon_creator/lib/drivers/hmac.h" @@ -157,6 +158,7 @@ static uint8_t all_certs[8192]; // 1K should be enough for the largest certificate perso LTV object. enum { kBufferSize = 1024 }; static alignas(uint32_t) uint8_t cert_buffer[kBufferSize]; +static alignas(uint32_t) dice_page_t dice_page; static size_t uds_offset; static size_t cdi_0_offset; static size_t cdi_1_offset; @@ -166,12 +168,14 @@ static cert_flash_info_layout_t cert_flash_layout[] = { // post manufacturing. This page should never be erased by ROM_EXT, nor // owner firmware. .used = true, + .need_digest = true, .group_name = "FACTORY", .info_page = &kFlashCtrlInfoPageFactoryCerts, .num_certs = 1, }, { .used = true, + .need_digest = true, .group_name = "DICE", .info_page = &kFlashCtrlInfoPageDiceCerts, .num_certs = 2, @@ -180,12 +184,14 @@ static cert_flash_info_layout_t cert_flash_layout[] = { // additional certificates SKU owners may desire to provision. { .used = false, + .need_digest = false, .group_name = "Ext0", .info_page = &kFlashCtrlInfoPageOwnerReserved6, .num_certs = 0, }, { .used = false, + .need_digest = false, .group_name = "Ext1", .info_page = &kFlashCtrlInfoPageOwnerReserved7, .num_certs = 0, @@ -853,32 +859,32 @@ static status_t extract_next_cert(uint8_t **dest, size_t *free_room) { return OK_STATUS(); } -static status_t write_cert_to_flash_info_page( - const cert_flash_info_layout_t *layout, perso_tlv_cert_obj_t *block, - uint8_t *cert_data, uint32_t page_offset, uint32_t cert_write_size_bytes, - uint32_t cert_write_size_words) { - if ((page_offset + cert_write_size_bytes) > FLASH_CTRL_PARAM_BYTES_PER_PAGE) { +static status_t write_cert_to_dice_page(const cert_flash_info_layout_t *layout, + perso_tlv_cert_obj_t *block, + uint8_t *cert_data, + uint32_t page_offset, + uint32_t cert_write_size_bytes) { + base_printf("Importing %s cert to %s ...\n", block->name, layout->group_name); + if ((page_offset + cert_write_size_bytes) > sizeof(dice_page.data)) { LOG_ERROR("%s %s certificate did not fit into the info page.", layout->group_name, block->name); return OUT_OF_RANGE(); } - if (sizeof(cert_buffer) < cert_write_size_bytes) { - LOG_ERROR("%s %s certificate did not fit into the buffer.", - layout->group_name, block->name); - return OUT_OF_RANGE(); - } - memset(cert_buffer, 0, cert_write_size_bytes); + // The page will be zero-padded between obj_size to cert_write_size_bytes. + TRY_CHECK(block->obj_size <= cert_write_size_bytes); // Copy the actual certificate data into the cert buffer. - // This is necessary because flash_ctrl_info_write() requires the - // data source pointer to be word-aligned. The input cert_data - // pointer might not meet this alignment requirement, whereas - // cert_buffer is expected to be world-aligned. - memcpy(cert_buffer, cert_data, block->obj_size); + memcpy(dice_page.data + page_offset, cert_data, block->obj_size); + + return OK_STATUS(); +} - TRY(flash_ctrl_info_write(layout->info_page, page_offset, - cert_write_size_words, cert_buffer)); +static status_t write_digest_to_dice_page( + const cert_flash_info_layout_t *layout, uint32_t page_offset) { + base_printf("Digesting %s page ...\n", layout->group_name); + + hmac_sha256(dice_page.data, sizeof(dice_page.data), &dice_page.digest); return OK_STATUS(); } @@ -973,6 +979,8 @@ static status_t personalize_endorse_certificates(ujson_t *uj) { continue; } + memset(&dice_page, 0, sizeof(dice_page)); + // This is a bit brittle, but we expect the sum of {layout}.num_certs values // in the following flash layout sections to be equal to the number of // endorsed extension certificates received from the host. @@ -982,15 +990,22 @@ static status_t personalize_endorse_certificates(ujson_t *uj) { // Round up the size to the nearest word boundary. uint32_t cert_size_words = util_size_to_words(block.obj_size); uint32_t cert_size_bytes_ru = cert_size_words * sizeof(uint32_t); - TRY(write_cert_to_flash_info_page(&curr_layout, &block, next_cert, - page_offset, cert_size_bytes_ru, - cert_size_words)); + TRY(write_cert_to_dice_page(&curr_layout, &block, next_cert, page_offset, + cert_size_bytes_ru)); page_offset += cert_size_bytes_ru; next_cert += block.obj_size; // Each certificate must be 8 bytes aligned (flash word size). page_offset = util_round_up_to(page_offset, 3); } + + if (curr_layout.need_digest) { + TRY(write_digest_to_dice_page(&curr_layout, page_offset)); + } + + TRY(flash_ctrl_info_write(curr_layout.info_page, /*page_offset=*/0, + util_size_to_words(sizeof(dice_page)), + &dice_page)); } // DO NOT CHANGE THE BELOW STRING without modifying the host code in diff --git a/sw/device/silicon_creator/rom_ext/e2e/dice_chain/BUILD b/sw/device/silicon_creator/rom_ext/e2e/dice_chain/BUILD index a9b17de660b52..e576f6aa9b083 100644 --- a/sw/device/silicon_creator/rom_ext/e2e/dice_chain/BUILD +++ b/sw/device/silicon_creator/rom_ext/e2e/dice_chain/BUILD @@ -138,3 +138,58 @@ test_suite( for tc in _VARIATION_INTEROP_TEST_CASES ], ) + +opentitan_binary( + name = "modify_digest", + testonly = True, + srcs = ["modify_digest.c"], + exec_env = [ + "//hw/top_earlgrey:fpga_cw340_rom_with_fake_keys", + ], + deps = [ + "//hw/top:flash_ctrl_c_regs", + "//hw/top/dt", + "//sw/device/lib/base:status", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing/test_framework:ottf_alerts", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/silicon_creator/lib/drivers:flash_ctrl", + "//sw/device/silicon_creator/lib/drivers:hmac", + ], +) + +opentitan_test( + name = "corrupted_digest_test", + srcs = ["//sw/device/silicon_creator/rom_ext/e2e/verified_boot:boot_test"], + exec_env = { + "//hw/top_earlgrey:fpga_cw340_rom_ext": None, + }, + fpga = fpga_params( + binaries = { + ":modify_digest": "modify_digest", + }, + test_cmd = """ + --exec="transport init" + --exec="fpga clear-bitstream" + --exec="fpga load-bitstream {bitstream}" + --exec="no-op --info='##### Prepare CDI cert page'" + --exec="bootstrap --clear-uart=true {firmware}" + --exec="console --non-interactive --exit-success='{exit_success}' --exit-failure='{exit_failure}' --timeout=10s" + --exec="no-op --info='##### Corrupt CDI cert page'" + --exec="bootstrap --clear-uart=true {modify_digest}" + --exec="console --non-interactive --exit-success='PASS!' --timeout=10s" + --exec="bootstrap --clear-uart=true {firmware}" + --exec="no-op --info='##### Corruption should be detected and reboot'" + --exec="console --non-interactive --exit-success='BFV:03444303' --exit-failure='{exit_success}' --timeout=10s" + --exec="no-op --info='##### Corruption fixed in the second boot'" + --exec="console --non-interactive --exit-success='{exit_success}' --exit-failure='{exit_failure}' --timeout=10s" + no-op + """, + ), + deps = [ + "//sw/device/lib/base:status", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/silicon_creator/lib:boot_log", + "//sw/device/silicon_creator/lib/drivers:retention_sram", + ], +) diff --git a/sw/device/silicon_creator/rom_ext/e2e/dice_chain/modify_digest.c b/sw/device/silicon_creator/rom_ext/e2e/dice_chain/modify_digest.c new file mode 100644 index 0000000000000..11a8d56c9f256 --- /dev/null +++ b/sw/device/silicon_creator/rom_ext/e2e/dice_chain/modify_digest.c @@ -0,0 +1,57 @@ +// 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/lib/base/status.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/test_framework/ottf_alerts.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h" +#include "sw/device/silicon_creator/lib/drivers/hmac.h" + +#include "hw/top/flash_ctrl_regs.h" +#include "hw/top/dt/flash_ctrl.h" + +OTTF_DEFINE_TEST_CONFIG(); + +enum { + kNumWords = FLASH_CTRL_PARAM_BYTES_PER_PAGE / sizeof(uint32_t), +}; + +static status_t clear_digest_page(const flash_ctrl_info_page_t *info_page) { + uint32_t data[kNumWords]; + + TRY(flash_ctrl_info_read_zeros_on_read_error(info_page, 0, kNumWords, data)); + + // Invalidate the digest + uint32_t prev = data[kNumWords - 1]; + data[kNumWords - 1] ^= 1; + LOG_INFO("Modify digest %x -> %x", prev, data[kNumWords - 1]); + + TRY(flash_ctrl_info_erase(info_page, kFlashCtrlEraseTypePage)); + TRY(flash_ctrl_info_write(info_page, 0, kNumWords, data)); + + return OK_STATUS(); +} + +static status_t modify_digest_test(void) { + flash_ctrl_cert_info_page_creator_cfg(&kFlashCtrlInfoPageFactoryCerts); + flash_ctrl_cert_info_page_creator_cfg(&kFlashCtrlInfoPageDiceCerts); + TRY(clear_digest_page(&kFlashCtrlInfoPageFactoryCerts)); + TRY(clear_digest_page(&kFlashCtrlInfoPageDiceCerts)); + return OK_STATUS(); +} + +bool test_main(void) { + // This test intentionally creates an invalid digest triggering recoverable + // errors. Ignore the alert in OTTF. + CHECK_STATUS_OK( + ottf_alerts_expect_alert_start(dt_flash_ctrl_alert_to_alert_id( + kDtFlashCtrlFirst, kDtFlashCtrlAlertRecovErr))); + + status_t sts = modify_digest_test(); + if (status_err(sts)) { + LOG_ERROR("modify_digest_test: %r", sts); + } + return status_ok(sts); +} diff --git a/sw/device/silicon_creator/rom_ext/rom_ext.c b/sw/device/silicon_creator/rom_ext/rom_ext.c index 5b3058cfc00e1..a8020dd23a2b8 100644 --- a/sw/device/silicon_creator/rom_ext/rom_ext.c +++ b/sw/device/silicon_creator/rom_ext/rom_ext.c @@ -679,6 +679,7 @@ static rom_error_t rom_ext_start(boot_data_t *boot_data, boot_log_t *boot_log) { // Prepare dice chain builder for CDI_1. HARDENED_RETURN_IF_ERROR(dice_chain_init()); + HARDENED_RETURN_IF_ERROR(dice_chain_rom_ext_check()); // Initialize the boot_log in retention RAM. const chip_info_t *rom_chip_info = (const chip_info_t *)_rom_chip_info_start;