blob: 914936d6ec2b1fd40ae83d12db04e4f4bbdda556 [file] [log] [blame]
// 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/bitfield.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/base/mmio.h"
#include "hmac_regs.h" // Generated.
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
void hmac_sha256_init(void) {
mmio_region_t hmac_base = mmio_region_from_addr(TOP_EARLGREY_HMAC_BASE_ADDR);
// Clear the config, stopping the SHA engine.
mmio_region_write32(hmac_base, HMAC_CFG_REG_OFFSET, 0);
// Disable and clear interrupts. INTR_STATE register is rw1c.
mmio_region_write32(hmac_base, HMAC_INTR_ENABLE_REG_OFFSET, 0);
mmio_region_write32(hmac_base, HMAC_INTR_STATE_REG_OFFSET, 0);
uint32_t reg = 0;
// Digest is little-endian by default.
reg = bitfield_bit32_write(reg, HMAC_CFG_DIGEST_SWAP_BIT, false);
// Enable endian swap since our inputs are little-endian.
reg = bitfield_bit32_write(reg, HMAC_CFG_ENDIAN_SWAP_BIT, true);
reg = bitfield_bit32_write(reg, HMAC_CFG_SHA_EN_BIT, true);
reg = bitfield_bit32_write(reg, HMAC_CFG_HMAC_EN_BIT, false);
mmio_region_write32(hmac_base, HMAC_CFG_REG_OFFSET, reg);
reg = 0;
reg = bitfield_bit32_write(reg, HMAC_CMD_HASH_START_BIT, true);
mmio_region_write32(hmac_base, HMAC_CMD_REG_OFFSET, reg);
}
hmac_error_t hmac_sha256_update(const void *data, size_t len) {
if (data == NULL) {
return kHmacErrorBadArg;
}
mmio_region_t hmac_base = mmio_region_from_addr(TOP_EARLGREY_HMAC_BASE_ADDR);
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) {
mmio_region_write8(hmac_base, HMAC_MSG_FIFO_REG_OFFSET, *data_sent++);
}
for (; len >= sizeof(uint32_t); len -= sizeof(uint32_t)) {
uint32_t data_aligned = read_32(data_sent);
mmio_region_write32(hmac_base, 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) {
mmio_region_write8(hmac_base, HMAC_MSG_FIFO_REG_OFFSET, *data_sent++);
}
return kHmacOk;
}
hmac_error_t hmac_sha256_final(hmac_digest_t *digest) {
if (digest == NULL) {
return kHmacErrorBadArg;
}
mmio_region_t hmac_base = mmio_region_from_addr(TOP_EARLGREY_HMAC_BASE_ADDR);
uint32_t reg = 0;
reg = bitfield_bit32_write(reg, HMAC_CMD_HASH_PROCESS_BIT, true);
mmio_region_write32(hmac_base, HMAC_CMD_REG_OFFSET, reg);
do {
reg = mmio_region_read32(hmac_base, HMAC_INTR_STATE_REG_OFFSET);
} while (!bitfield_bit32_read(reg, HMAC_INTR_STATE_HMAC_DONE_BIT));
mmio_region_write32(hmac_base, 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] = mmio_region_read32(
hmac_base, HMAC_DIGEST_7_REG_OFFSET - (i * sizeof(uint32_t)));
}
return kHmacOk;
}