Software APIs
otbn_boot_services.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/otbn_boot_services.h"
6 
8 #include "sw/device/silicon_creator/lib/attestation.h"
10 #include "sw/device/silicon_creator/lib/base/util.h"
11 #include "sw/device/silicon_creator/lib/dbg_print.h"
12 #include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
13 #include "sw/device/silicon_creator/lib/drivers/hmac.h"
14 #include "sw/device/silicon_creator/lib/drivers/keymgr.h"
15 #include "sw/device/silicon_creator/lib/drivers/otbn.h"
16 
17 #include "otbn_regs.h" // Generated.
18 
19 static_assert(kAttestationSeedWords <= 16,
20  "Additional attestation seed needs must be <= 516 bits.");
21 
22 OTBN_DECLARE_APP_SYMBOLS(boot); // The OTBN boot-services app.
23 OTBN_DECLARE_SYMBOL_ADDR(boot, mode); // Application mode.
24 OTBN_DECLARE_SYMBOL_ADDR(boot, msg); // ECDSA message digest.
25 OTBN_DECLARE_SYMBOL_ADDR(boot, x); // ECDSA public key x-coordinate.
26 OTBN_DECLARE_SYMBOL_ADDR(boot, y); // ECDSA public key y-coordinate.
27 OTBN_DECLARE_SYMBOL_ADDR(boot, r); // ECDSA signature component r.
28 OTBN_DECLARE_SYMBOL_ADDR(boot, s); // ECDSA signature component s.
29 OTBN_DECLARE_SYMBOL_ADDR(boot, x_r); // ECDSA verification result.
30 OTBN_DECLARE_SYMBOL_ADDR(boot, ok); // ECDSA verification status.
32  boot, attestation_additional_seed); // Additional seed for ECDSA keygen.
33 
34 static const sc_otbn_app_t kOtbnAppBoot = OTBN_APP_T_INIT(boot);
35 static const sc_otbn_addr_t kOtbnVarBootMode = OTBN_ADDR_T_INIT(boot, mode);
36 static const sc_otbn_addr_t kOtbnVarBootMsg = OTBN_ADDR_T_INIT(boot, msg);
37 static const sc_otbn_addr_t kOtbnVarBootX = OTBN_ADDR_T_INIT(boot, x);
38 static const sc_otbn_addr_t kOtbnVarBootY = OTBN_ADDR_T_INIT(boot, y);
39 static const sc_otbn_addr_t kOtbnVarBootR = OTBN_ADDR_T_INIT(boot, r);
40 static const sc_otbn_addr_t kOtbnVarBootS = OTBN_ADDR_T_INIT(boot, s);
41 static const sc_otbn_addr_t kOtbnVarBootXr = OTBN_ADDR_T_INIT(boot, x_r);
42 static const sc_otbn_addr_t kOtbnVarBootOk = OTBN_ADDR_T_INIT(boot, ok);
43 static const sc_otbn_addr_t kOtbnVarBootAttestationAdditionalSeed =
44  OTBN_ADDR_T_INIT(boot, attestation_additional_seed);
45 
46 enum {
47  /*
48  * Mode is represented by a single word.
49  */
50  kOtbnBootModeWords = 1,
51  /*
52  * Mode to run signature verification.
53  *
54  * Value taken from `boot.s`.
55  */
56  kOtbnBootModeSigverify = 0x7d3,
57  /*
58  * Mode to generate an attestation keypair.
59  *
60  * Value taken from `boot.s`.
61  */
62  kOtbnBootModeAttestationKeygen = 0x2bf,
63  /*
64  * Mode to endorse a message with a saved private key.
65  *
66  * Value taken from `boot.s`.
67  */
68  kOtbnBootModeAttestationEndorse = 0x5e8,
69  /*
70  * Mode to save an attesation private key.
71  *
72  * Value taken from `boot.s`.
73  */
74  kOtbnBootModeAttestationKeySave = 0x64d,
75  /* Size of the OTBN attestation seed buffer in 32-bit words (rounding the
76  attestation seed size up to the next OTBN wide word). */
77  kOtbnAttestationSeedBufferWords =
78  ((kAttestationSeedWords + kScOtbnWideWordNumWords - 1) /
79  kScOtbnWideWordNumWords) *
80  kScOtbnWideWordNumWords,
81 };
82 
84 static rom_error_t load_attestation_keygen_seed(uint32_t additional_seed_idx,
85  uint32_t *seed) {
86  // Read seed from flash info page.
87  uint32_t seed_flash_offset =
88  0 + (additional_seed_idx * kAttestationSeedBytes);
89  rom_error_t err =
90  flash_ctrl_info_read(&kFlashCtrlInfoPageAttestationKeySeeds,
91  seed_flash_offset, kAttestationSeedWords, seed);
92 
93  if (err != kErrorOk) {
94  flash_ctrl_error_code_t flash_ctrl_err_code;
95  flash_ctrl_error_code_get(&flash_ctrl_err_code);
96  if (flash_ctrl_err_code.rd_err) {
97  // If we encountered a read error, this means the attestation seed page
98  // has not been provisioned yet. In this case, we clear the seed and
99  // continue, which will simply result in generating an invalid identity.
100  dbg_puts(
101  "Warning: Attestation key seed flash info page not provisioned.\r\n");
102  memset(seed, 0, kAttestationSeedBytes);
103  return kErrorOk;
104  }
105  return err;
106  }
107 
108  return kErrorOk;
109 }
110 
111 rom_error_t otbn_boot_app_load(void) { return sc_otbn_load_app(kOtbnAppBoot); }
112 
113 rom_error_t otbn_boot_attestation_keygen(
114  uint32_t additional_seed_idx, sc_keymgr_key_type_t key_type,
115  sc_keymgr_diversification_t diversification,
116  ecdsa_p256_public_key_t *public_key) {
117  // Trigger key manager to sideload the attestation key into OTBN.
118  HARDENED_RETURN_IF_ERROR(
119  sc_keymgr_generate_key_otbn(key_type, diversification));
120 
121  // Write the mode.
122  uint32_t mode = kOtbnBootModeAttestationKeygen;
123  HARDENED_RETURN_IF_ERROR(
124  sc_otbn_dmem_write(kOtbnBootModeWords, &mode, kOtbnVarBootMode));
125 
126  // Load the additional seed from flash info.
127  uint32_t seed[kAttestationSeedWords];
128  HARDENED_RETURN_IF_ERROR(
129  load_attestation_keygen_seed(additional_seed_idx, seed));
130 
131  // Write the additional seed to OTBN DMEM.
132  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(
133  kAttestationSeedWords, seed, kOtbnVarBootAttestationAdditionalSeed));
134  // Pad remaining DMEM field with zeros to prevent a DMEM integrity error
135  // (since data is aligned to 256-bit words).
136  uint32_t zero_buf[kOtbnAttestationSeedBufferWords - kAttestationSeedWords] = {
137  0};
138  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(
139  ARRAYSIZE(zero_buf), zero_buf,
140  kOtbnVarBootAttestationAdditionalSeed + kAttestationSeedBytes));
141 
142  // Run the OTBN program (blocks until OTBN is done).
143  HARDENED_RETURN_IF_ERROR(sc_otbn_execute());
144  SEC_MMIO_WRITE_INCREMENT(kScOtbnSecMmioExecute);
145 
146  // TODO(#20023): Check the instruction count register (see `mod_exp_otbn`).
147 
148  // Retrieve the public key.
149  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(kEcdsaP256PublicKeyCoordWords,
150  kOtbnVarBootX, public_key->x));
151  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(kEcdsaP256PublicKeyCoordWords,
152  kOtbnVarBootY, public_key->y));
153 
154  return kErrorOk;
155 }
156 
157 /**
158  * Helper function to convert an ECC P256 public key from little to big endian
159  * in place.
160  */
161 static void pubkey_le_to_be_convert(ecdsa_p256_public_key_t *pubkey) {
162  util_reverse_bytes(pubkey->x, kEcdsaP256PublicKeyCoordBytes);
163  util_reverse_bytes(pubkey->y, kEcdsaP256PublicKeyCoordBytes);
164 }
165 
166 rom_error_t otbn_boot_cert_ecc_p256_keygen(sc_keymgr_ecc_key_t key,
167  hmac_digest_t *pubkey_id,
168  ecdsa_p256_public_key_t *pubkey) {
169  HARDENED_RETURN_IF_ERROR(sc_keymgr_state_check(key.required_keymgr_state));
170 
171  // Generate / sideload key material into OTBN, and generate the ECC keypair.
172  HARDENED_RETURN_IF_ERROR(otbn_boot_attestation_keygen(
173  key.keygen_seed_idx, key.type, *key.keymgr_diversifier, pubkey));
174 
175  // Keys are represented in certificates in big endian format, but the key is
176  // output from OTBN in little endian format, so we convert the key to
177  // big endian format.
178  pubkey_le_to_be_convert(pubkey);
179 
180  // Generate the key ID.
181  //
182  // Note: the certificate generation functions expect the digest to be in big
183  // endian form, but the HMAC driver returns the digest in little endian, so we
184  // re-format it.
185  hmac_sha256(pubkey, sizeof(*pubkey), pubkey_id);
186  util_reverse_bytes(pubkey_id, sizeof(*pubkey_id));
187 
188  return kErrorOk;
189 }
190 
191 rom_error_t otbn_boot_attestation_key_save(
192  uint32_t additional_seed_idx, sc_keymgr_key_type_t key_type,
193  sc_keymgr_diversification_t diversification) {
194  // Trigger key manager to sideload the attestation key into OTBN.
195  HARDENED_RETURN_IF_ERROR(
196  sc_keymgr_generate_key_otbn(key_type, diversification));
197 
198  // Write the mode.
199  uint32_t mode = kOtbnBootModeAttestationKeySave;
200  HARDENED_RETURN_IF_ERROR(
201  sc_otbn_dmem_write(kOtbnBootModeWords, &mode, kOtbnVarBootMode));
202 
203  // Load the additional seed from flash info.
204  uint32_t seed[kAttestationSeedWords];
205  HARDENED_RETURN_IF_ERROR(
206  load_attestation_keygen_seed(additional_seed_idx, seed));
207  // Pad remaining DMEM field with zeros to prevent a DMEM integrity error
208  // (since data is aligned to 256-bit words).
209  uint32_t zero_buf[kOtbnAttestationSeedBufferWords - kAttestationSeedWords] = {
210  0};
211  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(
212  ARRAYSIZE(zero_buf), zero_buf,
213  kOtbnVarBootAttestationAdditionalSeed + kAttestationSeedBytes));
214 
215  // Write the additional seed to OTBN DMEM.
216  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(
217  kAttestationSeedWords, seed, kOtbnVarBootAttestationAdditionalSeed));
218 
219  // Run the OTBN program (blocks until OTBN is done).
220  HARDENED_RETURN_IF_ERROR(sc_otbn_execute());
221  SEC_MMIO_WRITE_INCREMENT(kScOtbnSecMmioExecute);
222 
223  // TODO(#20023): Check the instruction count register (see `mod_exp_otbn`).
224 
225  return kErrorOk;
226 }
227 
228 rom_error_t otbn_boot_attestation_key_clear(void) {
229  // Trigger a full DMEM wipe.
230  RETURN_IF_ERROR(sc_otbn_dmem_sec_wipe());
231  HARDENED_RETURN_IF_ERROR(sc_otbn_busy_wait_for_done());
232 
233  // Re-load the data portion of the boot services app. This is like a
234  // stripped-down version of `sc_otbn_load_app`, where we skip the IMEM.
235  if (kOtbnAppBoot.dmem_data_end < kOtbnAppBoot.dmem_data_start) {
236  return kErrorOtbnInvalidArgument;
237  }
238  HARDENED_CHECK_GE(kOtbnAppBoot.dmem_data_end, kOtbnAppBoot.dmem_data_start);
239  const size_t data_num_words =
240  (size_t)(kOtbnAppBoot.dmem_data_end - kOtbnAppBoot.dmem_data_start);
241  if (data_num_words > 0) {
242  HARDENED_RETURN_IF_ERROR(
243  sc_otbn_dmem_write(data_num_words, kOtbnAppBoot.dmem_data_start,
244  kOtbnAppBoot.dmem_data_start_addr));
245  }
246  return kErrorOk;
247 }
248 
249 rom_error_t otbn_boot_attestation_endorse(const hmac_digest_t *digest,
250  ecdsa_p256_signature_t *sig) {
251  // Write the mode.
252  uint32_t mode = kOtbnBootModeAttestationEndorse;
253  HARDENED_RETURN_IF_ERROR(
254  sc_otbn_dmem_write(kOtbnBootModeWords, &mode, kOtbnVarBootMode));
255 
256  // Write the message digest.
257  HARDENED_RETURN_IF_ERROR(
258  sc_otbn_dmem_write(kHmacDigestNumWords, digest->digest, kOtbnVarBootMsg));
259 
260  // Run the OTBN program (blocks until OTBN is done).
261  HARDENED_RETURN_IF_ERROR(sc_otbn_execute());
262  SEC_MMIO_WRITE_INCREMENT(kScOtbnSecMmioExecute);
263 
264  // TODO(#20023): Check the instruction count register (see `mod_exp_otbn`).
265 
266  // Retrieve the signature (in two parts, r and s).
267  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(kEcdsaP256SignatureComponentWords,
268  kOtbnVarBootR, sig->r));
269  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(kEcdsaP256SignatureComponentWords,
270  kOtbnVarBootS, sig->s));
271 
272  return kErrorOk;
273 }
274 
275 rom_error_t otbn_boot_sigverify(const ecdsa_p256_public_key_t *key,
276  const ecdsa_p256_signature_t *sig,
277  const hmac_digest_t *digest,
278  uint32_t *recovered_r) {
279  // Write the mode.
280  uint32_t mode = kOtbnBootModeSigverify;
281  HARDENED_RETURN_IF_ERROR(
282  sc_otbn_dmem_write(kOtbnBootModeWords, &mode, kOtbnVarBootMode));
283 
284  // Write the public key.
285  HARDENED_RETURN_IF_ERROR(
286  sc_otbn_dmem_write(kEcdsaP256PublicKeyCoordWords, key->x, kOtbnVarBootX));
287  HARDENED_RETURN_IF_ERROR(
288  sc_otbn_dmem_write(kEcdsaP256PublicKeyCoordWords, key->y, kOtbnVarBootY));
289 
290  // Write the message digest.
291  HARDENED_RETURN_IF_ERROR(
292  sc_otbn_dmem_write(kHmacDigestNumWords, digest->digest, kOtbnVarBootMsg));
293 
294  // Write the signature.
295  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(kEcdsaP256SignatureComponentWords,
296  sig->r, kOtbnVarBootR));
297  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(kEcdsaP256SignatureComponentWords,
298  sig->s, kOtbnVarBootS));
299 
300  // Start the OTBN routine.
301  HARDENED_RETURN_IF_ERROR(sc_otbn_execute());
302  SEC_MMIO_WRITE_INCREMENT(kScOtbnSecMmioExecute);
303 
304  // Check if the signature passed basic checks.
305  uint32_t ok;
306  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(1, kOtbnVarBootOk, &ok));
307  if (launder32(ok) != kHardenedBoolTrue) {
308  return kErrorSigverifyBadEcdsaSignature;
309  }
310 
311  // Read the status value again as an extra hardening measure.
312  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(1, kOtbnVarBootOk, &ok));
314 
315  // TODO(#20023): Check the instruction count register (see `mod_exp_otbn`).
316 
317  // Read the recovered `r` value from DMEM.
318  return sc_otbn_dmem_read(kEcdsaP256SignatureComponentWords, kOtbnVarBootXr,
319  recovered_r);
320 }