| // 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/hmac.h" | |
| #include "sw/device/lib/base/abs_mmio.h" | |
| #include "sw/device/lib/base/bitfield.h" | |
| #include "sw/device/lib/base/memory.h" | |
| #include "sw/device/lib/crypto/impl/status.h" | |
| #include "hmac_regs.h" // Generated. | |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | |
| void hmac_sha256_init(void) { | |
| // Clear the config, stopping the SHA engine. | |
| abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_CFG_REG_OFFSET, 0); | |
| // Disable and clear interrupts. INTR_STATE register is rw1c. | |
| abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_INTR_ENABLE_REG_OFFSET, | |
| 0); | |
| abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_INTR_STATE_REG_OFFSET, | |
| UINT32_MAX); | |
| uint32_t reg = 0; | |
| // Digest is little-endian by default. | |
| reg = bitfield_bit32_write(reg, HMAC_CFG_DIGEST_SWAP_BIT, false); | |
| // Message is little-endian by default. | |
| reg = bitfield_bit32_write(reg, HMAC_CFG_ENDIAN_SWAP_BIT, false); | |
| reg = bitfield_bit32_write(reg, HMAC_CFG_SHA_EN_BIT, true); | |
| reg = bitfield_bit32_write(reg, HMAC_CFG_HMAC_EN_BIT, false); | |
| abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_CFG_REG_OFFSET, reg); | |
| reg = 0; | |
| reg = bitfield_bit32_write(reg, HMAC_CMD_HASH_START_BIT, true); | |
| abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_CMD_REG_OFFSET, reg); | |
| } | |
| status_t hmac_sha256_update(const void *data, size_t len) { | |
| if (data == NULL) { | |
| return OTCRYPTO_BAD_ARGS; | |
| } | |
| const uint8_t *data_sent = (const uint8_t *)data; | |
| // Individual byte writes are needed if the buffer isn't word aligned. | |
| for (; len != 0 && (uintptr_t)data_sent & 3; --len) { | |
| abs_mmio_write8(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_MSG_FIFO_REG_OFFSET, | |
| *data_sent++); | |
| } | |
| for (; len >= sizeof(uint32_t); len -= sizeof(uint32_t)) { | |
| uint32_t data_aligned = read_32(data_sent); | |
| abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_MSG_FIFO_REG_OFFSET, | |
| data_aligned); | |
| data_sent += sizeof(uint32_t); | |
| } | |
| // Handle non-32bit aligned bytes at the end of the buffer. | |
| for (; len != 0; --len) { | |
| abs_mmio_write8(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_MSG_FIFO_REG_OFFSET, | |
| *data_sent++); | |
| } | |
| return OTCRYPTO_OK; | |
| } | |
| status_t hmac_sha256_final(hmac_digest_t *digest) { | |
| if (digest == NULL) { | |
| return OTCRYPTO_BAD_ARGS; | |
| } | |
| uint32_t reg = 0; | |
| reg = bitfield_bit32_write(reg, HMAC_CMD_HASH_PROCESS_BIT, true); | |
| abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_CMD_REG_OFFSET, reg); | |
| do { | |
| reg = abs_mmio_read32(TOP_EARLGREY_HMAC_BASE_ADDR + | |
| HMAC_INTR_STATE_REG_OFFSET); | |
| } while (!bitfield_bit32_read(reg, HMAC_INTR_STATE_HMAC_DONE_BIT)); | |
| abs_mmio_write32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_INTR_STATE_REG_OFFSET, | |
| reg); | |
| // Read the digest in reverse to preserve the numerical value. | |
| // The least significant word is at HMAC_DIGEST_7_REG_OFFSET. | |
| for (size_t i = 0; i < ARRAYSIZE(digest->digest); ++i) { | |
| digest->digest[i] = | |
| abs_mmio_read32(TOP_EARLGREY_HMAC_BASE_ADDR + HMAC_DIGEST_7_REG_OFFSET - | |
| (i * sizeof(uint32_t))); | |
| } | |
| return OTCRYPTO_OK; | |
| } |