Skip to content

Commit 83d5671

Browse files
committed
[ot] hw/opentitan: ot_keymgr: Wipe unused keys with entropy
Resolves many TODOs in the `keymgr`, focusing on writing internal key states, SW visible output key states and HW sideloaded keys with random data at points in operation when their values are not in use. Importantly, the same entropy is used when wiping different keys, as in the RTL. This also means that when KMAC masking is supported, XORing key shares will still function as intended for the KMAC key. To more closely align with the HW, an option is provided to seed PRNG from the LFSR seed, with a property added to expose this option. Note however that the entropy reseed refresh count is still not implemented; emulating this would introduce a lot of additional complexity for little benefit, so this is left incomplete for now. Now that key wiping is added, we introduce the key validity check and report both errors and debug status related to invalid keys. Signed-off-by: Alex Jones <[email protected]>
1 parent eee2b32 commit 83d5671

File tree

2 files changed

+93
-54
lines changed

2 files changed

+93
-54
lines changed

hw/opentitan/ot_keymgr.c

Lines changed: 92 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ typedef enum {
269269
KEYMGR_KEY_SINK_COUNT
270270
} OtKeyMgrKeySink;
271271

272+
static_assert(KEYMGR_KEY_SINK_COUNT < 32,
273+
"Sideload clear bitmasking logic needs < 32 key sinks");
274+
272275
#define KEY_SINK_OFFSET 1
273276

274277
typedef enum {
@@ -445,6 +448,7 @@ typedef struct OtKeyMgrState {
445448
OtRomCtrlState *rom_ctrl;
446449
DeviceState *key_sinks[KEYMGR_KEY_SINK_COUNT];
447450
char *seed_xstrs[KEYMGR_SEED_COUNT];
451+
bool use_default_entropy_seed; /* flag to seed PRNG with default seed */
448452
} OtKeyMgrState;
449453

450454
struct OtKeyMgrClass {
@@ -832,16 +836,14 @@ static void ot_keymgr_request_entropy(OtKeyMgrState *s)
832836
}
833837
}
834838

835-
static void ot_keymgr_wipe_key_states(OtKeyMgrState *s)
839+
/* Returns true if 'data' is not all zeros or all ones */
840+
static bool ot_keymgr_valid_data_check(const uint8_t *data, size_t len)
836841
{
837-
/* @todo: should wipe with random entropy, but just zero for now */
838-
for (unsigned cdi = 0u; cdi < NUM_CDIS; cdi++) {
839-
memset(s->key_states[cdi].share0, 0u, KEYMGR_KEY_BYTES);
840-
memset(s->key_states[cdi].share1, 0u, KEYMGR_KEY_BYTES);
841-
s->key_states[cdi].valid = false;
842-
}
843-
memset(s->sw_out_key->share0, 0u, KEYMGR_KEY_BYTES);
844-
memset(s->sw_out_key->share1, 0u, KEYMGR_KEY_BYTES);
842+
size_t popcount = 0u;
843+
for (unsigned ix = 0u; ix < len; ix++) {
844+
popcount += __builtin_popcount(data[ix]);
845+
}
846+
return (popcount && popcount != (len * BITS_PER_BYTE));
845847
}
846848

