Skip to content

Commit

Permalink
[dice,rom_ext] support CWT CDI_* cert updates
Browse files Browse the repository at this point in the history
This complete the implementation of the `dice_cert_check_valid()`
function in the `dice_cwt` library to enable regenerating DICE CWT CDI_*
certs in the ROM_EXT if firmware is ever updated. This fixes #24281.

Signed-off-by: Tim Trippel <[email protected]>
  • Loading branch information
timothytrippel committed Nov 21, 2024
1 parent 999cbb4 commit 080b32c
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 23 deletions.
1 change: 1 addition & 0 deletions sw/device/silicon_creator/lib/cert/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ cc_library(
"//sw/device/silicon_creator/lib/cert:dice_keys",
"//sw/device/silicon_creator/lib/drivers:lifecycle",
"//sw/device/silicon_creator/manuf/base:perso_tlv_data",
"@open-dice//:cbor_reader_writer",
],
)

Expand Down
13 changes: 12 additions & 1 deletion sw/device/silicon_creator/lib/cert/cbor.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_out_init(
CborOutInit(buf, buf_size, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add a "map" header along with the elements count to a CborOut structure.
*
Expand All @@ -50,6 +51,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_map_init(
CborWriteMap(num_pairs, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add a "array" header along with the elements count to a CborOut structure.
*
Expand All @@ -63,6 +65,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_array_init(
CborWriteArray(num_elements, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add a "tstr" to a CborOut structure
*
Expand All @@ -76,6 +79,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_string(
CborWriteTstr(str, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add a "bstr" to a CborOut structure
*
Expand Down Expand Up @@ -109,6 +113,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_pair_uint_uint(
CborWriteUint(value, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add 2 elements, "int" and "uint", to a CborOut structure
*
Expand All @@ -124,6 +129,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_pair_int_uint(
CborWriteUint(value, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add 2 elements, "uint" and "int", to a CborOut structure
*
Expand All @@ -139,6 +145,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_pair_uint_int(
CborWriteInt(value, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add 2 elements, "int" and "bstr", to a CborOut structure
*
Expand All @@ -156,6 +163,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_pair_int_bytes(
CborWriteBstr(value_size, value, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add 2 elements, "uint" and "tstr", to a CborOut structure
*
Expand All @@ -171,6 +179,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_pair_uint_tstr(
CborWriteTstr(value, p);
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add 2 elements, "int" and "tstr", to a CborOut structure
*
Expand Down Expand Up @@ -210,6 +219,7 @@ static inline size_t cbor_calc_arg_size(uint64_t value) {
return 8;
};
}

/**
* Calculate how much space is needed in the header for a "signed interger" type
* of CBOR argument.
Expand All @@ -224,7 +234,6 @@ static inline size_t cbor_calc_int_size(int64_t value) {
return cbor_calc_arg_size((uint64_t)value);
}

// Add a bstr/tstr header with size, and rewind the cursor
/**
* Add a "bstr" header along with the payload size, and rewind the cursor of
* CborOut structure.
Expand All @@ -241,6 +250,7 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_bstr_header(
p->cursor -= bstr_size;
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

/**
* Add a "tstr" header along with the payload size, and rewind the cursor of
* CborOut structure.
Expand Down Expand Up @@ -275,4 +285,5 @@ OT_WARN_UNUSED_RESULT static inline rom_error_t cbor_write_raw_bytes(
p->cursor += raw_size;
CBOR_CHECK_OVERFLOWED_AND_RETURN(p);
}

#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_CERT_CBOR_H_
144 changes: 143 additions & 1 deletion sw/device/silicon_creator/lib/cert/dice_cwt.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <stdint.h>

#include "include/dice/cbor_reader.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/silicon_creator/lib/base/util.h"
#include "sw/device/silicon_creator/lib/cert/cbor.h"
Expand All @@ -26,6 +27,17 @@

const dice_cert_format_t kDiceCertFormat = kDiceCertFormatCWTAndroid;

enum {
// Must match the label used for the public key in
// `cwt_dice_chain_entry_payload.hjson`.
kDiceCwtSubjectPublicKeyLabel = -4670552,

// Must match the labels used for the public key X/Y coords in
// `cwt_cose_key.hjson`.
kDiceCwtCoseKeyXCoordLabel = -2,
kDiceCwtCoseKeyYCoordLabel = -3,
};

enum config_desc_labels {
kSecurityVersionLabel = -70005,
// Implementataion specific value,
Expand Down Expand Up @@ -310,10 +322,140 @@ rom_error_t dice_cdi_1_cert_build(hmac_digest_t *owner_measurement,
return kErrorOk;
}

static rom_error_t extract_pubkey_from_cose_key(const uint8_t *obj,
size_t obj_size,
const uint32_t **x_coord_p,
const uint32_t **y_coord_p) {
// Initialize CBOR object.
struct CborIn cbor_obj;
CborInInit(obj, obj_size, &cbor_obj);

// Find the x/y-coord bstrs.
size_t num_pairs = 0;
CborReadMap(&cbor_obj, &num_pairs);
if (num_pairs == 0) {
return kErrorDiceCwtKeyCoordsNotFound;
}

size_t x_coord_size = 0;
size_t y_coord_size = 0;
*x_coord_p = NULL;
*y_coord_p = NULL;

// Locate public key coords bstrs.
int64_t key = 0;
for (size_t i = 0; i < num_pairs; ++i) {
CborReadInt(&cbor_obj, &key);
if (key == kDiceCwtCoseKeyXCoordLabel) {
CborReadBstr(&cbor_obj, &x_coord_size, (const uint8_t **)x_coord_p);
continue;
} else if (key == kDiceCwtCoseKeyYCoordLabel) {
CborReadBstr(&cbor_obj, &y_coord_size, (const uint8_t **)y_coord_p);
continue;
}
CborReadSkip(&cbor_obj);
}

// Confirm we found the key.
if (x_coord_p == NULL || y_coord_p == NULL) {
return kErrorDiceCwtKeyCoordsNotFound;
}
// Confirm the key is the correct size.
if (x_coord_size != kEcdsaP256PublicKeyCoordBytes ||
y_coord_size != kEcdsaP256PublicKeyCoordBytes) {
return kErrorDiceCwtCoseKeyBadSize;
}

return kErrorOk;
}

static rom_error_t extract_pubkey_from_cose_sign1_payload(
const uint8_t *payload, size_t payload_size, const uint32_t **x_coord_p,
const uint32_t **y_coord_p) {
// Initialize CBOR object.
struct CborIn cbor_obj;
CborInInit(payload, payload_size, &cbor_obj);

// Find the COSE_Key object in the COSE_Sign1 payload.
size_t num_payload_pairs = 0;
CborReadMap(&cbor_obj, &num_payload_pairs);
if (num_payload_pairs == 0) {
return kErrorDiceCwtCoseKeyNotFound;
}

int64_t key = 0;
size_t cose_key_size = 0;
const uint8_t *cose_key_p = NULL;
for (size_t i = 0; i < num_payload_pairs; ++i) {
CborReadInt(&cbor_obj, &key);
if (key == kDiceCwtSubjectPublicKeyLabel) {
CborReadBstr(&cbor_obj, &cose_key_size, &cose_key_p);
break;
}
CborReadSkip(&cbor_obj);
}

// Check the COSE_Key object was found in the COSE_Sign1 payload.
if (key != kDiceCwtSubjectPublicKeyLabel) {
return kErrorDiceCwtCoseKeyNotFound;
}

// Extract the pubkey from the COSE_Key payload.
RETURN_IF_ERROR(extract_pubkey_from_cose_key(cose_key_p, cose_key_size,
x_coord_p, y_coord_p));

return kErrorOk;
}

rom_error_t dice_cert_check_valid(const perso_tlv_cert_obj_t *cert_obj,
const hmac_digest_t *pubkey_id,
const ecdsa_p256_public_key_t *pubkey,
hardened_bool_t *cert_valid_output) {
// TODO(lowRISC/opentitan:#24281): implement body
const uint32_t *x_coord_p = NULL;
const uint32_t *y_coord_p = NULL;

struct CborIn cbor_obj;
CborInInit(cert_obj->cert_body_p, cert_obj->cert_body_size, &cbor_obj);

// Check if CBOR object is array or map. UDS is a COSE_Key which is a map.
// CDI_* are COSE_Sign1 objects, which are arrays.
size_t num_array_items = 0;
CborReadArray(&cbor_obj, &num_array_items);
size_t num_map_items = 0;
CborReadMap(&cbor_obj, &num_map_items);

// Extract the public key from the CBOR certificate object.
if (num_array_items > 0 && num_map_items == 0) {
// Skip first two parent items; subject public key is in the "payload" item
// of COSE_Sign1 object, which is encoded as a bstr.
CborReadSkip(&cbor_obj);
CborReadSkip(&cbor_obj);
size_t payload_size = 0;
const uint8_t *payload_p = NULL;
CborReadBstr(&cbor_obj, &payload_size, &payload_p);
RETURN_IF_ERROR(extract_pubkey_from_cose_sign1_payload(
payload_p, payload_size, &x_coord_p, &y_coord_p));
} else if (num_map_items > 0 && num_array_items == 0) {
RETURN_IF_ERROR(extract_pubkey_from_cose_key(cert_obj->cert_body_p,
cert_obj->cert_body_size,
&x_coord_p, &y_coord_p));
} else {
return kErrorDiceCwtCoseKeyNotFound;
}

// Compare the public key in the certificate to the public updated key.
for (size_t i = 0; i < kEcdsaP256PublicKeyCoordWords; ++i) {
if (x_coord_p[i] != pubkey->x[i]) {
*cert_valid_output = kHardenedBoolFalse;
return kErrorOk;
}
if (y_coord_p[i] != pubkey->y[i]) {
*cert_valid_output = kHardenedBoolFalse;
return kErrorOk;
}
}

*cert_valid_output = kHardenedBoolTrue;

return kErrorOk;
}
6 changes: 6 additions & 0 deletions sw/device/silicon_creator/lib/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum module_ {
kModuleCert = MODULE_CODE('C', 'E'),
kModuleOwnership = MODULE_CODE('O', 'W'),
kModulePersoTlv = MODULE_CODE('P', 'T'),
kModuleDice = MODULE_CODE('D', 'C'),
// clang-format on
};

Expand Down Expand Up @@ -232,6 +233,11 @@ enum module_ {
X(kErrorPersoTlvCertNameTooLong, ERROR_(2, kModulePersoTlv, kOutOfRange)), \
X(kErrorPersoTlvOutputBufTooSmall, ERROR_(3, kModulePersoTlv, kOutOfRange)), \
\
X(kErrorDiceInternal, ERROR_(0, kModuleDice, kInternal)), \
X(kErrorDiceCwtCoseKeyNotFound, ERROR_(1, kModuleDice, kNotFound)), \
X(kErrorDiceCwtCoseKeyBadSize, ERROR_(1, kModuleDice, kInternal)), \
X(kErrorDiceCwtKeyCoordsNotFound, ERROR_(2, kModuleDice, kNotFound)), \
\
/* This comment prevent clang from trying to format the macro. */

// clang-format on
Expand Down
48 changes: 28 additions & 20 deletions sw/device/silicon_creator/rom_ext/e2e/dice_chain/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,34 @@ load(
"fpga_params",
"opentitan_test",
)
load(
"//sw/device/silicon_creator/rom_ext:defs.bzl",
"ROM_EXT_VARIATIONS",
)

package(default_visibility = ["//visibility:public"])

opentitan_test(
name = "no_refresh_test",
srcs = ["no_refresh_test.c"],
exec_env = {
"//hw/top_earlgrey:fpga_cw310_rom_ext": None,
"//hw/top_earlgrey:fpga_cw340_rom_ext": None,
},
fpga = fpga_params(
exit_failure = "Rebooting[\\s\\S]*CDI_1 certificate not valid[\\s\\S]*Rebooted",
exit_success = "Rebooted\r\n",
),
deps = [
"//sw/device/lib/base:status",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing/test_framework:check",
"//sw/device/lib/testing/test_framework:ottf_main",
"//sw/device/silicon_creator/lib/drivers:retention_sram",
"//sw/device/silicon_creator/lib/drivers:rstmgr",
],
)
[
opentitan_test(
name = "no_refresh_{}_test".format(variation),
srcs = ["no_refresh_test.c"],
exec_env = {
"//hw/top_earlgrey:fpga_cw310_rom_ext": None,
"//hw/top_earlgrey:fpga_cw340_rom_ext": None,
},
fpga = fpga_params(
exit_failure = "Rebooting[\\s\\S]*CDI_1 certificate not valid[\\s\\S]*Rebooted",
exit_success = "Rebooted\r\n",
rom_ext = "//sw/device/silicon_creator/rom_ext:rom_ext_{}_slot_a".format(variation),
),
deps = [
"//sw/device/lib/base:status",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing/test_framework:check",
"//sw/device/lib/testing/test_framework:ottf_main",
"//sw/device/silicon_creator/lib/drivers:retention_sram",
"//sw/device/silicon_creator/lib/drivers:rstmgr",
],
)
for variation in ROM_EXT_VARIATIONS.keys()
]
5 changes: 4 additions & 1 deletion sw/device/silicon_creator/rom_ext/rom_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,11 @@ static rom_error_t dice_chain_push_cert(const char *name, const uint8_t *cert,

// Encode the certificate to the tail buffer.
size_t cert_page_left = dice_chain_get_tail_size();
perso_tlv_object_type_t cert_type =
kDiceCertFormat == kDiceCertFormatX509TcbInfo ? kPersoObjectTypeX509Cert
: kPersoObjectTypeCwtCert;
HARDENED_RETURN_IF_ERROR(
perso_tlv_cert_obj_build(name, kPersoObjectTypeX509Cert, cert, cert_size,
perso_tlv_cert_obj_build(name, cert_type, cert, cert_size,
dice_chain_get_tail_buffer(), &cert_page_left));

// Move the offset to the new tail.
Expand Down

0 comments on commit 080b32c

Please sign in to comment.