| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "sw/device/silicon_creator/rom/sigverify_keys.h" |
| |
| #include <assert.h> |
| #include <stddef.h> |
| |
| #include "sw/device/lib/base/bitfield.h" |
| #include "sw/device/lib/base/hardened.h" |
| #include "sw/device/silicon_creator/lib/drivers/otp.h" |
| #include "sw/device/silicon_creator/lib/drivers/rnd.h" |
| #include "sw/device/silicon_creator/lib/sigverify/sigverify.h" |
| |
| #include "otp_ctrl_regs.h" |
| |
| /** |
| * Checks the validity of a key in OTP. |
| * |
| * Validity of each public key is encoded using a byte-sized |
| * `hardened_byte_bool_t` in the `CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN` OTP item. |
| * |
| * @param key_index Index of the key to check. |
| * @return Whether the key is valid or not. |
| */ |
| static rom_error_t key_is_valid_in_otp(size_t key_index) { |
| const uint32_t addr = |
| OTP_CTRL_PARAM_CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN_OFFSET + |
| (key_index / kSigverifyNumEntriesPerOtpWord) * sizeof(uint32_t); |
| const bitfield_field32_t field = { |
| .mask = UINT8_MAX, |
| .index = (key_index % kSigverifyNumEntriesPerOtpWord) * 8, |
| }; |
| hardened_byte_bool_t is_valid = |
| bitfield_field32_read(otp_read32(addr), field); |
| if (launder32(is_valid) == kHardenedByteBoolTrue) { |
| HARDENED_CHECK_EQ(is_valid, kHardenedByteBoolTrue); |
| return kErrorOk; |
| } |
| return kErrorSigverifyBadKey; |
| } |
| |
| /** |
| * Determines whether a key is valid in the RMA life cycle state. |
| * |
| * Only test and production keys that are not invalidated in OTP are valid in |
| * the RMA life cycle state. |
| * |
| * @param key_type Type of the key. |
| * @param key_index Index of the key. |
| * @return The result of the operation. |
| */ |
| static rom_error_t key_is_valid_in_lc_state_rma(sigverify_key_type_t key_type, |
| size_t key_index) { |
| switch (launder32(key_type)) { |
| case kSigverifyKeyTypeTest: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeTest); |
| return key_is_valid_in_otp(key_index); |
| case kSigverifyKeyTypeProd: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeProd); |
| return key_is_valid_in_otp(key_index); |
| case kSigverifyKeyTypeDev: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeDev); |
| return kErrorSigverifyBadKey; |
| default: |
| HARDENED_UNREACHABLE(); |
| } |
| } |
| |
| /** |
| * Determines whether a key is valid in the DEV life cycle state. |
| * |
| * Only production and development keys that are not invalidated in OTP are |
| * valid in the DEV life cycle state. |
| * |
| * @param key_type Type of the key. |
| * @param key_index Index of the key. |
| * @return The result of the operation. |
| */ |
| static rom_error_t key_is_valid_in_lc_state_dev(sigverify_key_type_t key_type, |
| size_t key_index) { |
| switch (launder32(key_type)) { |
| case kSigverifyKeyTypeTest: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeTest); |
| return kErrorSigverifyBadKey; |
| case kSigverifyKeyTypeProd: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeProd); |
| return key_is_valid_in_otp(key_index); |
| case kSigverifyKeyTypeDev: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeDev); |
| return key_is_valid_in_otp(key_index); |
| default: |
| HARDENED_UNREACHABLE(); |
| } |
| } |
| |
| /** |
| * Determines whether a key is valid in PROD and PROD_END life cycle states. |
| * |
| * Only production keys that are not invalidated in OTP are valid in PROD and |
| * PROD_END life cycle states. |
| * |
| * @param key_type Type of the key. |
| * @param key_index Index of the key. |
| * @return The result of the operation. |
| */ |
| static rom_error_t key_is_valid_in_lc_state_prod(sigverify_key_type_t key_type, |
| size_t key_index) { |
| switch (launder32(key_type)) { |
| case kSigverifyKeyTypeTest: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeTest); |
| return kErrorSigverifyBadKey; |
| case kSigverifyKeyTypeProd: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeProd); |
| return key_is_valid_in_otp(key_index); |
| case kSigverifyKeyTypeDev: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeDev); |
| return kErrorSigverifyBadKey; |
| default: |
| HARDENED_UNREACHABLE(); |
| } |
| } |
| |
| /** |
| * Determines whether a key is valid in TEST_UNLOCKED_* life cycle states. |
| * |
| * Only test and production keys are valid in TEST_UNLOCKED_* states. We don't |
| * read from OTP since it may not be programmed yet. |
| * |
| * @param key_type Type of the key. |
| * @return The result of the operation. |
| */ |
| static rom_error_t key_is_valid_in_lc_state_test( |
| sigverify_key_type_t key_type) { |
| switch (launder32(key_type)) { |
| case kSigverifyKeyTypeTest: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeTest); |
| return kErrorOk; |
| case kSigverifyKeyTypeProd: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeProd); |
| return kErrorOk; |
| case kSigverifyKeyTypeDev: |
| HARDENED_CHECK_EQ(key_type, kSigverifyKeyTypeDev); |
| return kErrorSigverifyBadKey; |
| default: |
| HARDENED_UNREACHABLE(); |
| } |
| } |
| |
| /** |
| * Determines whether a given key is valid in the given life cycle state. |
| * |
| * @param key_type Type of the key. |
| * @param lc_state Life cycle state of the device. |
| * @param key_index Index of the key. |
| * @return The result of the operation. |
| */ |
| static rom_error_t key_is_valid(sigverify_key_type_t key_type, |
| lifecycle_state_t lc_state, size_t key_index) { |
| switch (launder32(lc_state)) { |
| case kLcStateTest: |
| HARDENED_CHECK_EQ(lc_state, kLcStateTest); |
| return key_is_valid_in_lc_state_test(key_type); |
| case kLcStateProd: |
| HARDENED_CHECK_EQ(lc_state, kLcStateProd); |
| return key_is_valid_in_lc_state_prod(key_type, key_index); |
| case kLcStateProdEnd: |
| HARDENED_CHECK_EQ(lc_state, kLcStateProdEnd); |
| return key_is_valid_in_lc_state_prod(key_type, key_index); |
| case kLcStateDev: |
| HARDENED_CHECK_EQ(lc_state, kLcStateDev); |
| return key_is_valid_in_lc_state_dev(key_type, key_index); |
| case kLcStateRma: |
| HARDENED_CHECK_EQ(lc_state, kLcStateRma); |
| return key_is_valid_in_lc_state_rma(key_type, key_index); |
| default: |
| HARDENED_UNREACHABLE(); |
| } |
| } |
| |
| rom_error_t sigverify_rsa_key_get(uint32_t key_id, lifecycle_state_t lc_state, |
| const sigverify_rsa_key_t **key) { |
| size_t cand_key_index = UINT32_MAX; |
| // Random start index that is less than `kSigverifyRsaKeysCnt`. |
| size_t i = ((uint64_t)rnd_uint32() * (uint64_t)kSigverifyRsaKeysCnt) >> 32; |
| size_t iter_cnt = 0; |
| for (; launder32(iter_cnt) < kSigverifyRsaKeysCnt; ++iter_cnt) { |
| const sigverify_rom_key_t *k = &kSigverifyRsaKeys[i]; |
| size_t k_id = sigverify_rsa_key_id_get(&k->key.n); |
| if (launder32(k_id) == key_id) { |
| HARDENED_CHECK_EQ(k_id, key_id); |
| rom_error_t error = key_is_valid(k->key_type, lc_state, i); |
| if (launder32(error) == kErrorOk) { |
| HARDENED_CHECK_EQ(error, kErrorOk); |
| cand_key_index = i; |
| } |
| } |
| i += kSigverifyRsaKeysStep; |
| if (launder32(i) >= kSigverifyRsaKeysCnt) { |
| i -= kSigverifyRsaKeysCnt; |
| } |
| HARDENED_CHECK_LT(i, kSigverifyRsaKeysCnt); |
| } |
| HARDENED_CHECK_EQ(iter_cnt, kSigverifyRsaKeysCnt); |
| |
| if (launder32(cand_key_index) < kSigverifyRsaKeysCnt) { |
| HARDENED_CHECK_LT(cand_key_index, kSigverifyRsaKeysCnt); |
| rom_error_t error = key_is_valid(kSigverifyRsaKeys[cand_key_index].key_type, |
| lc_state, cand_key_index); |
| HARDENED_CHECK_EQ(error, kErrorOk); |
| *key = &kSigverifyRsaKeys[cand_key_index].key; |
| return error; |
| } |
| |
| return kErrorSigverifyBadKey; |
| } |