847849
static void ot_keymgr_push_key(
@@ -855,12 +857,14 @@ static void ot_keymgr_push_key(
855857

856858
g_assert(sink);
857859

860+
g_assert(key_share0);
861+
g_assert(key_share1);
862+
858863
/*
859864
* Save the latest KMAC sideloading key, as it needs to be restored after
860865
* any keymgr operations that load the KMAC KDF key to offload KDF.
861866
*/
862-
if (key_share0 && key_share1 && sideload_key &&
863-
key_sink == KEYMGR_KEY_SINK_KMAC) {
867+
if (sideload_key && key_sink == KEYMGR_KEY_SINK_KMAC) {
864868
memcpy(s->saved_kmac_key->share0, key_share0, key_size);
865869
memcpy(s->saved_kmac_key->share1, key_share1, key_size);
866870
s->saved_kmac_key->valid = valid;
@@ -898,23 +902,65 @@ static void ot_keymgr_push_kdf_key(OtKeyMgrState *s, const uint8_t *key_share0,
898902
trace_ot_keymgr_push_kdf_key(s->ot_id, valid);
899903

900904
/*
901-
* @todo: when key invalidating/wiping with entropy is implemented,
902-
* pushing a new KDF key should perform a validity check for all 0s
903-
* or all 1s, and set R_DEBUG_INVALID_KEY_MASK if so, i.e.:
904-
*
905-
* if (!ot_keymgr_valid_data_check(key_share0, OT_KMAC_KEY_SIZE)) {
906-
* s->regs[R_DEBUG] |= R_DEBUG_INVALID_KEY_MASK;
907-
* s->op_state.valid_inputs = false;
908-
* }
909-
*
910905
* @todo: when KMAC masking is introduced, this should check both key
911906
* shares and not just key share 0 for validity.
912907
*/
908+
if (!ot_keymgr_valid_data_check(key_share0, OT_KMAC_KEY_SIZE)) {
909+
s->regs[R_DEBUG] |= R_DEBUG_INVALID_KEY_MASK;
910+
s->op_state.valid_inputs = false;
911+
}
913912

914913
ot_keymgr_push_key(s, KEYMGR_KEY_SINK_KMAC, key_share0, key_share1, valid,
915914
false);
916915
}
917916

917+
static void ot_keymgr_wipe_keys(OtKeyMgrState *s, bool key_states,
918+
bool sw_outputs, uint32_t key_sink_bm)
919+
{
920+
trace_ot_keymgr_wipe_keys(s->ot_id, key_states, sw_outputs, key_sink_bm);
921+
922+
uint8_t sideload_share0[KEYMGR_KEY_SIZE_MAX];
923+
uint8_t sideload_share1[KEYMGR_KEY_SIZE_MAX];
924+
925+
/* all keys (CDIs, shares etc.) are wiped with the same entropy */
926+
for (unsigned ix = 0; ix < KEYMGR_KEY_SIZE_MAX; ix += sizeof(uint32_t)) {
927+
/* @todo: use reseed_cnt and reseed if > RESEED_INTERVAL_SHADOWED */
928+
uint32_t randval = ot_prng_random_u32(s->prng.state);
929+
930+
if (key_states && ix < KEYMGR_KEY_BYTES) {
931+
for (unsigned cdi = 0u; cdi < NUM_CDIS; cdi++) {
932+
stl_le_p(&s->key_states[cdi].share0[ix], randval);
933+
stl_le_p(&s->key_states[cdi].share1[ix], randval);
934+
}
935+
}
936+
937+
if (sw_outputs && ix < KEYMGR_KEY_BYTES) {
938+
stl_le_p(&s->sw_out_key->share0[ix], randval);
939+
stl_le_p(&s->sw_out_key->share1[ix], randval);
940+
}
941+
942+
if (key_sink_bm) {
943+
stl_le_p(&sideload_share0[ix], randval);
944+
stl_le_p(&sideload_share1[ix], randval);
945+
}
946+
}
947+
948+
for (unsigned ix = 0; key_sink_bm && ix < KEYMGR_KEY_SINK_COUNT; ix++) {
949+
uint32_t key_sink_mask = (1u << ix);
950+
if (key_sink_bm & key_sink_mask) {
951+
key_sink_bm &= ~key_sink_mask;
952+
ot_keymgr_push_key(s, (OtKeyMgrKeySink)ix, sideload_share0,
953+
sideload_share1, false, true);
954+
}
955+
}
956+
}
957+
958+
static void ot_keymgr_wipe_all_keys(OtKeyMgrState *s)
959+
{
960+
uint32_t all_keys_bm = (1u << KEYMGR_KEY_SINK_COUNT) - 1u;
961+
ot_keymgr_wipe_keys(s, true, true, all_keys_bm);
962+
}
963+
918964
static void ot_keymgr_sideload_clear(OtKeyMgrState *s)
919965
{
920966
int sideload_clear =
@@ -924,38 +970,22 @@ static void ot_keymgr_sideload_clear(OtKeyMgrState *s)
924970
SIDELOAD_CLEAR_NAME(sideload_clear),
925971
sideload_clear);
926972

927-
/* @todo: this should use random dummy data instead */
928-
uint8_t share0[KEYMGR_KEY_SIZE_MAX] = { 0 };
929-
uint8_t share1[KEYMGR_KEY_SIZE_MAX] = { 0 };
930-
973+
uint32_t sideload_clear_bm;
931974
switch (sideload_clear) {
932975
case KEYMGR_SIDELOAD_CLEAR_NONE:
933-
break;
976+
return;
934977
case KEYMGR_SIDELOAD_CLEAR_AES:
935978
case KEYMGR_SIDELOAD_CLEAR_OTBN:
936-
case KEYMGR_SIDELOAD_CLEAR_KMAC: {
937-
OtKeyMgrKeySink sink =
938-
(OtKeyMgrKeySink)(sideload_clear - KEY_SINK_OFFSET);
939-
ot_keymgr_push_key(s, sink, share0, share1, false, true);
979+
case KEYMGR_SIDELOAD_CLEAR_KMAC:
980+
sideload_clear_bm = 1u << (sideload_clear - KEY_SINK_OFFSET);
940981
break;
941-
}
942982
default:
943983
/* continuously clear ALL slots if a non-enumerated value is written */
944-
for (unsigned ix = 0; ix < KEYMGR_KEY_SINK_COUNT; ix++) {
945-
ot_keymgr_push_key(s, (OtKeyMgrKeySink)ix, share0, share1, false,
946-
true);
947-
}
984+
sideload_clear_bm = (1u << KEYMGR_KEY_SINK_COUNT) - 1u;
985+
break;
948986
}
949-
}
950987

951-
/* check that 'data' is not all zeros or all ones */
952-
static bool ot_keymgr_valid_data_check(const uint8_t *data, size_t len)
953-
{
954-
size_t popcount = 0u;
955-
for (unsigned ix = 0u; ix < len; ix++) {
956-
popcount += __builtin_popcount(data[ix]);
957-
}
958-
return (popcount && popcount != (len * BITS_PER_BYTE));
988+
ot_keymgr_wipe_keys(s, false, false, sideload_clear_bm);
959989
}
960990

961991
static void ot_keymgr_reset_kdf_buffer(OtKeyMgrState *s)
@@ -1204,7 +1234,7 @@ static void ot_keymgr_send_kmac_req(OtKeyMgrState *s)
12041234

12051235
static void ot_keymgr_operation_disable(OtKeyMgrState *s)
12061236
{
1207-
ot_keymgr_wipe_key_states(s);
1237+
ot_keymgr_wipe_all_keys(s);
12081238
ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_DISABLED);
12091239
s->op_state.op_ack = true;
12101240
ot_keymgr_schedule_fsm(s);
@@ -1739,7 +1769,7 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s)
17391769
break;
17401770
case KEYMGR_ST_WIPE:
17411771
/* whilst wiping, keymgr retains the previous working state */
1742-
ot_keymgr_wipe_key_states(s);
1772+
ot_keymgr_wipe_all_keys(s);
17431773
/* see OT keymgr_ctrl.sv RTL: maintain & complete ongoing ops */
17441774
if (op_start) {
17451775
s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_OP_MASK;
@@ -2050,6 +2080,11 @@ static void ot_keymgr_write(void *opaque, hwaddr addr, uint64_t val64,
20502080
val32 &= R_RESEED_INTERVAL_SHADOWED_VAL_MASK;
20512081
switch (ot_shadow_reg_write(&s->reseed_interval, val32)) {
20522082
case OT_SHADOW_REG_STAGED:
2083+
/* @todo: implement entropy reseeding & better entropy modelling */
2084+
qemu_log_mask(LOG_UNIMP,
2085+
"%s: %s: Entropy reseeding not implemented.\n",
2086+
__func__, s->ot_id);
2087+
break;
20532088
case OT_SHADOW_REG_COMMITTED:
20542089
break;
20552090
case OT_SHADOW_REG_ERROR:
@@ -2285,6 +2320,8 @@ static Property ot_keymgr_properties[] = {
22852320
DEFINE_PROP_STRING("cdi_seed", OtKeyMgrState, seed_xstrs[KEYMGR_SEED_CDI]),
22862321
DEFINE_PROP_STRING("none_seed", OtKeyMgrState,
22872322
seed_xstrs[KEYMGR_SEED_NONE]),
2323+
DEFINE_PROP_BOOL("use-default-entropy-seed", OtKeyMgrState,
2324+
use_default_entropy_seed, false),
22882325
DEFINE_PROP_END_OF_LIST(),
22892326
};
22902327

@@ -2341,17 +2378,20 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type)
23412378
s->prng.reseed_req = false;
23422379
s->prng.reseed_ack = false;
23432380
s->prng.reseed_cnt = 0u;
2381+
if (s->use_default_entropy_seed) {
2382+
const uint8_t *default_prng_seed = s->seeds[KEYMGR_SEED_LFSR];
2383+
for (unsigned ix = 0; ix < KEYMGR_SEED_BYTES; ix += sizeof(uint32_t)) {
2384+
uint32_t seed = ldl_le_p(&default_prng_seed[ix]);
2385+
ot_prng_reseed(s->prng.state, seed);
2386+
}
2387+
}
23442388
s->op_state.op_req = false;
23452389
s->op_state.op_ack = false;
23462390
s->op_state.valid_inputs = true;
23472391
s->op_state.stage = KEYMGR_STAGE_DISABLE;
23482392
s->op_state.adv_cdi_cnt = 0u;
23492393
ot_keymgr_reset_kdf_buffer(s);
23502394

2351-
/* reset key state */
2352-
memset(s->key_states, 0u, NUM_CDIS * sizeof(OtKeyMgrKey));
2353-
memset(s->saved_kmac_key, 0u, sizeof(OtKeyMgrKey));
2354-
23552395
/* reset output keys */
23562396
memset(s->sw_out_key, 0u, sizeof(OtKeyMgrKey));
23572397

@@ -2376,11 +2416,9 @@ static void ot_keymgr_reset_exit(Object *obj, ResetType type)
23762416
c->parent_phases.exit(obj, type);
23772417
}
23782418

2379-
/* invalidate all sideloaded keys when exiting reset */
2380-
for (unsigned ix = 0u; ix < KEYMGR_KEY_SINK_COUNT; ix++) {
2381-
OtKeyMgrKeySink key_sink = (OtKeyMgrKeySink)ix;
2382-
ot_keymgr_push_key(s, key_sink, NULL, NULL, false, true);
2383-
}
2419+
/* invalidate internal key state & sideloaded keys when exiting reset */
2420+
uint32_t all_keys_bm = (1u << KEYMGR_KEY_SINK_COUNT) - 1u;
2421+
ot_keymgr_wipe_keys(s, true, false, all_keys_bm);
23842422
}
23852423

23862424
static void ot_keymgr_realize(DeviceState *dev, Error **errp)

hw/opentitan/trace-events

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ ot_keymgr_schedule_fsm(const char *id, const char *func, int line) "%s @ %s:%d"
305305
ot_keymgr_seed_missing(const char *id, unsigned ix) "%s: #%u"
306306
ot_keymgr_sideload_clear(const char *id, const char *sc, unsigned nsc) "%s: [%s:%u]"
307307
ot_keymgr_update_alert(const char *id, int prev, int next) "%s: %d -> %d"
308+
ot_keymgr_wipe_keys(const char *id, bool key_states, bool sw_out, uint32_t sink_bm) "%s: key_states:%u sw_out:%u sink_bm:0x%08x"
308309

309310
# ot_keymgr_dpe.c
310311

0 commit comments

Comments
 (0)