Software APIs
ecc256_keygen_sca.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/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.h"
6 
12 #include "sw/device/lib/testing/test_framework/ottf_test_config.h"
13 #include "sw/device/lib/testing/test_framework/ujson_ottf.h"
14 #include "sw/device/lib/ujson/ujson.h"
15 #include "sw/device/sca/lib/prng.h"
16 #include "sw/device/tests/penetrationtests/firmware/lib/pentest_lib.h"
17 #include "sw/device/tests/penetrationtests/json/otbn_sca_commands.h"
18 
20 #include "otbn_regs.h"
21 
22 enum {
23  /**
24  * Number of bytes for ECDSA P-256 seeds and masked private keys.
25  */
26  kEcc256SeedNumBytes = 320 / 8,
27  /**
28  * Number of 32b words for ECDSA P-256 seeds and masked private keys.
29  */
30  kEcc256SeedNumWords = kEcc256SeedNumBytes / sizeof(uint32_t),
31  /**
32  * Number of bytes for ECDSA P-256 point coordinates.
33  */
34  kEcc256CoordNumBytes = 256 / 8,
35  /**
36  * Number of 32b words for ECDSA P-256 point coordinates.
37  */
38  kEcc256CoordNumWords = kEcc256CoordNumBytes / sizeof(uint32_t),
39  /**
40  * Mode option for the ECDSA keygen app (generates the private key only).
41  */
42  kEcc256ModePrivateKeyOnly = 1,
43  /**
44  * Mode option for the ECDSA keygen app (generates the full keypair).
45  */
46  kEcc256ModeKeypair = 2,
47  /**
48  * Max number of traces per batch.
49  */
50  kNumBatchOpsMax = 256,
51  /**
52  * Number of cycles that Ibex should sleep to minimize noise during OTBN
53  * operations. Caution: This number should be chosen to provide enough time
54  * to complete the operation. Otherwise, Ibex might wake up while OTBN is
55  * still busy and disturb the capture. This was measured using mcycle_read
56  * when otbn_manual_trigger and otbn_busy_wait_for_done get executed.
57  */
58  kIbexOtbnSleepCycles = 1100,
59 };
60 
61 /**
62  * App configuration for p256_key_from_seed_sca
63  */
64 const otbn_app_t kOtbnAppP256KeyFromSeed =
65  OTBN_APP_T_INIT(p256_key_from_seed_sca);
66 
67 static const otbn_addr_t kOtbnVarMode =
68  OTBN_ADDR_T_INIT(p256_key_from_seed_sca, mode);
69 static const otbn_addr_t kOtbnVarSeed0 =
70  OTBN_ADDR_T_INIT(p256_key_from_seed_sca, seed0);
71 static const otbn_addr_t kOtbnVarSeed1 =
72  OTBN_ADDR_T_INIT(p256_key_from_seed_sca, seed1);
73 static const otbn_addr_t kOtbnVarD0 =
74  OTBN_ADDR_T_INIT(p256_key_from_seed_sca, d0);
75 static const otbn_addr_t kOtbnVarD1 =
76  OTBN_ADDR_T_INIT(p256_key_from_seed_sca, d1);
77 
78 /**
79  * An array of seeds to be used in a batch
80  */
81 uint32_t batch_share0[kNumBatchOpsMax][kEcc256SeedNumWords];
82 
83 /**
84  * An array of masks to be used in a batch
85  */
86 uint32_t batch_share1[kNumBatchOpsMax][kEcc256SeedNumWords];
87 
88 /**
89  * Arrays for first and second share of masked private key d to be used in a
90  * batch
91  */
92 uint32_t d0_batch[kEcc256SeedNumWords];
93 uint32_t d1_batch[kEcc256SeedNumWords];
94 
95 /**
96  * Fixed-message indicator.
97  *
98  * Used in the 'b' (batch capture) command for indicating whether to use fixed
99  * or random message.
100  */
101 static bool run_fixed = true;
102 
103 /**
104  * Masking indicator.
105  *
106  * Used in the 'b' (batch capture) command for indicating whether to use masks.
107  */
108 static bool en_masks = false;
109 
110 uint32_t ecc256_seed[kEcc256SeedNumWords];
111 
112 uint32_t ecc256_C[kEcc256SeedNumWords];
113 
114 uint32_t random_number[kEcc256CoordNumWords];
115 
116 uint32_t ecc256_fixed_number[kEcc256CoordNumWords];
117 
118 /**
119  * Adds two integers storred in byte arrays.
120  *
121  * Adds the integer stored in source array to the integer stored in
122  * destination aray.
123  * The user needs to ensure that dest_len is enough to store the result
124  * without overflow.
125  *
126  * @param[in] dest Location of the first input array and the result.
127  * @param[in] source Location of the second input array.
128  * @param[in] dest_len Length of the dest array in bytes.
129  * @param[in] source_len Length of the source array in bytes.
130  */
131 static void add_arrays(uint8_t *dest, uint8_t *source, size_t dest_len,
132  size_t source_len) {
133  uint16_t temp = 0;
134 
135  for (size_t i = 0; i < source_len; i++) {
136  temp += (uint16_t)source[i] + dest[i];
137  dest[i] = (uint8_t)(temp & 0x00FF);
138  temp >>= 8;
139  }
140 
141  for (size_t i = source_len; i < dest_len; i++) {
142  temp += (uint16_t)dest[i];
143  dest[i] = (uint8_t)(temp & 0x00FF);
144  temp >>= 8;
145  }
146 }
147 
148 /**
149  * Callback wrapper for OTBN manual trigger function.
150  */
151 static void otbn_manual_trigger(void) { otbn_execute(); }
152 
153 /**
154  * Runs the OTBN key generation program.
155  *
156  * The seed shares must be `kEcc256SeedNumWords` words long.
157  *
158  * @param[in] mode Mode parameter (private key only or full keypair).
159  * @param[in] seed Seed for key generation.
160  * @param[in] mask Mask for seed.
161  * @return OK or error.
162  */
163 static status_t p256_run_keygen(uint32_t mode, const uint32_t *share0,
164  const uint32_t *share1) {
165  // Secure wipe to scramble DMEM.
166  TRY(otbn_load_app(kOtbnAppP256KeyFromSeed));
167 
168  // Write mode.
169  TRY(otbn_dmem_write(/*num_words=*/1, &mode, kOtbnVarMode));
170 
171  // Write seed shares.
172  TRY(otbn_dmem_write(kEcc256SeedNumWords, share0, kOtbnVarSeed0));
173  TRY(otbn_dmem_write(kEcc256SeedNumWords, share1, kOtbnVarSeed1));
174 
175  // Execute program. Trigger is set inside this function.
176  pentest_call_and_sleep(otbn_manual_trigger, kIbexOtbnSleepCycles, true, true);
177 
178  return OK_STATUS();
179 }
180 
181 status_t handle_otbn_sca_ecc256_ecdsa_keygen_fvsr_key_batch(ujson_t *uj) {
182  penetrationtest_otbn_sca_num_traces_t uj_data;
183  TRY(ujson_deserialize_penetrationtest_otbn_sca_num_traces_t(uj, &uj_data));
184 
185  uint32_t num_traces = uj_data.num_traces;
186  uint32_t batch_digest[kEcc256SeedNumWords];
187 
188  if (num_traces > kNumBatchOpsMax) {
189  return OUT_OF_RANGE();
190  }
191 
192  // Zero the batch digest.
193  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
194  batch_digest[j] = 0;
195  }
196 
197  for (size_t i = 0; i < num_traces; ++i) {
198  // Set mask to a random mask (en_masks = true) or to 0 (en_masks = false).
199  if (en_masks) {
200  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
201  batch_share1[i][j] = prng_rand_uint32();
202  }
203  } else {
204  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
205  batch_share1[i][j] = 0;
206  }
207  }
208 
209  // If the run is fixed, take the fixed seed received from the host. Else,
210  // generate a random seed.
211  if (run_fixed) {
212  memcpy(batch_share0[i], ecc256_seed, kEcc256SeedNumBytes);
213  } else {
214  // Here change to random_number + C.
215  // It is necessary to set the C first.
216  memcpy(batch_share0[i], ecc256_C, kEcc256SeedNumBytes);
217  for (size_t j = 0; j < kEcc256CoordNumWords; ++j) {
218  random_number[j] = prng_rand_uint32();
219  }
220  add_arrays((unsigned char *)batch_share0[i],
221  (unsigned char *)random_number, kEcc256SeedNumBytes,
222  kEcc256CoordNumBytes);
223  }
224  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
225  batch_share0[i][j] ^= batch_share1[i][j];
226  }
227  // Another PRNG run to determine 'run_fixed' for the next cycle.
228  run_fixed = prng_rand_uint32() & 0x1;
229  }
230 
231  for (size_t i = 0; i < num_traces; ++i) {
232  TRY(p256_run_keygen(kEcc256ModePrivateKeyOnly, batch_share0[i],
233  batch_share1[i]));
234 
235  // Read results.
236  TRY(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD0, d0_batch));
237  TRY(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD1, d1_batch));
238 
239  // The correctness of each batch is verified by computing and sending
240  // the batch digest. This digest is computed by XORing all d0 shares of
241  // the batch.
242  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
243  batch_digest[j] ^= d0_batch[j];
244  }
245  }
246  // Send the batch digest to the host for verification.
247  penetrationtest_otbn_sca_batch_digest_t uj_output;
248  memcpy(uj_output.batch_digest, (uint8_t *)batch_digest,
249  kEcc256SeedNumWords * 4);
250  RESP_OK(ujson_serialize_penetrationtest_otbn_sca_batch_digest_t, uj,
251  &uj_output);
252 
253  return OK_STATUS();
254 }
255 
256 status_t handle_otbn_sca_ecc256_ecdsa_keygen_fvsr_seed_batch(ujson_t *uj) {
257  penetrationtest_otbn_sca_num_traces_t uj_data;
258  TRY(ujson_deserialize_penetrationtest_otbn_sca_num_traces_t(uj, &uj_data));
259 
260  uint32_t num_traces = uj_data.num_traces;
261  uint32_t batch_digest[kEcc256SeedNumWords];
262 
263  if (num_traces > kNumBatchOpsMax) {
264  return OUT_OF_RANGE();
265  }
266 
267  // Zero the batch digest.
268  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
269  batch_digest[j] = 0;
270  }
271 
272  for (size_t i = 0; i < num_traces; ++i) {
273  // Set mask to a random mask (en_masks = true) or to 0 (en_masks = false).
274  if (en_masks) {
275  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
276  batch_share1[i][j] = prng_rand_uint32();
277  }
278  } else {
279  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
280  batch_share1[i][j] = 0;
281  }
282  }
283 
284  // If the run is fixed, take the fixed seed received from the host. Else,
285  // generate a random seed.
286  if (run_fixed) {
287  memcpy(batch_share0[i], ecc256_seed, kEcc256SeedNumBytes);
288  } else {
289  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
290  batch_share0[i][j] = prng_rand_uint32();
291  }
292  }
293 
294  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
295  batch_share0[i][j] ^= batch_share1[i][j];
296  }
297  run_fixed = prng_rand_uint32() & 0x1;
298  }
299 
300  for (size_t i = 0; i < num_traces; ++i) {
301  TRY(p256_run_keygen(kEcc256ModePrivateKeyOnly, batch_share0[i],
302  batch_share1[i]));
303 
304  // Read results.
305  TRY(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD0, d0_batch));
306  TRY(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD1, d1_batch));
307 
308  // The correctness of each batch is verified by computing and sending
309  // the batch digest. This digest is computed by XORing all d0 shares of
310  // the batch.
311  for (size_t j = 0; j < kEcc256SeedNumWords; ++j) {
312  batch_digest[j] ^= d0_batch[j];
313  }
314  }
315 
316  // Send the batch digest to the host for verification.
317  penetrationtest_otbn_sca_batch_digest_t uj_output;
318  memcpy(uj_output.batch_digest, (uint8_t *)batch_digest,
319  kEcc256SeedNumWords * 4);
320  RESP_OK(ujson_serialize_penetrationtest_otbn_sca_batch_digest_t, uj,
321  &uj_output);
322 
323  return OK_STATUS();
324 }
325 
326 status_t handle_otbn_sca_ecc256_en_masks(ujson_t *uj) {
327  penetrationtest_otbn_sca_en_masks_t uj_data;
328  TRY(ujson_deserialize_penetrationtest_otbn_sca_en_masks_t(uj, &uj_data));
329  if (uj_data.en_masks) {
330  en_masks = true;
331  } else {
332  en_masks = false;
333  }
334  return OK_STATUS();
335 }
336 
337 status_t handle_otbn_sca_ecc256_set_c(ujson_t *uj) {
338  penetrationtest_otbn_sca_constant_t uj_data;
339  TRY(ujson_deserialize_penetrationtest_otbn_sca_constant_t(uj, &uj_data));
340 
341  memcpy(ecc256_C, uj_data.constant, kEcc256SeedNumBytes);
342 
343  return OK_STATUS();
344 }
345 
346 status_t handle_otbn_sca_ecc256_set_seed(ujson_t *uj) {
347  penetrationtest_otbn_sca_seed_t uj_data;
348  TRY(ujson_deserialize_penetrationtest_otbn_sca_seed_t(uj, &uj_data));
349 
350  memcpy(ecc256_seed, uj_data.seed, kEcc256SeedNumBytes);
351 
352  return OK_STATUS();
353 }