Skip to content

Commit

Permalink
[personalize] generate unendorsed UDS certificate on the device
Browse files Browse the repository at this point in the history
This update the personalization firmware to generate the UDS certificate
on the device, and write it out over the console so it may be endorsed.

Signed-off-by: Tim Trippel <[email protected]>
  • Loading branch information
timothytrippel committed Feb 10, 2024
1 parent 2d32a43 commit 97f4912
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 64 deletions.
6 changes: 4 additions & 2 deletions sw/device/lib/testing/json/provisioning_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ UJSON_SERDE_STRUCT(ManufRmaTokenPersoDataIn, \
#define STRUCT_MANUF_CERT_PERSO_DATA_IN(field, string) \
field(rom_ext_measurement, uint32_t, 8) \
field(owner_manifest_measurement, uint32_t, 8) \
field(owner_measurement, uint32_t, 8)
field(owner_measurement, uint32_t, 8) \
field(auth_key_key_id, uint8_t, 20)
UJSON_SERDE_STRUCT(ManufCertPersoDataIn, \
manuf_cert_perso_data_in_t, \
STRUCT_MANUF_CERT_PERSO_DATA_IN);
Expand Down Expand Up @@ -97,7 +98,8 @@ UJSON_SERDE_STRUCT(ManufRmaTokenPersoDataOut, \
*/
// clang-format off
#define STRUCT_MANUF_CERT_PERSO_DATA_OUT(field, string) \
field(uds_certificate, ecc_p256_public_key_t) \
field(uds_certificate, uint8_t, 596) \
field(uds_certificate_size, size_t) \
field(cdi_0_certificate, ecc_p256_public_key_t) \
field(cdi_1_certificate, ecc_p256_public_key_t)
UJSON_SERDE_STRUCT(ManufCertPersoDataOut, \
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/manuf/provisioning_inputs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ FT_PROVISIONING_INPUTS = _DEVICE_ID_AND_TEST_TOKENS + """
--rom-ext-measurement="0x00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"
--owner-manifest-measurement="0x00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"
--owner-measurement="0x00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"
--uds-auth-key-id="0x11223344_55667788_99112233_44556677_88991122"
"""

FT_PERSONALIZE_SIGNING_KEYS = select({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@
load("@rules_pkg//pkg:tar.bzl", "pkg_tar")
load("//rules:const.bzl", "CONST", "hex")
load("//rules:manifest.bzl", "manifest")
load(
"//rules:opentitan.bzl",
"RSA_ONLY_KEY_STRUCTS",
)
load("//rules:signing.bzl", "offline_presigning_artifacts", "offline_signature_attach")
load(
"//rules/opentitan:defs.bzl",
"cw310_jtag_params",
"hyper310_jtag_params",
"opentitan_binary",
"opentitan_test",
"rsa_key_for_lc_state",
"silicon_jtag_params",
)
load(
Expand Down Expand Up @@ -277,6 +272,7 @@ opentitan_binary(
"//sw/device/silicon_creator/lib:attestation_key_diversifiers",
"//sw/device/silicon_creator/lib:error",
"//sw/device/silicon_creator/lib:otbn_boot_services",
"//sw/device/silicon_creator/lib/cert:uds_template_library",
"//sw/device/silicon_creator/lib/drivers:flash_ctrl",
"//sw/device/silicon_creator/lib/drivers:hmac",
"//sw/device/silicon_creator/lib/drivers:keymgr",
Expand Down Expand Up @@ -317,10 +313,6 @@ opentitan_test(
"//hw/top_earlgrey:fpga_cw310_sival": None,
"//hw/top_earlgrey:silicon_creator": None,
},
rsa_key = rsa_key_for_lc_state(
RSA_ONLY_KEY_STRUCTS,
CONST.LCV.PROD,
),
silicon = silicon_jtag_params(
binaries = _FT_PROVISIONING_BINARIES,
data = ["//sw/device/silicon_creator/manuf/keys/fake:rma_unlock_token_export_key.sk_hsm.der"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "sw/device/lib/testing/test_framework/ujson_ottf.h"
#include "sw/device/silicon_creator/lib/attestation.h"
#include "sw/device/silicon_creator/lib/attestation_key_diversifiers.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"
#include "sw/device/silicon_creator/lib/drivers/keymgr.h"
Expand All @@ -24,18 +25,39 @@
OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true);

enum {
/**
* Attestation measurement sizes, comprised of a SHA256 digest.
*/
kAttestMeasurementSizeInBits = 256,
kAttestMeasurementSizeInBytes = kAttestMeasurementSizeInBits / 8,
kAttestMeasurementSizeIn32BitWords =
kAttestMeasurementSizeInBytes / sizeof(uint32_t),

/**
* Certificate Key ID size.
*/
kCertKeyIdSizeInBytes = 20,
};

static manuf_cert_perso_data_in_t in_data;
static manuf_cert_perso_data_out_t out_data;
static keymgr_binding_value_t attestation_binding_value = {.data = {0}};
static keymgr_binding_value_t sealing_binding_value = {.data = {0}};
static uint32_t rom_ext_measurements[kAttestMeasurementSizeIn32BitWords * 2];

// Certificate data.
static manuf_cert_perso_data_out_t out_data = {
.uds_certificate = {0},
.uds_certificate_size = kUdsMaxCertSizeBytes,
};
static uint8_t curr_attestation_pubkey_x_bytes[kAttestationPublicKeyCoordBytes];
static uint8_t curr_attestation_pubkey_y_bytes[kAttestationPublicKeyCoordBytes];
// UDS.
static uint8_t uds_tbs_buffer[kUdsMaxTbsSizeBytes];
static uds_sig_values_t uds_cert_tbs = {
.tbs = uds_tbs_buffer,
.tbs_size = kUdsMaxTbsSizeBytes,
};

static const flash_ctrl_perms_t kCertificateFlashInfoPerms = {
.read = kMultiBitBool4True,
.write = kMultiBitBool4True,
Expand All @@ -62,6 +84,54 @@ static status_t config_certificate_flash_pages(void) {
return OK_STATUS();
}

static status_t gen_uds_keys_and_cert(void) {
// Generate the UDS key.
attestation_public_key_t uds_pubkey = {.x = {0}, .y = {0}};
TRY(otbn_boot_attestation_keygen(kUdsAttestationKeySeed,
kUdsKeymgrDiversifier, &uds_pubkey));
memcpy(curr_attestation_pubkey_x_bytes, uds_pubkey.x,
kAttestationPublicKeyCoordBytes);
memcpy(curr_attestation_pubkey_y_bytes, uds_pubkey.y,
kAttestationPublicKeyCoordBytes);
TRY(otbn_boot_attestation_key_save(kUdsAttestationKeySeed,
kUdsKeymgrDiversifier));
uint8_t creator_pub_key_id[kCertKeyIdSizeInBytes] = {0};

// Generate the UDS (unendorsed) UDS certificate.
uds_tbs_values_t uds_cert_tbs_params = {
// TODO(#19455): include OTP measurements in attestation keygen / cert.
// TODO(#19455): include creator pub key ID in cert.
.otp_creator_sw_cfg_hash = NULL,
.otp_creator_sw_cfg_hash_size = 0,
.otp_owner_sw_cfg_hash = NULL,
.otp_owner_sw_cfg_hash_size = 0,
.otp_hw_cfg0_hash = NULL,
.otp_hw_cfg0_hash_size = 0,
.creator_pub_key_id = creator_pub_key_id,
.creator_pub_key_id_size = kCertKeyIdSizeInBytes,
.auth_key_key_id = in_data.auth_key_key_id,
.auth_key_key_id_size = kCertKeyIdSizeInBytes,
.creator_pub_key_ec_x = curr_attestation_pubkey_x_bytes,
.creator_pub_key_ec_x_size = kAttestationPublicKeyCoordBytes,
.creator_pub_key_ec_y = curr_attestation_pubkey_y_bytes,
.creator_pub_key_ec_y_size = kAttestationPublicKeyCoordBytes,
};
TRY(uds_build_tbs(&uds_cert_tbs_params, uds_cert_tbs.tbs,
&uds_cert_tbs.tbs_size));
TRY(uds_build_cert(&uds_cert_tbs, out_data.uds_certificate,
&out_data.uds_certificate_size));

// Write the (unendorsed) certificate to a flash info page.
TRY(flash_ctrl_info_erase(&kFlashCtrlInfoPageUdsCertificate,
kFlashCtrlEraseTypePage));
TRY(flash_ctrl_info_write(&kFlashCtrlInfoPageUdsCertificate,
kFlashInfoFieldUdsCertificate.byte_offset,
out_data.uds_certificate_size / sizeof(uint32_t),
out_data.uds_certificate));

return OK_STATUS();
}

/**
* Crank the keymgr to produce the attestation keys and certificates.
*/
Expand All @@ -70,7 +140,7 @@ static status_t personalize(ujson_t *uj) {
LOG_INFO("Waiting for FT provisioning data ...");
TRY(ujson_deserialize_manuf_cert_perso_data_in_t(uj, &in_data));

// Configure certificat flash info page permissions.
// Configure certificate flash info page permissions.
TRY(config_certificate_flash_pages());

// Advance keymgr to Initialized state.
Expand All @@ -88,29 +158,7 @@ static status_t personalize(ujson_t *uj) {
// TODO(#19455): set attestation binding to OTP *Cfg partition measurements.
keymgr_advance_state();
TRY(keymgr_state_check(kKeymgrStateCreatorRootKey));
TRY(otbn_boot_attestation_keygen(kUdsAttestationKeySeed,
kUdsKeymgrDiversifier, &curr_pubkey));
// TODO(#19455): create certificate and self-sign it. While it will be
// endorsed off-device, i.e., the signature will be replaced, the self-sign
// will be used to gaurantee the integrity of the cert to the host.
//
// Note: the offline endorsement will take place in a secure environment,
// hence we are not taking any measure to authenticate the device to the host.
//
// Until then, we just write the public key to flash and export it over the
// console.
memcpy(out_data.uds_certificate.x, curr_pubkey.x,
kAttestationPublicKeyCoordBytes);
memcpy(out_data.uds_certificate.y, curr_pubkey.y,
kAttestationPublicKeyCoordBytes);
TRY(flash_ctrl_info_erase(&kFlashCtrlInfoPageUdsCertificate,
kFlashCtrlEraseTypePage));
TRY(flash_ctrl_info_write(&kFlashCtrlInfoPageUdsCertificate,
kFlashInfoFieldUdsCertificate.byte_offset,
sizeof(attestation_public_key_t) / sizeof(uint32_t),
&curr_pubkey));
TRY(otbn_boot_attestation_key_save(kUdsAttestationKeySeed,
kCdi0KeymgrDiversifier));
TRY(gen_uds_keys_and_cert());

// Set attestation binding to ROM_EXT / Ownership Manifest measurements.
// We set the sealing binding value to all zeros as it is unused in the
Expand Down
45 changes: 20 additions & 25 deletions sw/host/provisioning/ft_lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ use opentitanlib::test_utils::load_sram_program::{
use opentitanlib::test_utils::rpc::{UartRecv, UartSend};
use opentitanlib::uart::console::UartConsole;
use ujson_lib::provisioning_data::{
EccP256PublicKey, ManufCertPersoDataIn, ManufFtIndividualizeData, ManufRmaTokenPersoDataIn,
ManufRmaTokenPersoDataOut,
EccP256PublicKey, ManufCertPersoDataIn, ManufCertPersoDataOut, ManufFtIndividualizeData,
ManufRmaTokenPersoDataIn, ManufRmaTokenPersoDataOut,
};

pub fn test_unlock(
Expand Down Expand Up @@ -156,9 +156,9 @@ pub fn run_ft_personalize(
transport: &TransportWrapper,
init: &InitializeTest,
second_bootstrap: PathBuf,
_third_bootstrap: PathBuf,
third_bootstrap: PathBuf,
host_ecc_sk: PathBuf,
_attestation_tcb_measurements: &ManufCertPersoDataIn,
perso_data_in: &ManufCertPersoDataIn,
timeout: Duration,
) -> Result<()> {
let uart = transport.uart("console")?;
Expand Down Expand Up @@ -216,33 +216,28 @@ pub fn run_ft_personalize(
// Wait until device exports the wrapped RMA unlock token.
let _ = UartConsole::wait_for(&*uart, r"Exporting FT provisioning data ...", timeout)?;
let rma_token_out_data = ManufRmaTokenPersoDataOut::recv(&*uart, timeout, false)?;
// TODO(#19455): write the wrapped RMA unlock token to a file.
log::info!("{:x?}", rma_token_out_data);
// TODO(#20580): uncomment when UART RX flakiness is resolved.
//let _ = UartConsole::wait_for(&*uart, r"PASS.*\n", timeout)?;

// // -------------------------------------------------------------------------
// // FT Personalize 3 |
// // -------------------------------------------------------------------------
let _ = UartConsole::wait_for(&*uart, r"PASS.*\n", timeout)?;

// // Bootstrap third personalization binary into flash.
// uart.clear_rx_buffer()?;
// init.bootstrap.load(transport, &_third_bootstrap)?;
// -------------------------------------------------------------------------
// FT Personalize 3 |
// -------------------------------------------------------------------------

// // Get UART, set flow control, and wait for test to start running.
// uart.set_flow_control(true)?;
// let _ = UartConsole::wait_for(&*uart, r"Waiting for FT provisioning data ...", timeout)?;
// Bootstrap third personalization binary into flash.
uart.clear_rx_buffer()?;
init.bootstrap.load(transport, &third_bootstrap)?;

// // Send attestation TCB measurements for generating certificates.
// _attestation_tcb_measurements.send(&*uart)?;
// Get UART, set flow control, and wait for test to start running.
uart.set_flow_control(true)?;
let _ = UartConsole::wait_for(&*uart, r"Waiting for FT provisioning data ...", timeout)?;

// // Wait until device exports the attestation certificates.
// let _ = UartConsole::wait_for(&*uart, r"Exporting attestation certificates ...", timeout)?;
// let attestation_cert_out_data = ManufCertPersoDataOut::recv(&*uart, timeout, false)?;
// // TODO(#19455): write the attestation certificates to files.
// log::info!("{:x?}", attestation_cert_out_data);
// Send attestation TCB measurements for generating certificates.
perso_data_in.send(&*uart)?;

// let _ = UartConsole::wait_for(&*uart, r"PASS.*\n", timeout)?;
// Wait until device exports the attestation certificates.
let _ = UartConsole::wait_for(&*uart, r"Exporting attestation certificates ...", timeout)?;
let _ = ManufCertPersoDataOut::recv(&*uart, timeout, false)?;
let _ = UartConsole::wait_for(&*uart, r"PASS.*\n", timeout)?;

Ok(())
}
13 changes: 13 additions & 0 deletions sw/host/provisioning/util_lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,16 @@ pub fn hex_string_to_u32_arrayvec<const N: usize>(hex_str: &str) -> Result<Array
.map(|bytes| u32::from_be_bytes(bytes.try_into().unwrap()))
.collect::<ArrayVec<u32, N>>())
}

pub fn hex_string_to_u8_arrayvec<const N: usize>(hex_str: &str) -> Result<ArrayVec<u8, N>> {
let hex_str_no_sep = hex_str.replace('_', "");
let hex_str_prefix = "0x";
let sanitized_hex_str = if hex_str.starts_with(hex_str_prefix) {
hex_str_no_sep.strip_prefix(hex_str_prefix).unwrap()
} else {
hex_str_no_sep.as_str()
};
Ok(decode(sanitized_hex_str)?
.into_iter()
.collect::<ArrayVec<u8, N>>())
}
13 changes: 10 additions & 3 deletions sw/host/tests/manuf/provisioning/ft/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use opentitanlib::test_utils::init::InitializeTest;
use opentitanlib::test_utils::lc::read_lc_state;
use opentitanlib::test_utils::load_sram_program::SramProgramParams;
use ujson_lib::provisioning_data::{ManufCertPersoDataIn, ManufFtIndividualizeData};
use util_lib::hex_string_to_u32_arrayvec;
use util_lib::{hex_string_to_u32_arrayvec, hex_string_to_u8_arrayvec};

/// Provisioning data command-line parameters.
#[derive(Debug, Args, Clone)]
Expand All @@ -42,6 +42,10 @@ pub struct ManufFtProvisioningDataInput {
#[arg(long)]
host_ecc_sk: PathBuf,

/// UDS authority (endorsement) key ID hexstring.
#[arg(long)]
pub uds_auth_key_id: String,

/// Measurement of the ROM_EXT image to be loaded onto the device.
#[arg(long)]
pub rom_ext_measurement: String,
Expand Down Expand Up @@ -106,10 +110,13 @@ fn main() -> Result<()> {
)?;
let owner_measurement =
hex_string_to_u32_arrayvec::<8>(opts.provisioning_data.owner_measurement.as_str())?;
let _attestation_tcb_measurements = ManufCertPersoDataIn {
let uds_auth_key_id =
hex_string_to_u8_arrayvec::<20>(opts.provisioning_data.uds_auth_key_id.as_str())?;
let _perso_data_in = ManufCertPersoDataIn {
rom_ext_measurement: rom_ext_measurement.clone(),
owner_manifest_measurement: owner_manifest_measurement.clone(),
owner_measurement: owner_measurement.clone(),
auth_key_key_id: uds_auth_key_id.clone(),
};

// Only run test unlock operation if we are in a locked LC state.
Expand Down Expand Up @@ -184,7 +191,7 @@ fn main() -> Result<()> {
opts.second_bootstrap,
opts.third_bootstrap,
opts.provisioning_data.host_ecc_sk,
&_attestation_tcb_measurements,
&_perso_data_in,
opts.timeout,
)?;

Expand Down

0 comments on commit 97f4912

Please sign in to comment.