Skip to content

[rom_ext,dice] Add hash integrity check for DICE pages #27424

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: earlgrey_1.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sw/device/silicon_creator/lib/cert/cert.h
Original file line number Diff line number Diff line change
Expand Up @@ -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").
*/
Expand Down
115 changes: 77 additions & 38 deletions sw/device/silicon_creator/lib/cert/dice_chain.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@

enum {
kFlashPageSize = FLASH_CTRL_PARAM_BYTES_PER_PAGE,

/**
* The size of the scratch buffer that is large enough for constructing the
* CDI certs.
*/
kScratchCertSizeBytes = FLASH_CTRL_PARAM_BYTES_PER_PAGE,
};

/**
Expand All @@ -44,20 +38,20 @@ typedef struct dice_chain {
/**
* RAM buffer that mirrors the DICE cert chain in a flash page.
*/
uint8_t data[kFlashPageSize];
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;
Expand Down Expand Up @@ -85,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.
Expand All @@ -110,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.
Expand All @@ -143,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();
}
Expand All @@ -170,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 =
Expand Down Expand Up @@ -227,11 +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) == kFlashPageSize,
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=*/kFlashPageSize / 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;
Expand All @@ -242,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,
Expand Down Expand Up @@ -323,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;
Expand Down Expand Up @@ -382,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;

Expand All @@ -398,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);
Expand Down Expand Up @@ -436,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;
HARDENED_RETURN_IF_ERROR(dice_cdi_1_cert_build(
(hmac_digest_t *)bl0_measurement, owner_measurement, owner_history_hash,
owner_manifest->security_version, key_domain, &dice_chain.key_ids,
Expand Down Expand Up @@ -464,15 +501,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) == kFlashPageSize,
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;
Expand Down
28 changes: 28 additions & 0 deletions sw/device/silicon_creator/lib/cert/dice_chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,27 @@
#include "sw/device/silicon_creator/lib/manifest.h"
#include "sw/device/silicon_creator/lib/ownership/datatypes.h"

#include "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.
*
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/lib/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,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(kErrorUsbBadSetup, ERROR_(0, kModuleUsb, kInvalidArgument)), \
X(kErrorUsbBadEndpointNumber, ERROR_(1, kModuleUsb, kInvalidArgument)), \
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/manuf/base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,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",
Expand Down
Loading
Loading