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 "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  */
19 static 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  */
31 static 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  */
38 static 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  */
58 static 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 
93  return kDifOk;
94 }
95 
97  const uint8_t *key,
98  const dif_hmac_transaction_t config) {
99  if (hmac == NULL) {
100  return kDifBadArg;
101  }
102 
103  // Read current CFG register value.
104  uint32_t reg = mmio_region_read32(hmac->base_addr, HMAC_CFG_REG_OFFSET);
105 
106  // Set the byte-order of the input message and the digest.
107  DIF_RETURN_IF_ERROR(dif_hmac_calculate_device_config_value(&reg, config));
108 
109  if (key != NULL) {
110  // Set the HMAC key.
111  // The least significant word is at HMAC_KEY_7_REG_OFFSET.
112  // From the HWIP spec: "Order of the secret key is: key[255:0] = {KEY0,
113  // KEY1, KEY2, ... , KEY7};"
114  for (size_t i = 0; i < 8; ++i) {
115  const ptrdiff_t word_offset = (ptrdiff_t)(i * sizeof(uint32_t));
116  mmio_region_write32(hmac->base_addr, HMAC_KEY_7_REG_OFFSET - word_offset,
117  read_32((char *)key + word_offset));
118  }
119  }
120 
121  // Set HMAC to process in HMAC mode (not SHA256-only mode).
122  reg = bitfield_bit32_write(reg, HMAC_CFG_SHA_EN_BIT, true);
123  reg = bitfield_bit32_write(reg, HMAC_CFG_HMAC_EN_BIT, true);
124 
125  // Set digest size to SHA-2 256 and 256-bit key
126  reg = bitfield_field32_write(reg, HMAC_CFG_DIGEST_SIZE_FIELD,
127  HMAC_CFG_DIGEST_SIZE_VALUE_SHA2_256);
128  reg = bitfield_field32_write(reg, HMAC_CFG_KEY_LENGTH_FIELD,
129  HMAC_CFG_KEY_LENGTH_VALUE_KEY_256);
130 
131  mmio_region_write32(hmac->base_addr, HMAC_CFG_REG_OFFSET, reg);
132 
133  // Begin HMAC operation.
134  mmio_region_nonatomic_set_bit32(hmac->base_addr, HMAC_CMD_REG_OFFSET,
135  HMAC_CMD_HASH_START_BIT);
136  return kDifOk;
137 }
138 
140  const dif_hmac_transaction_t config) {
141  if (hmac == NULL) {
142  return kDifBadArg;
143  }
144 
145  // Read current CFG register value.
146  uint32_t reg = mmio_region_read32(hmac->base_addr, HMAC_CFG_REG_OFFSET);
147 
148  // Set the byte-order of the input message and the digest.
149  DIF_RETURN_IF_ERROR(dif_hmac_calculate_device_config_value(&reg, config));
150 
151  // Set HMAC to process in SHA256-only mode (without HMAC mode).
152  reg = bitfield_bit32_write(reg, HMAC_CFG_SHA_EN_BIT, true);
153  reg = bitfield_bit32_write(reg, HMAC_CFG_HMAC_EN_BIT, false);
154 
155  // Set digest size to SHA-2 256 and 256-bit key
156  reg = bitfield_field32_write(reg, HMAC_CFG_DIGEST_SIZE_FIELD,
157  HMAC_CFG_DIGEST_SIZE_VALUE_SHA2_256);
158  reg = bitfield_field32_write(reg, HMAC_CFG_KEY_LENGTH_FIELD,
159  HMAC_CFG_KEY_LENGTH_VALUE_KEY_256);
160 
161  // Write new CFG register value.
162  mmio_region_write32(hmac->base_addr, HMAC_CFG_REG_OFFSET, reg);
163 
164  // Begin SHA256-only operation.
165  mmio_region_nonatomic_set_bit32(hmac->base_addr, HMAC_CMD_REG_OFFSET,
166  HMAC_CMD_HASH_START_BIT);
167 
168  return kDifOk;
169 }
170 
171 dif_result_t dif_hmac_fifo_push(const dif_hmac_t *hmac, const void *data,
172  size_t len, size_t *bytes_sent) {
173  if (hmac == NULL || data == NULL) {
174  return kDifBadArg;
175  }
176 
177  const uint8_t *data_sent = (const uint8_t *)data;
178  size_t bytes_remaining = len;
179 
180  while (bytes_remaining > 0 && get_fifo_available_space(hmac) > 0) {
181  bool word_aligned = (uintptr_t)data_sent % sizeof(uint32_t) == 0;
182  size_t bytes_written = 0;
183 
184  if (bytes_remaining < sizeof(uint32_t) || !word_aligned) {
185  // Individual byte writes are needed if the buffer isn't aligned or
186  // there are no more full words to write.
187  mmio_region_write8(hmac->base_addr, HMAC_MSG_FIFO_REG_OFFSET, *data_sent);
188  bytes_written = 1;
189  } else {
190  // `data_sent` is word-aligned and there are still words to write.
191  uint32_t word = read_32(data_sent);
192  mmio_region_write32(hmac->base_addr, HMAC_MSG_FIFO_REG_OFFSET, word);
193  bytes_written = sizeof(uint32_t);
194  }
195 
196  bytes_remaining -= bytes_written;
197  data_sent += bytes_written;
198  }
199 
200  if (bytes_sent != NULL) {
201  *bytes_sent = len - bytes_remaining;
202  }
203 
204  if (bytes_remaining > 0) {
205  return kDifIpFifoFull;
206  }
207 
208  return kDifOk;
209 }
210 
212  uint32_t *num_entries) {
213  if (hmac == NULL || num_entries == NULL) {
214  return kDifBadArg;
215  }
216 
217  *num_entries = get_fifo_entry_count(hmac);
218 
219  return kDifOk;
220 }
221 
223  uint64_t *msg_len) {
224  if (hmac == NULL || msg_len == NULL) {
225  return kDifBadArg;
226  }
227  uint64_t msg_lower =
228  mmio_region_read32(hmac->base_addr, HMAC_MSG_LENGTH_LOWER_REG_OFFSET);
229  uint64_t msg_upper =
230  mmio_region_read32(hmac->base_addr, HMAC_MSG_LENGTH_UPPER_REG_OFFSET);
231 
232  *msg_len = (msg_upper << 32) | msg_lower;
233 
234  return kDifOk;
235 }
236 
238  if (hmac == NULL) {
239  return kDifBadArg;
240  }
241 
242  mmio_region_nonatomic_set_bit32(hmac->base_addr, HMAC_CMD_REG_OFFSET,
243  HMAC_CMD_HASH_PROCESS_BIT);
244  return kDifOk;
245 }
246 
247 static void read_digest(const dif_hmac_t *hmac, dif_hmac_digest_t *digest) {
248  // Read the digest in reverse to preserve the numerical value.
249  // The least significant word is at HMAC_DIGEST_7_REG_OFFSET.
250  // From the HWIP spec: "Order of the digest is: digest[255:0] = {DIGEST0,
251  // DIGEST1, DIGEST2, ... , DIGEST7};"
252  for (size_t i = 0; i < ARRAYSIZE(digest->digest); ++i) {
253  digest->digest[i] = mmio_region_read32(
254  hmac->base_addr,
255  HMAC_DIGEST_7_REG_OFFSET - (ptrdiff_t)(i * sizeof(uint32_t)));
256  }
257 }
258 
259 dif_result_t dif_hmac_finish(const dif_hmac_t *hmac, bool disable_after_done,
260  dif_hmac_digest_t *digest) {
261  if (hmac == NULL || digest == NULL) {
262  return kDifBadArg;
263  }
264 
265  // Check if hmac_done is asserted.
266  bool done = mmio_region_get_bit32(hmac->base_addr, HMAC_INTR_STATE_REG_OFFSET,
267  HMAC_INTR_STATE_HMAC_DONE_BIT);
268 
269  // Check if fifo_empty is asserted.
270  bool fifo_empty = mmio_region_get_bit32(
271  hmac->base_addr, HMAC_STATUS_REG_OFFSET, HMAC_STATUS_FIFO_EMPTY_BIT);
272 
273  bool hmac_error =
274  mmio_region_get_bit32(hmac->base_addr, HMAC_INTR_STATE_REG_OFFSET,
275  HMAC_INTR_STATE_HMAC_ERR_BIT);
276 
277  if (hmac_error) {
278  // Detected error.
279  return kDifError;
280  }
281 
282  if (done) {
283  // Clear hmac_done.
284  mmio_region_nonatomic_set_bit32(hmac->base_addr, HMAC_INTR_STATE_REG_OFFSET,
285  HMAC_INTR_STATE_HMAC_DONE_BIT);
286  } else if (!fifo_empty) {
287  return kDifUnavailable;
288  }
289 
290  read_digest(hmac, digest);
291 
292  if (disable_after_done) {
293  // Disable HMAC and SHA256 until the next transaction, clearing the
294  // current digest.
295  uint32_t device_config =
296  mmio_region_read32(hmac->base_addr, HMAC_CFG_REG_OFFSET);
297  device_config =
298  bitfield_bit32_write(device_config, HMAC_CFG_SHA_EN_BIT, false);
299  device_config =
300  bitfield_bit32_write(device_config, HMAC_CFG_HMAC_EN_BIT, false);
301  device_config =
302  bitfield_field32_write(device_config, HMAC_CFG_DIGEST_SIZE_FIELD,
303  HMAC_CFG_DIGEST_SIZE_VALUE_SHA2_NONE);
304  device_config =
305  bitfield_field32_write(device_config, HMAC_CFG_KEY_LENGTH_FIELD,
306  HMAC_CFG_KEY_LENGTH_VALUE_KEY_256);
307 
308  mmio_region_write32(hmac->base_addr, HMAC_CFG_REG_OFFSET, device_config);
309  }
310 
311  return kDifOk;
312 }
313 
314 dif_result_t dif_hmac_wipe_secret(const dif_hmac_t *hmac, uint32_t entropy,
315  dif_hmac_digest_t *digest) {
316  if (hmac == NULL || digest == NULL) {
317  return kDifBadArg;
318  }
319  mmio_region_write32(hmac->base_addr, HMAC_WIPE_SECRET_REG_OFFSET, entropy);
320  read_digest(hmac, digest);
321  return kDifOk;
322 }