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  memset(seed, 0, kAttestationSeedBytes);
101  return kErrorOk;
102  }
103  return err;
104  }
105 
106  return kErrorOk;
107 }
108 
109 rom_error_t otbn_boot_app_load(void) { return sc_otbn_load_app(kOtbnAppBoot); }
110 
111 rom_error_t otbn_boot_attestation_keygen(
112  uint32_t additional_seed_idx, sc_keymgr_key_type_t key_type,
113  sc_keymgr_diversification_t diversification,
114  ecdsa_p256_public_key_t *public_key) {
115  // Trigger key manager to sideload the attestation key into OTBN.
116  HARDENED_RETURN_IF_ERROR(
117  sc_keymgr_generate_key_otbn(key_type, diversification));
118 
119  // Write the mode.
120  uint32_t mode = kOtbnBootModeAttestationKeygen;
121  HARDENED_RETURN_IF_ERROR(
122  sc_otbn_dmem_write(kOtbnBootModeWords, &mode, kOtbnVarBootMode));
123 
124  // Load the additional seed from flash info.
125  uint32_t seed[kAttestationSeedWords];
126  HARDENED_RETURN_IF_ERROR(
127  load_attestation_keygen_seed(additional_seed_idx, seed));
128 
129  // Write the additional seed to OTBN DMEM.
130  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(
131  kAttestationSeedWords, seed, kOtbnVarBootAttestationAdditionalSeed));
132  // Pad remaining DMEM field with zeros to prevent a DMEM integrity error
133  // (since data is aligned to 256-bit words).
134  uint32_t zero_buf[kOtbnAttestationSeedBufferWords - kAttestationSeedWords] = {
135  0};
136  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(
137  ARRAYSIZE(zero_buf), zero_buf,
138  kOtbnVarBootAttestationAdditionalSeed + kAttestationSeedBytes));
139 
140  // Run the OTBN program (blocks until OTBN is done).
141  HARDENED_RETURN_IF_ERROR(sc_otbn_execute());
142  SEC_MMIO_WRITE_INCREMENT(kScOtbnSecMmioExecute);
143 
144  // TODO(#20023): Check the instruction count register (see `mod_exp_otbn`).
145 
146  // Retrieve the public key.
147  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(kEcdsaP256PublicKeyCoordWords,
148  kOtbnVarBootX, public_key->x));
149  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(kEcdsaP256PublicKeyCoordWords,
150  kOtbnVarBootY, public_key->y));
151 
152  return kErrorOk;
153 }
154 
155 /**
156  * Helper function to convert an ECC P256 public key from little to big endian
157  * in place.
158  */
159 static void pubkey_le_to_be_convert(ecdsa_p256_public_key_t *pubkey) {
160  util_reverse_bytes(pubkey->x, kEcdsaP256PublicKeyCoordBytes);
161  util_reverse_bytes(pubkey->y, kEcdsaP256PublicKeyCoordBytes);
162 }
163 
164 rom_error_t otbn_boot_cert_ecc_p256_keygen(sc_keymgr_ecc_key_t key,
165  hmac_digest_t *pubkey_id,
166  ecdsa_p256_public_key_t *pubkey) {
167  HARDENED_RETURN_IF_ERROR(sc_keymgr_state_check(key.required_keymgr_state));
168 
169  // Generate / sideload key material into OTBN, and generate the ECC keypair.
170  HARDENED_RETURN_IF_ERROR(otbn_boot_attestation_keygen(
171  key.keygen_seed_idx, key.type, *key.keymgr_diversifier, pubkey));
172 
173  // Keys are represented in certificates in big endian format, but the key is
174  // output from OTBN in little endian format, so we convert the key to
175  // big endian format.
176  pubkey_le_to_be_convert(pubkey);
177 
178  // Generate the key ID.
179  //
180  // Note: the certificate generation functions expect the digest to be in big
181  // endian form, but the HMAC driver returns the digest in little endian, so we
182  // re-format it.
183  hmac_sha256(pubkey, sizeof(*pubkey), pubkey_id);
184  util_reverse_bytes(pubkey_id, sizeof(*pubkey_id));
185 
186  return kErrorOk;
187 }
188 
189 rom_error_t otbn_boot_attestation_key_save(
190  uint32_t additional_seed_idx, sc_keymgr_key_type_t key_type,
191  sc_keymgr_diversification_t diversification) {
192  // Trigger key manager to sideload the attestation key into OTBN.
193  HARDENED_RETURN_IF_ERROR(
194  sc_keymgr_generate_key_otbn(key_type, diversification));
195 
196  // Write the mode.
197  uint32_t mode = kOtbnBootModeAttestationKeySave;
198  HARDENED_RETURN_IF_ERROR(
199  sc_otbn_dmem_write(kOtbnBootModeWords, &mode, kOtbnVarBootMode));
200 
201  // Load the additional seed from flash info.
202  uint32_t seed[kAttestationSeedWords];
203  HARDENED_RETURN_IF_ERROR(
204  load_attestation_keygen_seed(additional_seed_idx, seed));
205  // Pad remaining DMEM field with zeros to prevent a DMEM integrity error
206  // (since data is aligned to 256-bit words).
207  uint32_t zero_buf[kOtbnAttestationSeedBufferWords - kAttestationSeedWords] = {
208  0};
209  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(
210  ARRAYSIZE(zero_buf), zero_buf,
211  kOtbnVarBootAttestationAdditionalSeed + kAttestationSeedBytes));
212 
213  // Write the additional seed to OTBN DMEM.
214  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(
215  kAttestationSeedWords, seed, kOtbnVarBootAttestationAdditionalSeed));
216 
217  // Run the OTBN program (blocks until OTBN is done).
218  HARDENED_RETURN_IF_ERROR(sc_otbn_execute());
219  SEC_MMIO_WRITE_INCREMENT(kScOtbnSecMmioExecute);
220 
221  // TODO(#20023): Check the instruction count register (see `mod_exp_otbn`).
222 
223  return kErrorOk;
224 }
225 
226 rom_error_t otbn_boot_attestation_key_clear(void) {
227  // Trigger a full DMEM wipe.
228  RETURN_IF_ERROR(sc_otbn_dmem_sec_wipe());
229  HARDENED_RETURN_IF_ERROR(sc_otbn_busy_wait_for_done());
230 
231  // Re-load the data portion of the boot services app. This is like a
232  // stripped-down version of `sc_otbn_load_app`, where we skip the IMEM.
233  if (kOtbnAppBoot.dmem_data_end < kOtbnAppBoot.dmem_data_start) {
234  return kErrorOtbnInvalidArgument;
235  }
236  HARDENED_CHECK_GE(kOtbnAppBoot.dmem_data_end, kOtbnAppBoot.dmem_data_start);
237  const size_t data_num_words =
238  (size_t)(kOtbnAppBoot.dmem_data_end - kOtbnAppBoot.dmem_data_start);
239  if (data_num_words > 0) {
240  HARDENED_RETURN_IF_ERROR(
241  sc_otbn_dmem_write(data_num_words, kOtbnAppBoot.dmem_data_start,
242  kOtbnAppBoot.dmem_data_start_addr));
243  }
244  return kErrorOk;
245 }
246 
247 rom_error_t otbn_boot_attestation_endorse(const hmac_digest_t *digest,
248  ecdsa_p256_signature_t *sig) {
249  // Write the mode.
250  uint32_t mode = kOtbnBootModeAttestationEndorse;
251  HARDENED_RETURN_IF_ERROR(
252  sc_otbn_dmem_write(kOtbnBootModeWords, &mode, kOtbnVarBootMode));
253 
254  // Write the message digest.
255  HARDENED_RETURN_IF_ERROR(
256  sc_otbn_dmem_write(kHmacDigestNumWords, digest->digest, kOtbnVarBootMsg));
257 
258  // Run the OTBN program (blocks until OTBN is done).
259  HARDENED_RETURN_IF_ERROR(sc_otbn_execute());
260  SEC_MMIO_WRITE_INCREMENT(kScOtbnSecMmioExecute);
261 
262  // TODO(#20023): Check the instruction count register (see `mod_exp_otbn`).
263 
264  // Retrieve the signature (in two parts, r and s).
265  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(kEcdsaP256SignatureComponentWords,
266  kOtbnVarBootR, sig->r));
267  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(kEcdsaP256SignatureComponentWords,
268  kOtbnVarBootS, sig->s));
269 
270  return kErrorOk;
271 }
272 
273 rom_error_t otbn_boot_sigverify(const ecdsa_p256_public_key_t *key,
274  const ecdsa_p256_signature_t *sig,
275  const hmac_digest_t *digest,
276  uint32_t *recovered_r) {
277  // Write the mode.
278  uint32_t mode = kOtbnBootModeSigverify;
279  HARDENED_RETURN_IF_ERROR(
280  sc_otbn_dmem_write(kOtbnBootModeWords, &mode, kOtbnVarBootMode));
281 
282  // Write the public key.
283  HARDENED_RETURN_IF_ERROR(
284  sc_otbn_dmem_write(kEcdsaP256PublicKeyCoordWords, key->x, kOtbnVarBootX));
285  HARDENED_RETURN_IF_ERROR(
286  sc_otbn_dmem_write(kEcdsaP256PublicKeyCoordWords, key->y, kOtbnVarBootY));
287 
288  // Write the message digest.
289  HARDENED_RETURN_IF_ERROR(
290  sc_otbn_dmem_write(kHmacDigestNumWords, digest->digest, kOtbnVarBootMsg));
291 
292  // Write the signature.
293  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(kEcdsaP256SignatureComponentWords,
294  sig->r, kOtbnVarBootR));
295  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_write(kEcdsaP256SignatureComponentWords,
296  sig->s, kOtbnVarBootS));
297 
298  // Start the OTBN routine.
299  HARDENED_RETURN_IF_ERROR(sc_otbn_execute());
300  SEC_MMIO_WRITE_INCREMENT(kScOtbnSecMmioExecute);
301 
302  // Check if the signature passed basic checks.
303  uint32_t ok;
304  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(1, kOtbnVarBootOk, &ok));
305  if (launder32(ok) != kHardenedBoolTrue) {
306  return kErrorSigverifyBadEcdsaSignature;
307  }
308 
309  // Read the status value again as an extra hardening measure.
310  HARDENED_RETURN_IF_ERROR(sc_otbn_dmem_read(1, kOtbnVarBootOk, &ok));
312 
313  // TODO(#20023): Check the instruction count register (see `mod_exp_otbn`).
314 
315  // Read the recovered `r` value from DMEM.
316  return sc_otbn_dmem_read(kEcdsaP256SignatureComponentWords, kOtbnVarBootXr,
317  recovered_r);
318 }