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 "hw/top/dt/hmac.h"
12#include "sw/device/silicon_creator/lib/error.h"
13
14#include "hw/top/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
22static void hmac_configure(bool big_endian_digest, bool hmac_mode) {
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, hmac_mode);
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
43void sc_hmac_hmac_sha256_configure(bool big_endian_digest, hmac_key_t key) {
44 hmac_configure(big_endian_digest, /*hmac_mode=*/true);
45 for (size_t i = 0; i < kHmacKeyNumWords; ++i) {
46 abs_mmio_write32(hmac_base() + HMAC_KEY_0_REG_OFFSET + i * sizeof(uint32_t),
47 key.key[i]);
48 }
49}
50
51void hmac_sha256_configure(bool big_endian_digest) {
52 hmac_configure(big_endian_digest, /*hmac_mode=*/false);
53}
54
55inline void hmac_sha256_start(void) {
56 uint32_t cmd = bitfield_bit32_write(0, HMAC_CMD_HASH_START_BIT, true);
57 abs_mmio_write32(hmac_base() + HMAC_CMD_REG_OFFSET, cmd);
58}
59
60void hmac_sha256_update(const void *data, size_t len) {
61 const uint8_t *data_sent = (const uint8_t *)data;
62
63 // Individual byte writes are needed if the buffer isn't word aligned.
64 for (; len != 0 && (uintptr_t)data_sent & 3; --len) {
65 abs_mmio_write8(hmac_base() + HMAC_MSG_FIFO_REG_OFFSET, *data_sent++);
66 }
67
68 for (; len >= sizeof(uint32_t); len -= sizeof(uint32_t)) {
69 uint32_t data_aligned = read_32(data_sent);
70 abs_mmio_write32(hmac_base() + HMAC_MSG_FIFO_REG_OFFSET, data_aligned);
71 data_sent += sizeof(uint32_t);
72 }
73
74 // Handle non-32bit aligned bytes at the end of the buffer.
75 for (; len != 0; --len) {
76 abs_mmio_write8(hmac_base() + HMAC_MSG_FIFO_REG_OFFSET, *data_sent++);
77 }
78}
79
80void hmac_sha256_update_words(const uint32_t *data, size_t len) {
81 for (size_t i = 0; i < len; i++) {
82 abs_mmio_write32(hmac_base() + HMAC_MSG_FIFO_REG_OFFSET, data[i]);
83 }
84}
85
86inline void hmac_sha256_process(void) {
87 uint32_t cmd = bitfield_bit32_write(0, HMAC_CMD_HASH_PROCESS_BIT, true);
88 abs_mmio_write32(hmac_base() + HMAC_CMD_REG_OFFSET, cmd);
89}
90
91/**
92 * Wait for the `hmac_done` interrupt and then clear it.
93 *
94 * Should only be called after a `process` or `stop` command.
95 */
96static void wait_for_done(void) {
97 uint32_t reg = 0;
98 do {
99 reg = abs_mmio_read32(hmac_base() + HMAC_INTR_STATE_REG_OFFSET);
100 } while (!bitfield_bit32_read(reg, HMAC_INTR_STATE_HMAC_DONE_BIT));
101 abs_mmio_write32(hmac_base() + HMAC_INTR_STATE_REG_OFFSET, reg);
102}
103
104void hmac_sha256_final_truncated(uint32_t *digest, size_t len) {
105 wait_for_done();
106
107 uint32_t result, incr;
108 uint32_t reg = abs_mmio_read32(hmac_base() + HMAC_CFG_REG_OFFSET);
109 if (bitfield_bit32_read(reg, HMAC_CFG_DIGEST_SWAP_BIT)) {
110 // Big-endian output.
111 result = HMAC_DIGEST_0_REG_OFFSET;
112 incr = sizeof(uint32_t);
113 } else {
114 // Little-endian output.
115 // Note: we rely on 32-bit integer wraparound to cause the result register
116 // index to count down from 7 to 0.
117 result = HMAC_DIGEST_7_REG_OFFSET;
118 incr = (uint32_t) - sizeof(uint32_t);
119 }
120
121 // Ensure len is at most the digest length; this function should never be
122 // called with a `len` that is too big, but this helps ensure it at runtime
123 // just in case.
124 len = len <= kHmacDigestNumWords ? len : kHmacDigestNumWords;
125 for (uint32_t i = 0; i < len; ++i, result += incr) {
126 digest[i] = abs_mmio_read32(hmac_base() + result);
127 }
128}
129
130void hmac_sha256(const void *data, size_t len, hmac_digest_t *digest) {
131 hmac_sha256_init();
132 hmac_sha256_update(data, len);
133 hmac_sha256_process();
134 hmac_sha256_final(digest);
135}
136
137void sc_hmac_hmac_sha256(const void *data, size_t len, hmac_key_t key,
138 bool big_endian_digest, hmac_digest_t *digest) {
139 sc_hmac_hmac_sha256_init(key, big_endian_digest);
140 hmac_sha256_update(data, len);
141 hmac_sha256_process();
142 hmac_sha256_final(digest);
143}
144
145void hmac_sha256_save(hmac_context_t *ctx) {
146 // Issue the STOP command to halt the operation and compute the intermediate
147 // digest.
148 uint32_t cmd = bitfield_bit32_write(0, HMAC_CMD_HASH_STOP_BIT, true);
149 abs_mmio_write32(hmac_base() + HMAC_CMD_REG_OFFSET, cmd);
150 wait_for_done();
151
152 // Read the digest registers. Note that endianness does not matter here,
153 // because we will simply restore the registers in the same order as we saved
154 // them.
155 for (uint32_t i = 0; i < kHmacDigestNumWords; i++) {
156 ctx->digest[i] = abs_mmio_read32(hmac_base() + HMAC_DIGEST_0_REG_OFFSET +
157 i * sizeof(uint32_t));
158 }
159
160 // Read the message length registers.
161 ctx->msg_len_lower =
162 abs_mmio_read32(hmac_base() + HMAC_MSG_LENGTH_LOWER_REG_OFFSET);
163 ctx->msg_len_upper =
164 abs_mmio_read32(hmac_base() + HMAC_MSG_LENGTH_UPPER_REG_OFFSET);
165
166 // Momentarily clear the `sha_en` bit, which clears the digest.
167 uint32_t cfg = abs_mmio_read32(hmac_base() + HMAC_CFG_REG_OFFSET);
168 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET,
169 bitfield_bit32_write(cfg, HMAC_CFG_SHA_EN_BIT, false));
170
171 // Restore the full original configuration.
172 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET, cfg);
173}
174
175void hmac_sha256_restore(const hmac_context_t *ctx) {
176 // Clear the `sha_en` bit to ensure the message length registers are
177 // writeable. Leave the rest of the configuration unchanged.
178 uint32_t cfg = abs_mmio_read32(hmac_base() + HMAC_CFG_REG_OFFSET);
179 cfg = bitfield_bit32_write(cfg, HMAC_CFG_SHA_EN_BIT, false);
180 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET, cfg);
181
182 // Write the digest registers. Note that endianness does not matter here,
183 // because we will simply restore the registers in the same order as we saved
184 // them.
185 for (uint32_t i = 0; i < kHmacDigestNumWords; i++) {
186 abs_mmio_write32(
187 hmac_base() + HMAC_DIGEST_0_REG_OFFSET + i * sizeof(uint32_t),
188 ctx->digest[i]);
189 }
190
191 // Write the message length registers.
192 abs_mmio_write32(hmac_base() + HMAC_MSG_LENGTH_LOWER_REG_OFFSET,
193 ctx->msg_len_lower);
194 abs_mmio_write32(hmac_base() + HMAC_MSG_LENGTH_UPPER_REG_OFFSET,
195 ctx->msg_len_upper);
196
197 // Re-enable the SHA engine.
198 cfg = bitfield_bit32_write(cfg, HMAC_CFG_SHA_EN_BIT, true);
199 abs_mmio_write32(hmac_base() + HMAC_CFG_REG_OFFSET, cfg);
200
201 // Issue the CONTINUE command to restart the operation.
202 uint32_t cmd = bitfield_bit32_write(0, HMAC_CMD_HASH_CONTINUE_BIT, true);
203 abs_mmio_write32(hmac_base() + HMAC_CMD_REG_OFFSET, cmd);
204}
205
206extern void sc_hmac_hmac_sha256_init(hmac_key_t key, bool big_endian_digest);
207extern void hmac_sha256_init(void);
208extern void hmac_sha256_final(hmac_digest_t *digest);