Software APIs
edn_kat.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 
7 #include "sw/device/lib/dif/dif_csrng_shared.h"
9 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
12 #include "sw/device/lib/testing/edn_testutils.h"
13 #include "sw/device/lib/testing/entropy_testutils.h"
14 #include "sw/device/lib/testing/rv_core_ibex_testutils.h"
15 #include "sw/device/lib/testing/test_framework/check.h"
17 
18 #include "edn_regs.h" // Generated
20 
21 enum {
22  kEdnKatTimeout = (10 * 1000 * 1000),
23  kEdnKatMaxClen = 12,
24  kEdnKatOutputLen = 16,
25  kEdnKatWordsPerBlock = 4,
26 };
27 
28 static dif_csrng_t csrng;
29 static dif_edn_t edn0;
30 static dif_edn_t edn1;
31 static dif_rv_core_ibex_t rv_core_ibex;
32 
33 // CTR_DRBG Known-Answer-Tests (KATs).
34 //
35 // Test vector sourced from NIST's CAVP website:
36 // https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/random-number-generators
37 //
38 // The number format in this docstring follows the CAVP format to simplify
39 // auditing of this test case.
40 //
41 // Test vector: CTR_DRBG AES-256 no DF.
42 //
43 // - EntropyInput =
44 // df5d73faa468649edda33b5cca79b0b05600419ccb7a879ddfec9db32ee494e5531b51de16a30f769262474c73bec010
45 // - Nonce = EMPTY
46 // - PersonalizationString = EMPTY
47 //
48 // Command: Instantiate
49 // - Key = 8c52f901632d522774c08fad0eb2c33b98a701a1861aecf3d8a25860941709fd
50 // - V = 217b52142105250243c0b2c206b8f59e
51 //
52 // Command: Generate (first call):
53 // - Key = 72f4af5c93258eb3eeec8c0cacea6c1d1978a4fad44312725f1ac43b167f2d52
54 // - V = e86f6d07dfb551cebad80e6bf6830ac4
55 //
56 // Command: Generate (second call):
57 // - Key = 1a1c6e5f1cccc6974436e5fd3f015bc8e9dc0f90053b73e3c19d4dfd66d1b85a
58 // - V = 53c78ac61a0bac9d7d2e92b1e73e3392
59 // - ReturnedBits =
60 // d1c07cd95af8a7f11012c84ce48bb8cb87189e99d40fccb1771c619bdf82ab2280b1dc2f2581f39164f7ac0c510494b3a43c41b7db17514c87b107ae793e01c5
61 
62 // Seed material for the EDN KAT instantiate command.
63 const dif_edn_seed_material_t kEdnKatSeedMaterialInstantiate = {
64  .len = kEdnKatMaxClen,
65  .data = {0x73bec010, 0x9262474c, 0x16a30f76, 0x531b51de, 0x2ee494e5,
66  0xdfec9db3, 0xcb7a879d, 0x5600419c, 0xca79b0b0, 0xdda33b5c,
67  0xa468649e, 0xdf5d73fa},
68 };
69 // Seed material for the EDN KAT reseed command.
70 const dif_edn_seed_material_t kEdnKatSeedMaterialReseed = {
71  .len = kEdnKatMaxClen,
72  .data = {0x73bec010, 0x9262474c, 0x16a30f76, 0x531b51de, 0x2ee494e5,
73  0xdfec9db3, 0xcb7a879d, 0x5600419c, 0xca79b0b0, 0xdda33b5c,
74  0xa468649e, 0xdf5d73fa},
75 };
76 // Seed material for the EDN KAT generate command.
77 const dif_edn_seed_material_t kEdnKatSeedMaterialGenerate = {
78  .len = 0,
79 };
80 // Expected output data for the EDN KAT.
81 const uint32_t kExpectedOutput[kEdnKatOutputLen] = {
82  0xe48bb8cb, 0x1012c84c, 0x5af8a7f1, 0xd1c07cd9, 0xdf82ab22, 0x771c619b,
83  0xd40fccb1, 0x87189e99, 0x510494b3, 0x64f7ac0c, 0x2581f391, 0x80b1dc2f,
84  0x793e01c5, 0x87b107ae, 0xdb17514c, 0xa43c41b7,
85 };
86 
87 // Seed material for the edn alert part of this test. The CTR_DRBG construction
88 // implemented by CSRNG produces
89 //
90 // key = 00 01 02 03 04 05 06 07 - 08 09 0a 0b 0c 0d 0e 0f
91 // 10 11 12 13 14 15 16 17 - 18 19 1a 1b 1c 1d 1e 1f
92 //
93 // V = 8d 97 b4 1b c2 0a cb bb - 81 06 d3 91 85 46 67 f8
94 //
95 // from this seed material upon instantiate. The key is arbitrarily chosen.
96 // Encrypting V using this key then gives the required output seed. This seed
97 // material was taken from aes_testutils.c.
98 
99 // Seed material for the EDN alert test instantiate command.
100 const dif_edn_seed_material_t kEdnAlertTestSeedMaterialInstantiate = {
101  .len = kEdnKatMaxClen,
102  .data = {0xf0405279, 0x50a4261f, 0xf5ace1cf, 0xfff7b7d1, 0xa6ee8307,
103  0x1f57dfc8, 0x59757d79, 0xdeb6522e, 0xc8c67d84, 0xa16abefa,
104  0xc34030be, 0x530e88f8},
105 };
106 // Seed material for reseed command. After one generate, this seed material
107 // will bring the key and V of CSRNG back to the state after instantiate.
108 const dif_edn_seed_material_t kEdnAlertTestSeedMaterialReseed = {
109  .len = kEdnKatMaxClen,
110  .data = {0x5f6fb665, 0x21ca8e3f, 0x5ba3dba1, 0x2c10a9ec, 0x03b8cd4b,
111  0x8264aaea, 0x371e6305, 0x8fb186e1, 0xf622bc3e, 0x98e5d247,
112  0x73040c38, 0x6596739e},
113 };
114 
115 OTTF_DEFINE_TEST_CONFIG();
116 
117 // Initializes the peripherals used in this test.
118 static void init_peripherals(void) {
119  CHECK_DIF_OK(dif_csrng_init(
121  CHECK_DIF_OK(
122  dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0));
123  CHECK_DIF_OK(
124  dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1));
125  CHECK_DIF_OK(dif_rv_core_ibex_init(
127  &rv_core_ibex));
128 }
129 
130 dif_edn_auto_params_t kat_auto_params_build(bool alert_test) {
131  return (dif_edn_auto_params_t){
132  .instantiate_cmd =
133  {
134  .cmd = csrng_cmd_header_build(
135  kCsrngAppCmdInstantiate, kDifCsrngEntropySrcToggleDisable,
136  alert_test ? kEdnAlertTestSeedMaterialInstantiate.len
137  : kEdnKatSeedMaterialInstantiate.len,
138  /*generate_len=*/0),
139  .seed_material = alert_test ? kEdnAlertTestSeedMaterialInstantiate
140  : kEdnKatSeedMaterialInstantiate,
141  },
142  .reseed_cmd =
143  {
144  .cmd = csrng_cmd_header_build(
145  kCsrngAppCmdReseed, kDifCsrngEntropySrcToggleDisable,
146  alert_test ? kEdnAlertTestSeedMaterialReseed.len
147  : kEdnKatSeedMaterialReseed.len,
148  /*generate_len=*/0),
149  .seed_material = alert_test ? kEdnAlertTestSeedMaterialReseed
150  : kEdnKatSeedMaterialReseed,
151  },
152  .generate_cmd =
153  {
154  .cmd = csrng_cmd_header_build(
155  kCsrngAppCmdGenerate, kDifCsrngEntropySrcToggleDisable,
156  kEdnKatSeedMaterialGenerate.len,
157  /*generate_len=*/
158  alert_test ? 1 : kEdnKatOutputLen / kEdnKatWordsPerBlock),
159  .seed_material = kEdnKatSeedMaterialGenerate,
160  },
161  .reseed_interval = alert_test ? 1 : 32,
162  };
163 }
164 
165 static uint32_t ibex_get_rnd_data(void) {
166  uint32_t ibex_rnd_data;
167  CHECK_STATUS_OK(rv_core_ibex_testutils_get_rnd_data(
168  &rv_core_ibex, kEdnKatTimeout, &ibex_rnd_data));
169  return ibex_rnd_data;
170 }
171 
172 // Configure the entropy complex.
173 static void entropy_config(bool alert_test) {
174  dif_edn_auto_params_t edn_params =
175  edn_testutils_auto_params_build(true, /*res_itval=*/0, /*glen_val=*/0);
176  // Disable the entropy complex.
177  CHECK_STATUS_OK(entropy_testutils_stop_all());
178  // Enable ENTROPY_SRC in FIPS mode.
179  CHECK_STATUS_OK(entropy_testutils_entropy_src_init());
180  // Enable CSRNG.
181  CHECK_DIF_OK(dif_csrng_configure(&csrng));
182  // Enable EDN1 in auto request mode.
183  CHECK_DIF_OK(dif_edn_set_auto_mode(&edn1, edn_params));
184  // Enable EDN0 in auto request mode.
185  CHECK_DIF_OK(dif_edn_set_auto_mode(&edn0, kat_auto_params_build(alert_test)));
186 }
187 
188 static bool execute_kat(void) {
189  uint32_t ibex_rnd_data_got;
190  unsigned int next_val_pos = 0;
191  unsigned int hits = 0;
192 
193  // kExpectedOutput contains kEdnKatOutputLen precalculated words of the
194  // expected random output. The EDN might hold some buffered words from
195  // previous instantiations of the entropy complex. Since there are other
196  // blocks that consume entropy in between ibex rnd data requests, the first
197  // hit might not be the first word in the expected sequence. The following
198  // hits might also skip some words in the expected sequence if random words
199  // have been stolen by other blocks.
200  while (next_val_pos < kEdnKatOutputLen) {
201  // Fetch a new word of random bits.
202  ibex_rnd_data_got = ibex_get_rnd_data();
203  // Iterate over the remaining words in the expected sequence.
204  for (unsigned int i = next_val_pos; i < kEdnKatOutputLen; ++i) {
205  // If there is a hit, the next potential hit is the current position +1.
206  if (ibex_rnd_data_got == kExpectedOutput[i]) {
207  next_val_pos = i + 1;
208  hits++;
209  break;
210  // If the first hit has happened and there is no hit in this position,
211  // this word has been stolen by another block. Thus, we need to
212  // increment the potential next_val_pos.
213  } else if (hits) {
214  next_val_pos = i + 1;
215  }
216  }
217  }
218  // If less than half of the words in the expected sequence were hit throw an
219  // exception.
220  LOG_INFO("%d/%d expected random words have been hit.", hits,
221  kEdnKatOutputLen);
222  CHECK(hits >= kEdnKatOutputLen / 2);
223  return true;
224 }
225 
226 static void execute_alert_test(void) {
227  uint32_t alerts;
228  for (int i = 0; i <= 2 * kEdnKatWordsPerBlock; ++i) {
229  ibex_get_rnd_data();
230  }
231  CHECK_DIF_OK(dif_edn_get_recoverable_alerts(&edn0, &alerts));
232  CHECK((alerts >> kDifEdnRecoverableAlertRepeatedGenBits) & 0x1);
233 }
234 
235 bool test_main(void) {
236  LOG_INFO("init_peripherals start");
237  init_peripherals();
238  // Verify that EDN0 delivers the expected entropy bits using the
239  // rv_core_ibex.RND_DATA and rv_core_ibex.RND_STATUS registers.
240  entropy_config(/*alert_test=*/false);
241  IBEX_SPIN_FOR(execute_kat(), kEdnKatTimeout);
242  // Verify that EDN0 sets the RECOV_ALERT_STS.EDN_BUS_CMP_ALERT bit as CSRNG
243  // continues to provide the same entropy bits to EDN0.
244  entropy_config(/*alert_test=*/true);
245  execute_alert_test();
246 
247  return true;
248 }