diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index f893cccfd530f..eb9ab941d210f 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -31,13 +31,19 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qapi/error.h" +#include "hw/opentitan/ot_aes.h" #include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_edn.h" +#include "hw/opentitan/ot_flash.h" +#include "hw/opentitan/ot_key_sink.h" #include "hw/opentitan/ot_keymgr.h" #include "hw/opentitan/ot_kmac.h" +#include "hw/opentitan/ot_lc_ctrl.h" +#include "hw/opentitan/ot_otbn.h" #include "hw/opentitan/ot_otp.h" #include "hw/opentitan/ot_prng.h" +#include "hw/opentitan/ot_rom_ctrl.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/ibex_common.h" @@ -79,6 +85,8 @@ #define KEYMGR_KDF_BUFFER_WIDTH 1600u #define KEYMGR_KDF_BUFFER_BYTES ((KEYMGR_KDF_BUFFER_WIDTH) / 8u) #define KEYMGR_SEED_BYTES (KEYMGR_KEY_BYTES) +#define _MAX(_a_, _b_) ((_a_) > (_b_) ? (_a_) : (_b_)) +#define KEYMGR_KEY_SIZE_MAX _MAX(KEYMGR_KEY_BYTES, OT_OTBN_KEY_SIZE) /* * The EDN provides words of entropy at a time, so the keymgr needs @@ -92,12 +100,16 @@ static_assert(KEYMGR_ID_DATA_BYTES <= KEYMGR_KDF_BUFFER_BYTES, "KeyMgr ID data does not fit in KDF buffer"); static_assert(KEYMGR_GEN_DATA_BYTES <= KEYMGR_KDF_BUFFER_BYTES, "KeyMgr GEN data does not fit in KDF buffer"); +static_assert((KEYMGR_KDF_BUFFER_BYTES % OT_KMAC_APP_MSG_BYTES) == 0u, + "KeyMgr KDF buffer not a multiple of KMAC message size"); /* NOLINTBEGIN(misc-redundant-expression) */ static_assert(KEYMGR_KEY_BYTES == OT_OTP_KEYMGR_SECRET_SIZE, "KeyMgr key size does not match OTP KeyMgr secret size"); static_assert(KEYMGR_KEY_BYTES == OT_KMAC_KEY_SIZE, "KeyMgr key size does not match KMAC key size"); /* NOLINTEND(misc-redundant-expression) */ +static_assert(KEYMGR_KEY_SIZE_MAX <= OT_KMAC_APP_DIGEST_BYTES, + "KeyMgr key size does not match KMAC digest size"); #define KEYMGR_ENTROPY_WIDTH (KEYMGR_LFSR_WIDTH / 2u) #define KEYMGR_ENTROPY_ROUNDS (KEYMGR_KEY_WIDTH / KEYMGR_ENTROPY_WIDTH) @@ -259,6 +271,13 @@ typedef enum { #define KEY_SINK_OFFSET 1 +typedef enum { + KEYMGR_STAGE_CREATOR, + KEYMGR_STAGE_OWNER_INT, + KEYMGR_STAGE_OWNER, + KEYMGR_STAGE_DISABLE, +} OtKeyMgrStage; + /* values for CONTROL_SHADOWED.OPERATION */ typedef enum { KEYMGR_OP_ADVANCE = 0, @@ -370,8 +389,16 @@ typedef struct { typedef struct { bool op_req; bool op_ack; + OtKeyMgrStage stage; + OtKeyMgrCdi adv_cdi_cnt; } OtKeyMgrOpState; +typedef struct { + uint8_t *data; + unsigned offset; /* current read offset (in bytes ) */ + unsigned length; /* current length (in bytes) */ +} OtKeyMgrKdfBuffer; + typedef struct OtKeyMgrState { SysBusDevice parent_obj; @@ -379,6 +406,7 @@ typedef struct OtKeyMgrState { IbexIRQ irq; IbexIRQ alerts[ALERT_COUNT]; QEMUTimer *fsm_tick_timer; + OtKeyMgrKdfBuffer kdf_buf; uint32_t regs[REGS_COUNT]; OtShadowReg control; @@ -404,7 +432,13 @@ typedef struct OtKeyMgrState { /* properties */ char *ot_id; OtKeyMgrEDN edn; + OtKMACState *kmac; + uint8_t kmac_app; + OtFlashState *flash_ctrl; + OtLcCtrlState *lc_ctrl; OtOTPState *otp_ctrl; + OtRomCtrlState *rom_ctrl; + DeviceState *key_sinks[KEYMGR_KEY_SINK_COUNT]; char *seed_xstrs[KEYMGR_SEED_COUNT]; } OtKeyMgrState; @@ -413,6 +447,15 @@ struct OtKeyMgrClass { ResettablePhases parent_phases; }; +static const size_t OT_KEY_MGR_KEY_SINK_SIZES[KEYMGR_KEY_SINK_COUNT] = { + [KEYMGR_KEY_SINK_AES] = OT_AES_KEY_SIZE, + [KEYMGR_KEY_SINK_KMAC] = OT_KMAC_KEY_SIZE, + [KEYMGR_KEY_SINK_OTBN] = OT_OTBN_KEY_SIZE, +}; + +/* see kmac_pkg::AppCfg in `kmac_pkg.sv` */ +static const OtKMACAppCfg KMAC_APP_CFG = OT_KMAC_CONFIG(KMAC, 256u, "KMAC", ""); + #define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) static const char *REG_NAMES[REGS_COUNT] = { REG_NAME_ENTRY(INTR_STATE), @@ -483,6 +526,26 @@ static const char *REG_NAMES[REGS_COUNT] = { #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") +#define STAGE_ENTRY(_st_) [KEYMGR_STAGE_##_st_] = stringify(_st_) +static const char *STAGE_NAMES[] = { + STAGE_ENTRY(CREATOR), + STAGE_ENTRY(OWNER_INT), + STAGE_ENTRY(OWNER), + STAGE_ENTRY(DISABLE), +}; +#undef STAGE_ENTRY +#define STAGE_NAME(_st_) \ + (((unsigned)(_st_)) < ARRAY_SIZE(STAGE_NAMES) ? STAGE_NAMES[(_st_)] : "?") + +#define CDI_ENTRY(_cd_) [KEYMGR_CDI_##_cd_] = stringify(_cd_) +static const char *CDI_NAMES[] = { + CDI_ENTRY(SEALING), + CDI_ENTRY(ATTESTATION), +}; +#undef CDI_ENTRY +#define CDI_NAME(_cd_) \ + (((unsigned)(_cd_)) < ARRAY_SIZE(CDI_NAMES) ? CDI_NAMES[(_cd_)] : "?") + #define OP_ENTRY(_op_) [KEYMGR_OP_##_op_] = stringify(_op_) static const char *OP_NAMES[] = { OP_ENTRY(ADVANCE), @@ -495,6 +558,18 @@ static const char *OP_NAMES[] = { #define OP_NAME(_op_) \ (((unsigned)(_op_)) < ARRAY_SIZE(OP_NAMES) ? OP_NAMES[(_op_)] : "?") +#define KEY_SINK_ENTRY(_st_) [KEYMGR_KEY_SINK_##_st_] = stringify(_st_) +static const char *KEY_SINK_NAMES[] = { + KEY_SINK_ENTRY(AES), + KEY_SINK_ENTRY(KMAC), + KEY_SINK_ENTRY(OTBN), +}; +#undef KEY_SINK_ENTRY +#define KEY_SINK_NAME(_st_) \ + (((unsigned)(_st_)) < ARRAY_SIZE(KEY_SINK_NAMES) ? \ + KEY_SINK_NAMES[(_st_)] : \ + "?") + #define WORKING_STATE_ENTRY(_st_) \ [KEYMGR_WORKING_STATE_##_st_] = stringify(_st_) static const char *WORKING_STATE_NAMES[] = { @@ -550,6 +625,22 @@ static const char *FST_NAMES[] = { #define ot_keymgr_dump_bigint(_s_, _b_, _l_) \ ot_common_lhexdump(_b_, _l_, true, (_s_)->hexstr, OT_KEYMGR_HEXSTR_SIZE) +static void ot_keymgr_dump_kdf_buf(const OtKeyMgrState *s, const char *op) +{ + if (trace_event_get_state(TRACE_OT_KEYMGR_DUMP_KDF_BUF)) { + size_t msgs = (s->kdf_buf.length + OT_KMAC_APP_MSG_BYTES - 1u) / + OT_KMAC_APP_MSG_BYTES; + for (size_t ix = 0u; ix < msgs; ix++) { + trace_ot_keymgr_dump_kdf_buf( + s->ot_id, op, ix, + ot_keymgr_dump_bigint(s, + &s->kdf_buf + .data[ix * OT_KMAC_APP_MSG_BYTES], + OT_KMAC_APP_MSG_BYTES)); + } + } +} + static void ot_keymgr_update_irq(OtKeyMgrState *s) { bool level = (bool)(s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]); @@ -722,6 +813,175 @@ static void ot_keymgr_request_entropy(OtKeyMgrState *s) } } +static void ot_keymgr_wipe_key_states(OtKeyMgrState *s) +{ + /* @todo: should wipe with random entropy, but just zero for now */ + for (unsigned cdi = 0u; cdi < NUM_CDIS; cdi++) { + memset(s->key_states[cdi].share0, 0u, KEYMGR_KEY_BYTES); + memset(s->key_states[cdi].share1, 0u, KEYMGR_KEY_BYTES); + s->key_states[cdi].valid = false; + } +} + +static void ot_keymgr_push_key(OtKeyMgrState *s, OtKeyMgrKeySink key_sink, + const uint8_t *key_share0, + const uint8_t *key_share1, bool valid) +{ + g_assert((unsigned)key_sink < KEYMGR_KEY_SINK_COUNT); + + size_t key_size = OT_KEY_MGR_KEY_SINK_SIZES[key_sink]; + DeviceState *sink = s->key_sinks[key_sink]; + + g_assert(sink); + + if (trace_event_get_state(TRACE_OT_KEYMGR_PUSH_KEY)) { + if (!key_share0 || !key_share1) { + trace_ot_keymgr_push_key(s->ot_id, KEY_SINK_NAME(key_sink), valid, + ""); + } else { + /* compute the unmasked key for tracing */ + uint8_t key_value[KEYMGR_KEY_SIZE_MAX]; + for (unsigned ix = 0u; ix < key_size; ix++) { + key_value[ix] = key_share0[ix] ^ key_share1[ix]; + } + + trace_ot_keymgr_push_key(s->ot_id, KEY_SINK_NAME(key_sink), valid, + ot_keymgr_dump_bigint(s, key_value, + key_size)); + } + } + + OtKeySinkIfClass *kc = OT_KEY_SINK_IF_GET_CLASS(sink); + OtKeySinkIf *ki = OT_KEY_SINK_IF(sink); + + g_assert(kc->push_key); + + kc->push_key(ki, key_share0, key_share1, key_size, valid); +} + +/* check that 'data' is not all zeros or all ones */ +static bool ot_keymgr_valid_data_check(const uint8_t *data, size_t len) +{ + size_t popcount = 0u; + for (unsigned ix = 0u; ix < len; ix++) { + popcount += __builtin_popcount(data[ix]); + } + return (popcount && popcount != (len * BITS_PER_BYTE)); +} + +static void ot_keymgr_reset_kdf_buffer(OtKeyMgrState *s) +{ + memset(s->kdf_buf.data, 0u, KEYMGR_KDF_BUFFER_BYTES); + s->kdf_buf.offset = 0u; + s->kdf_buf.length = 0u; +} + +static void ot_keymgr_kdf_push_bytes(OtKeyMgrState *s, const uint8_t *data, + size_t len) +{ + g_assert(s->kdf_buf.length + len <= KEYMGR_KDF_BUFFER_BYTES); + + memcpy(&s->kdf_buf.data[s->kdf_buf.length], data, len); + s->kdf_buf.length += len; +} + +static void ot_keymgr_dump_kdf_material( + const OtKeyMgrState *s, const char *what, const uint8_t *buf, size_t len) +{ + if (trace_event_get_state(TRACE_OT_KEYMGR_DUMP_KDF_MATERIAL)) { + const char *hexstr = ot_keymgr_dump_bigint(s, buf, len); + trace_ot_keymgr_dump_kdf_material(s->ot_id, what, hexstr); + } +} + +static size_t ot_keymgr_kdf_append_rev_seed(OtKeyMgrState *s) +{ + ot_keymgr_kdf_push_bytes(s, s->seeds[KEYMGR_SEED_REV], KEYMGR_SEED_BYTES); + ot_keymgr_dump_kdf_material(s, "REV_SEED", s->seeds[KEYMGR_SEED_REV], + KEYMGR_SEED_BYTES); + return KEYMGR_SEED_BYTES; +} + +static size_t ot_keymgr_kdf_append_rom_digest(OtKeyMgrState *s, bool *dvalid) +{ + uint8_t rom_digest[OT_ROM_DIGEST_BYTES] = { 0u }; + + OtRomCtrlClass *rcc = OT_ROM_CTRL_GET_CLASS(s->rom_ctrl); + rcc->get_rom_digest(s->rom_ctrl, rom_digest); + + ot_keymgr_kdf_push_bytes(s, rom_digest, OT_ROM_DIGEST_BYTES); + *dvalid &= ot_keymgr_valid_data_check(rom_digest, OT_ROM_DIGEST_BYTES); + + ot_keymgr_dump_kdf_material(s, "ROM_DIGEST", rom_digest, + OT_ROM_DIGEST_BYTES); + return OT_ROM_DIGEST_BYTES; +} + +static size_t ot_keymgr_kdf_append_km_div(OtKeyMgrState *s, bool *dvalid) +{ + OtLcCtrlKeyMgrDiv km_div = { 0u }; + + OtLcCtrlClass *lc = OT_LC_CTRL_GET_CLASS(s->lc_ctrl); + lc->get_keymgr_div(s->lc_ctrl, &km_div); + + ot_keymgr_kdf_push_bytes(s, km_div.data, OT_LC_KEYMGR_DIV_BYTES); + *dvalid &= ot_keymgr_valid_data_check(km_div.data, OT_LC_KEYMGR_DIV_BYTES); + + ot_keymgr_dump_kdf_material(s, "KM_DIV", km_div.data, + OT_LC_KEYMGR_DIV_BYTES); + return OT_LC_KEYMGR_DIV_BYTES; +} + +static size_t ot_keymgr_kdf_append_dev_id(OtKeyMgrState *s, bool *dvalid) +{ + OtOTPClass *otp_oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); + const OtOTPHWCfg *hw_cfg = otp_oc->get_hw_cfg(s->otp_ctrl); + + ot_keymgr_kdf_push_bytes(s, hw_cfg->device_id, + OT_OTP_HWCFG_DEVICE_ID_BYTES); + *dvalid &= ot_keymgr_valid_data_check(hw_cfg->device_id, + OT_OTP_HWCFG_DEVICE_ID_BYTES); + + ot_keymgr_dump_kdf_material(s, "DEVICE_ID", hw_cfg->device_id, + OT_OTP_HWCFG_DEVICE_ID_BYTES); + return OT_OTP_HWCFG_DEVICE_ID_BYTES; +} + +static size_t +ot_keymgr_kdf_append_seed(OtKeyMgrState *s, OtFlashKeyMgrSecretType type, + const char *seed_name, bool *dvalid) +{ + OtFlashKeyMgrSecret seed = { 0u }; + + OtFlashClass *fc = OT_FLASH_GET_CLASS(s->flash_ctrl); + fc->get_keymgr_secret(s->flash_ctrl, type, &seed); + + ot_keymgr_kdf_push_bytes(s, seed.secret, OT_FLASH_KEYMGR_SECRET_BYTES); + *dvalid &= + ot_keymgr_valid_data_check(seed.secret, OT_FLASH_KEYMGR_SECRET_BYTES); + *dvalid &= seed.valid; + + ot_keymgr_dump_kdf_material(s, seed_name, seed.secret, + OT_FLASH_KEYMGR_SECRET_BYTES); + return OT_FLASH_KEYMGR_SECRET_BYTES; +} + +static size_t ot_keymgr_kdf_append_sw_binding(OtKeyMgrState *s, OtKeyMgrCdi cdi) +{ + uint8_t *sw_binding = (cdi == KEYMGR_CDI_SEALING) ? s->sealing_sw_binding : + s->attest_sw_binding; + + ot_keymgr_kdf_push_bytes(s, sw_binding, KEYMGR_SW_BINDING_BYTES); + + char buf[32u]; + const char *binding_suffix = "_SW_BINDING"; + const char *cdi_name = CDI_NAME(cdi); + g_assert(strlen(binding_suffix) + strlen(cdi_name) + 1 < sizeof(buf)); + snprintf(buf, sizeof(buf), "%s%s", cdi_name, binding_suffix); + ot_keymgr_dump_kdf_material(s, buf, sw_binding, KEYMGR_SW_BINDING_BYTES); + return KEYMGR_SW_BINDING_BYTES; +} + static void ot_keymgr_get_root_key(OtKeyMgrState *s, OtOTPKeyMgrSecret *share0, OtOTPKeyMgrSecret *share1) { @@ -758,24 +1018,306 @@ static void ot_keymgr_xchange_main_fsm_state(OtKeyMgrState *s, } } +static void ot_keymgr_send_kmac_req(OtKeyMgrState *s) +{ + g_assert(s->kdf_buf.length); + g_assert(s->kdf_buf.offset < s->kdf_buf.length); + + unsigned msg_len = s->kdf_buf.length - s->kdf_buf.offset; + if (msg_len > OT_KMAC_APP_MSG_BYTES) { + msg_len = OT_KMAC_APP_MSG_BYTES; + } + + unsigned next_offset = s->kdf_buf.offset + msg_len; + + OtKMACAppReq req = { + .last = next_offset == s->kdf_buf.length, + .msg_len = msg_len, + }; + memcpy(req.msg_data, &s->kdf_buf.data[s->kdf_buf.offset], msg_len); + + trace_ot_keymgr_kmac_req(s->ot_id, s->kdf_buf.length, s->kdf_buf.offset, + req.msg_len, req.last); + + s->kdf_buf.offset = next_offset; + + OtKMACClass *kc = OT_KMAC_GET_CLASS(s->kmac); + kc->app_request(s->kmac, s->kmac_app, &req); +} + +static void ot_keymgr_operation_disable(OtKeyMgrState *s) +{ + ot_keymgr_wipe_key_states(s); + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_DISABLED); + s->op_state.op_ack = true; + ot_keymgr_schedule_fsm(s); +} + +static void ot_keymgr_operation_advance(OtKeyMgrState *s, OtKeyMgrStage stage, + OtKeyMgrCdi cdi) +{ + trace_ot_keymgr_advance(s->ot_id, STAGE_NAME(stage), (int)stage, + CDI_NAME(cdi), (int)cdi); + + bool dvalid = true; + + /* @todo: do we need to check for any error states here? */ + + ot_keymgr_reset_kdf_buffer(s); + + size_t expected_kdf_len = 0u; + + switch (stage) { + case KEYMGR_STAGE_CREATOR: + /* Revision Seed (which is a netlist constant) */ + expected_kdf_len += ot_keymgr_kdf_append_rev_seed(s); + + /* Rom Digest (from rom_ctrl) */ + expected_kdf_len += ot_keymgr_kdf_append_rom_digest(s, &dvalid); + + /* KeyManager Diversification (from lc_ctrl) */ + expected_kdf_len += ot_keymgr_kdf_append_km_div(s, &dvalid); + + /* Device ID (from OTP) */ + expected_kdf_len += ot_keymgr_kdf_append_dev_id(s, &dvalid); + break; + case KEYMGR_STAGE_OWNER_INT: + /* Creator Seed (from flash) */ + expected_kdf_len += + ot_keymgr_kdf_append_seed(s, FLASH_KEYMGR_SECRET_CREATOR_SEED, + "CREATOR_SEED", &dvalid); + break; + case KEYMGR_STAGE_OWNER: + /* Owner Seed (from flash) */ + expected_kdf_len += + ot_keymgr_kdf_append_seed(s, FLASH_KEYMGR_SECRET_OWNER_SEED, + "OWNER_SEED", &dvalid); + break; + case KEYMGR_STAGE_DISABLE: + /* you can "advance" from the OwnerRootKey to the `Disabled` state */ + ot_keymgr_operation_disable(s); + return; + default: + g_assert_not_reached(); + } + + /* Software Binding (software-provided via {x_SW_BINDING_y registers) */ + expected_kdf_len += ot_keymgr_kdf_append_sw_binding(s, cdi); + + /* check that we have pushed all expected KDF data */ + g_assert(s->kdf_buf.length == expected_kdf_len); + + /* + * @todo: store `dvalid` somewhere and, if the data is invalid, replace the + * KMAC response with decoy data (a random permutation of entropy share 1). + */ + if (!dvalid) { + s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_KMAC_INPUT_MASK; + } + + g_assert(s->kdf_buf.length <= KEYMGR_ADV_DATA_BYTES); + s->kdf_buf.length = KEYMGR_ADV_DATA_BYTES; + + /* send the current key state to KMAC as the KDF key */ + g_assert(cdi < NUM_CDIS); + OtKeyMgrKey *key_state = &s->key_states[cdi]; + ot_keymgr_push_key(s, KEYMGR_KEY_SINK_KMAC, key_state->share0, + key_state->share1, key_state->valid); + + /* transmit the contents of the KDF buffer to KMAC for computation */ + ot_keymgr_dump_kdf_buf(s, "adv"); + ot_keymgr_send_kmac_req(s); +} + static void ot_keymgr_start_operation(OtKeyMgrState *s) { uint32_t ctrl = ot_shadow_reg_peek(&s->control); int op = (int)FIELD_EX32(ctrl, CONTROL_SHADOWED, OPERATION); - trace_ot_keymgr_operation(s->ot_id, OP_NAME(op), op); + trace_ot_keymgr_operation(s->ot_id, STAGE_NAME(s->op_state.stage), + OP_NAME(op), op); switch (op) { case KEYMGR_OP_ADVANCE: - case KEYMGR_OP_DISABLE: - /* @todo: implement keymgr operations */ + s->op_state.adv_cdi_cnt = (OtKeyMgrCdi)0; + ot_keymgr_operation_advance(s, s->op_state.stage, + s->op_state.adv_cdi_cnt); + break; + case KEYMGR_OP_GENERATE_ID: + case KEYMGR_OP_GENERATE_SW_OUTPUT: + case KEYMGR_OP_GENERATE_HW_OUTPUT: + /* @todo: implement generate operations */ qemu_log_mask(LOG_UNIMP, "%s: %s, Operation %s is not implemented.\n", __func__, s->ot_id, OP_NAME(op)); break; + case KEYMGR_OP_DISABLE: + ot_keymgr_operation_disable(s); + break; + default: + /* should only be called with a valid operation */ + g_assert_not_reached(); + } +} + +static void ot_keymgr_reset_kmac_key(OtKeyMgrState *s) +{ + /* @todo: when not in use, this should be filled with dummy entropy */ + trace_ot_keymgr_reset_kmac_key(s->ot_id); + ot_keymgr_push_key(s, KEYMGR_KEY_SINK_KMAC, NULL, NULL, false); +} + +static bool +ot_keymgr_handle_kmac_resp_advance(OtKeyMgrState *s, const OtKMACAppRsp *rsp) +{ + unsigned cdi = s->op_state.adv_cdi_cnt; + + g_assert(cdi < NUM_CDIS); + + OtKeyMgrKey *key_state = &s->key_states[cdi]; + key_state->valid = true; + memcpy(key_state->share0, rsp->digest_share0, OT_KMAC_KEY_SIZE); + memcpy(key_state->share1, rsp->digest_share1, OT_KMAC_KEY_SIZE); + + /* SW can lock the `SW_BINDING` regs, and HW unlocks after an advance */ + s->regs[R_SW_BINDING_REGWEN] |= R_SW_BINDING_REGWEN_EN_MASK; + + unsigned next_cdi = cdi + 1; + if (next_cdi < NUM_CDIS) { + /* when advancing, we must advance all CDIs before completing */ + s->op_state.adv_cdi_cnt = next_cdi; + ot_keymgr_operation_advance(s, s->op_state.stage, next_cdi); + return false; + } + + /* all CDIs have been advanced, so complete the advance operation */ + ot_keymgr_reset_kmac_key(s); + s->op_state.adv_cdi_cnt = 0u; + + /* SW can lock the `SW_BINDING` regs, and HW unlocks after an advance */ + s->regs[R_SW_BINDING_REGWEN] |= R_SW_BINDING_REGWEN_EN_MASK; + + switch (s->op_state.stage) { + case KEYMGR_STAGE_CREATOR: + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_CREATOR_ROOT_KEY); + break; + case KEYMGR_STAGE_OWNER_INT: + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_OWNER_INT_KEY); + break; + case KEYMGR_STAGE_OWNER: + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_OWNER_KEY); + break; + case KEYMGR_STAGE_DISABLE: + default: + /* should not be advancing to the `disable` state */ + g_assert_not_reached(); + } + + return true; +} + +static void +ot_keymgr_handle_kmac_response(void *opaque, const OtKMACAppRsp *rsp) +{ + OtKeyMgrState *s = OT_KEYMGR(opaque); + + if (trace_event_get_state(TRACE_OT_KEYMGR_KMAC_RSP)) { + /* if tracing, trace the unmasked KMAC response key */ + if (rsp->done) { + uint8_t key[OT_KMAC_APP_DIGEST_BYTES]; + for (unsigned ix = 0u; ix < OT_KMAC_APP_DIGEST_BYTES; ix++) { + key[ix] = rsp->digest_share0[ix] ^ rsp->digest_share1[ix]; + } + trace_ot_keymgr_kmac_rsp( + s->ot_id, true, + ot_keymgr_dump_bigint(s, key, OT_KMAC_APP_DIGEST_BYTES)); + } else { + trace_ot_keymgr_kmac_rsp(s->ot_id, false, ""); + } + } + + if (!rsp->done) { + /* not the last response from KMAC, send more data */ + ot_keymgr_send_kmac_req(s); + return; + } + + g_assert(s->kdf_buf.offset == s->kdf_buf.length); + + uint32_t ctrl = ot_shadow_reg_peek(&s->control); + bool op_complete; + + int op = (int)FIELD_EX32(ctrl, CONTROL_SHADOWED, OPERATION); + switch (op) { + case KEYMGR_OP_ADVANCE: + op_complete = ot_keymgr_handle_kmac_resp_advance(s, rsp); + break; + case KEYMGR_OP_GENERATE_ID: + case KEYMGR_OP_GENERATE_SW_OUTPUT: + case KEYMGR_OP_GENERATE_HW_OUTPUT: + case KEYMGR_OP_DISABLE: + /* @todo: handle KMAC app responses in different keymgr ops */ + op_complete = true; + qemu_log_mask(LOG_UNIMP, + "%s: %s: KMAC response in op %s is not implemented.\n", + __func__, s->ot_id, OP_NAME(s->state)); + break; default: - /* should only be called with a valid init operation */ g_assert_not_reached(); } + + if (op_complete) { + /* complete the operation, and reschedule the FSM */ + s->op_state.op_ack = true; + ot_keymgr_schedule_fsm(s); + } +} + +static void ot_keymgr_fsm_key_stage(OtKeyMgrState *s, bool supports_generation, + OtKeyMgrStage advance, + OtKeyMgrStage generate) +{ + bool op_start = s->regs[R_START] & R_START_EN_MASK; + bool invalid_state = s->regs[R_FAULT_STATUS] & FAULT_STATUS_MASK; + uint32_t ctrl = ot_shadow_reg_peek(&s->control); + + /* check for op errors before enablement & state validity */ + uint32_t op = FIELD_EX32(ctrl, CONTROL_SHADOWED, OPERATION); + bool invalid_op = false; + switch (op) { + case KEYMGR_OP_GENERATE_ID: + case KEYMGR_OP_GENERATE_HW_OUTPUT: + case KEYMGR_OP_GENERATE_SW_OUTPUT: + if (op_start && !supports_generation) { + s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_OP_MASK; + invalid_op = true; + } + break; + default: + break; + } + + /* wipe if disabled or in an invalid state */ + if (!s->enabled || invalid_state) { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); + return; + } + + if (!op_start || s->op_state.op_req || invalid_op) { + return; /* no state change if op_start is not set */ + } + + switch (op) { + case KEYMGR_OP_DISABLE: + s->op_state.stage = KEYMGR_STAGE_DISABLE; + break; + case KEYMGR_OP_ADVANCE: + s->op_state.stage = advance; + break; + default: + s->op_state.stage = generate; + } + s->op_state.op_req = true; + ot_keymgr_start_operation(s); } /* Tick the main FSM. Returns whether there was any state change this tick. */ @@ -862,31 +1404,47 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) break; case KEYMGR_ST_INIT: ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_INIT); - if (!s->enabled || invalid_state) { - ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); - break; - } - if (!op_start) { - break; /* no state change if op_start is not set */ - } - uint32_t op = FIELD_EX32(ctrl, CONTROL_SHADOWED, OPERATION); - bool valid_op = (op == KEYMGR_OP_ADVANCE) || (op == KEYMGR_OP_DISABLE); - if (!valid_op) { - s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_OP_MASK; - } else if (!s->op_state.op_req) { - s->op_state.op_req = true; - ot_keymgr_start_operation(s); - } + ot_keymgr_fsm_key_stage(s, false, KEYMGR_STAGE_CREATOR, + KEYMGR_STAGE_DISABLE); break; case KEYMGR_ST_CREATOR_ROOT_KEY: + ot_keymgr_change_working_state(s, + KEYMGR_WORKING_STATE_CREATOR_ROOT_KEY); + ot_keymgr_fsm_key_stage(s, true, KEYMGR_STAGE_OWNER_INT, + KEYMGR_STAGE_CREATOR); + break; case KEYMGR_ST_OWNER_INT_KEY: + ot_keymgr_change_working_state( + s, KEYMGR_WORKING_STATE_OWNER_INTERMEDIATE_KEY); + ot_keymgr_fsm_key_stage(s, true, KEYMGR_STAGE_OWNER, + KEYMGR_STAGE_OWNER_INT); + break; case KEYMGR_ST_OWNER_KEY: + ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_OWNER_KEY); + ot_keymgr_fsm_key_stage(s, true, KEYMGR_STAGE_DISABLE, + KEYMGR_STAGE_OWNER); + break; case KEYMGR_ST_DISABLED: + ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_DISABLED); + if (!s->enabled || invalid_state) { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); + } + break; case KEYMGR_ST_WIPE: + /* whilst wiping, keymgr retains the previous working state */ + ot_keymgr_wipe_key_states(s); + /* see OT keymgr_ctrl.sv RTL: maintain & complete ongoing ops */ + if (op_start) { + s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_OP_MASK; + } else { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_INVALID); + } + break; case KEYMGR_ST_INVALID: - /* @todo: implement other keymgr FSM states */ - qemu_log_mask(LOG_UNIMP, "%s: %s: FSM State %s is not implemented.\n", - __func__, s->ot_id, FST_NAME(s->state)); + ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_INVALID); + if (op_start) { + s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_OP_MASK; + } break; default: break; @@ -1378,8 +1936,20 @@ static Property ot_keymgr_properties[] = { DEFINE_PROP_LINK("edn", OtKeyMgrState, edn.device, TYPE_OT_EDN, OtEDNState *), DEFINE_PROP_UINT8("edn-ep", OtKeyMgrState, edn.ep, UINT8_MAX), + DEFINE_PROP_LINK("flash_ctrl", OtKeyMgrState, flash_ctrl, TYPE_OT_FLASH, + OtFlashState *), + DEFINE_PROP_LINK("kmac", OtKeyMgrState, kmac, TYPE_OT_KMAC, OtKMACState *), + DEFINE_PROP_UINT8("kmac-app", OtKeyMgrState, kmac_app, UINT8_MAX), + DEFINE_PROP_LINK("lc_ctrl", OtKeyMgrState, lc_ctrl, TYPE_OT_LC_CTRL, + OtLcCtrlState *), DEFINE_PROP_LINK("otp_ctrl", OtKeyMgrState, otp_ctrl, TYPE_OT_OTP, OtOTPState *), + DEFINE_PROP_LINK("rom_ctrl", OtKeyMgrState, rom_ctrl, TYPE_OT_ROM_CTRL, + OtRomCtrlState *), + DEFINE_PROP_LINK("aes", OtKeyMgrState, key_sinks[KEYMGR_KEY_SINK_AES], + TYPE_OT_KEY_SINK_IF, DeviceState *), + DEFINE_PROP_LINK("otbn", OtKeyMgrState, key_sinks[KEYMGR_KEY_SINK_OTBN], + TYPE_OT_KEY_SINK_IF, DeviceState *), DEFINE_PROP_STRING("lfsr_seed", OtKeyMgrState, seed_xstrs[KEYMGR_SEED_LFSR]), DEFINE_PROP_STRING("revision_seed", OtKeyMgrState, @@ -1428,7 +1998,12 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) g_assert(s->edn.device); g_assert(s->edn.ep != UINT8_MAX); + g_assert(s->flash_ctrl); + g_assert(s->kmac); + g_assert(s->kmac_app != UINT8_MAX); + g_assert(s->lc_ctrl); g_assert(s->otp_ctrl); + g_assert(s->rom_ctrl); /* reset registers */ memset(s->regs, 0u, sizeof(s->regs)); @@ -1455,6 +2030,9 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) s->prng.reseed_cnt = 0u; s->op_state.op_req = false; s->op_state.op_ack = false; + s->op_state.stage = KEYMGR_STAGE_DISABLE; + s->op_state.adv_cdi_cnt = 0u; + ot_keymgr_reset_kdf_buffer(s); /* reset key state */ memset(s->key_states, 0u, NUM_CDIS * sizeof(OtKeyMgrKey)); @@ -1462,6 +2040,11 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) /* update IRQ and alert states */ ot_keymgr_update_irq(s); ot_keymgr_update_alerts(s); + + /* connect to KMAC */ + OtKMACClass *kc = OT_KMAC_GET_CLASS(s->kmac); + kc->connect_app(s->kmac, s->kmac_app, &KMAC_APP_CFG, + ot_keymgr_handle_kmac_response, s); } static void ot_keymgr_reset_exit(Object *obj, ResetType type) @@ -1474,6 +2057,12 @@ static void ot_keymgr_reset_exit(Object *obj, ResetType type) if (c->parent_phases.exit) { c->parent_phases.exit(obj, type); } + + /* invalidate all sideloaded keys when exiting reset */ + for (unsigned ix = 0u; ix < KEYMGR_KEY_SINK_COUNT; ix++) { + OtKeyMgrKeySink key_sink = (OtKeyMgrKeySink)ix; + ot_keymgr_push_key(s, key_sink, NULL, NULL, false); + } } static void ot_keymgr_realize(DeviceState *dev, Error **errp) @@ -1487,7 +2076,19 @@ static void ot_keymgr_realize(DeviceState *dev, Error **errp) g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); } + for (unsigned ix = 0u; ix < KEYMGR_KEY_SINK_COUNT; ix++) { + if (s->key_sinks[ix]) { + OBJECT_CHECK(OtKeySinkIf, s->key_sinks[ix], TYPE_OT_KEY_SINK_IF); + } + } + ot_keymgr_configure_constants(s); + + /* + * Define KMAC separately in `s->kmac` instead of directly as a key sink, + * as we also need to offload KDF operations to it. + */ + s->key_sinks[KEYMGR_KEY_SINK_KMAC] = DEVICE(s->kmac); } static void ot_keymgr_init(Object *obj) @@ -1507,6 +2108,7 @@ static void ot_keymgr_init(Object *obj) s->prng.state = ot_prng_allocate(); + s->kdf_buf.data = g_new0(uint8_t, KEYMGR_KDF_BUFFER_BYTES); s->salt = g_new0(uint8_t, KEYMGR_SALT_BYTES); s->sealing_sw_binding = g_new0(uint8_t, KEYMGR_SW_BINDING_BYTES); s->attest_sw_binding = g_new0(uint8_t, KEYMGR_SW_BINDING_BYTES); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 56d6263c6faf1..8e0c225f19239 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -278,20 +278,27 @@ ot_ibex_wrapper_unmap(const char *id, unsigned slot) "%s: region %u" ot_ibex_wrapper_update_exec(const char *id, uint32_t bm, bool esc_rx, bool halted, bool in_reset, bool cpu_en) "%s: bm:0x%x esc:%u halted:%u in_reset:%u -> CPU enable %u" # ot_keymgr.c +ot_keymgr_advance(const char *id, const char *stage, int nstage, const char *cdi, int ncdi) "%s: [%s:%d], [%s:%d]" ot_keymgr_change_main_fsm_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" ot_keymgr_change_op_status(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" ot_keymgr_change_working_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" ot_keymgr_dump_creator_root_key(const char *id, unsigned idx, bool valid, const char *hexstr) "%s: share:%u valid:%u %s" +ot_keymgr_dump_kdf_buf(const char *id, const char *op, size_t idx, const char *hexstr) "%s: %s: kdf[%02zu] %s" +ot_keymgr_dump_kdf_material(const char *id, const char *what, const char *hexstr) "%s: %s %s" ot_keymgr_entropy(const char *id, unsigned reseed_cnt, bool resched) "%s: reseed_cnt: %u, resched: %u" ot_keymgr_error(const char *id, const char *msg) "%s: %s" ot_keymgr_go_idle(const char *id) "%s" ot_keymgr_io_read_out(const char *id, uint32_t addr, const char *regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%08x, pc=0x%x" ot_keymgr_io_write(const char *id, uint32_t addr, const char *regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%08x, pc=0x%x" ot_keymgr_irq(const char *id, uint32_t active, uint32_t mask, bool level) "%s: act:0x%08x msk:0x%08x lvl:%u" +ot_keymgr_kmac_req(const char *id, uint32_t fifo_len, uint32_t offset, uint32_t msg_len, bool last) "%s: fifo_len:%u offset:%u msg_len:%u, last:%d" +ot_keymgr_kmac_rsp(const char *id, bool done, const char *hexstr) "%s: done:%d %s" ot_keymgr_lc_signal(const char *id, int level) "%s: LC signal level:%d" ot_keymgr_main_fsm_tick(const char *id, const char *st, int nst) "%s: [%s:%d]" -ot_keymgr_operation(const char *id, const char *op, int nop) "%s: op:[%s:%d]" +ot_keymgr_operation(const char *id, const char *stage, const char *op, int nop) "%s: stage:%s op:[%s:%d]" +ot_keymgr_push_key(const char *id, const char *sink, bool valid, const char *hexstr) "%s: sink:%s valid:%u key:%s" ot_keymgr_reset(const char *id, const char *stage) "%s: %s" +ot_keymgr_reset_kmac_key(const char *id) "%s" ot_keymgr_schedule_fsm(const char *id, const char *func, int line) "%s @ %s:%d" ot_keymgr_seed_missing(const char *id, unsigned ix) "%s: #%u" ot_keymgr_update_alert(const char *id, int prev, int next) "%s: %d -> %d" diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index c53d0f21d5370..a971dda944e2a 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -1186,11 +1186,18 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(1, 50) ), .link = IBEXDEVICELINKDEFS( + OT_EG_SOC_DEVLINK("aes", AES), OT_EG_SOC_DEVLINK("edn", EDN0), - OT_EG_SOC_DEVLINK("otp_ctrl", OTP_CTRL) + OT_EG_SOC_DEVLINK("flash_ctrl", FLASH_CTRL), + OT_EG_SOC_DEVLINK("kmac", KMAC), + OT_EG_SOC_DEVLINK("lc_ctrl", LC_CTRL), + OT_EG_SOC_DEVLINK("otbn", OTBN), + OT_EG_SOC_DEVLINK("otp_ctrl", OTP_CTRL), + OT_EG_SOC_DEVLINK("rom_ctrl", ROM_CTRL) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_UINT_PROP("edn-ep", 0u) + IBEX_DEV_UINT_PROP("edn-ep", 0u), + IBEX_DEV_UINT_PROP("kmac-app", 0u) ), }, [OT_EG_SOC_DEV_CSRNG] = { diff --git a/scripts/opentitan/tests-passing.txt b/scripts/opentitan/tests-passing.txt index 6dccbfb1bc8e1..39a6b621df040 100644 --- a/scripts/opentitan/tests-passing.txt +++ b/scripts/opentitan/tests-passing.txt @@ -6,6 +6,7 @@ //sw/device/lib/crypto/drivers:rv_core_ibex_test_sim_qemu_rom_with_fake_keys //sw/device/silicon_creator/lib/drivers:hmac_functest_sim_qemu_rom_with_fake_keys //sw/device/silicon_creator/lib/drivers:kmac_functest_sim_qemu_rom_with_fake_keys +//sw/device/silicon_creator/lib/drivers:keymgr_functest_sim_qemu_rom_with_fake_keys //sw/device/silicon_creator/lib/drivers:retention_sram_functest_sim_qemu_rom_with_fake_keys //sw/device/silicon_creator/lib/drivers:rnd_functest_sim_qemu_rom_with_fake_keys //sw/device/silicon_creator/lib/drivers:rstmgr_functest_sim_qemu_rom_with_fake_keys