Software APIs
aes_prng_reseed_test.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 "hw/ip/aes/model/aes_modes.h"
8 #include "sw/device/lib/base/multibits.h"
11 #include "sw/device/lib/dif/dif_csrng_shared.h"
14 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
17 #include "sw/device/lib/testing/csrng_testutils.h"
18 #include "sw/device/lib/testing/entropy_testutils.h"
19 #include "sw/device/lib/testing/rv_core_ibex_testutils.h"
20 #include "sw/device/lib/testing/test_framework/check.h"
23 
25 
26 OTTF_DEFINE_TEST_CONFIG();
27 
28 static dif_aes_t aes;
30 enum {
31  kNumTestConfigs = 2,
32  kMaxChunkSize = 300,
33 };
34 
35 typedef struct {
36  dif_aes_mask_reseeding_t mask_reseeding;
37  uint32_t kAesNumBlocks;
38  // NumBytes in one block is 4x8 i.e. 32 bits in 1 block
39  uint32_t kDifAesBlockNumBytes;
40  uint32_t disable_entropy_after_block;
41  uint32_t enable_entropy_at_block;
43 
44 aes_test_config_t test_configs[kNumTestConfigs] = {
45  // Test 1 configuration with kDifAesReseedPer64Block
46  {
47  .mask_reseeding = kDifAesReseedPer64Block,
48  .kAesNumBlocks = 68,
49  .kDifAesBlockNumBytes = 4,
50  .disable_entropy_after_block = 32,
51  .enable_entropy_at_block = 63,
52  },
53  // Test 2 configuration with kDifAesReseedPer8kBlock
54  {
55  .mask_reseeding = kDifAesReseedPer8kBlock,
56  .kAesNumBlocks = 8400,
57  .kDifAesBlockNumBytes = 4,
58  .disable_entropy_after_block = 41,
59  .enable_entropy_at_block = 8191,
60  },
61 };
62 
63 static const uint8_t kKeyShare1[16] = {
64  0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f,
65  0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff,
66 };
67 
68 static dif_edn_t edn0, edn1;
69 static dif_csrng_t csrng;
70 static dif_entropy_src_t entropy_src;
71 // Function to disable entropy complex
72 status_t disable_entropy_complex(void) {
73  // Using entropy test utility to stop all EDN0, EDN1, CSRNG, and the Entropy
74  // Source
75  TRY(entropy_testutils_stop_all());
76  LOG_INFO("The entire entropy complex is stopped");
77  return OK_STATUS();
78 }
79 
80 // Function to enable entropy complex
81 status_t enable_entropy_complex(void) {
82  // Using entropy test utility to enable all EDN0, EDN1, CSRNG, and the
83  // Entropy Source
84  TRY(entropy_testutils_auto_mode_init());
85  LOG_INFO("The entire entropy complex is enabled");
86  return OK_STATUS();
87 }
88 
89 // Function to generate a new key based on provided index value
90 void generate_new_key(dif_aes_key_share_t *key, int index) {
91  uint8_t new_key_share0[sizeof(kAesModesKey128)];
92  // Generate new key share0 by XOR-ing base key with kKeyShare1*index
93  for (size_t i = 0; i < sizeof(kAesModesKey128); ++i) {
94  new_key_share0[i] = kAesModesKey128[i] ^ kKeyShare1[i] * (uint8_t)(index);
95  }
96  // Update the key shares
97  memcpy(key->share0, new_key_share0, sizeof(key->share0));
98  memcpy(key->share1, kKeyShare1, sizeof(key->share1));
99 }
100 
101 // status_t execute_test(void) {
102 status_t execute_test(aes_test_config_t *config) {
103  bool aes_idle = false;
104  bool input_ready = false;
105  bool output_valid = false;
106 
107  LOG_INFO(
108  "Testing AES PRNG Reseed Test with number of blocks: %d (Block 0 to %d), "
109  "and disabling entropy after block number: %d",
110  config->kAesNumBlocks, config->kAesNumBlocks - 1,
111  config->disable_entropy_after_block - 1);
112  // Initialize AES
113  TRY(dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes));
114  TRY(dif_aes_reset(&aes));
115  // Initialize EDN0, EDN1, CSRNG and Entropy Source
116  TRY(dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0));
117  TRY(dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1));
119  &csrng));
120  TRY(dif_entropy_src_init(
122 
123  // Generate key with index 0
124  generate_new_key(&key, 0);
125 
126  // Prepare transaction
127  dif_aes_transaction_t transaction = {
128  .operation = kDifAesOperationEncrypt,
129  .mode = kDifAesModeEcb,
130  .key_len = kDifAesKey128,
131  .key_provider = kDifAesKeySoftwareProvided,
132  .mask_reseeding = config->mask_reseeding,
133  .manual_operation = kDifAesManualOperationAuto,
134  .reseed_on_key_change = true,
135  .force_masks = false,
136  .ctrl_aux_lock = false,
137  };
138  // Start the AES operation
139  // Wait for AES to be idle before starting encryption
140  aes_idle = false;
141  do {
142  TRY(dif_aes_get_status(&aes, kDifAesStatusIdle, &aes_idle));
143  } while (!aes_idle);
144  TRY(dif_aes_start(&aes, &transaction, &key, NULL));
145 
146  dif_aes_data_t plain_text[kMaxChunkSize];
147  dif_aes_data_t cipher_text[kMaxChunkSize];
148 
149  uint32_t total_blocks = config->kAesNumBlocks;
150 
151  uint32_t blocks_processed = 0;
152 
153  // Initialize plaintext data dynamically
154  // Create plaintext with random data
155  dif_rv_core_ibex_t rv_core_ibex;
156  TRY(dif_rv_core_ibex_init(
158  &rv_core_ibex));
159  for (uint32_t i = 0; i < ARRAYSIZE(plain_text); ++i) {
160  for (uint32_t j = 0; j < config->kDifAesBlockNumBytes; ++j) {
161  uint32_t rand;
162  TRY(rv_core_ibex_testutils_get_rnd_data(&rv_core_ibex, 2000, &rand));
163  plain_text[i].data[j] = rand;
164  }
165  }
166 
167  // Now start encryption process for the blocks in chunks
168  while (blocks_processed < total_blocks) {
169  uint32_t blocks_to_process = total_blocks - blocks_processed;
170  if (blocks_to_process > kMaxChunkSize) {
171  blocks_to_process = kMaxChunkSize;
172  }
173 
174  // Start encryption process
175  // Process the blocks in this chunk
176  for (uint32_t i = 0; i < blocks_to_process; ++i) {
177  uint32_t block_index = blocks_processed + i;
178 
179  // Wait for input ready
180  input_ready = false;
181  do {
182  TRY(dif_aes_get_status(&aes, kDifAesStatusInputReady, &input_ready));
183  } while (!input_ready);
184 
185  // Load data
186  TRY(dif_aes_load_data(&aes, plain_text[i]));
187 
188  // Disable entropy complex after processing enough blocks
189  if (block_index == config->disable_entropy_after_block) {
190  LOG_INFO("Disabling entropy complex to induce AES halt at block %d",
191  block_index);
192  CHECK_STATUS_OK(disable_entropy_complex());
193  busy_spin_micros(1000);
194  }
195 
196  // Check for halt when PRNG reseed is required
197  if (block_index == config->enable_entropy_at_block) {
198  output_valid = false;
199  TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &output_valid));
200  CHECK(!output_valid,
201  "ERROR: AES encryption did not halt when entropy was disabled at "
202  "block %d",
203  block_index);
204  LOG_INFO("AES encryption is halted as expected at block %d",
205  block_index);
206 
207  // Re-enable the entropy complex
208  LOG_INFO("Re-enabling entropy complex at block %d", block_index);
209  CHECK_STATUS_OK(enable_entropy_complex());
210  busy_spin_micros(500);
211  }
212 
213  // Wait for the AES module to produce output valid
214  output_valid = false;
215  do {
216  TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &output_valid));
217  } while (!output_valid);
218 
219  // Read output data
220  TRY(dif_aes_read_output(&aes, &cipher_text[i]));
221  }
222  // Update blocks_processed
223  blocks_processed += blocks_to_process;
224  }
225 
226  // Finish the AES encryption operation
227  LOG_INFO("End AES encryption operation");
228  TRY(dif_aes_end(&aes));
229 
230  // Wait for AES to be idle to check aes encryption process has ended
231  aes_idle = false;
232  do {
233  TRY(dif_aes_get_status(&aes, kDifAesStatusIdle, &aes_idle));
234  } while (!aes_idle);
235  LOG_INFO("AES module is idle");
236 
237  // After ensuring the AES module is idle
238  // Reset the AES module before starting decryption
239  TRY(dif_aes_reset(&aes));
240 
241  return OK_STATUS();
242 }
243 
244 bool test_main(void) {
245  for (size_t i = 0; i < kNumTestConfigs; ++i) {
246  aes_test_config_t *config = &test_configs[i];
247 
248  // Log the current test configuration
249  LOG_INFO("Starting AES PRNG Reseed Test with mask_reseeding: %d",
250  config->mask_reseeding);
251 
252  // Call execute_test with the current configuration
253  CHECK_STATUS_OK(execute_test(config));
254  }
255 
256  return true;
257 }