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  // Update the cert page buffer.
324  static_dice_cdi_0.cert_size = sizeof(static_dice_cdi_0.cert_data);
325  HARDENED_RETURN_IF_ERROR(dice_cdi_0_cert_build(
326  (hmac_digest_t *)rom_ext_measurement->data,
327  rom_ext_manifest->security_version, &dice_chain_cdi_0_key_ids,
328  &static_dice_cdi_0.cdi_0_pubkey, static_dice_cdi_0.cert_data,
329  &static_dice_cdi_0.cert_size));
330  } else {
331  // Replace UDS with CDI_0 key for endorsing next stage cert.
332  HARDENED_RETURN_IF_ERROR(otbn_boot_attestation_key_save(
333  kDiceKeyCdi0.keygen_seed_idx, kDiceKeyCdi0.type,
334  *kDiceKeyCdi0.keymgr_diversifier));
335  }
336 
337  sc_keymgr_sw_binding_unlock_wait();
338 
339  return kErrorOk;
340 }
341 
342 // Compare the UDS identity in the static critical section to the UDS cert
343 // cached in the flash.
344 static rom_error_t dice_chain_attestation_check_uds(void) {
345  // Switch page for the factory provisioned UDS cert.
346  RETURN_IF_ERROR(dice_chain_load_flash(&kFlashCtrlInfoPageFactoryCerts));
347 
348  // Check if the UDS cert is valid.
349  dice_chain.endorsement_pubkey_id = static_dice_cdi_0.uds_pubkey_id;
350  dice_chain.subject_pubkey_id = static_dice_cdi_0.uds_pubkey_id;
351  dice_chain.subject_pubkey = static_dice_cdi_0.uds_pubkey;
352  RETURN_IF_ERROR(dice_chain_load_cert_obj("UDS", /*name_size=*/4));
354  // The UDS key ID (and cert itself) should never change unless:
355  // 1. there is a hardware issue / the page has been corrupted, or
356  // 2. the cert has not yet been provisioned.
357  //
358  // In both cases, we do nothing, and boot normally, later attestation
359  // attempts will fail in a detectable manner.
360 
361  // CAUTION: This error message should match the one in
362  // //sw/host/provisioning/ft_lib/src/lib.rs
363  dbg_puts("error: UDS certificate not valid\r\n");
364  }
365 
366  return kErrorOk;
367 }
368 
369 // Compare the CDI_0 identity in the static critical section to the CDI_0 cert
370 // cached in the flash, and refresh the cache if invalid.
371 static rom_error_t dice_chain_attestation_check_cdi_0(void) {
372  // Switch page for the device CDI chain.
373  RETURN_IF_ERROR(dice_chain_load_flash(&kFlashCtrlInfoPageDiceCerts));
374 
375  // Seek to skip previous objects.
376  RETURN_IF_ERROR(dice_chain_skip_cert_obj("UDS", /*name_size=*/4));
377 
378  // Refresh cdi 0 if invalid
379  dice_chain.endorsement_pubkey_id = static_dice_cdi_0.cdi_0_pubkey_id;
380  dice_chain.subject_pubkey_id = static_dice_cdi_0.cdi_0_pubkey_id;
381  dice_chain.subject_pubkey = static_dice_cdi_0.cdi_0_pubkey;
382  RETURN_IF_ERROR(dice_chain_load_cert_obj("CDI_0", /*name_size=*/6));
384  dbg_puts("warning: CDI_0 certificate not valid; updating\r\n");
385  // Update the cert page buffer.
386  RETURN_IF_ERROR(dice_chain_push_cert("CDI_0", static_dice_cdi_0.cert_data,
387  static_dice_cdi_0.cert_size));
388  } else {
389  // Cert is valid, move to the next one.
390  dice_chain_next_cert_obj();
391  }
392 
393  return kErrorOk;
394 }
395 
396 rom_error_t dice_chain_attestation_owner(
397  const manifest_t *owner_manifest, keymgr_binding_value_t *bl0_measurement,
398  hmac_digest_t *owner_measurement, keymgr_binding_value_t *sealing_binding,
399  owner_app_domain_t key_domain) {
400  // Handles the certificates from the immutable rom_ext first.
401  RETURN_IF_ERROR(dice_chain_attestation_check_uds());
402  RETURN_IF_ERROR(dice_chain_attestation_check_cdi_0());
403 
404  // Generate CDI_1 attestation keys and (potentially) update certificate.
405  SEC_MMIO_WRITE_INCREMENT(kScKeymgrSecMmioSwBindingSet +
406  kScKeymgrSecMmioOwnerIntMaxVerSet);
407  static_assert(
408  sizeof(hmac_digest_t) == sizeof(keymgr_binding_value_t),
409  "Expect the keymgr binding value to be the same size as a sha256 digest");
410 
411  // Aggregate the owner firmware (BL0) measurement and the ownership
412  // measurement into a single attestation measurment. The attestation
413  // measurement is used to initialize the keymgr.
414  hmac_digest_t attest_measurement;
415  hmac_sha256_configure(false);
416  hmac_sha256_start();
417  hmac_sha256_update(bl0_measurement, sizeof(*bl0_measurement));
418  hmac_sha256_update(owner_measurement, sizeof(*owner_measurement));
419  hmac_sha256_process();
420  hmac_sha256_final(&attest_measurement);
421 
422  HARDENED_RETURN_IF_ERROR(sc_keymgr_owner_advance(
423  /*sealing_binding=*/sealing_binding,
424  /*attest_binding=*/(keymgr_binding_value_t *)&attest_measurement,
425  owner_manifest->max_key_version));
426  HARDENED_RETURN_IF_ERROR(otbn_boot_cert_ecc_p256_keygen(
428 
429  // Check if the current CDI_1 cert is valid.
430  RETURN_IF_ERROR(dice_chain_load_cert_obj("CDI_1", /*name_size=*/6));
432  dbg_puts("CDI_1 certificate not valid. Updating it ...\r\n");
433  // Update the cert page buffer.
434  size_t updated_cert_size = kScratchCertSizeBytes;
435  // TODO(#19596): add owner configuration block measurement to CDI_1 cert.
436  HARDENED_RETURN_IF_ERROR(dice_cdi_1_cert_build(
437  (hmac_digest_t *)bl0_measurement, owner_measurement,
438  owner_manifest->security_version, key_domain, &dice_chain.key_ids,
440  &updated_cert_size));
441  RETURN_IF_ERROR(dice_chain_push_cert("CDI_1", dice_chain.scratch_cert,
442  updated_cert_size));
443  } else {
444  // Cert is valid, move to the next one.
445  dice_chain_next_cert_obj();
446 
447  // Replace CDI_0 with CDI_1 key for endorsing next stage cert.
448  HARDENED_RETURN_IF_ERROR(otbn_boot_attestation_key_save(
449  kDiceKeyCdi1.keygen_seed_idx, kDiceKeyCdi1.type,
450  *kDiceKeyCdi1.keymgr_diversifier));
451  }
453 
454  sc_keymgr_sw_binding_unlock_wait();
455 
456  return kErrorOk;
457 }
458 
459 // Write the DICE certs to flash if they have been updated.
460 rom_error_t dice_chain_flush_flash(void) {
462  dice_chain.info_page != NULL) {
463  RETURN_IF_ERROR(
464  flash_ctrl_info_erase(dice_chain.info_page, kFlashCtrlEraseTypePage));
465  static_assert(sizeof(dice_chain.data) == FLASH_CTRL_PARAM_BYTES_PER_PAGE,
466  "Invalid dice_chain buffer size");
467  RETURN_IF_ERROR(flash_ctrl_info_write(
469  /*offset=*/0,
470  /*word_count=*/FLASH_CTRL_PARAM_BYTES_PER_PAGE / sizeof(uint32_t),
471  dice_chain.data));
473  }
474  return kErrorOk;
475 }
476 
477 rom_error_t dice_chain_init(void) {
478  // Variable initialization.
479  memset(&dice_chain, 0, sizeof(dice_chain));
483  .cert = &dice_chain.subject_pubkey_id,
484  };
485  dice_chain_reset_cert_obj();
486 
487  // Configure DICE certificate flash info page and buffer it into RAM.
488  flash_ctrl_cert_info_page_creator_cfg(&kFlashCtrlInfoPageDiceCerts);
489  flash_ctrl_info_cfg_set(&kFlashCtrlInfoPageFactoryCerts,
490  kCertificateInfoPageCfg);
491  flash_ctrl_cert_info_page_owner_restrict(&kFlashCtrlInfoPageFactoryCerts);
492  return kErrorOk;
493 }