Skip to content

Commit

Permalink
[rom_ext] r/w DICE certs from/to flash in perso LTV object format
Browse files Browse the repository at this point in the history
The format by which DICE certs have been to provisioned into flash
recently changed such that the certs were written in the perso LTV
structure. This updates the ROM_EXT to use the same structure when
reading and writing DICE certs from/to flash.

Signed-off-by: Tim Trippel <[email protected]>
  • Loading branch information
timothytrippel committed Nov 5, 2024
1 parent 25e681e commit 4eafa4f
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 37 deletions.
4 changes: 3 additions & 1 deletion sw/device/silicon_creator/manuf/base/perso_tlv_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ rom_error_t perso_tlv_get_cert_obj(uint8_t *buf, size_t ltv_buf_size,
memcpy(&objh, buf, sizeof(perso_tlv_object_header_t));
// Extract LTV object size.
PERSO_TLV_GET_FIELD(Objh, Size, objh, &obj_size);
if (obj_size == 0)
return kErrorPersoTlvCertObjNotFound; // Object is empty.
if (obj_size > ltv_buf_size)
return kErrorPersoTlvInternal; // Something is really screwed up.
return kErrorPersoTlvInternal; // Object exceeds the size of host buffer.
obj->obj_size = obj_size;
// Extract LTV object type.
PERSO_TLV_GET_FIELD(Objh, Type, objh, &obj_type);
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/rom_ext/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ cc_library(
"//sw/device/silicon_creator/lib/ownership:ownership_activate",
"//sw/device/silicon_creator/lib/ownership:ownership_unlock",
"//sw/device/silicon_creator/lib/sigverify",
"//sw/device/silicon_creator/manuf/base:perso_tlv_data",
"//sw/otbn/crypto:boot",
],
)
Expand Down
121 changes: 85 additions & 36 deletions sw/device/silicon_creator/rom_ext/rom_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "sw/device/silicon_creator/lib/sigverify/ecdsa_p256_key.h"
#include "sw/device/silicon_creator/lib/sigverify/rsa_verify.h"
#include "sw/device/silicon_creator/lib/sigverify/sigverify.h"
#include "sw/device/silicon_creator/manuf/base/perso_tlv_data.h"
#include "sw/device/silicon_creator/rom_ext/rescue.h"
#include "sw/device/silicon_creator/rom_ext/rom_ext_boot_policy.h"
#include "sw/device/silicon_creator/rom_ext/rom_ext_boot_policy_ptrs.h"
Expand Down Expand Up @@ -101,6 +102,13 @@ const epmp_region_t kFlashRegion = {
// Certificate data.
static uint8_t dice_certs_page[FLASH_CTRL_PARAM_BYTES_PER_PAGE];
static hardened_bool_t dice_certs_page_dirty = kHardenedBoolFalse;
static perso_tlv_cert_obj_t dice_cert_obj = {
.obj_p = dice_certs_page, // Pointer to a perso TLV cert obj.
.obj_size = 0, // Perso TLV object size in bytes.
.cert_body_p = dice_certs_page, // Pointer to the cert data in the TLV obj.
.cert_body_size = 0, // Size of the cert data in bytes.
.name = {0}, // Name of the cert.
};
static size_t dice_certs_page_offset = 0;
static hmac_digest_t uds_pubkey_id;
static hmac_digest_t cdi_0_pubkey_id;
Expand Down Expand Up @@ -309,14 +317,35 @@ static uintptr_t owner_vma_get(const manifest_t *manifest, uintptr_t lma_addr) {
(uintptr_t)_owner_virtual_start_address + CHIP_ROM_EXT_SIZE_MAX);
}

static size_t dice_certs_buffer_space_remaining(void) {
return FLASH_CTRL_PARAM_BYTES_PER_PAGE -
(size_t)(dice_cert_obj.obj_p - dice_certs_page);
}

