Software APIs
keymgr_derive_cdi_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 <stdbool.h>
6 #include <stdint.h>
7 #include <string.h>
8 
9 #include "sw/device/lib/testing/keymgr_testutils.h"
11 #include "sw/device/lib/testing/ret_sram_testutils.h"
12 #include "sw/device/lib/testing/rstmgr_testutils.h"
13 #include "sw/device/lib/testing/sram_ctrl_testutils.h"
15 #include "sw/device/silicon_creator/lib/drivers/retention_sram.h"
16 
18 
19 static dif_keymgr_t keymgr;
20 static dif_kmac_t kmac;
21 static dif_otbn_t otbn;
22 static dif_rstmgr_t rstmgr;
23 static dif_sram_ctrl_t sram_ctrl;
24 
25 enum {
26  kKeymgrOutputSizeWords = 8,
27  kKeymgrOutputSizeBytes = 32,
28 
29  kX2551PublicKeySizeBytes = 32,
30 
31  // The retention SRAM testutils allocate some internal data as well as a
32  // number of counters; both of which should not be overwritten by this
33  // test. Hence, the actual base address is offset to account for this.
34  kRetSramBaseAddr = TOP_EARLGREY_RAM_RET_AON_BASE_ADDR +
35  offsetof(retention_sram_t, owner) +
36  4 * kRetSramTestutilsNumberOfCounters,
37 
38 // Compile-time directive to choose between CDI flow types. The default
39 // derivation type is sealing.
40 #ifndef DERIVE_ATTESTATION
41  kCdiType = kDifKeymgrSealingCdi
42 #else
43  kCdiType = kDifKeymgrAttestationCdi
44 #endif
45 };
46 
47 /**
48  * Grouping of the three CDI output variants that can be generated in
49  * each key manager state (identity, versioned software key, sideload OTBN key).
50  *
51  * Note that the sideload OTBN key is not visible to software. In order to
52  * run the same verification steps as for the identity and software keys, a
53  * X25519 public key is generated in the OTBN and retrieved. For the sake of
54  * simplicity, and only in the confines of this test, we can assume that the
55  * X25519 public key and the sideload OTBN key refer to the same thing.
56  */
57 typedef struct cdi_outputs {
58  dif_keymgr_output_t identity;
59  dif_keymgr_output_t sw_key;
60  uint32_t sideload_key[kX2551PublicKeySizeBytes];
62 
63 // Symbols of the OTBN X22519 public key generation program.
64 // See sw/otbn/crypto/x25519_sideload.s for the source code.
65 OTBN_DECLARE_APP_SYMBOLS(x25519_sideload);
66 OTBN_DECLARE_SYMBOL_ADDR(x25519_sideload, enc_u);
67 OTBN_DECLARE_SYMBOL_ADDR(x25519_sideload, enc_result);
68 static const otbn_app_t kOtbnAppX25519 = OTBN_APP_T_INIT(x25519_sideload);
69 static const otbn_addr_t kOtbnVarEncU =
70  OTBN_ADDR_T_INIT(x25519_sideload, enc_u);
71 static const otbn_addr_t kOtbnVarEncResult =
72  OTBN_ADDR_T_INIT(x25519_sideload, enc_result);
73 
74 OTTF_DEFINE_TEST_CONFIG();
75 
76 /**
77  * Initialize the dif handles required for this test.
78  */
79 static void init_peripheral_handles(void) {
80  // The testutils initialize the key manager and KMAC handles.
81  CHECK_STATUS_OK(keymgr_testutils_initialize(&keymgr, &kmac));
82 
83  // Reconfigure the KMAC to use the EDN entropy source as opposed to a
84  // software-provided one as is default in the testutils.
85  dif_kmac_config_t config = {
86  .entropy_mode = kDifKmacEntropyModeEdn,
87  .entropy_fast_process = true,
88  .sideload = true,
89  };
90  CHECK_DIF_OK(dif_kmac_configure(&kmac, config));
91 
92  CHECK_DIF_OK(dif_rstmgr_init(
94  CHECK_DIF_OK(dif_sram_ctrl_init(
96  &sram_ctrl));
97  CHECK_DIF_OK(
98  dif_otbn_init(mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), &otbn));
99 }
100 
101 /**
102  * Equality check of two masked key manager outputs without unmasking.
103  *
104  * @param output_a First key manager output.
105  * @param output_b Second key manager output.
106  * @return Result of the equality check.
107  */
108 static bool compare_outputs(const dif_keymgr_output_t *output_a,
109  const dif_keymgr_output_t *output_b) {
110  uint32_t scratch[kKeymgrOutputSizeWords];
111  for (int i = 0; i < kKeymgrOutputSizeWords; i++) {
112  // Check whether two masked keys are equal without unmasking them:
113  //
114  // output_a0 = k0 + m0, output_a1 = m0
115  // output_b0 = k1 + m1, output_b1 = m1
116  // t = output_a0 + output_b0 = m0 + m1
117  // 0 = t ^ output_a1 ^ output_b1
118  //
119  // In order to prevent an accidental unmasking through reordering of the
120  // operation after compilation, `t` is read into an auxiliary array.
121  scratch[i] = output_a->value[0][i] ^ output_b->value[0][i];
122  if ((scratch[i] ^ output_a->value[1][i] ^ output_b->value[1][i]) != 0x0) {
123  return false;
124  }
125  }
126  return true;
127 }
128 
129 /**
130  * Write a `cdi_outputs_t` object into the retention SRAM at a specific offset
131  * from `kRetSramBaseAddr`. The offset then is incremented by the size of the
132  * written data.
133  *
134  * @param outputs The CDI outputs to be stored.
135  * @param offset The offset from `kRetSramBaseAddr`.
136  */
137 static void ret_sram_write_keys(const cdi_outputs_t *outputs, size_t *offset) {
138  uint32_t buf[sizeof(cdi_outputs_t)];
139  memcpy(buf, outputs, sizeof(cdi_outputs_t));
140 
141  sram_ctrl_testutils_write(
142  kRetSramBaseAddr + *offset,
143  (sram_ctrl_testutils_data_t){.words = buf, .len = sizeof(cdi_outputs_t)});
144  *offset += sizeof(cdi_outputs_t);
145 }
146 
147 /**
148  * Read a `cdi_outputs_t` object from the retention SRAM at a specific offset
149  * from `kRetSramBaseAddr`. The offset then is incremented by the size of the
150  * read data.
151  *
152  * @param outputs The destination of the read CDI outputs.
153  * @param offset The offset from `kRetSramBaseAddr`.
154  */
155 static void ret_sram_read_keys(cdi_outputs_t *outputs, size_t *offset) {
156  memcpy(outputs, (uint8_t *)(kRetSramBaseAddr + *offset),
157  sizeof(cdi_outputs_t));
158  *offset += sizeof(cdi_outputs_t);
159 }
160 
161 /**
162  * Invoke the generation of an CDI identity and read it back.
163  *
164  * @param state_name The current key manager state string.
165  * @param identity The destination of the read identity.
166  */
167 static void derive_identity(const char *state_name,
168  dif_keymgr_output_t *identity) {
169  CHECK_STATUS_OK(keymgr_testutils_generate_identity(
170  &keymgr, (dif_keymgr_identity_seed_params_t){.cdi_type = kCdiType}));
171  LOG_INFO("Keymgr generated identity at %s State", state_name);
172 
173  CHECK_DIF_OK(dif_keymgr_read_output(&keymgr, identity));
174 }
175 
176 /**
177  * Invoke the generation of a CDI versioned software key and read it back.
178  * A second generation with an invalid key version should fail.
179  *
180  * @param state_name The current key manager state string.
181  * @param key The destination of the read software key.
182  */
183 static void derive_sw_key(const char *state_name, dif_keymgr_output_t *key) {
184  uint32_t max_version;
185  CHECK_STATUS_OK(keymgr_testutils_max_key_version_get(&keymgr, &max_version));
186 
187  dif_keymgr_versioned_key_params_t params = kKeyVersionedParams;
189  params.version = max_version;
190  params.cdi_type = kCdiType;
191 
192  CHECK_STATUS_OK(keymgr_testutils_generate_versioned_key(&keymgr, params));
193  LOG_INFO("Keymgr generated SW output at %s State", state_name);
194 
195  CHECK_DIF_OK(dif_keymgr_read_output(&keymgr, key));
196 
197 #ifndef DERIVE_ATTESTATION
198  // If the key version is larger than the permitted maximum version, then
199  // the key generation must fail.
200  params.version += 1;
201  CHECK_STATUS_NOT_OK(keymgr_testutils_generate_versioned_key(&keymgr, params));
202 #endif
203 }
204 
205 /**
206  * Invoke the generation of sideload OTBN key, run the X25519 OTBN program and
207  * read back the resulting public key. A second generation with an invalid key
208  * version should fail.
209  *
210  * @param state_name The current key manager state string.
211  * @param The destination of the read X25519 public key.
212  */
213 static void derive_sideload_otbn_key(const char *state_name,
214  uint32_t key[kKeymgrOutputSizeWords]) {
215  uint32_t max_version;
216  CHECK_STATUS_OK(keymgr_testutils_max_key_version_get(&keymgr, &max_version));
217 
218  dif_keymgr_versioned_key_params_t params = kKeyVersionedParams;
220  params.version = max_version;
221 
222  CHECK_STATUS_OK(keymgr_testutils_generate_versioned_key(&keymgr, params));
223  LOG_INFO("Keymgr generated HW output for Otbn at %s State", state_name);
224 
225  // Run the X25519 public key generation. For more details, see the OTBN
226  // sideload test sw/device/tests/keymgr_sideload_otbn_test.c.
227  CHECK_STATUS_OK(otbn_testutils_load_app(&otbn, kOtbnAppX25519));
228  CHECK_DIF_OK(dif_otbn_set_ctrl_software_errs_fatal(&otbn, false));
229 
230  const uint32_t kEncodedU[8] = {
231  // Montgomery u-Coordinate.
232  0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
233  };
234  CHECK_STATUS_OK(otbn_testutils_write_data(&otbn, sizeof(kEncodedU),
235  &kEncodedU, kOtbnVarEncU));
236  LOG_INFO("Starting OTBN program...");
237  CHECK_DIF_OK(dif_otbn_set_ctrl_software_errs_fatal(&otbn, false));
238  CHECK_STATUS_OK(otbn_testutils_execute(&otbn));
239  CHECK_STATUS_OK(otbn_testutils_wait_for_done(&otbn, 0));
240  CHECK_STATUS_OK(otbn_testutils_read_data(&otbn, kX2551PublicKeySizeBytes,
241  kOtbnVarEncResult, key));
242 
243 #ifndef DERIVE_ATTESTATION
244  // If the key version is larger than the permitted maximum version, then
245  // the key generation must fail.
246  params.version += 1;
247  CHECK_STATUS_NOT_OK(keymgr_testutils_generate_versioned_key(&keymgr, params));
248 #endif
249 }
250 
251 /**
252  * Derive a CDI identity, software key and sideload OTBN key. If the `write`
253  * flag is set, then the keys are written at specific offset in the retention
254  * SRAM. In the other case, the generated outputs are compared against outputs
255  * from the retention SRAM (corresponding to a run before the reset). Pre- and
256  * post- prefix outputs should match. If some previous outputs are provided from
257  * an earlier key manager state, compare that the newly generated ones differ.
258  *
259  * @param state_name The current key manager state string.
260  * @param prev_outputs Generated outptus from a previous key manager state.
261  * @param offset The offset at which to read or write in the retention SRAM.
262  * @param write Indicating whether to write the new keys to the retention SRAM.
263  * @param next_outputs The destination of the newly generated CDI outputs.
264  */
265 static void derive_keys(const char *state_name,
266  const cdi_outputs_t *prev_outputs, size_t *offset,
267  bool write, cdi_outputs_t *next_outputs) {
268  derive_identity(state_name, &next_outputs->identity);
269  derive_sw_key(state_name, &next_outputs->sw_key);
270  derive_sideload_otbn_key(state_name, next_outputs->sideload_key);
271 
272  if (prev_outputs) {
273  CHECK(!compare_outputs(&prev_outputs->identity, &next_outputs->identity));
274  CHECK(!compare_outputs(&prev_outputs->sw_key, &next_outputs->sw_key));
275  CHECK_ARRAYS_NE(prev_outputs->sideload_key, next_outputs->sideload_key,
276  kKeymgrOutputSizeWords);
277  }
278 
279  if (write) {
280  ret_sram_write_keys(next_outputs, offset);
281  } else {
282  cdi_outputs_t scratch;
283  ret_sram_read_keys(&scratch, offset);
284 
285  CHECK(compare_outputs(&scratch.identity, &next_outputs->identity));
286  CHECK(compare_outputs(&scratch.sw_key, &next_outputs->sw_key));
287  CHECK_ARRAYS_EQ(scratch.sideload_key, next_outputs->sideload_key,
288  kKeymgrOutputSizeWords);
289  }
290 }
291 
292 /**
293  * This test implements both the `chip_sw_keymgr_derive_sealing` and
294  * `chip_sw_keymgr_derive_attestation` testplan items, i.e.,
295  * verifies the validity of the sealing and attestation CDI flows. The
296  * individual testpoint requirements are as follows:
297  *
298  * - For each keymgr operational state: `CreatorRootKey`, `OwnerIntKey` and
299  * `OwnerKey`:
300  * - Generate identity SW output for the Attestation CDI.
301  * - Generate SW output for the Attestation CDI.
302  * - Generate OTBN sideload output for the Attestation CDI.
303  *
304  * The creation of the three outputs is handled by the `derive_identity`,
305  * `derive_sw_key` and `derive_sideload_otbn_key` functions, which are invoked
306  * by `derive_keys` through `test_derive_cdi` that traverses the three
307  * operational states.
308  *
309  * - Ensure that the key output changes after calculating the previous steps
310  * after a keymgr advance operation.
311  *
312  * The generated CDI outputs in state `i` are passed to state `i+1` and checked
313  * in `derive_keys` that they differ.
314  *
315  * - The keymgr shall be able to reproduce the same keys for a give device
316  * configuration and known set of inputs.
317  *
318  * The CDI outputs in each state are written into the retention SRAM. After
319  * having cycled through all the states, a software reset is performed and
320  * CDI output generation is repeated making sure that the same keys are being
321  * created (same as the keys in the retention SRAM from the first run) as the
322  * device configuration and inputs are unchanged. Reading/writing and key
323  * comparison all happen in `derive_keys`.
324  *
325  * - The software binding registers must be locked after configuration until a
326  * keymgr advance operation.
327  *
328  * This check is implemented in the keymgr DIF (see `dif_keymgr_advance_state`).
329  *
330  * - OTP SECRET2 partition (which contains the CREATOR_ROOT_KEY) and flash info
331  * pages 1 and 2 in partition 0 must be configured, otherwise the keymgr will
332  * fail to advance into operational states.
333  *
334  * The provisioning of the device secrets in the OTP SECRET2 partition is
335  * handled by the Bazel execution environment, and the flash info secrets are
336  * handled by the keymgr testutils (see `keymgr_testutils_init_nvm_then_reset`).
337  *
338  * - Ensure the entropy complex is running in continuous mode, and that KMAC is
339  * configured to extract entropy from EDN.
340  *
341  * This is configured in the keymgr testutils (see `keymgr_testutils_startup`)
342  * and in `init_peripheral_handles`.
343  *
344  * - The test should check for any error or fault code status, to ensure all
345  * operations executed successfully.
346  *
347  * Every API call to either the keymgr testutils or the DIF is checked for its
348  * correctness.
349  *
350  * @param reset_counter Indicator of the test phase.
351  */
352 static void test_derive_cdi(size_t reset_counter) {
353  const char *state_name;
354 
355  dif_keymgr_state_t keymgr_state;
356  CHECK_DIF_OK(dif_keymgr_get_state(&keymgr, &keymgr_state));
357 
358  cdi_outputs_t curr_outputs;
359  cdi_outputs_t next_outputs;
360 
361  size_t offset = 0;
362  const bool write = reset_counter == 0;
363 
364  switch (keymgr_state) {
366 
367  CHECK_STATUS_OK(keymgr_testutils_state_string_get(&keymgr, &state_name));
368  LOG_INFO("Keymgr entered %s State", state_name);
369 
370  derive_keys(state_name, NULL, &offset, write, &curr_outputs);
371 
372  CHECK_STATUS_OK(
373  keymgr_testutils_advance_state(&keymgr, &kOwnerIntParams));
374 
377 
378  CHECK_STATUS_OK(keymgr_testutils_state_string_get(&keymgr, &state_name));
379  LOG_INFO("Keymgr entered %s State", state_name);
380 
381  derive_keys(state_name,
382  // If the initial key manager state is smaller than
383  // the current one, verify that the new keys differ
384  // from the previous ones.
386  ? &curr_outputs
387  : NULL,
388  &offset, write, &next_outputs);
389  memcpy(&curr_outputs, &next_outputs, sizeof(cdi_outputs_t));
390 
391  CHECK_STATUS_OK(
392  keymgr_testutils_advance_state(&keymgr, &kOwnerRootKeyParams));
393 
396 
397  CHECK_STATUS_OK(keymgr_testutils_state_string_get(&keymgr, &state_name));
398  LOG_INFO("Keymgr entered %s State", state_name);
399 
400  derive_keys(
401  state_name,
402  keymgr_state < kDifKeymgrStateOwnerRootKey ? &curr_outputs : NULL,
403  &offset, write, &next_outputs);
404 
405  break;
406  default:
407  // Theoretically, the key manager can boot into an earlier state
408  // (`kDifKeymgrStateReset` or `kDifKeymgrStateInitialized`). This is not
409  // supported by the testutils and thus must not occur here.
410  CHECK(0, "unexpected key manager state %u", keymgr_state);
411  }
412 }
413 
414 bool test_main(void) {
415  const dif_rstmgr_reset_info_bitfield_t reset_info =
416  rstmgr_testutils_reason_get();
417  size_t reset_counter;
418 
419  ret_sram_testutils_init();
420 
421  // Reset the reset counter after the first power up.
422  if (reset_info == kDifRstmgrResetInfoPor) {
423  CHECK_STATUS_OK(ret_sram_testutils_counter_clear(0));
424  }
425  CHECK_STATUS_OK(ret_sram_testutils_counter_get(0, &reset_counter));
426 
427  init_peripheral_handles();
428 
429  test_derive_cdi(reset_counter);
430 
431  // Increment the reset counter then reset the device.
432  if (reset_counter == 0) {
433  CHECK_STATUS_OK(ret_sram_testutils_counter_increment(0));
434  CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
436  }
437 
438  return true;
439 }