Software APIs
aes_testutils.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/testing/aes_testutils.h"
6
7#include "hw/ip/aes/model/aes_modes.h"
8#include "hw/top/dt/alert_handler.h"
11#include "sw/device/lib/testing/test_framework/check.h"
12
13#ifdef AES_TESTUTILS_HAS_EDN_AND_CSRNG
14#include "hw/top/dt/csrng.h"
16#include "sw/device/lib/testing/csrng_testutils.h"
17
18#include "hw/top/csrng_regs.h" // Generated
19#endif
20
21// `extern` declarations to give the inline functions in the
22// corresponding header a link location.
23
24extern bool aes_testutils_get_status(dif_aes_t *aes, dif_aes_status_t flag);
25
26// The mask share, used to mask kKey. Note that the masking should not be done
27// manually. Software is expected to get the key in two shares right from the
28// beginning.
29static const uint8_t kKeyShare1[] = {
30 0x0f, 0x1f, 0x2f, 0x3F, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf,
31 0xbf, 0xcf, 0xdf, 0xef, 0xff, 0x0a, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a,
32 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa,
33};
34
36
37enum {
38 kAesTestutilsTimeout = (10 * 1000 * 1000),
39};
40
41#ifdef AES_TESTUTILS_HAS_EDN_AND_CSRNG
42/**
43 * Constants for switching AES masking off.
44 */
45enum {
46 kCsrngBlockLen = 4,
47 kCsrngKeyLen = 8,
48 kEdnSeedMaterialLen = 12,
49};
50
51// CSRNG needs to constantly output these bits to EDN. If reseeded to an all-
52// zero value the AES masking PRNG will output an all-zero vector. It will
53// further keep this value if the CTRL_AUX_SHADOWED.FORCE_MASKS bit is set.
54const uint32_t kAesMaskingPrngZeroOutputSeed[kCsrngBlockLen] = {
55 0x00000000, 0x000000000, 0x00000000, 0x00000000};
56
57// Seed material for instantiate command. The CTR_DRBG construction
58// implemented by CSRNG produces
59//
60// key = 00 01 02 03 04 05 06 07 - 08 09 0a 0b 0c 0d 0e 0f
61// 10 11 12 13 14 15 16 17 - 18 19 1a 1b 1c 1d 1e 1f
62//
63// V = 6d 9f 08 eb 2a 2e 27 7a - b4 89 84 cf f1 ab 9a 09
64//
65// from this seed material upon instantiate. The key is arbitrarily chosen.
66// Encrypting V using this key then gives the required
67// kAesMaskingPrngZeroOutputSeed above.
68const uint32_t kEdnSeedMaterialInstantiate[kEdnSeedMaterialLen] = {
69 0x84adaf86, 0x652b7141, 0x1d880d0e, 0x1fff0b21, 0xa6ee8307, 0x1f57dfc8,
70 0x59757d79, 0xdeb6522e, 0xc8c67d84, 0xa16abefa, 0xc34030be, 0x530e88f8};
71
72// V and key after instantiate.
73const uint32_t kCsrngVInstantiate[kCsrngBlockLen] = {0xf1ab9a08, 0xb48984cf,
74 0x2a2e277a, 0x6d9f08eb};
75const uint32_t kCsrngKeyInstantiate[kCsrngKeyLen] = {
76 0x1c1d1e1f, 0x18191a1b, 0x14151617, 0x10111213,
77 0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203};
78
79// V and key after generate.
80const uint32_t kCsrngVGenerate[kCsrngBlockLen] = {0x654600bd, 0xf0c32787,
81 0x3eb52114, 0x8a1e0dce};
82const uint32_t kCsrngKeyGenerate[kCsrngKeyLen] = {
83 0xff6589b5, 0x4bb8e5f9, 0x62847098, 0x1e9f9cd1,
84 0x3c005fbd, 0x9a1b6e70, 0xe30eb080, 0x71dea927};
85
86// Seed material for reseed command. After one generate, this seed material
87// will bring the key and V of CSRNG back to the state after instantiate.
88// I.e., one can again run one generate to produce the seed required for AES
89// (see kAesMaskingPrngZeroOutputSeed).
90const uint32_t kEdnSeedMaterialReseed[kEdnSeedMaterialLen] = {
91 0x96994362, 0x7ef8f0b9, 0x5b5332dc, 0xd0df9b12, 0x96dfbaa9, 0xac0b5af7,
92 0xec2504be, 0xb00fb68c, 0xf37e0a7f, 0x88172eec, 0x4e4b5f58, 0xfec120c0};
93
94status_t aes_testutils_masking_prng_zero_output_seed(
95 const dif_csrng_t *csrng, const dif_edn_t *edn0,
96 bool gen_zero_output_seed) {
97 // Shutdown EDN0 and CSRNG
98 TRY(dif_edn_stop(edn0));
99 TRY(dif_csrng_stop(csrng));
100
101 // Re-enable CSRNG
102 TRY(dif_csrng_configure(csrng));
103
104 // Re-enable EDN0 and configure it to produce the seed that if loaded into AES
105 // causes the AES masking PRNG to output and all-zero output.
106 dif_edn_auto_params_t edn0_params = {
107 .instantiate_cmd =
108 {
109 .cmd = csrng_cmd_header_build(kCsrngAppCmdInstantiate,
111 kEdnSeedMaterialLen,
112 /*generate_len=*/0),
113 .seed_material =
114 {
115 .len = kEdnSeedMaterialLen,
116 },
117 },
118 .reseed_cmd =
119 {
120 .cmd = csrng_cmd_header_build(kCsrngAppCmdReseed,
122 kEdnSeedMaterialLen,
123 /*generate_len=*/0),
124 .seed_material =
125 {
126 .len = kEdnSeedMaterialLen,
127 },
128 },
129 .generate_cmd =
130 {
131 .cmd = csrng_cmd_header_build(kCsrngAppCmdGenerate,
133 /*cmd_len=*/0,
134 /*generate_len=*/1),
135 .seed_material =
136 {
137 .len = 0,
138 },
139 },
140 // When producing the seed causing AES to output an all-zero output, we
141 // need to reseed CSRNG after every single generate. Otherwise, we reseed
142 // less frequently. This produces a different seed and avoids repetition
143 // alerts in EDN.
144 .reseed_interval = gen_zero_output_seed ? 1 : 16,
145 };
146 memcpy(edn0_params.instantiate_cmd.seed_material.data,
147 kEdnSeedMaterialInstantiate, sizeof(kEdnSeedMaterialInstantiate));
148 memcpy(edn0_params.reseed_cmd.seed_material.data, kEdnSeedMaterialReseed,
149 sizeof(kEdnSeedMaterialReseed));
150 TRY(dif_edn_set_auto_mode(edn0, edn0_params));
151 return OK_STATUS();
152}
153
154status_t aes_testutils_csrng_kat(const dif_csrng_t *csrng) {
155 // Instantiate CSRNG with seed material suitable for switching the AES masking
156 // off.
157 dif_csrng_seed_material_t seed_material_instantiate = {
158 .seed_material_len = 12,
159 };
160 memcpy(seed_material_instantiate.seed_material, kEdnSeedMaterialInstantiate,
161 sizeof(kEdnSeedMaterialInstantiate));
162 dif_csrng_internal_state_t expected_state_instantiate = {
163 .reseed_counter = 0,
164 .instantiated = true,
165 .fips_compliance = false,
166 };
167 memcpy(expected_state_instantiate.v, kCsrngVInstantiate,
168 sizeof(kCsrngVInstantiate));
169 memcpy(expected_state_instantiate.key, kCsrngKeyInstantiate,
170 sizeof(kCsrngKeyInstantiate));
171 TRY(csrng_testutils_kat_instantiate(csrng, false, &seed_material_instantiate,
172 &expected_state_instantiate));
173
174 // Generate one block containing the required seed for the AES masking PRNG
175 // to output an all-zero vector.
176 dif_csrng_internal_state_t expected_state_generate = {
177 .reseed_counter = 1,
178 .instantiated = true,
179 .fips_compliance = false,
180 };
181 memcpy(expected_state_generate.v, kCsrngVGenerate, sizeof(kCsrngVGenerate));
182 memcpy(expected_state_generate.key, kCsrngKeyGenerate,
183 sizeof(kCsrngKeyGenerate));
184 TRY(csrng_testutils_kat_generate(csrng, 1, kCsrngBlockLen, NULL,
185 kAesMaskingPrngZeroOutputSeed,
186 &expected_state_generate));
187
188 // Reseed the CSRNG instance to produce the required seed for the AES masking
189 // PRNG to output an all-zero vector upon the next generate command.
190 dif_csrng_seed_material_t seed_material_reseed = {
191 .seed_material_len = 12,
192 };
193 memcpy(seed_material_reseed.seed_material, kEdnSeedMaterialReseed,
194 sizeof(kEdnSeedMaterialReseed));
195 dif_csrng_internal_state_t expected_state_reseed = {
196 .reseed_counter = 0,
197 .instantiated = true,
198 .fips_compliance = false,
199 };
200 memcpy(expected_state_reseed.v, kCsrngVInstantiate,
201 sizeof(kCsrngVInstantiate));
202 memcpy(expected_state_reseed.key, kCsrngKeyInstantiate,
203 sizeof(kCsrngKeyInstantiate));
204 TRY(csrng_testutils_kat_reseed(csrng, &seed_material_reseed,
205 &expected_state_reseed));
206
207 // We're about to generate the same `genbits` again which will trigger a
208 // `csrng_recov_alert`. This is expected, so we must disable this alert if it
209 // is currently enabled and restore it after.
210 dif_alert_handler_t alert_handler;
211 TRY(dif_alert_handler_init_from_dt(kDtAlertHandler, &alert_handler));
212
213 dt_alert_id_t csrng_recov_alert =
215
216 dif_toggle_t was_enabled = kDifToggleDisabled;
217 TRY(dif_alert_handler_alert_is_enabled(&alert_handler, csrng_recov_alert,
218 &was_enabled));
219
220 if (was_enabled == kDifToggleEnabled) {
221 TRY(dif_alert_handler_alert_set_enabled(&alert_handler, csrng_recov_alert,
223 }
224
225 // Generate one block containing the required seed for the AES masking PRNG
226 // to output an all-zero vector.
227 TRY(csrng_testutils_kat_generate(csrng, 1, kCsrngBlockLen, NULL,
228 kAesMaskingPrngZeroOutputSeed,
229 &expected_state_generate));
230
231 // Check that the correct recoverable alert fired even if the alert was
232 // ignored.
233 uint32_t recov_alerts = 0;
234 TRY(dif_csrng_get_recoverable_alerts(csrng, &recov_alerts));
235 TRY_CHECK(recov_alerts == kDifCsrngRecoverableAlertRepeatedGenBits);
236 TRY(dif_csrng_clear_recoverable_alerts(csrng));
237
238 // Restore the alert if it was enabled.
239 if (was_enabled == kDifToggleEnabled) {
240 TRY(dif_alert_handler_alert_set_enabled(&alert_handler, csrng_recov_alert,
242 }
243
244 return OK_STATUS();
245}
246#endif
247
248status_t aes_testutils_setup_encryption(dif_aes_transaction_t transaction,
249 dif_aes_t *aes) {
250 // Mask the key. Note that this should not be done manually. Software is
251 // expected to get the key in two shares right from the beginning.
252 uint8_t key_share0[sizeof(kAesModesKey256)];
253 for (int i = 0; i < sizeof(kAesModesKey256); ++i) {
254 key_share0[i] = kAesModesKey256[i] ^ kKeyShare1[i];
255 }
256
257 // "Convert" key share byte arrays to `dif_aes_key_share_t`.
258 memcpy(key.share0, key_share0, sizeof(key.share0));
259 memcpy(key.share1, kKeyShare1, sizeof(key.share1));
260
261 AES_TESTUTILS_WAIT_FOR_STATUS(aes, kDifAesStatusIdle, true,
262 kAesTestutilsTimeout);
263 CHECK_DIF_OK(dif_aes_start(aes, &transaction, &key, NULL));
264
265 // "Convert" plain data byte arrays to `dif_aes_data_t`.
266 dif_aes_data_t in_data_plain;
267 memcpy(in_data_plain.data, kAesModesPlainText, sizeof(in_data_plain.data));
268
269 // Load the plain text to trigger the encryption operation.
270 AES_TESTUTILS_WAIT_FOR_STATUS(aes, kDifAesStatusIdle, true,
271 kAesTestutilsTimeout);
272 AES_TESTUTILS_WAIT_FOR_STATUS(aes, kDifAesStatusInputReady, true,
273 kAesTestutilsTimeout);
274 CHECK_DIF_OK(dif_aes_load_data(aes, in_data_plain));
275
276 return OK_STATUS();
277}
278
279status_t aes_testutils_decrypt_ciphertext(dif_aes_transaction_t transaction,
280 dif_aes_t *aes) {
281 // Read out the produced cipher text.
282 dif_aes_data_t out_data;
283 CHECK_DIF_OK(dif_aes_read_output(aes, &out_data));
284
285 // Finish the ECB encryption transaction.
286 CHECK_DIF_OK(dif_aes_end(aes));
287 CHECK_ARRAYS_EQ((uint8_t *)out_data.data, kAesModesCipherTextEcb256,
288 sizeof(out_data.data));
289
290 // Setup ECB decryption transaction.
291 transaction.operation = kDifAesOperationDecrypt;
292 CHECK_DIF_OK(dif_aes_start(aes, &transaction, &key, NULL));
293
294 // Load the previously produced cipher text to start the decryption operation.
295 AES_TESTUTILS_WAIT_FOR_STATUS(aes, kDifAesStatusInputReady, true,
296 kAesTestutilsTimeout);
297 CHECK_DIF_OK(dif_aes_load_data(aes, out_data));
298
299 // Read out the produced plain text.
300 AES_TESTUTILS_WAIT_FOR_STATUS(aes, kDifAesStatusOutputValid, true,
301 kAesTestutilsTimeout);
302 CHECK_DIF_OK(dif_aes_read_output(aes, &out_data));
303
304 // Finish the ECB encryption transaction.
305 CHECK_DIF_OK(dif_aes_end(aes));
306
307 CHECK_ARRAYS_EQ((uint8_t *)out_data.data, kAesModesPlainText,
308 sizeof(out_data.data));
309 return OK_STATUS();
310}