Software APIs
dif_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
6
10
11#include "hw/top/hmac_regs.h" // Generated.
12
13/**
14 * Read the status register from `hmac`.
15 *
16 * @param hmac The HMAC device to read the status register from.
17 * @return The contents of hmac.STATUS.
18 */
19static uint32_t get_status(const dif_hmac_t *hmac) {
20 return mmio_region_read32(hmac->base_addr, HMAC_STATUS_REG_OFFSET);
21}
22
23/**
24 * Returns the number of entries in the FIFO of `hmac`. If the FIFO is empty,
25 * this function will return 0, and if the FIFO is full, this funciton will
26 * return `HMAC_FIFO_MAX`.
27 *
28 * @param hmac The HMAC device to check the FIFO size of.
29 * @return The number of entries in the HMAC FIFO.
30 */
31static uint32_t get_fifo_entry_count(const dif_hmac_t *hmac) {
32 return bitfield_field32_read(get_status(hmac), HMAC_STATUS_FIFO_DEPTH_FIELD);
33}
34
35/**
36 * A helper function for calculating `HMAC_FIFO_MAX` - `get_fifo_entry_count()`.
37 */
38static uint32_t get_fifo_available_space(const dif_hmac_t *hmac) {
39 return HMAC_MSG_FIFO_SIZE_WORDS - get_fifo_entry_count(hmac);
40}
41
42/**
43 * Sets up the CFG value for a given per-transaction configuration.
44 *
45 * This only sets the right values for the ENDIAN_SWAP / DIGEST_SWAP values,
46 * using the values in `config`.
47 *
48 * The implementation here is careful to only update `*device_config` once it
49 * has calculated the entire value for the register, rather than gradually
50 * updating it early. The value of `*device_config` is only updated if the
51 * function returns #kDifOk.
52 *
53 * @param[inout] device_config HMAC CFG register value to be updated;
54 * @param config A per-transaction configuration.
55 * @returns #kDifError if the config is invalid, #kDifOk if
56 * `*device_config` was sucessfully updated.
57 */
58static dif_result_t dif_hmac_calculate_device_config_value(
59 uint32_t *device_config, const dif_hmac_transaction_t config) {
60 // Set the byte-order of the input message.
61 bool swap_message_endianness;
62 switch (config.message_endianness) {
64 swap_message_endianness = true;
65 break;
67 swap_message_endianness = false;
68 break;
69 default:
70 return kDifError;
71 }
72
73 // Set the byte-order of the digest.
74 bool swap_digest_endianness;
75 switch (config.digest_endianness) {
77 swap_digest_endianness = true;
78 break;
80 swap_digest_endianness = false;
81 break;
82 default:
83 return kDifError;
84 }
85
86 // `*device_config` must only be updated after the two switch statements,
87 // because they can return #kDifError.
88 *device_config = bitfield_bit32_write(
89 *device_config, HMAC_CFG_ENDIAN_SWAP_BIT, swap_message_endianness);
90 *device_config = bitfield_bit32_write(
91 *device_config, HMAC_CFG_DIGEST_SWAP_BIT, swap_digest_endianness);
92 *device_config = bitfield_bit32_write(*device_config, HMAC_CFG_SIDELOAD_BIT,
93 config.sideload);
94
95 return kDifOk;
96}
97
98dif_result_t dif_hmac_mode_hmac_start(const dif_hmac_t *hmac,
99 const uint8_t *key,
100 const dif_hmac_transaction_t config) {
101 if (hmac == NULL) {
102 return kDifBadArg;
103 }
104
105 // Read current CFG register value.
106 uint32_t reg = mmio_region_read32(hmac->base_addr, HMAC_CFG_REG_OFFSET);
107
108 // Set the byte-order of the input message and the digest.
109 DIF_RETURN_IF_ERROR(dif_hmac_calculate_device_config_value(&reg, config));
110
111 if (key != NULL && !config.sideload) {
112 // Set the HMAC key.
113 // The least significant word is at HMAC_KEY_7_REG_OFFSET.
114 // From the HWIP spec: "Order of the secret key is: key[255:0] = {KEY0,
115 // KEY1, KEY2, ... , KEY7};"
116 for (size_t i = 0; i < 8; ++i) {
117 const ptrdiff_t word_offset = (ptrdiff_t)(i * sizeof(uint32_t));
118 mmio_region_write32(hmac->base_addr, HMAC_KEY_7_REG_OFFSET - word_offset,
119 read_32((char *)key + word_offset));
120 }
121 }
122
123 // Set HMAC to process in HMAC mode (not SHA256-only mode).
124 reg = bitfield_bit32_write(reg, HMAC_CFG_SHA_EN_BIT, true);
125 reg = bitfield_bit32_write(reg, HMAC_CFG_HMAC_EN_BIT, true);
126
127 // Set digest size to SHA-2 256 and 256-bit key
128 reg = bitfield_field32_write(reg, HMAC_CFG_DIGEST_SIZE_FIELD,
129 HMAC_CFG_DIGEST_SIZE_VALUE_SHA2_256);
130 reg = bitfield_field32_write(reg, HMAC_CFG_KEY_LENGTH_FIELD,
131 HMAC_CFG_KEY_LENGTH_VALUE_KEY_256);
132
133 mmio_region_write32(hmac->base_addr, HMAC_CFG_REG_OFFSET, reg);
134
135 // Begin HMAC operation.
136 mmio_region_nonatomic_set_bit32(hmac->base_addr, HMAC_CMD_REG_OFFSET,
137 HMAC_CMD_HASH_START_BIT);
138 return kDifOk;
139}
140
141dif_result_t dif_hmac_mode_sha256_start(const dif_hmac_t *hmac,
142 const dif_hmac_transaction_t config) {
143 if (hmac == NULL) {
144 return kDifBadArg;
145 }
146
147 // Read current CFG register value.
148 uint32_t reg = mmio_region_read32(hmac->base_addr, HMAC_CFG_REG_OFFSET);
149
150 // Set the byte-order of the input message and the digest.
151 DIF_RETURN_IF_ERROR(dif_hmac_calculate_device_config_value(&reg, config));
152
153 // Set HMAC to process in SHA256-only mode (without HMAC mode).
154 reg = bitfield_bit32_write(reg, HMAC_CFG_SHA_EN_BIT, true);
155 reg = bitfield_bit32_write(reg, HMAC_CFG_HMAC_EN_BIT, false);
156
157 // Set digest size to SHA-2 256 and 256-bit key
158 reg = bitfield_field32_write(reg, HMAC_CFG_DIGEST_SIZE_FIELD,
159 HMAC_CFG_DIGEST_SIZE_VALUE_SHA2_256);
160 reg = bitfield_field32_write(reg, HMAC_CFG_KEY_LENGTH_FIELD,
161 HMAC_CFG_KEY_LENGTH_VALUE_KEY_256);
162
163 // Write new CFG register value.
164 mmio_region_write32(hmac->base_addr, HMAC_CFG_REG_OFFSET, reg);
165
166 // Begin SHA256-only operation.
167 mmio_region_nonatomic_set_bit32(hmac->base_addr, HMAC_CMD_REG_OFFSET,
168 HMAC_CMD_HASH_START_BIT);
169
170 return kDifOk;
171}
172
173dif_result_t dif_hmac_fifo_push(const dif_hmac_t *hmac, const void *data,
174 size_t len, size_t *bytes_sent) {
175 if (hmac == NULL || data == NULL) {
176 return kDifBadArg;
177 }
178
179 const uint8_t *data_sent = (const uint8_t *)data;
180 size_t bytes_remaining = len;
181
182 while (bytes_remaining > 0 && get_fifo_available_space(hmac) > 0) {
183 bool word_aligned = (uintptr_t)data_sent % sizeof(uint32_t) == 0;
184 size_t bytes_written = 0;
185
186 if (bytes_remaining < sizeof(uint32_t) || !word_aligned) {
187 // Individual byte writes are needed if the buffer isn't aligned or
188 // there are no more full words to write.
189 mmio_region_write8(hmac->base_addr, HMAC_MSG_FIFO_REG_OFFSET, *data_sent);
190 bytes_written = 1;
191 } else {
192 // `data_sent` is word-aligned and there are still words to write.
193 uint32_t word = read_32(data_sent);
194 mmio_region_write32(hmac->base_addr, HMAC_MSG_FIFO_REG_OFFSET, word);
195 bytes_written = sizeof(uint32_t);
196 }
197
198 bytes_remaining -= bytes_written;
199 data_sent += bytes_written;
200 }
201
202 if (bytes_sent != NULL) {
203 *bytes_sent = len - bytes_remaining;
204 }
205
206 if (bytes_remaining > 0) {
207 return kDifIpFifoFull;
208 }
209
210 return kDifOk;
211}
212
213dif_result_t dif_hmac_fifo_count_entries(const dif_hmac_t *hmac,
214 uint32_t *num_entries) {
215 if (hmac == NULL || num_entries == NULL) {
216 return kDifBadArg;
217 }
218
219 *num_entries = get_fifo_entry_count(hmac);
220
221 return kDifOk;
222}
223
224dif_result_t dif_hmac_get_message_length(const dif_hmac_t *hmac,
225 uint64_t *msg_len) {
226 if (hmac == NULL || msg_len == NULL) {
227 return kDifBadArg;
228 }
229 uint64_t msg_lower =
230 mmio_region_read32(hmac->base_addr, HMAC_MSG_LENGTH_LOWER_REG_OFFSET);
231 uint64_t msg_upper =
232 mmio_region_read32(hmac->base_addr, HMAC_MSG_LENGTH_UPPER_REG_OFFSET);
233
234 *msg_len = (msg_upper << 32) | msg_lower;
235
236 return kDifOk;
237}
238
239dif_result_t dif_hmac_process(const dif_hmac_t *hmac) {
240 if (hmac == NULL) {
241 return kDifBadArg;
242 }
243
244 mmio_region_nonatomic_set_bit32(hmac->base_addr, HMAC_CMD_REG_OFFSET,
245 HMAC_CMD_HASH_PROCESS_BIT);
246 return kDifOk;
247}
248
249static void read_digest(const dif_hmac_t *hmac, dif_hmac_digest_t *digest) {
250 // Read the digest in reverse to preserve the numerical value.
251 // The least significant word is at HMAC_DIGEST_7_REG_OFFSET.
252 // From the HWIP spec: "Order of the digest is: digest[255:0] = {DIGEST0,
253 // DIGEST1, DIGEST2, ... , DIGEST7};"
254 for (size_t i = 0; i < ARRAYSIZE(digest->digest); ++i) {
255 digest->digest[i] = mmio_region_read32(
256 hmac->base_addr,
257 HMAC_DIGEST_7_REG_OFFSET - (ptrdiff_t)(i * sizeof(uint32_t)));
258 }
259}
260
261dif_result_t dif_hmac_finish(const dif_hmac_t *hmac, bool disable_after_done,
262 dif_hmac_digest_t *digest) {
263 if (hmac == NULL || digest == NULL) {
264 return kDifBadArg;
265 }
266
267 // Check if hmac_done is asserted.
268 bool done = mmio_region_get_bit32(hmac->base_addr, HMAC_INTR_STATE_REG_OFFSET,
269 HMAC_INTR_STATE_HMAC_DONE_BIT);
270
271 // Check if fifo_empty is asserted.
272 bool fifo_empty = mmio_region_get_bit32(
273 hmac->base_addr, HMAC_STATUS_REG_OFFSET, HMAC_STATUS_FIFO_EMPTY_BIT);
274
275 bool hmac_error =
276 mmio_region_get_bit32(hmac->base_addr, HMAC_INTR_STATE_REG_OFFSET,
277 HMAC_INTR_STATE_HMAC_ERR_BIT);
278
279 if (hmac_error) {
280 // Detected error.
281 return kDifError;
282 }
283
284 if (done) {
285 // Clear hmac_done.
286 mmio_region_nonatomic_set_bit32(hmac->base_addr, HMAC_INTR_STATE_REG_OFFSET,
287 HMAC_INTR_STATE_HMAC_DONE_BIT);
288 } else if (!fifo_empty) {
289 return kDifUnavailable;
290 }
291
292 read_digest(hmac, digest);
293
294 if (disable_after_done) {
295 // Disable HMAC and SHA256 until the next transaction, clearing the
296 // current digest.
297 uint32_t device_config =
298 mmio_region_read32(hmac->base_addr, HMAC_CFG_REG_OFFSET);
299 device_config =
300 bitfield_bit32_write(device_config, HMAC_CFG_SHA_EN_BIT, false);
301 device_config =
302 bitfield_bit32_write(device_config, HMAC_CFG_HMAC_EN_BIT, false);
303 device_config =
304 bitfield_field32_write(device_config, HMAC_CFG_DIGEST_SIZE_FIELD,
305 HMAC_CFG_DIGEST_SIZE_VALUE_SHA2_NONE);
306 device_config =
307 bitfield_field32_write(device_config, HMAC_CFG_KEY_LENGTH_FIELD,
308 HMAC_CFG_KEY_LENGTH_VALUE_KEY_256);
309
310 mmio_region_write32(hmac->base_addr, HMAC_CFG_REG_OFFSET, device_config);
311 }
312
313 return kDifOk;
314}
315
316dif_result_t dif_hmac_wipe_secret(const dif_hmac_t *hmac, uint32_t entropy,
317 dif_hmac_digest_t *digest) {
318 if (hmac == NULL || digest == NULL) {
319 return kDifBadArg;
320 }
321 mmio_region_write32(hmac->base_addr, HMAC_WIPE_SECRET_REG_OFFSET, entropy);
322 read_digest(hmac, digest);
323 return kDifOk;
324}