Software APIs
dice_chain.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/silicon_creator/lib/cert/dice_chain.h"
6 
10 #include "sw/device/lib/crypto/drivers/entropy.h"
11 #include "sw/device/silicon_creator/lib/base/boot_measurements.h"
13 #include "sw/device/silicon_creator/lib/base/static_dice_cdi_0.h"
14 #include "sw/device/silicon_creator/lib/base/util.h"
15 #include "sw/device/silicon_creator/lib/cert/dice.h"
16 #include "sw/device/silicon_creator/lib/dbg_print.h"
17 #include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
18 #include "sw/device/silicon_creator/lib/drivers/kmac.h"
19 #include "sw/device/silicon_creator/lib/error.h"
20 #include "sw/device/silicon_creator/lib/manifest.h"
21 #include "sw/device/silicon_creator/lib/otbn_boot_services.h"
22 #include "sw/device/silicon_creator/lib/ownership/datatypes.h"
23 #include "sw/device/silicon_creator/manuf/base/perso_tlv_data.h"
24 
25 #include "flash_ctrl_regs.h" // Generated.
26 
27 enum {
28  /**
29  * The size of the scratch buffer that is large enough for constructing the
30  * CDI certs.
31  */
32  kScratchCertSizeBytes = FLASH_CTRL_PARAM_BYTES_PER_PAGE,
33 };
34 
35 /**
36  * Defines a class for parsing and building the DICE cert chain.
37  *
38  * All of the fields in this struct should be considered private, and users
39  * should call the public `dice_chain_*` functions instead.
40  */
41 typedef struct dice_chain {
42  /**
43  * RAM buffer that mirrors the DICE cert chain in a flash page.
44  */
45  uint8_t data[FLASH_CTRL_PARAM_BYTES_PER_PAGE];
46 
47  /**
48  * Indicate whether `data` needs to be written back to flash.
49  */
51 
52  /**
53  * The amount of bytes in `data` that has been processed.
54  */
55  size_t tail_offset;
56 
57  /**
58  * Indicate the info page currently buffered in `data`.
59  * This is used to skip unnecessary read ops.
60  */
62 
63  /**
64  * Id pair which points to the endorsement and cert ids below.
65  */
67 
68  /**
69  * Public key id for signing endorsement cert.
70  */
72 
73  /**
74  * Subject public key id of the current cert.
75  */
77 
78  /**
79  * Subject public key contents of the current cert.
80  */
82 
83  /**
84  * Scratch buffer for constructing CDI certs.
85  */
86  uint8_t scratch_cert[kScratchCertSizeBytes];
87 
88  /**
89  * The current tlv cert the builder is processing.
90  */
92 
93  /**
94  * Indicate whether the `cert_obj` is valid for the current `subject_pubkey`.
95  */
97 
98 } dice_chain_t;
99 
100 static dice_chain_t dice_chain;
101 
102 cert_key_id_pair_t dice_chain_cdi_0_key_ids = (cert_key_id_pair_t){
103  .endorsement = &static_dice_cdi_0.uds_pubkey_id,
104  .cert = &static_dice_cdi_0.cdi_0_pubkey_id,
105 };
106 
107 // Get the size of the remaining tail space that is not processed yet.
110 static size_t dice_chain_get_tail_size(void) {
111  HARDENED_CHECK_GE(sizeof(dice_chain.data), dice_chain.tail_offset);
112  return sizeof(dice_chain.data) - dice_chain.tail_offset;
113 }
114 
115 // Get the pointer to the remaining tail space that is not processed yet.
117 static uint8_t *dice_chain_get_tail_buffer(void) {
119 }
120 
121 // Cleanup stale `cert_obj` data and mark it as invalid.
122 static void dice_chain_reset_cert_obj(void) {
125 }
126 
127 /**
128  * Increments the DICE cert buffer offset to the next TLV object.
129  * (ensuring to round up to the 64-bit flash word offset to prevent potential
130  * ECC issues).
131  */
132 static void dice_chain_next_cert_obj(void) {
133  // Round up to next flash word for next perso TLV object offset.
134  size_t cert_size = dice_chain.cert_obj.obj_size;
135 
136  // The cert_size is only 12-bit, which won't cause unsigned overflow.
137  cert_size = util_size_to_words(cert_size) * sizeof(uint32_t);
138  cert_size = util_round_up_to(cert_size, 3);
139 
140  // Jump to the next object.
141  dice_chain.tail_offset += cert_size;
142 
143  // Post-check for the buffer boundary.
144  HARDENED_CHECK_LE(dice_chain.tail_offset, sizeof(dice_chain.data));
145 
146  dice_chain_reset_cert_obj();
147 }
148 
149 /**
150  * Load the tlv cert obj from the tail buffer and check if it's valid.
151  *
152  * This method will update the `dice_chain` fields of current certificate:
153  * * `cert_obj` will be all zeros if not TLV cert entry is found.
154  * * `cert_valid` will only be set to true if name and pubkey matches.
155  *
156  * @param name The cert name to match.
157  * @param name_size Size in byte of the `name` argument. Caller has to ensure it
158  * is smaller than kCrthNameSizeFieldMask.
159  * @return errors encountered during the operation.
160  */
162 static rom_error_t dice_chain_load_cert_obj(const char *name,
163  size_t name_size) {
164  rom_error_t err =
165  perso_tlv_get_cert_obj(dice_chain_get_tail_buffer(),
166  dice_chain_get_tail_size(), &dice_chain.cert_obj);
167 
168  if (err != kErrorOk) {
169  // Cleanup the stale value if error.
170  dice_chain_reset_cert_obj();
171  }
172 
173  if (err == kErrorPersoTlvCertObjNotFound) {
174  // If the cert is not found it is because we are running on a sim or FPGA
175  // platform, or the device has not yet been provisioned. Continue, and let
176  // the ROM_EXT generate an identity certificate for the current DICE stage.
177  // The error is not fatal, and the cert obj has been marked as invalid.
178  return kErrorOk;
179  }
180 
181  RETURN_IF_ERROR(err);
182 
183  // Check if this cert is what we are looking for.
184  if (name == NULL || memcmp(dice_chain.cert_obj.name, name, name_size) != 0) {
185  // Name unmatched, keep the cert_obj but mark it as invalid.
187  return kErrorOk;
188  }
189 
190  // Check if the subject pubkey is matched. `cert_valid` will be set to false
191  // if unmatched.
192  RETURN_IF_ERROR(dice_cert_check_valid(
195 
196  return kErrorOk;
197 }
198 
199 // Skip the TLV entry if the name matches.
200 static rom_error_t dice_chain_skip_cert_obj(const char *name,
201  size_t name_size) {
202  RETURN_IF_ERROR(dice_chain_load_cert_obj(NULL, 0));
203  if (memcmp(dice_chain.cert_obj.name, name, name_size) == 0) {
204  dice_chain_next_cert_obj();
205  }
206  return kErrorOk;
207 }
208 
209 // Load the certificate data from flash to RAM buffer.
211 static rom_error_t dice_chain_load_flash(
212  const flash_ctrl_info_page_t *info_page) {
213  // Skip reload if it's already buffered.
214  if (dice_chain.info_page == info_page) {
216  return kErrorOk;
217  }
218 
219  // We are switching to a different page, flush changes (if dirty) first.
220  RETURN_IF_ERROR(dice_chain_flush_flash());
221 
222  // Read in a DICE certificate(s) page.
223  static_assert(sizeof(dice_chain.data) == FLASH_CTRL_PARAM_BYTES_PER_PAGE,
224  "Invalid dice_chain buffer size");
225  RETURN_IF_ERROR(flash_ctrl_info_read_zeros_on_read_error(
226  info_page, /*offset=*/0,
227  /*word_count=*/FLASH_CTRL_PARAM_BYTES_PER_PAGE / sizeof(uint32_t),
228  dice_chain.data));
229 
230  // Resets the flash page status.
233  dice_chain.info_page = info_page;
234  dice_chain_reset_cert_obj();
235 
236  return kErrorOk;
237 }
238 
239 // Push the certificate to the tail with TLV header.
241 static rom_error_t dice_chain_push_cert(const char *name, const uint8_t *cert,
242  const size_t cert_size) {
243  // The data is going to be updated, mark it as dirty and clear the tail.
245 
246  // Invalidate all the remaining certificates in the tail buffer.
247  memset(dice_chain_get_tail_buffer(), 0, dice_chain_get_tail_size());
248 
249  // Encode the certificate to the tail buffer.
250  size_t cert_page_left = dice_chain_get_tail_size();
251  RETURN_IF_ERROR(
252  perso_tlv_cert_obj_build(name, kPersoObjectTypeX509Cert, cert, cert_size,
253  dice_chain_get_tail_buffer(), &cert_page_left));
254 
255  // Move the offset to the new tail.
256  RETURN_IF_ERROR(perso_tlv_get_cert_obj(dice_chain_get_tail_buffer(),
257  dice_chain_get_tail_size(),
258  &dice_chain.cert_obj));
259  dice_chain_next_cert_obj();
260  return kErrorOk;
261 }
262 
263 rom_error_t dice_chain_attestation_silicon(void) {
264  // Initialize the entropy complex and KMAC for key manager operations.
265  // Note: `OTCRYPTO_OK.value` is equal to `kErrorOk` but we cannot add a static
266  // assertion here since its definition is not an integer constant expression.
267  HARDENED_RETURN_IF_ERROR((rom_error_t)entropy_complex_init().value);
268  HARDENED_RETURN_IF_ERROR(kmac_keymgr_configure());
269 
270  // Set keymgr reseed interval. Start with the maximum value to avoid
271  // entropy complex contention during the boot process.
272  const uint16_t kScKeymgrEntropyReseedInterval = UINT16_MAX;
273  sc_keymgr_entropy_reseed_interval_set(kScKeymgrEntropyReseedInterval);
274  SEC_MMIO_WRITE_INCREMENT(kScKeymgrSecMmioEntropyReseedIntervalSet);
275 
276  // ROM sets the SW binding values for the first key stage (CreatorRootKey) but
277  // does not initialize the key manager. Advance key manager state twice to
278  // transition to the CreatorRootKey state.
279  RETURN_IF_ERROR(sc_keymgr_state_check(kScKeymgrStateReset));
280  sc_keymgr_advance_state();
281  RETURN_IF_ERROR(sc_keymgr_state_check(kScKeymgrStateInit));
282 
283  // Generate UDS keys.
284  sc_keymgr_advance_state();
285  HARDENED_RETURN_IF_ERROR(sc_keymgr_state_check(kScKeymgrStateCreatorRootKey));
286  HARDENED_RETURN_IF_ERROR(otbn_boot_cert_ecc_p256_keygen(
287  kDiceKeyUds, &static_dice_cdi_0.uds_pubkey_id,
288  &static_dice_cdi_0.uds_pubkey));
289 
290  // Save UDS key for signing next stage cert.
291  RETURN_IF_ERROR(otbn_boot_attestation_key_save(
292  kDiceKeyUds.keygen_seed_idx, kDiceKeyUds.type,
293  *kDiceKeyUds.keymgr_diversifier));
294 
295  return kErrorOk;
296 }
297 
298 rom_error_t dice_chain_attestation_creator(
299  keymgr_binding_value_t *rom_ext_measurement,
300  const manifest_t *rom_ext_manifest) {
301  // Generate CDI_0 attestation keys and (potentially) update certificate.
302  keymgr_binding_value_t seal_binding_value = {
303  .data = {rom_ext_manifest->identifier, 0}};
304  SEC_MMIO_WRITE_INCREMENT(kScKeymgrSecMmioSwBindingSet +
305  kScKeymgrSecMmioOwnerIntMaxVerSet);
306  HARDENED_RETURN_IF_ERROR(sc_keymgr_owner_int_advance(
307  /*sealing_binding=*/&seal_binding_value,
308  /*attest_binding=*/rom_ext_measurement,
309  rom_ext_manifest->max_key_version));
310  HARDENED_RETURN_IF_ERROR(otbn_boot_cert_ecc_p256_keygen(
311  kDiceKeyCdi0, &static_dice_cdi_0.cdi_0_pubkey_id,
312  &static_dice_cdi_0.cdi_0_pubkey));
313 
314  // Switch page for the device generated CDI_0.
315  RETURN_IF_ERROR(dice_chain_load_flash(&kFlashCtrlInfoPageDiceCerts));
316 
317  // Seek to skip previous objects.
318  RETURN_IF_ERROR(dice_chain_skip_cert_obj("UDS", /*name_size=*/4));
319 
320  // Check if the current CDI_0 cert is valid.
321  RETURN_IF_ERROR(dice_chain_load_cert_obj("CDI_0", /*name_size=*/6));
323  dbg_puts("CDI_0 certificate not valid. Updating it ...\r\n");
324  // Update the cert page buffer.
325  static_dice_cdi_0.cert_size = sizeof(static_dice_cdi_0.cert_data);
326  HARDENED_RETURN_IF_ERROR(dice_cdi_0_cert_build(
327  (hmac_digest_t *)rom_ext_measurement->data,
328  rom_ext_manifest->security_version, &dice_chain_cdi_0_key_ids,
329  &static_dice_cdi_0.cdi_0_pubkey, static_dice_cdi_0.cert_data,
330  &static_dice_cdi_0.cert_size));
331  } else {
332  // Replace UDS with CDI_0 key for endorsing next stage cert.
333  HARDENED_RETURN_IF_ERROR(otbn_boot_attestation_key_save(
334  kDiceKeyCdi0.keygen_seed_idx, kDiceKeyCdi0.type,
335  *kDiceKeyCdi0.keymgr_diversifier));
336  }
337 
338  sc_keymgr_sw_binding_unlock_wait();
339 
340  return kErrorOk;
341 }
342 
343 // Compare the UDS identity in the static critical section to the UDS cert
344 // cached in the flash.
345 static rom_error_t dice_chain_attestation_check_uds(void) {
346  // Switch page for the factory provisioned UDS cert.
347  RETURN_IF_ERROR(dice_chain_load_flash(&kFlashCtrlInfoPageFactoryCerts));
348 
349  // Check if the UDS cert is valid.
350  dice_chain.endorsement_pubkey_id = static_dice_cdi_0.uds_pubkey_id;
351  dice_chain.subject_pubkey_id = static_dice_cdi_0.uds_pubkey_id;
352  dice_chain.subject_pubkey = static_dice_cdi_0.uds_pubkey;
353  RETURN_IF_ERROR(dice_chain_load_cert_obj("UDS", /*name_size=*/4));
355  // The UDS key ID (and cert itself) should never change unless:
356  // 1. there is a hardware issue / the page has been corrupted, or
357  // 2. the cert has not yet been provisioned.
358  //
359  // In both cases, we do nothing, and boot normally, later attestation
360  // attempts will fail in a detectable manner.
361 
362  // CAUTION: This error message should match the one in
363  // //sw/host/provisioning/ft_lib/src/lib.rs
364  dbg_puts("error: UDS certificate not valid\r\n");
365  }
366 
367  return kErrorOk;
368 }
369 
370 // Compare the CDI_0 identity in the static critical section to the CDI_0 cert
371 // cached in the flash, and refresh the cache if invalid.
372 static rom_error_t dice_chain_attestation_check_cdi_0(void) {
373  // Switch page for the device CDI chain.
374  RETURN_IF_ERROR(dice_chain_load_flash(&kFlashCtrlInfoPageDiceCerts));
375 
376  // Seek to skip previous objects.
377  RETURN_IF_ERROR(dice_chain_skip_cert_obj("UDS", /*name_size=*/4));
378 
379  // Refresh cdi 0 if invalid
380  dice_chain.endorsement_pubkey_id = static_dice_cdi_0.cdi_0_pubkey_id;
381  dice_chain.subject_pubkey_id = static_dice_cdi_0.cdi_0_pubkey_id;
382  dice_chain.subject_pubkey = static_dice_cdi_0.cdi_0_pubkey;
383  RETURN_IF_ERROR(dice_chain_load_cert_obj("CDI_0", /*name_size=*/6));
385  dbg_puts("warning: CDI_0 certificate not valid; updating\r\n");
386  // Update the cert page buffer.
387  RETURN_IF_ERROR(dice_chain_push_cert("CDI_0", static_dice_cdi_0.cert_data,
388  static_dice_cdi_0.cert_size));
389  } else {
390  // Cert is valid, move to the next one.
391  dice_chain_next_cert_obj();
392  }
393 
394  return kErrorOk;
395 }
396 
397 rom_error_t dice_chain_attestation_owner(
398  const manifest_t *owner_manifest, keymgr_binding_value_t *bl0_measurement,
399  hmac_digest_t *owner_measurement, keymgr_binding_value_t *sealing_binding,
400  owner_app_domain_t key_domain) {
401  // Handles the certificates from the immutable rom_ext first.
402  RETURN_IF_ERROR(dice_chain_attestation_check_uds());
403  RETURN_IF_ERROR(dice_chain_attestation_check_cdi_0());
404 
405  // Generate CDI_1 attestation keys and (potentially) update certificate.
406  SEC_MMIO_WRITE_INCREMENT(kScKeymgrSecMmioSwBindingSet +
407  kScKeymgrSecMmioOwnerIntMaxVerSet);
408  static_assert(
409  sizeof(hmac_digest_t) == sizeof(keymgr_binding_value_t),
410  "Expect the keymgr binding value to be the same size as a sha256 digest");
411 
412  // Aggregate the owner firmware (BL0) measurement and the ownership
413  // measurement into a single attestation measurment. The attestation
414  // measurement is used to initialize the keymgr.
415  hmac_digest_t attest_measurement;
416  hmac_sha256_configure(false);
417  hmac_sha256_start();
418  hmac_sha256_update(bl0_measurement, sizeof(*bl0_measurement));
419  hmac_sha256_update(owner_measurement, sizeof(*owner_measurement));
420  hmac_sha256_process();
421  hmac_sha256_final(&attest_measurement);
422 
423  HARDENED_RETURN_IF_ERROR(sc_keymgr_owner_advance(
424  /*sealing_binding=*/sealing_binding,
425  /*attest_binding=*/(keymgr_binding_value_t *)&attest_measurement,
426  owner_manifest->max_key_version));
427  HARDENED_RETURN_IF_ERROR(otbn_boot_cert_ecc_p256_keygen(
429 
430  // Check if the current CDI_1 cert is valid.
431  RETURN_IF_ERROR(dice_chain_load_cert_obj("CDI_1", /*name_size=*/6));
433  dbg_puts("CDI_1 certificate not valid. Updating it ...\r\n");
434  // Update the cert page buffer.
435  size_t updated_cert_size = kScratchCertSizeBytes;
436  // TODO(#19596): add owner configuration block measurement to CDI_1 cert.
437  HARDENED_RETURN_IF_ERROR(dice_cdi_1_cert_build(
438  (hmac_digest_t *)bl0_measurement, owner_measurement,
439  owner_manifest->security_version, key_domain, &dice_chain.key_ids,
441  &updated_cert_size));
442  RETURN_IF_ERROR(dice_chain_push_cert("CDI_1", dice_chain.scratch_cert,
443  updated_cert_size));
444  } else {
445  // Cert is valid, move to the next one.
446  dice_chain_next_cert_obj();
447 
448  // Replace CDI_0 with CDI_1 key for endorsing next stage cert.
449  HARDENED_RETURN_IF_ERROR(otbn_boot_attestation_key_save(
450  kDiceKeyCdi1.keygen_seed_idx, kDiceKeyCdi1.type,
451  *kDiceKeyCdi1.keymgr_diversifier));
452  }
454 
455  sc_keymgr_sw_binding_unlock_wait();
456 
457  return kErrorOk;
458 }
459 
460 // Write the DICE certs to flash if they have been updated.
461 rom_error_t dice_chain_flush_flash(void) {
463  dice_chain.info_page != NULL) {
464  RETURN_IF_ERROR(
465  flash_ctrl_info_erase(dice_chain.info_page, kFlashCtrlEraseTypePage));
466  static_assert(sizeof(dice_chain.data) == FLASH_CTRL_PARAM_BYTES_PER_PAGE,
467  "Invalid dice_chain buffer size");
468  RETURN_IF_ERROR(flash_ctrl_info_write(
470  /*offset=*/0,
471  /*word_count=*/FLASH_CTRL_PARAM_BYTES_PER_PAGE / sizeof(uint32_t),
472  dice_chain.data));
473  dbg_puts("Flushed dice cert page\r\n");
475  }
476  return kErrorOk;
477 }
478 
479 rom_error_t dice_chain_init(void) {
480  // Variable initialization.
481  memset(&dice_chain, 0, sizeof(dice_chain));
485  .cert = &dice_chain.subject_pubkey_id,
486  };
487  dice_chain_reset_cert_obj();
488 
489  // Configure DICE certificate flash info page and buffer it into RAM.
490  flash_ctrl_cert_info_page_creator_cfg(&kFlashCtrlInfoPageDiceCerts);
491  flash_ctrl_info_cfg_set(&kFlashCtrlInfoPageFactoryCerts,
492  kCertificateInfoPageCfg);
493  flash_ctrl_cert_info_page_owner_restrict(&kFlashCtrlInfoPageFactoryCerts);
494  return kErrorOk;
495 }