/**
* Increments the DICE cert page offset (ensuring to round up to the 64-bit
* flash word offset to prevent potential ECC issues).
*/
static void rom_ext_attestation_increment_cert_offset(size_t cert_size) {
HARDENED_CHECK_GE(cert_size, 4);
dice_certs_page_offset += util_size_to_words(cert_size) * sizeof(uint32_t);
static rom_error_t rom_ext_attestation_increment_cert_offset(void) {
// Round up to next flash word for next perso TLV object offset.
HARDENED_CHECK_GE(dice_cert_obj.cert_body_size, 4);
dice_certs_page_offset +=
util_size_to_words(dice_cert_obj.obj_size) * sizeof(uint32_t);
dice_certs_page_offset = util_round_up_to(dice_certs_page_offset, 3);

// Attempt to retrieve a perso TLV cert object.
rom_error_t err = perso_tlv_get_cert_obj(
dice_certs_page + dice_certs_page_offset,
FLASH_CTRL_PARAM_BYTES_PER_PAGE - dice_certs_page_offset, &dice_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.
return kErrorOk;
}
HARDENED_RETURN_IF_ERROR(err);

return kErrorOk;
}

OT_WARN_UNUSED_RESULT
Expand All @@ -331,12 +360,23 @@ static rom_error_t rom_ext_buffer_dice_certs_into_ram(void) {
if (flash_ctrl_err_code.rd_err) {
// If we encountered a read error, this could mean the certificate page
// has been corrupted or is not provisioned yet. In this case, we mark the
// page as "dirty" and set the buffer to all 1s, which are the values read
// from a freshly erased flash page.
memset(dice_certs_page, UINT8_MAX, FLASH_CTRL_PARAM_BYTES_PER_PAGE);
// page as "dirty" and set the buffer to all 0s.
memset(dice_certs_page, 0, FLASH_CTRL_PARAM_BYTES_PER_PAGE);
dice_certs_page_dirty = true;
return kErrorOk;
}
return err;
}

// Certificates are stored on flash info pages in perso LTV object form, see
// `sw/device/silicon_creator/manuf/base/perso_tlv_data.h` for more details.
// We must extract the offsets of the first DICE certificate (i.e., UDS).
err = perso_tlv_get_cert_obj(dice_certs_page, FLASH_CTRL_PARAM_BYTES_PER_PAGE,
&dice_cert_obj);
if (err == kErrorPersoTlvCertObjNotFound) {
// If the UDS cert is not found it is because we are running on a sim or
// FPGA platform, or the device has not yet been provisioned.
return kErrorOk;
}
return err;
}
Expand Down Expand Up @@ -370,10 +410,9 @@ static rom_error_t rom_ext_attestation_silicon(void) {
kDiceKeyUds.keygen_seed_idx, kDiceKeyUds.type,
*kDiceKeyUds.keymgr_diversifier));
hardened_bool_t cert_valid = kHardenedBoolFalse;
uint32_t cert_size = 0;
HARDENED_RETURN_IF_ERROR(cert_x509_asn1_check_serial_number(
dice_certs_page, dice_certs_page_offset, (uint8_t *)uds_pubkey_id.digest,
&cert_valid, &cert_size));
dice_cert_obj.cert_body_p, 0, (uint8_t *)uds_pubkey_id.digest,
&cert_valid, /*out_cert_size=*/NULL));
if (launder32(cert_valid) == kHardenedBoolFalse) {
// The UDS key ID (and cert itself) should never change unless:
// 1. there is a hardware issue, or
Expand All @@ -388,19 +427,21 @@ static rom_error_t rom_ext_attestation_silicon(void) {
// fully, we expect the cert to be missing. In this case, we write a
// 0-length (invalid) ASN.1 certificate header blob to avoid breaking tests
// that run on these platforms.
if (cert_size == 0) {
if (dice_cert_obj.cert_body_size == 0) {
dbg_printf("Writing empty UDS certificate ASN.1 blob.\r\n");
dice_certs_page[0] = 0x30;
dice_certs_page[1] = 0x82;
dice_certs_page[2] = 0x00;
dice_certs_page[3] = 0x00;
cert_size = 4;
uint8_t uds_cert[4] = {0x30, 0x82, 0x00, 0x00};
size_t cert_page_left = dice_certs_buffer_space_remaining();
HARDENED_RETURN_IF_ERROR(perso_tlv_cert_obj_build(
"UDS", /*needs_endorsement=*/false, uds_cert, sizeof(uds_cert),
dice_cert_obj.obj_p, &cert_page_left));
dice_certs_page_dirty = kHardenedBoolTrue;
// Reload the cert perso LTV object.
HARDENED_RETURN_IF_ERROR(perso_tlv_get_cert_obj(
dice_cert_obj.obj_p, dice_certs_buffer_space_remaining(),
&dice_cert_obj));
}
}

rom_ext_attestation_increment_cert_offset(cert_size);

HARDENED_RETURN_IF_ERROR(rom_ext_attestation_increment_cert_offset());
return kErrorOk;
}

Expand All @@ -419,10 +460,9 @@ static rom_error_t rom_ext_attestation_creator(
HARDENED_RETURN_IF_ERROR(otbn_boot_cert_ecc_p256_keygen(
kDiceKeyCdi0, &cdi_0_pubkey_id, &curr_attestation_pubkey));
hardened_bool_t cert_valid = kHardenedBoolFalse;
uint32_t cert_size = 0;
HARDENED_RETURN_IF_ERROR(cert_x509_asn1_check_serial_number(
dice_certs_page, dice_certs_page_offset,
(uint8_t *)cdi_0_pubkey_id.digest, &cert_valid, &cert_size));
dice_cert_obj.cert_body_p, /*offset=*/0,
(uint8_t *)cdi_0_pubkey_id.digest, &cert_valid, /*out_cert_size=*/NULL));
if (launder32(cert_valid) == kHardenedBoolFalse) {
HARDENED_CHECK_EQ(cert_valid, kHardenedBoolFalse);
dbg_printf("CDI_0 certificate not valid. Updating it ...\r\n");
Expand All @@ -432,16 +472,21 @@ static rom_error_t rom_ext_attestation_creator(
rom_ext_manifest->security_version, &cdi_0_key_ids,
&curr_attestation_pubkey, cdi_0_cert, &updated_cert_size));
// Update the cert page buffer.
memcpy(&dice_certs_page[dice_certs_page_offset], cdi_0_cert,
updated_cert_size);
size_t cert_page_left = dice_certs_buffer_space_remaining();
HARDENED_RETURN_IF_ERROR(perso_tlv_cert_obj_build(
"CDI_0", /*needs_endorsement=*/false, cdi_0_cert, updated_cert_size,
dice_cert_obj.obj_p, &cert_page_left));
dice_certs_page_dirty = kHardenedBoolTrue;
rom_ext_attestation_increment_cert_offset(updated_cert_size);
// Reload the cert perso LTV object.
HARDENED_RETURN_IF_ERROR(perso_tlv_get_cert_obj(
dice_cert_obj.obj_p, dice_certs_buffer_space_remaining(),
&dice_cert_obj));
HARDENED_RETURN_IF_ERROR(rom_ext_attestation_increment_cert_offset());
// If the CDI_0 cert is updated, it could overrun the CDI_1 cert, so we make
// sure to update that as well by erasing the existing cert from the buffer.
memset(&dice_certs_page[dice_certs_page_offset], UINT8_MAX,
FLASH_CTRL_PARAM_BYTES_PER_PAGE - dice_certs_page_offset);
memset(dice_cert_obj.obj_p, 0, dice_certs_buffer_space_remaining());
} else {
rom_ext_attestation_increment_cert_offset(cert_size);
HARDENED_RETURN_IF_ERROR(rom_ext_attestation_increment_cert_offset());
}
return kErrorOk;
}
Expand All @@ -461,10 +506,9 @@ static rom_error_t rom_ext_attestation_owner(const manifest_t *owner_manifest) {
HARDENED_RETURN_IF_ERROR(otbn_boot_cert_ecc_p256_keygen(
kDiceKeyCdi1, &cdi_1_pubkey_id, &curr_attestation_pubkey));
hardened_bool_t cert_valid = kHardenedBoolFalse;
uint32_t cert_size = 0;
HARDENED_RETURN_IF_ERROR(cert_x509_asn1_check_serial_number(
dice_certs_page, dice_certs_page_offset,
(uint8_t *)cdi_1_pubkey_id.digest, &cert_valid, &cert_size));
dice_cert_obj.cert_body_p, /*offset=*/0,
(uint8_t *)cdi_1_pubkey_id.digest, &cert_valid, /*out_cert_size=*/NULL));
if (launder32(cert_valid) == kHardenedBoolFalse) {
HARDENED_CHECK_EQ(cert_valid, kHardenedBoolFalse);
dbg_printf("CDI_1 certificate not valid. Updating it ...\r\n");
Expand All @@ -476,16 +520,21 @@ static rom_error_t rom_ext_attestation_owner(const manifest_t *owner_manifest) {
owner_manifest->security_version, &cdi_1_key_ids,
&curr_attestation_pubkey, cdi_1_cert, &updated_cert_size));
// Update the cert page buffer.
memcpy(&dice_certs_page[dice_certs_page_offset], cdi_1_cert,
updated_cert_size);
size_t cert_page_left = dice_certs_buffer_space_remaining();
HARDENED_RETURN_IF_ERROR(perso_tlv_cert_obj_build(
"CDI_1", /*needs_endorsement=*/false, cdi_1_cert, updated_cert_size,
dice_cert_obj.obj_p, &cert_page_left));
dice_certs_page_dirty = kHardenedBoolTrue;
rom_ext_attestation_increment_cert_offset(updated_cert_size);
// Reload the cert perso LTV object.
HARDENED_RETURN_IF_ERROR(perso_tlv_get_cert_obj(
dice_cert_obj.obj_p, dice_certs_buffer_space_remaining(),
&dice_cert_obj));
HARDENED_RETURN_IF_ERROR(rom_ext_attestation_increment_cert_offset());
// If the CDI_1 cert is updated, it could be smaller than the previous CDI_1
// cert, so we make sure to clear out the rest of the buffer.
memset(&dice_certs_page[dice_certs_page_offset], UINT8_MAX,
FLASH_CTRL_PARAM_BYTES_PER_PAGE - dice_certs_page_offset);
memset(dice_cert_obj.obj_p, 0, dice_certs_buffer_space_remaining());
} else {
rom_ext_attestation_increment_cert_offset(cert_size);
HARDENED_RETURN_IF_ERROR(rom_ext_attestation_increment_cert_offset());
}

// TODO: elimiate this call when we've fully programmed keymgr and lock it.
Expand Down

0 comments on commit 4eafa4f

Please sign in to comment.