| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "sw/device/lib/crypto/drivers/aes.h" |
| |
| #include "sw/device/lib/base/abs_mmio.h" |
| #include "sw/device/lib/base/bitfield.h" |
| #include "sw/device/lib/base/hardened.h" |
| #include "sw/device/lib/base/hardened_status.h" |
| #include "sw/device/lib/base/macros.h" |
| #include "sw/device/lib/base/memory.h" |
| #include "sw/device/lib/crypto/impl/status.h" |
| |
| #include "aes_regs.h" // Generated. |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" |
| |
| // Static assertions for enum values. |
| OT_ASSERT_ENUM_VALUE(kAesCipherModeEcb, AES_CTRL_SHADOWED_MODE_VALUE_AES_ECB); |
| OT_ASSERT_ENUM_VALUE(kAesCipherModeCbc, AES_CTRL_SHADOWED_MODE_VALUE_AES_CBC); |
| OT_ASSERT_ENUM_VALUE(kAesCipherModeCfb, AES_CTRL_SHADOWED_MODE_VALUE_AES_CFB); |
| OT_ASSERT_ENUM_VALUE(kAesCipherModeOfb, AES_CTRL_SHADOWED_MODE_VALUE_AES_OFB); |
| OT_ASSERT_ENUM_VALUE(kAesCipherModeCtr, AES_CTRL_SHADOWED_MODE_VALUE_AES_CTR); |
| |
| OT_ASSERT_ENUM_VALUE(kAesKeyLen128, AES_CTRL_SHADOWED_KEY_LEN_VALUE_AES_128); |
| OT_ASSERT_ENUM_VALUE(kAesKeyLen192, AES_CTRL_SHADOWED_KEY_LEN_VALUE_AES_192); |
| OT_ASSERT_ENUM_VALUE(kAesKeyLen256, AES_CTRL_SHADOWED_KEY_LEN_VALUE_AES_256); |
| |
| enum { |
| kBase = TOP_EARLGREY_AES_BASE_ADDR, |
| |
| kAesKeyWordLen128 = 4, |
| kAesKeyWordLen192 = 6, |
| kAesKeyWordLen256 = 8, |
| }; |
| |
| /** |
| * Spins until the AES hardware reports a specific status bit. |
| */ |
| static status_t spin_until(uint32_t bit) { |
| while (true) { |
| uint32_t reg = abs_mmio_read32(kBase + AES_STATUS_REG_OFFSET); |
| if (bitfield_bit32_read(reg, AES_STATUS_ALERT_RECOV_CTRL_UPDATE_ERR_BIT) || |
| bitfield_bit32_read(reg, AES_STATUS_ALERT_FATAL_FAULT_BIT)) { |
| return OTCRYPTO_RECOV_ERR; |
| } |
| if (bitfield_bit32_read(reg, bit)) { |
| return OTCRYPTO_OK; |
| } |
| } |
| } |
| |
| /** |
| * Write the key to the AES hardware block. |
| * |
| * If the key is sideloaded, this is a no-op. |
| * |
| * @param key AES key. |
| * @return result, OK or error. |
| */ |
| static status_t aes_write_key(aes_key_t key) { |
| if (key.sideload != kHardenedBoolFalse) { |
| // Nothing to be done; key must be separately loaded from keymgr. |
| return OTCRYPTO_OK; |
| } |
| |
| size_t key_words; |
| switch (key.key_len) { |
| case kAesKeyLen128: |
| key_words = kAesKeyWordLen128; |
| break; |
| case kAesKeyLen192: |
| key_words = kAesKeyWordLen192; |
| break; |
| case kAesKeyLen256: |
| key_words = kAesKeyWordLen256; |
| break; |
| default: |
| return OTCRYPTO_BAD_ARGS; |
| } |
| |
| uint32_t share0 = kBase + AES_KEY_SHARE0_0_REG_OFFSET; |
| uint32_t share1 = kBase + AES_KEY_SHARE1_0_REG_OFFSET; |
| |
| for (size_t i = 0; i < key_words; ++i) { |
| abs_mmio_write32(share0 + i * sizeof(uint32_t), key.key_shares[0][i]); |
| abs_mmio_write32(share1 + i * sizeof(uint32_t), key.key_shares[1][i]); |
| } |
| for (size_t i = key_words; i < 8; ++i) { |
| // NOTE: all eight share registers must be written; in the case we don't |
| // have enough key data, we fill it with zeroes. |
| abs_mmio_write32(share0 + i * sizeof(uint32_t), 0); |
| abs_mmio_write32(share1 + i * sizeof(uint32_t), 0); |
| } |
| return spin_until(AES_STATUS_IDLE_BIT); |
| } |
| |
| /** |
| * Configure the AES block and write the key and IV if applicable. |
| * |
| * @param key AES key. |
| * @param iv IV to use (ignored if the mode does not require an IV). |
| * @param encrypt True for encryption, false for decryption. |
| * @return result, OK or error. |
| */ |
| static status_t aes_begin(aes_key_t key, const aes_block_t *iv, |
| hardened_bool_t encrypt) { |
| HARDENED_TRY(spin_until(AES_STATUS_IDLE_BIT)); |
| |
| uint32_t ctrl_reg = AES_CTRL_SHADOWED_REG_RESVAL; |
| |
| // Set the operation (encrypt or decrypt). |
| hardened_bool_t operation_written = kHardenedBoolFalse; |
| switch (encrypt) { |
| case kHardenedBoolTrue: |
| ctrl_reg = |
| bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_OPERATION_FIELD, |
| AES_CTRL_SHADOWED_OPERATION_VALUE_AES_ENC); |
| operation_written = kHardenedBoolTrue; |
| break; |
| case kHardenedBoolFalse: |
| ctrl_reg = |
| bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_OPERATION_FIELD, |
| AES_CTRL_SHADOWED_OPERATION_VALUE_AES_DEC); |
| operation_written = kHardenedBoolTrue; |
| break; |
| default: |
| // Invalid value. |
| return OTCRYPTO_BAD_ARGS; |
| } |
| HARDENED_CHECK_EQ(operation_written, kHardenedBoolTrue); |
| |
| // Indicate whether the key will be sideloaded. |
| hardened_bool_t sideload_written = kHardenedBoolFalse; |
| switch (key.sideload) { |
| case kHardenedBoolTrue: |
| ctrl_reg = |
| bitfield_bit32_write(ctrl_reg, AES_CTRL_SHADOWED_SIDELOAD_BIT, true); |
| sideload_written = kHardenedBoolTrue; |
| break; |
| case kHardenedBoolFalse: |
| ctrl_reg = |
| bitfield_bit32_write(ctrl_reg, AES_CTRL_SHADOWED_SIDELOAD_BIT, false); |
| sideload_written = kHardenedBoolTrue; |
| break; |
| default: |
| // Invalid value. |
| return OTCRYPTO_BAD_ARGS; |
| } |
| HARDENED_CHECK_EQ(sideload_written, kHardenedBoolTrue); |
| |
| // Set the mode and the key length. |
| ctrl_reg = |
| bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_MODE_FIELD, key.mode); |
| ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_KEY_LEN_FIELD, |
| key.key_len); |
| |
| // Never enable manual operation. |
| ctrl_reg = bitfield_bit32_write( |
| ctrl_reg, AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, false); |
| |
| // Always set the PRNG reseed rate to once per 64 blocks. |
| ctrl_reg = |
| bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_PRNG_RESEED_RATE_FIELD, |
| AES_CTRL_SHADOWED_PRNG_RESEED_RATE_VALUE_PER_64); |
| |
| abs_mmio_write32_shadowed(kBase + AES_CTRL_SHADOWED_REG_OFFSET, ctrl_reg); |
| HARDENED_TRY(spin_until(AES_STATUS_IDLE_BIT)); |
| |
| // Write the key (if it is not sideloaded). |
| HARDENED_TRY(aes_write_key(key)); |
| |
| // ECB does not need to set an IV, so we're done early. |
| if (key.mode == kAesCipherModeEcb) { |
| return OTCRYPTO_OK; |
| } |
| |
| uint32_t iv_offset = kBase + AES_IV_0_REG_OFFSET; |
| for (size_t i = 0; i < ARRAYSIZE(iv->data); ++i) { |
| abs_mmio_write32(iv_offset + i * sizeof(uint32_t), iv->data[i]); |
| } |
| |
| return OTCRYPTO_OK; |
| } |
| |
| status_t aes_encrypt_begin(const aes_key_t key, const aes_block_t *iv) { |
| return aes_begin(key, iv, kHardenedBoolTrue); |
| } |
| |
| status_t aes_decrypt_begin(const aes_key_t key, const aes_block_t *iv) { |
| return aes_begin(key, iv, kHardenedBoolFalse); |
| } |
| |
| status_t aes_update(aes_block_t *dest, const aes_block_t *src) { |
| if (dest != NULL) { |
| // Check that either the output is valid or AES is busy, to avoid spinning |
| // forever if the user passes a non-null `dest` when there is no output |
| // pending. |
| uint32_t reg = abs_mmio_read32(kBase + AES_STATUS_REG_OFFSET); |
| if (bitfield_bit32_read(reg, AES_STATUS_IDLE_BIT) && |
| !bitfield_bit32_read(reg, AES_STATUS_OUTPUT_VALID_BIT)) { |
| return OTCRYPTO_RECOV_ERR; |
| } |
| |
| HARDENED_TRY(spin_until(AES_STATUS_OUTPUT_VALID_BIT)); |
| |
| uint32_t offset = kBase + AES_DATA_OUT_0_REG_OFFSET; |
| for (size_t i = 0; i < ARRAYSIZE(dest->data); ++i) { |
| dest->data[i] = abs_mmio_read32(offset + i * sizeof(uint32_t)); |
| } |
| } |
| |
| if (src != NULL) { |
| HARDENED_TRY(spin_until(AES_STATUS_INPUT_READY_BIT)); |
| |
| uint32_t offset = kBase + AES_DATA_IN_0_REG_OFFSET; |
| for (size_t i = 0; i < ARRAYSIZE(src->data); ++i) { |
| abs_mmio_write32(offset + i * sizeof(uint32_t), src->data[i]); |
| } |
| } |
| |
| return OTCRYPTO_OK; |
| } |
| |
| status_t aes_end(void) { |
| uint32_t ctrl_reg = AES_CTRL_SHADOWED_REG_RESVAL; |
| ctrl_reg = bitfield_bit32_write(ctrl_reg, |
| AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, true); |
| abs_mmio_write32_shadowed(kBase + AES_CTRL_SHADOWED_REG_OFFSET, ctrl_reg); |
| |
| uint32_t trigger_reg = 0; |
| trigger_reg = bitfield_bit32_write( |
| trigger_reg, AES_TRIGGER_KEY_IV_DATA_IN_CLEAR_BIT, true); |
| trigger_reg = |
| bitfield_bit32_write(trigger_reg, AES_TRIGGER_DATA_OUT_CLEAR_BIT, true); |
| abs_mmio_write32(kBase + AES_TRIGGER_REG_OFFSET, trigger_reg); |
| |
| return spin_until(AES_STATUS_IDLE_BIT); |
| } |