Software APIs
hmac.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#include "sw/device/silicon_creator/lib/drivers/hmac.h"
6
7#include "dt/dt_hmac.h"
12#include "sw/device/silicon_creator/lib/error.h"
13
14#include "hmac_regs.h" // Generated.
15
16static const dt_hmac_t kHmacDt = kDtHmac;
17
18static inline uint32_t hmac_base(void) {
19 return dt_hmac_primary_reg_block(kHmacDt);
20}
21
22void hmac_sha256_configure(bool big_endian_digest) {
23 // Clear the config, stopping the SHA engine.
24 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET, 0u);
25
26 // Disable and clear interrupts. INTR_STATE register is rw1c.
27 abs_mmio_write32(hmac_base() + HMAC_INTR_ENABLE_REG_OFFSET, 0u);
28 abs_mmio_write32(hmac_base() + HMAC_INTR_STATE_REG_OFFSET, UINT32_MAX);
29
30 uint32_t reg = 0;
31 reg = bitfield_bit32_write(reg, HMAC_CFG_DIGEST_SWAP_BIT, big_endian_digest);
32 reg = bitfield_bit32_write(reg, HMAC_CFG_ENDIAN_SWAP_BIT, false);
33 reg = bitfield_bit32_write(reg, HMAC_CFG_SHA_EN_BIT, true);
34 reg = bitfield_bit32_write(reg, HMAC_CFG_HMAC_EN_BIT, false);
35 // configure to run SHA-2 256 with 256-bit key
36 reg = bitfield_field32_write(reg, HMAC_CFG_DIGEST_SIZE_FIELD,
37 HMAC_CFG_DIGEST_SIZE_VALUE_SHA2_256);
38 reg = bitfield_field32_write(reg, HMAC_CFG_KEY_LENGTH_FIELD,
39 HMAC_CFG_KEY_LENGTH_VALUE_KEY_256);
40 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET, reg);
41}
42
43inline void hmac_sha256_start(void) {
44 uint32_t cmd = bitfield_bit32_write(0, HMAC_CMD_HASH_START_BIT, true);
45 abs_mmio_write32(hmac_base() + HMAC_CMD_REG_OFFSET, cmd);
46}
47
48void hmac_sha256_update(const void *data, size_t len) {
49 const uint8_t *data_sent = (const uint8_t *)data;
50
51 // Individual byte writes are needed if the buffer isn't word aligned.
52 for (; len != 0 && (uintptr_t)data_sent & 3; --len) {
53 abs_mmio_write8(hmac_base() + HMAC_MSG_FIFO_REG_OFFSET, *data_sent++);
54 }
55
56 for (; len >= sizeof(uint32_t); len -= sizeof(uint32_t)) {
57 uint32_t data_aligned = read_32(data_sent);
58 abs_mmio_write32(hmac_base() + HMAC_MSG_FIFO_REG_OFFSET, data_aligned);
59 data_sent += sizeof(uint32_t);
60 }
61
62 // Handle non-32bit aligned bytes at the end of the buffer.
63 for (; len != 0; --len) {
64 abs_mmio_write8(hmac_base() + HMAC_MSG_FIFO_REG_OFFSET, *data_sent++);
65 }
66}
67
68void hmac_sha256_update_words(const uint32_t *data, size_t len) {
69 for (size_t i = 0; i < len; i++) {
70 abs_mmio_write32(hmac_base() + HMAC_MSG_FIFO_REG_OFFSET, data[i]);
71 }
72}
73
74inline void hmac_sha256_process(void) {
75 uint32_t cmd = bitfield_bit32_write(0, HMAC_CMD_HASH_PROCESS_BIT, true);
76 abs_mmio_write32(hmac_base() + HMAC_CMD_REG_OFFSET, cmd);
77}
78
79/**
80 * Wait for the `hmac_done` interrupt and then clear it.
81 *
82 * Should only be called after a `process` or `stop` command.
83 */
84static void wait_for_done(void) {
85 uint32_t reg = 0;
86 do {
87 reg = abs_mmio_read32(hmac_base() + HMAC_INTR_STATE_REG_OFFSET);
88 } while (!bitfield_bit32_read(reg, HMAC_INTR_STATE_HMAC_DONE_BIT));
89 abs_mmio_write32(hmac_base() + HMAC_INTR_STATE_REG_OFFSET, reg);
90}
91
92void hmac_sha256_final_truncated(uint32_t *digest, size_t len) {
93 wait_for_done();
94
95 uint32_t result, incr;
96 uint32_t reg = abs_mmio_read32(hmac_base() + HMAC_CFG_REG_OFFSET);
97 if (bitfield_bit32_read(reg, HMAC_CFG_DIGEST_SWAP_BIT)) {
98 // Big-endian output.
99 result = HMAC_DIGEST_0_REG_OFFSET;
100 incr = sizeof(uint32_t);
101 } else {
102 // Little-endian output.
103 // Note: we rely on 32-bit integer wraparound to cause the result register
104 // index to count down from 7 to 0.
105 result = HMAC_DIGEST_7_REG_OFFSET;
106 incr = (uint32_t) - sizeof(uint32_t);
107 }
108
109 // Ensure len is at most the digest length; this function should never be
110 // called with a `len` that is too big, but this helps ensure it at runtime
111 // just in case.
112 len = len <= kHmacDigestNumWords ? len : kHmacDigestNumWords;
113 for (uint32_t i = 0; i < len; ++i, result += incr) {
114 digest[i] = abs_mmio_read32(hmac_base() + result);
115 }
116}
117
118void hmac_sha256(const void *data, size_t len, hmac_digest_t *digest) {
119 hmac_sha256_init();
120 hmac_sha256_update(data, len);
121 hmac_sha256_process();
122 hmac_sha256_final(digest);
123}
124
125void hmac_sha256_save(hmac_context_t *ctx) {
126 // Issue the STOP command to halt the operation and compute the intermediate
127 // digest.
128 uint32_t cmd = bitfield_bit32_write(0, HMAC_CMD_HASH_STOP_BIT, true);
129 abs_mmio_write32(hmac_base() + HMAC_CMD_REG_OFFSET, cmd);
130 wait_for_done();
131
132 // Read the digest registers. Note that endianness does not matter here,
133 // because we will simply restore the registers in the same order as we saved
134 // them.
135 for (uint32_t i = 0; i < kHmacDigestNumWords; i++) {
136 ctx->digest[i] = abs_mmio_read32(hmac_base() + HMAC_DIGEST_0_REG_OFFSET +
137 i * sizeof(uint32_t));
138 }
139
140 // Read the message length registers.
141 ctx->msg_len_lower =
142 abs_mmio_read32(hmac_base() + HMAC_MSG_LENGTH_LOWER_REG_OFFSET);
143 ctx->msg_len_upper =
144 abs_mmio_read32(hmac_base() + HMAC_MSG_LENGTH_UPPER_REG_OFFSET);
145
146 // Momentarily clear the `sha_en` bit, which clears the digest.
147 uint32_t cfg = abs_mmio_read32(hmac_base() + HMAC_CFG_REG_OFFSET);
148 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET,
149 bitfield_bit32_write(cfg, HMAC_CFG_SHA_EN_BIT, false));
150
151 // Restore the full original configuration.
152 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET, cfg);
153}
154
155void hmac_sha256_restore(const hmac_context_t *ctx) {
156 // Clear the `sha_en` bit to ensure the message length registers are
157 // writeable. Leave the rest of the configuration unchanged.
158 uint32_t cfg = abs_mmio_read32(hmac_base() + HMAC_CFG_REG_OFFSET);
159 cfg = bitfield_bit32_write(cfg, HMAC_CFG_SHA_EN_BIT, false);
160 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET, cfg);
161
162 // Write the digest registers. Note that endianness does not matter here,
163 // because we will simply restore the registers in the same order as we saved
164 // them.
165 for (uint32_t i = 0; i < kHmacDigestNumWords; i++) {
166 abs_mmio_write32(
167 hmac_base() + HMAC_DIGEST_0_REG_OFFSET + i * sizeof(uint32_t),
168 ctx->digest[i]);
169 }
170
171 // Write the message length registers.
172 abs_mmio_write32(hmac_base() + HMAC_MSG_LENGTH_LOWER_REG_OFFSET,
173 ctx->msg_len_lower);
174 abs_mmio_write32(hmac_base() + HMAC_MSG_LENGTH_UPPER_REG_OFFSET,
175 ctx->msg_len_upper);
176
177 // Re-enable the SHA engine.
178 cfg = bitfield_bit32_write(cfg, HMAC_CFG_SHA_EN_BIT, true);
179 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET, cfg);
180
181 // Issue the CONTINUE command to restart the operation.
182 uint32_t cmd = bitfield_bit32_write(0, HMAC_CMD_HASH_CONTINUE_BIT, true);
183 abs_mmio_write32(hmac_base() + HMAC_CMD_REG_OFFSET, cmd);
184}
185
186extern void hmac_sha256_init(void);
187extern void hmac_sha256_final(hmac_digest_t *digest);