Software APIs
otbn.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/lib/crypto/drivers/otbn.h"
6 
11 #include "sw/device/lib/base/status.h"
12 #include "sw/device/lib/crypto/drivers/entropy.h"
13 #include "sw/device/lib/crypto/impl/status.h"
14 
16 #include "otbn_regs.h" // Generated.
17 
18 // Module ID for status codes.
19 #define MODULE_ID MAKE_MODULE_ID('d', 'b', 'n')
20 
21 enum {
22  /**
23  * Base address for OTBN.
24  */
26  /**
27  * DMEM size in bytes.
28  */
29  kOtbnDMemSizeBytes = OTBN_DMEM_SIZE_BYTES,
30  /**
31  * IMEM size in bytes.
32  */
33  kOtbnIMemSizeBytes = OTBN_IMEM_SIZE_BYTES,
34  /**
35  * ERR_BITS register value in the case of no errors.
36  *
37  * Although some parts of the ERR_BITS register are marked reserved, the OTBN
38  * documentation explicitly guarantees that ERR_BITS is zero for a successful
39  * execution:
40  * https://opentitan.org/book/hw/ip/otbn/doc/theory_of_operation.html#software-execution-design-details
41  */
42  kOtbnErrBitsNoError = 0,
43 };
44 
45 /**
46  * OTBN commands
47  */
48 typedef enum otbn_cmd {
49  kOtbnCmdExecute = 0xd8,
50  kOtbnCmdSecWipeDmem = 0xc3,
51  kOtbnCmdSecWipeImem = 0x1e,
52 } otbn_cmd_t;
53 
54 /**
55  * OTBN status
56  */
57 typedef enum otbn_status {
58  kOtbnStatusIdle = 0x00,
59  kOtbnStatusBusyExecute = 0x01,
60  kOtbnStatusBusySecWipeDmem = 0x02,
61  kOtbnStatusBusySecWipeImem = 0x03,
62  kOtbnStatusBusySecWipeInt = 0x04,
63  kOtbnStatusLocked = 0xFF,
64 } otbn_status_t;
65 
66 /**
67  * Ensures that a memory access fits within the given memory size.
68  *
69  * @param offset_bytes Memory offset at which to start.
70  * @param num_words Number of 32b words to access.
71  * @param mem_size Size of memory.
72  * @return Result of the operation.
73  */
74 static status_t check_offset_len(uint32_t offset_bytes, size_t num_words,
75  size_t mem_size) {
76  if (num_words > UINT32_MAX / sizeof(uint32_t)) {
77  return OTCRYPTO_BAD_ARGS;
78  }
79  uint32_t num_bytes = num_words * sizeof(uint32_t);
80 
81  if (offset_bytes > UINT32_MAX - num_bytes) {
82  return OTCRYPTO_BAD_ARGS;
83  }
84  uint32_t adjusted_offset_bytes = offset_bytes + num_bytes;
85 
86  if (adjusted_offset_bytes > mem_size) {
87  return OTCRYPTO_BAD_ARGS;
88  }
89 
90  return OTCRYPTO_OK;
91 }
92 
93 /**
94  * Ensures OTBN is idle.
95  *
96  * If OTBN is busy or locked, this function will return
97  * `OTCRYPTO_ASYNC_INCOMPLETE`; otherwise it will return `OTCRYPTO_OK`.
98  *
99  * @return Result of the operation.
100  */
101 static status_t otbn_assert_idle(void) {
102  uint32_t status = launder32(~(uint32_t)kOtbnStatusIdle);
103  status_t res = (status_t){
104  .value = (int32_t)launder32((uint32_t)OTCRYPTO_OK.value ^ status)};
105  status = abs_mmio_read32(kBase + OTBN_STATUS_REG_OFFSET);
106  res.value ^= ~status;
107  if (launder32(OT_UNSIGNED(res.value)) == kHardenedBoolTrue) {
109  HARDENED_CHECK_EQ(abs_mmio_read32(kBase + OTBN_STATUS_REG_OFFSET),
110  kOtbnStatusIdle);
111  return res;
112  }
113  return OTCRYPTO_ASYNC_INCOMPLETE;
114 }
115 
116 /**
117  * Helper function for writing to OTBN's DMEM or IMEM.
118  *
119  * @param dest_addr Destination address.
120  * @param src Source buffer.
121  * @param num_words Number of words to copy.
122  */
123 static void otbn_write(uint32_t dest_addr, const uint32_t *src,
124  size_t num_words) {
125  // TODO: replace 0 with a random index like the silicon_creator driver
126  // (requires an interface to Ibex's RND valid bit and data register).
127  size_t i = ((uint64_t)0 * (uint64_t)num_words) >> 32;
128  enum { kStep = 1 };
129  size_t iter_cnt = 0;
130  for (; launder32(iter_cnt) < num_words; ++iter_cnt) {
131  abs_mmio_write32(dest_addr + i * sizeof(uint32_t), src[i]);
132  i += kStep;
133  if (launder32(i) >= num_words) {
134  i -= num_words;
135  }
136  HARDENED_CHECK_LT(i, num_words);
137  }
138  HARDENED_CHECK_EQ(iter_cnt, num_words);
139 }
140 
141 status_t otbn_dmem_write(size_t num_words, const uint32_t *src,
142  otbn_addr_t dest) {
143  HARDENED_TRY(check_offset_len(dest, num_words, kOtbnDMemSizeBytes));
144  otbn_write(kBase + OTBN_DMEM_REG_OFFSET + dest, src, num_words);
145  return OTCRYPTO_OK;
146 }
147 
148 status_t otbn_dmem_set(size_t num_words, const uint32_t src, otbn_addr_t dest) {
149  HARDENED_TRY(check_offset_len(dest, num_words, kOtbnDMemSizeBytes));
150 
151  // No need to randomize here, since all the values are the same.
152  size_t i = 0;
153  for (; launder32(i) < num_words; ++i) {
154  abs_mmio_write32(kBase + OTBN_DMEM_REG_OFFSET + dest + i * sizeof(uint32_t),
155  src);
156  HARDENED_CHECK_LT(i, num_words);
157  }
158  HARDENED_CHECK_EQ(i, num_words);
159  return OTCRYPTO_OK;
160 }
161 
162 status_t otbn_dmem_read(size_t num_words, otbn_addr_t src, uint32_t *dest) {
163  HARDENED_TRY(check_offset_len(src, num_words, kOtbnDMemSizeBytes));
164 
165  size_t i = 0;
166  for (; launder32(i) < num_words; ++i) {
167  dest[i] = abs_mmio_read32(kBase + OTBN_DMEM_REG_OFFSET + src +
168  i * sizeof(uint32_t));
169  }
170  HARDENED_CHECK_EQ(i, num_words);
171 
172  return OTCRYPTO_OK;
173 }
174 
175 status_t otbn_execute(void) {
176  // Ensure that the entropy complex is in a good state (for the RND
177  // instruction and data wiping).
178  HARDENED_TRY(entropy_complex_check());
179 
180  // Ensure OTBN is idle before attempting to run a command.
181  HARDENED_TRY(otbn_assert_idle());
182 
183  abs_mmio_write32(kBase + OTBN_CMD_REG_OFFSET, kOtbnCmdExecute);
184  return OTCRYPTO_OK;
185 }
186 
187 status_t otbn_busy_wait_for_done(void) {
188  uint32_t status = launder32(UINT32_MAX);
189  status_t res = (status_t){
190  .value = (int32_t)launder32((uint32_t)kHardenedBoolTrue ^ status)};
191  do {
192  status = abs_mmio_read32(kBase + OTBN_STATUS_REG_OFFSET);
193  } while (launder32(status) != kOtbnStatusIdle &&
194  launder32(status) != kOtbnStatusLocked);
195  res.value ^= ~status;
196 
197  uint32_t err_bits = otbn_err_bits_get();
198 
199  if (launder32(OT_UNSIGNED(res.value)) == kHardenedBoolTrue &&
200  launder32(err_bits) == kOtbnErrBitsNoError) {
202  err_bits = otbn_err_bits_get();
203  HARDENED_CHECK_EQ(err_bits, kOtbnErrBitsNoError);
204  HARDENED_CHECK_EQ(abs_mmio_read32(kBase + OTBN_STATUS_REG_OFFSET),
205  kOtbnStatusIdle);
206  return res;
207  }
208 
209  // If OTBN is idle (not locked), then return a recoverable error.
210  if (launder32(status) == kOtbnStatusIdle) {
211  HARDENED_CHECK_EQ(status, kOtbnStatusIdle);
212  return OTCRYPTO_RECOV_ERR;
213  }
214 
215  // OTBN is locked; return a fatal error.
216  HARDENED_CHECK_EQ(status, kOtbnStatusLocked);
217  return OTCRYPTO_FATAL_ERR;
218 }
219 
220 uint32_t otbn_err_bits_get(void) {
221  return abs_mmio_read32(kBase + OTBN_ERR_BITS_REG_OFFSET);
222 }
223 
224 uint32_t otbn_instruction_count_get(void) {
225  return abs_mmio_read32(kBase + OTBN_INSN_CNT_REG_OFFSET);
226 }
227 
228 status_t otbn_imem_sec_wipe(void) {
229  HARDENED_TRY(entropy_complex_check());
230  HARDENED_TRY(otbn_assert_idle());
231  abs_mmio_write32(kBase + OTBN_CMD_REG_OFFSET, kOtbnCmdSecWipeImem);
232  HARDENED_TRY(otbn_busy_wait_for_done());
233  return OTCRYPTO_OK;
234 }
235 
236 status_t otbn_dmem_sec_wipe(void) {
237  HARDENED_TRY(entropy_complex_check());
238  HARDENED_TRY(otbn_assert_idle());
239  abs_mmio_write32(kBase + OTBN_CMD_REG_OFFSET, kOtbnCmdSecWipeDmem);
240  HARDENED_TRY(otbn_busy_wait_for_done());
241  return OTCRYPTO_OK;
242 }
243 
244 status_t otbn_set_ctrl_software_errs_fatal(bool enable) {
245  // Ensure OTBN is idle (otherwise CTRL writes will be ignored).
246  HARDENED_TRY(otbn_assert_idle());
247 
248  // Only one bit in the CTRL register so no need to read current value.
249  uint32_t new_ctrl;
250 
251  if (enable) {
252  new_ctrl = 1;
253  } else {
254  new_ctrl = 0;
255  }
256 
257  abs_mmio_write32(kBase + OTBN_CTRL_REG_OFFSET, new_ctrl);
258 
259  return OTCRYPTO_OK;
260 }
261 
262 /**
263  * Checks if the OTBN application's IMEM and DMEM address parameters are valid.
264  *
265  * This function checks the following properties:
266  * - IMEM and DMEM ranges must not be "backwards" in memory, with the end
267  * address coming before the start address.
268  * - The IMEM range must be non-empty.
269  *
270  * @param app the OTBN application to check
271  * @return `OTCRYPTO_OK` if checks pass, otherwise `OTCRYPTO_BAD_ARGS`.
272  */
273 static status_t check_app_address_ranges(const otbn_app_t *app) {
274  // IMEM must not be backwards or empty.
275  if (app->imem_end <= app->imem_start) {
276  return OTCRYPTO_BAD_ARGS;
277  }
278  HARDENED_CHECK_LT(app->imem_start, app->imem_end);
279 
280  // DMEM data section must not be backwards.
281  if (app->dmem_data_end < app->dmem_data_start) {
282  return OTCRYPTO_BAD_ARGS;
283  }
284  HARDENED_CHECK_LE(app->dmem_data_start, app->dmem_data_end);
285 
286  return OTCRYPTO_OK;
287 }
288 
289 status_t otbn_load_app(const otbn_app_t app) {
290  HARDENED_TRY(check_app_address_ranges(&app));
291 
292  // Ensure OTBN is idle.
293  HARDENED_TRY(otbn_assert_idle());
294 
295  const size_t imem_num_words = (size_t)(app.imem_end - app.imem_start);
296  const size_t data_num_words =
297  (size_t)(app.dmem_data_end - app.dmem_data_start);
298 
299  HARDENED_TRY(otbn_imem_sec_wipe());
300  HARDENED_TRY(otbn_dmem_sec_wipe());
301 
302  // Reset the LOAD_CHECKSUM register.
303  abs_mmio_write32(kBase + OTBN_LOAD_CHECKSUM_REG_OFFSET, 0);
304 
305  // Ensure that the IMEM section fits in IMEM and the data section fits in
306  // DMEM.
307  HARDENED_TRY(check_offset_len(app.dmem_data_start_addr, data_num_words,
308  kOtbnDMemSizeBytes));
309 
310  // Write to IMEM. Always starts at zero on the OTBN side.
311  otbn_addr_t imem_offset = 0;
312  HARDENED_TRY(
313  check_offset_len(imem_offset, imem_num_words, kOtbnIMemSizeBytes));
314  uint32_t imem_start_addr = kBase + OTBN_IMEM_REG_OFFSET + imem_offset;
315  uint32_t i = 0;
316  for (; launder32(i) < imem_num_words; i++) {
317  HARDENED_CHECK_LT(i, imem_num_words);
318  abs_mmio_write32(imem_start_addr + i * sizeof(uint32_t), app.imem_start[i]);
319  }
320  HARDENED_CHECK_EQ(i, imem_num_words);
321 
322  // Write the data portion to DMEM.
323  otbn_addr_t data_offset = app.dmem_data_start_addr;
324  HARDENED_TRY(
325  check_offset_len(data_offset, data_num_words, kOtbnDMemSizeBytes));
326  uint32_t data_start_addr = kBase + OTBN_DMEM_REG_OFFSET + data_offset;
327  i = 0;
328  for (; launder32(i) < data_num_words; i++) {
329  HARDENED_CHECK_LT(i, data_num_words);
330  abs_mmio_write32(data_start_addr + i * sizeof(uint32_t),
331  app.dmem_data_start[i]);
332  }
333  HARDENED_CHECK_EQ(i, data_num_words);
334 
335  // Ensure that the checksum matches expectations.
336  uint32_t checksum = abs_mmio_read32(kBase + OTBN_LOAD_CHECKSUM_REG_OFFSET);
337  if (launder32(checksum) != app.checksum) {
338  return OTCRYPTO_FATAL_ERR;
339  }
340  HARDENED_CHECK_EQ(checksum, app.checksum);
341 
342  return OTCRYPTO_OK;
343 }