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 "hw/top/aes_regs.h" // Generated.
13
14/*
15 * From: https://opentitan.org/book/hw/ip/aes/doc/registers.html,
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->mode == kDifAesModeGcm) {
239 // Put AES-GCM into init mode.
240 DIF_RETURN_IF_ERROR(dif_aes_set_gcm_phase(
241 aes, AES_CTRL_GCM_SHADOWED_PHASE_VALUE_GCM_INIT, 16));
242 }
243
244 if (transaction->key_provider == kDifAesKeySoftwareProvided) {
245 aes_set_multireg(aes, &key->share0[0], AES_KEY_SHARE0_MULTIREG_COUNT,
246 AES_KEY_SHARE0_0_REG_OFFSET);
247
248 aes_set_multireg(aes, &key->share1[0], AES_KEY_SHARE1_MULTIREG_COUNT,
249 AES_KEY_SHARE1_0_REG_OFFSET);
250 }
251
252 if (transaction->mode != kDifAesModeEcb) {
253 // Make sure AES is idle before providing the IV. Depending on the
254 // configuration, updating the key might cause the AES to become non-idle
255 // and reseed the internal PRNGs.
256 AES_WAIT_FOR_STATUS(aes, AES_STATUS_IDLE_BIT, true);
257 aes_set_multireg(aes, &iv->iv[0], AES_IV_MULTIREG_COUNT,
258 AES_IV_0_REG_OFFSET);
259 }
260
261 return kDifOk;
262}
263
264dif_result_t dif_aes_end(const dif_aes_t *aes) {
265 if (aes == NULL) {
266 return kDifBadArg;
267 }
268
269 if (!aes_idle(aes)) {
270 return kDifUnavailable;
271 }
272
273 aes_clear_internal_state(aes);
274
275 return kDifOk;
276}
277
278dif_result_t dif_aes_load_data(const dif_aes_t *aes,
279 const dif_aes_data_t data) {
280 if (aes == NULL) {
281 return kDifBadArg;
282 }
283
284 if (!aes_input_ready(aes)) {
285 return kDifUnavailable;
286 }
287
288 aes_set_multireg(aes, &data.data[0], AES_DATA_IN_MULTIREG_COUNT,
289 AES_DATA_IN_0_REG_OFFSET);
290
291 return kDifOk;
292}
293
294dif_result_t dif_aes_read_output(const dif_aes_t *aes, dif_aes_data_t *data) {
295 if (aes == NULL || data == NULL) {
296 return kDifBadArg;
297 }
298
299 if (!aes_output_valid(aes)) {
300 return kDifError;
301 }
302
303 aes_read_multireg(aes, data->data, AES_DATA_OUT_MULTIREG_COUNT,
304 AES_DATA_OUT_0_REG_OFFSET);
305
306 return kDifOk;
307}
308
309dif_result_t dif_aes_set_gcm_phase(const dif_aes_t *aes, uint32_t phase,
310 size_t num_valid_bytes) {
311 if (aes == NULL) {
312 return kDifBadArg;
313 }
314
315 if (!aes_idle(aes)) {
316 return kDifUnavailable;
317 }
318
319 uint32_t reg = bitfield_field32_write(
320 0, AES_CTRL_GCM_SHADOWED_NUM_VALID_BYTES_FIELD, num_valid_bytes);
321 // Put AES-GCM into the given phase.
322 reg = bitfield_field32_write(reg, AES_CTRL_GCM_SHADOWED_PHASE_FIELD, phase);
323 aes_shadowed_write(aes->base_addr, AES_CTRL_GCM_SHADOWED_REG_OFFSET, reg);
324
325 return kDifOk;
326}
327
328dif_result_t dif_aes_load_gcm_tag_len(const dif_aes_t *aes, uint64_t len_ptx,
329 uint64_t len_aad) {
330 if (aes == NULL) {
331 return kDifBadArg;
332 }
333
334 // Ensure that the INPUT_READY bit in STATUS is 1.
335 if (!aes_input_ready(aes)) {
336 return kDifUnavailable;
337 }
338
339 // len_aad_ptx = len(aad) || len(ptx)
340 dif_aes_data_t len_aad_ptx;
341 len_aad_ptx.data[0] = __builtin_bswap32((uint32_t)(len_aad >> 32));
342 len_aad_ptx.data[1] = __builtin_bswap32((uint32_t)(len_aad & 0xFFFFFFFF));
343 len_aad_ptx.data[2] = __builtin_bswap32((uint32_t)(len_ptx >> 32));
344 len_aad_ptx.data[3] = __builtin_bswap32((uint32_t)(len_ptx & 0xFFFFFFFF));
345
346 aes_set_multireg(aes, &len_aad_ptx.data[0], AES_DATA_IN_MULTIREG_COUNT,
347 AES_DATA_IN_0_REG_OFFSET);
348
349 return kDifOk;
350}
351
352dif_result_t dif_aes_process_data(const dif_aes_t *aes,
353 const dif_aes_data_t *plain_text,
354 dif_aes_data_t *cipher_text,
355 size_t block_count) {
356 if (aes == NULL || plain_text == NULL || cipher_text == NULL ||
357 block_count == 0) {
358 return kDifBadArg;
359 }
360
361 // The algorithm below just makes sense for at least 2 blocks. Otherwise
362 // it is better to use the `load_data` and `read_output` functions.
363 if (block_count < 2) {
364 DIF_RETURN_IF_ERROR(dif_aes_load_data(aes, plain_text[0]));
365 return dif_aes_read_output(aes, cipher_text);
366 }
367
368 // Ensure that the INPUT_READY bit in STATUS is 1.
369 if (!aes_input_ready(aes)) {
370 return kDifUnavailable;
371 }
372
373 // Write Input Data Block 0 to the Input Data registers DATA_IN_0 - DATA_IN_3.
374 aes_set_multireg(aes, plain_text[0].data, AES_DATA_IN_MULTIREG_COUNT,
375 AES_DATA_IN_0_REG_OFFSET);
376
377 // Wait for the INPUT_READY bit in STATUS to become 1, i.e. wait for the AES
378 // unit to load Input Data Block 0 into the internal state register and start
379 // operation.
380 AES_WAIT_FOR_STATUS(aes, AES_STATUS_INPUT_READY_BIT, true);
381 // Then for every Data Block I=0,..,N-3, software must:
382 for (size_t i = 0; i < block_count; ++i) {
383 // Write Input Data Block I+1 into the Input Data register. There is no need
384 // to explicitly check INPUT_READY as in the same cycle OUTPUT_VALID becomes
385 // 1, the current input is loaded in (meaning INPUT_READY becomes 1 one
386 // cycle later).
387 if (i + 1 < block_count) {
388 aes_set_multireg(aes, plain_text[i + 1].data, AES_DATA_IN_MULTIREG_COUNT,
389 AES_DATA_IN_0_REG_OFFSET);
390 }
391
392 // Wait for the OUTPUT_VALID bit in STATUS to become 1, i.e., wait for the
393 // AES unit to finish encryption/decryption of Block I. The AES unit then
394 // directly starts processing the previously input block I+1.
395 AES_WAIT_FOR_STATUS(aes, AES_STATUS_OUTPUT_VALID_BIT, true);
396
397 // Read Output Data Block I from the Output Data registers DATA_OUT_0 -
398 // DATA_OUT_3. Each register must be read at least once.
399 aes_read_multireg(aes, cipher_text[i].data, AES_DATA_OUT_MULTIREG_COUNT,
400 AES_DATA_OUT_0_REG_OFFSET);
401 }
402
403 return kDifOk;
404}
405
407 if (aes == NULL) {
408 return kDifBadArg;
409 }
410
411 uint32_t reg = bitfield_bit32_write(0, trigger, true);
412 mmio_region_write32(aes->base_addr, AES_TRIGGER_REG_OFFSET, reg);
413
414 return kDifOk;
415}
416
417dif_result_t dif_aes_get_status(const dif_aes_t *aes, dif_aes_status_t flag,
418 bool *set) {
419 if (aes == NULL || set == NULL) {
420 return kDifBadArg;
421 }
422
423 switch (flag) {
425 *set = aes_idle(aes);
426 break;
428 *set = aes_stalled(aes);
429 break;
431 *set = aes_output_lost(aes);
432 break;
434 *set = aes_output_valid(aes);
435 break;
437 *set = aes_input_ready(aes);
438 break;
440 *set = aes_alert_fatal(aes);
441 break;
443 *set = aes_alert_recoverable(aes);
444 break;
445 default:
446 return kDifError;
447 }
448
449 return kDifOk;
450}
451
452dif_result_t dif_aes_read_iv(const dif_aes_t *aes, dif_aes_iv_t *iv) {
453 if (aes == NULL || iv == NULL) {
454 return kDifBadArg;
455 }
456
457 if (!aes_idle(aes)) {
458 return kDifUnavailable;
459 }
460
461 for (int i = 0; i < AES_IV_MULTIREG_COUNT; ++i) {
462 ptrdiff_t offset =
463 AES_IV_0_REG_OFFSET + (ptrdiff_t)i * (ptrdiff_t)sizeof(uint32_t);
464
465 iv->iv[i] = mmio_region_read32(aes->base_addr, offset);
466 }
467 return kDifOk;
468}