Software APIs
dif_aes.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
7#include <stddef.h>
8
11
12#include "aes_regs.h" // Generated.
13
14/*
15 * From: https://docs.opentitan.org/hw/ip/aes/doc/index.html#register-table,
16 * aes.CTRL.
17 */
18
19/**
20 * Waits for the given AES status flag to be set the given value.
21 *
22 * @param aes An aes DIF handle.
23 * @param flag Status flag to query.
24 * @param value The status flag value.
25 */
26#define AES_WAIT_FOR_STATUS(aes_, flag_, value_) \
27 while (mmio_region_get_bit32(aes->base_addr, AES_STATUS_REG_OFFSET, \
28 (flag_)) != value_) { \
29 }
30
31static bool aes_idle(const dif_aes_t *aes) {
32 return mmio_region_get_bit32(aes->base_addr, AES_STATUS_REG_OFFSET,
33 AES_STATUS_IDLE_BIT);
34}
35
36static bool aes_stalled(const dif_aes_t *aes) {
37 return mmio_region_get_bit32(aes->base_addr, AES_STATUS_REG_OFFSET,
38 AES_STATUS_STALL_BIT);
39}
40
41static bool aes_output_lost(const dif_aes_t *aes) {
42 return mmio_region_get_bit32(aes->base_addr, AES_STATUS_REG_OFFSET,
43 AES_STATUS_OUTPUT_LOST_BIT);
44}
45
46static bool aes_output_valid(const dif_aes_t *aes) {
47 return mmio_region_get_bit32(aes->base_addr, AES_STATUS_REG_OFFSET,
48 AES_STATUS_OUTPUT_VALID_BIT);
49}
50
51static bool aes_input_ready(const dif_aes_t *aes) {
52 return mmio_region_get_bit32(aes->base_addr, AES_STATUS_REG_OFFSET,
53 AES_STATUS_INPUT_READY_BIT);
54}
55
56static bool aes_alert_fatal(const dif_aes_t *aes) {
57 return mmio_region_get_bit32(aes->base_addr, AES_STATUS_REG_OFFSET,
58 AES_STATUS_ALERT_FATAL_FAULT_BIT);
59}
60
61static bool aes_alert_recoverable(const dif_aes_t *aes) {
62 return mmio_region_get_bit32(aes->base_addr, AES_STATUS_REG_OFFSET,
63 AES_STATUS_ALERT_RECOV_CTRL_UPDATE_ERR_BIT);
64}
65
66static void aes_shadowed_write(mmio_region_t base, ptrdiff_t offset,
67 uint32_t value) {
68 mmio_region_write32(base, offset, value);
69 mmio_region_write32(base, offset, value);
70}
71
72static void aes_clear_internal_state(const dif_aes_t *aes) {
73 // Make sure AES is idle before clearing.
74 AES_WAIT_FOR_STATUS(aes, AES_STATUS_IDLE_BIT, true);
75
76 // It should be fine to clobber the Control register. Only
77 // `AES_CTRL_SHADOWED_MANUAL_OPERATION` bit must be set.
78 uint32_t ctrl_reg =
79 bitfield_bit32_write(0, AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, true);
80
81 aes_shadowed_write(aes->base_addr, AES_CTRL_SHADOWED_REG_OFFSET, ctrl_reg);
82
83 uint32_t trigger_reg =
84 bitfield_bit32_write(0, AES_TRIGGER_KEY_IV_DATA_IN_CLEAR_BIT, true);
85
86 trigger_reg =
87 bitfield_bit32_write(trigger_reg, AES_TRIGGER_DATA_OUT_CLEAR_BIT, true);
88
89 mmio_region_write32(aes->base_addr, AES_TRIGGER_REG_OFFSET, trigger_reg);
90
91 // Make sure AES is cleared before proceeding (may take multiple cycles).
92 AES_WAIT_FOR_STATUS(aes, AES_STATUS_IDLE_BIT, true);
93}
94
95/**
96 * Configures AES. Is used by every `dif_aes_start_<mode>` function.
97 *
98 * @param aes AES state data.
99 * @param transaction Configuration data, common across all Cipher modes.
100 * @return `dif_result_t`.
101 */
102static dif_result_t configure(const dif_aes_t *aes,
103 const dif_aes_transaction_t *transaction) {
104 uint32_t reg = bitfield_field32_write(0, AES_CTRL_SHADOWED_OPERATION_FIELD,
105 transaction->operation);
106
107 reg = bitfield_field32_write(reg, AES_CTRL_SHADOWED_MODE_FIELD,
108 transaction->mode);
109
110 reg = bitfield_field32_write(reg, AES_CTRL_SHADOWED_KEY_LEN_FIELD,
111 transaction->key_len);
112
113 reg = bitfield_field32_write(reg, AES_CTRL_SHADOWED_PRNG_RESEED_RATE_FIELD,
114 transaction->mask_reseeding);
115
116 bool flag = transaction->manual_operation == kDifAesManualOperationManual;
117 reg = bitfield_bit32_write(reg, AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, flag);
118
119 flag = transaction->key_provider == kDifAesKeySideload;
120 reg = bitfield_bit32_write(reg, AES_CTRL_SHADOWED_SIDELOAD_BIT, flag);
121
122 aes_shadowed_write(aes->base_addr, AES_CTRL_SHADOWED_REG_OFFSET, reg);
123
124 return kDifOk;
125}
126
127/**
128 * Configures the auxiliary options for AES.
129 *
130 * @param aes AES state data.
131 * @param transaction Configuration data, common across all Cipher modes.
132 * @return `dif_result_t`.
133 */
134static dif_result_t configure_aux(const dif_aes_t *aes,
135 const dif_aes_transaction_t *transaction) {
136 // Return an error in case the register is locked with different values.
137 uint32_t reg_val =
138 mmio_region_read32(aes->base_addr, AES_CTRL_AUX_REGWEN_REG_OFFSET);
139 if (!reg_val) {
140 reg_val =
141 mmio_region_read32(aes->base_addr, AES_CTRL_AUX_SHADOWED_REG_OFFSET);
142 if (bitfield_bit32_read(
143 reg_val, AES_CTRL_AUX_SHADOWED_KEY_TOUCH_FORCES_RESEED_BIT) !=
144 transaction->reseed_on_key_change ||
145 bitfield_bit32_read(reg_val, AES_CTRL_AUX_SHADOWED_FORCE_MASKS_BIT) !=
146 transaction->force_masks) {
147 return kDifError;
148 }
149 return kDifOk;
150 }
151
152 reg_val =
153 bitfield_bit32_write(0, AES_CTRL_AUX_SHADOWED_KEY_TOUCH_FORCES_RESEED_BIT,
154 transaction->reseed_on_key_change);
155 reg_val = bitfield_bit32_write(reg_val, AES_CTRL_AUX_SHADOWED_FORCE_MASKS_BIT,
156 transaction->force_masks);
157 aes_shadowed_write(aes->base_addr, AES_CTRL_AUX_SHADOWED_REG_OFFSET, reg_val);
158
159 reg_val = transaction->ctrl_aux_lock == false;
160 mmio_region_write32(aes->base_addr, AES_CTRL_AUX_REGWEN_REG_OFFSET, reg_val);
161
162 return kDifOk;
163}
164
165/**
166 * Sets all "sub-registers" of `aes.KEY`, `aes.IV` or `aes.DATA_IN` multiregs.
167 *
168 * @param aes AES state data.
169 * @param data Data to be written into multi-reg registers.
170 * @param regs_num Number of "sub-registers" in the multireg.
171 * @param reg0_offset Offset to the "sub-register 0" in the multireg.
172 */
173static void aes_set_multireg(const dif_aes_t *aes, const uint32_t *data,
174 size_t regs_num, ptrdiff_t reg0_offset) {
175 for (int i = 0; i < regs_num; ++i) {
176 ptrdiff_t offset = reg0_offset + (ptrdiff_t)i * (ptrdiff_t)sizeof(uint32_t);
177
178 mmio_region_write32(aes->base_addr, offset, data[i]);
179 }
180}
181
182static void aes_read_multireg(const dif_aes_t *aes, uint32_t *data,
183 size_t regs_num, ptrdiff_t reg0_offset) {
184 for (int i = 0; i < regs_num; ++i) {
185 ptrdiff_t offset = reg0_offset + (ptrdiff_t)i * (ptrdiff_t)sizeof(uint32_t);
186
187 data[i] = mmio_region_read32(aes->base_addr, offset);
188 }
189}
190
191dif_result_t dif_aes_reset(const dif_aes_t *aes) {
192 if (aes == NULL) {
193 return kDifBadArg;
194 }
195
196 aes_clear_internal_state(aes);
197
198 // Any values would do, illegal values chosen here.
199 uint32_t reg = bitfield_field32_write(0, AES_CTRL_SHADOWED_OPERATION_FIELD,
200 AES_CTRL_SHADOWED_OPERATION_MASK);
201
202 reg = bitfield_field32_write(reg, AES_CTRL_SHADOWED_MODE_FIELD,
203 AES_CTRL_SHADOWED_MODE_VALUE_AES_NONE);
204
205 reg = bitfield_field32_write(reg, AES_CTRL_SHADOWED_KEY_LEN_FIELD,
206 AES_CTRL_SHADOWED_KEY_LEN_MASK);
207
208 aes_shadowed_write(aes->base_addr, AES_CTRL_SHADOWED_REG_OFFSET, reg);
209
210 return kDifOk;
211}
212
213dif_result_t dif_aes_start(const dif_aes_t *aes,
214 const dif_aes_transaction_t *transaction,
215 const dif_aes_key_share_t *key,
216 const dif_aes_iv_t *iv) {
217 if (aes == NULL || transaction == NULL ||
218 (iv == NULL && transaction->mode != kDifAesModeEcb) ||
219 (key == NULL &&
220 transaction->key_provider == kDifAesKeySoftwareProvided)) {
221 return kDifBadArg;
222 }
223
224 if (!aes_idle(aes)) {
225 return kDifUnavailable;
226 }
227
228 dif_result_t result = configure(aes, transaction);
229 if (result != kDifOk) {
230 return result;
231 }
232
233 result = configure_aux(aes, transaction);
234 if (result != kDifOk) {
235 return result;
236 }
237
238 if (transaction->key_provider == kDifAesKeySoftwareProvided) {
239 aes_set_multireg(aes, &key->share0[0], AES_KEY_SHARE0_MULTIREG_COUNT,
240 AES_KEY_SHARE0_0_REG_OFFSET);
241
242 aes_set_multireg(aes, &key->share1[0], AES_KEY_SHARE1_MULTIREG_COUNT,
243 AES_KEY_SHARE1_0_REG_OFFSET);
244 }
245
246 if (transaction->mode != kDifAesModeEcb) {
247 // Make sure AES is idle before providing the IV. Depending on the
248 // configuration, updating the key might cause the AES to become non-idle
249 // and reseed the internal PRNGs.
250 AES_WAIT_FOR_STATUS(aes, AES_STATUS_IDLE_BIT, true);
251 aes_set_multireg(aes, &iv->iv[0], AES_IV_MULTIREG_COUNT,
252 AES_IV_0_REG_OFFSET);
253 }
254
255 return kDifOk;
256}
257
258dif_result_t dif_aes_end(const dif_aes_t *aes) {
259 if (aes == NULL) {
260 return kDifBadArg;
261 }
262
263 if (!aes_idle(aes)) {
264 return kDifUnavailable;
265 }
266
267 aes_clear_internal_state(aes);
268
269 return kDifOk;
270}
271
272dif_result_t dif_aes_load_data(const dif_aes_t *aes,
273 const dif_aes_data_t data) {
274 if (aes == NULL) {
275 return kDifBadArg;
276 }
277
278 if (!aes_input_ready(aes)) {
279 return kDifUnavailable;
280 }
281
282 aes_set_multireg(aes, &data.data[0], AES_DATA_IN_MULTIREG_COUNT,
283 AES_DATA_IN_0_REG_OFFSET);
284
285 return kDifOk;
286}
287
288dif_result_t dif_aes_read_output(const dif_aes_t *aes, dif_aes_data_t *data) {
289 if (aes == NULL || data == NULL) {
290 return kDifBadArg;
291 }
292
293 if (!aes_output_valid(aes)) {
294 return kDifError;
295 }
296
297 aes_read_multireg(aes, data->data, AES_DATA_OUT_MULTIREG_COUNT,
298 AES_DATA_OUT_0_REG_OFFSET);
299
300 return kDifOk;
301}
302
303dif_result_t dif_aes_process_data(const dif_aes_t *aes,
304 const dif_aes_data_t *plain_text,
305 dif_aes_data_t *cipher_text,
306 size_t block_count) {
307 if (aes == NULL || plain_text == NULL || cipher_text == NULL ||
308 block_count == 0) {
309 return kDifBadArg;
310 }
311
312 // The algorithm below just makes sense for at least 2 blocks. Otherwise
313 // it is better to use the `load_data` and `read_output` functions.
314 if (block_count < 2) {
315 DIF_RETURN_IF_ERROR(dif_aes_load_data(aes, plain_text[0]));
316 return dif_aes_read_output(aes, cipher_text);
317 }
318
319 // Ensure that the INPUT_READY bit in STATUS is 1.
320 if (!aes_input_ready(aes)) {
321 return kDifUnavailable;
322 }
323
324 // Write Input Data Block 0 to the Input Data registers DATA_IN_0 - DATA_IN_3.
325 aes_set_multireg(aes, plain_text[0].data, AES_DATA_IN_MULTIREG_COUNT,
326 AES_DATA_IN_0_REG_OFFSET);
327
328 // Wait for the INPUT_READY bit in STATUS to become 1, i.e. wait for the AES
329 // unit to load Input Data Block 0 into the internal state register and start
330 // operation.
331 AES_WAIT_FOR_STATUS(aes, AES_STATUS_INPUT_READY_BIT, true);
332 // Then for every Data Block I=0,..,N-3, software must:
333 for (size_t i = 0; i < block_count; ++i) {
334 // Write Input Data Block I+1 into the Input Data register. There is no need
335 // to explicitly check INPUT_READY as in the same cycle OUTPUT_VALID becomes
336 // 1, the current input is loaded in (meaning INPUT_READY becomes 1 one
337 // cycle later).
338 if (i + 1 < block_count) {
339 aes_set_multireg(aes, plain_text[i + 1].data, AES_DATA_IN_MULTIREG_COUNT,
340 AES_DATA_IN_0_REG_OFFSET);
341 }
342
343 // Wait for the OUTPUT_VALID bit in STATUS to become 1, i.e., wait for the
344 // AES unit to finish encryption/decryption of Block I. The AES unit then
345 // directly starts processing the previously input block I+1.
346 AES_WAIT_FOR_STATUS(aes, AES_STATUS_OUTPUT_VALID_BIT, true);
347
348 // Read Output Data Block I from the Output Data registers DATA_OUT_0 -
349 // DATA_OUT_3. Each register must be read at least once.
350 aes_read_multireg(aes, cipher_text[i].data, AES_DATA_OUT_MULTIREG_COUNT,
351 AES_DATA_OUT_0_REG_OFFSET);
352 }
353
354 return kDifOk;
355}
356
358 if (aes == NULL) {
359 return kDifBadArg;
360 }
361
362 uint32_t reg = bitfield_bit32_write(0, trigger, true);
363 mmio_region_write32(aes->base_addr, AES_TRIGGER_REG_OFFSET, reg);
364
365 return kDifOk;
366}
367
368dif_result_t dif_aes_get_status(const dif_aes_t *aes, dif_aes_status_t flag,
369 bool *set) {
370 if (aes == NULL || set == NULL) {
371 return kDifBadArg;
372 }
373
374 switch (flag) {
376 *set = aes_idle(aes);
377 break;
379 *set = aes_stalled(aes);
380 break;
382 *set = aes_output_lost(aes);
383 break;
385 *set = aes_output_valid(aes);
386 break;
388 *set = aes_input_ready(aes);
389 break;
391 *set = aes_alert_fatal(aes);
392 break;
394 *set = aes_alert_recoverable(aes);
395 break;
396 default:
397 return kDifError;
398 }
399
400 return kDifOk;
401}
402
403dif_result_t dif_aes_read_iv(const dif_aes_t *aes, dif_aes_iv_t *iv) {
404 if (aes == NULL || iv == NULL) {
405 return kDifBadArg;
406 }
407
408 if (!aes_idle(aes)) {
409 return kDifUnavailable;
410 }
411
412 for (int i = 0; i < AES_IV_MULTIREG_COUNT; ++i) {
413 ptrdiff_t offset =
414 AES_IV_0_REG_OFFSET + (ptrdiff_t)i * (ptrdiff_t)sizeof(uint32_t);
415
416 iv->iv[i] = mmio_region_read32(aes->base_addr, offset);
417 }
418 return kDifOk;
419}