Software APIs
otbn_ecdsa_op_irq_test.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 
7 #include "sw/device/lib/runtime/irq.h"
9 #include "sw/device/lib/testing/entropy_testutils.h"
11 #include "sw/device/lib/testing/profile.h"
12 #include "sw/device/lib/testing/rv_plic_testutils.h"
13 #include "sw/device/lib/testing/test_framework/check.h"
15 
17 
18 /**
19  * ECDSA sign and verify test with the NIST P-256 curve using OTBN.
20  *
21  * IMPORTANT: This test is not a secure, complete, or reusable implementation of
22  * a cryptographic algorithm; it is not even close to being production-ready.
23  * It is only meant as an end-to-end test for OTBN during the bringup phase.
24  *
25  * The test contains constants and expected output, which can be independently
26  * and conveniently verified using a Python script.
27  *
28  * <code>
29  * # Optional: generate a new key
30  * $ openssl ecparam -name prime256v1 -genkey -noout -out \
31  * otbn_ecdsa_p256_test_private_key.pem
32  *
33  * # Create all constants/variables
34  * $ ./otbn_test_params.py ecc otbn_ecdsa_p256_test_private_key.pem
35  * </code>
36  */
37 
38 OTBN_DECLARE_APP_SYMBOLS(run_p256);
39 
40 OTBN_DECLARE_SYMBOL_ADDR(run_p256, mode);
41 OTBN_DECLARE_SYMBOL_ADDR(run_p256, msg);
42 OTBN_DECLARE_SYMBOL_ADDR(run_p256, r);
43 OTBN_DECLARE_SYMBOL_ADDR(run_p256, s);
44 OTBN_DECLARE_SYMBOL_ADDR(run_p256, x);
45 OTBN_DECLARE_SYMBOL_ADDR(run_p256, y);
46 OTBN_DECLARE_SYMBOL_ADDR(run_p256, d0);
47 OTBN_DECLARE_SYMBOL_ADDR(run_p256, d1);
48 OTBN_DECLARE_SYMBOL_ADDR(run_p256, x_r);
49 
50 static const otbn_app_t kOtbnAppP256Ecdsa = OTBN_APP_T_INIT(run_p256);
51 
52 static const otbn_addr_t kOtbnVarMode = OTBN_ADDR_T_INIT(run_p256, mode);
53 static const otbn_addr_t kOtbnVarMsg = OTBN_ADDR_T_INIT(run_p256, msg);
54 static const otbn_addr_t kOtbnVarR = OTBN_ADDR_T_INIT(run_p256, r);
55 static const otbn_addr_t kOtbnVarS = OTBN_ADDR_T_INIT(run_p256, s);
56 static const otbn_addr_t kOtbnVarX = OTBN_ADDR_T_INIT(run_p256, x);
57 static const otbn_addr_t kOtbnVarY = OTBN_ADDR_T_INIT(run_p256, y);
58 static const otbn_addr_t kOtbnVarD0 = OTBN_ADDR_T_INIT(run_p256, d0);
59 static const otbn_addr_t kOtbnVarD1 = OTBN_ADDR_T_INIT(run_p256, d1);
60 static const otbn_addr_t kOtbnVarXR = OTBN_ADDR_T_INIT(run_p256, x_r);
61 
62 // Declare mode constants.
63 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_SIGN);
64 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_VERIFY);
65 static const uint32_t kModeSign = OTBN_ADDR_T_INIT(run_p256, MODE_SIGN);
66 static const uint32_t kModeVerify = OTBN_ADDR_T_INIT(run_p256, MODE_VERIFY);
67 
68 OTTF_DEFINE_TEST_CONFIG();
69 
70 /**
71  * The plic dif to access the hardware.
72  */
73 static dif_rv_plic_t plic;
74 
75 /**
76  * The otbn context handler.
77  */
78 static dif_otbn_t otbn;
79 
80 /**
81  * The peripheral which fired the irq to be filled by the irq handler.
82  */
83 static volatile top_earlgrey_plic_peripheral_t plic_peripheral;
84 
85 /**
86  * The irq id to be filled by the irq handler.
87  */
88 static volatile dif_rv_plic_irq_id_t irq_id;
89 
90 /**
91  * The otbn irq to be filled by the irq handler.
92  */
93 static volatile dif_otbn_irq_t irq;
94 
95 /**
96  * Provides external IRQ handling for otbn tests.
97  *
98  * This function overrides the default OTTF external ISR.
99  *
100  * It performs the following:
101  * 1. Claims the IRQ fired (finds PLIC IRQ index).
102  * 2. Compute the OTBN peripheral.
103  * 3. Compute the otbn irq.
104  * 4. Clears the IRQ at the peripheral.
105  * 5. Completes the IRQ service at PLIC.
106  */
107 void ottf_external_isr(uint32_t *exc_info) {
109  (dif_rv_plic_irq_id_t *)&irq_id));
110 
111  plic_peripheral = (top_earlgrey_plic_peripheral_t)
113 
114  irq = (dif_otbn_irq_t)(irq_id -
116 
117  CHECK_DIF_OK(dif_otbn_irq_acknowledge(&otbn, irq));
118 
119  // Complete the IRQ by writing the IRQ source to the Ibex specific CC.
120  // register.
121  CHECK_DIF_OK(
123 }
124 
125 static void otbn_wait_for_done_irq(dif_otbn_t *otbn) {
126  // Clear the otbn irq variable: we'll set it in the interrupt handler when
127  // we see the Done interrupt fire.
128  irq = UINT32_MAX;
129  irq_id = UINT32_MAX;
130  plic_peripheral = UINT32_MAX;
131  // Enable Done interrupt.
132  CHECK_DIF_OK(
133  dif_otbn_irq_set_enabled(otbn, kDifOtbnIrqDone, kDifToggleEnabled));
134 
135  // At this point, OTBN should be running. Wait for an interrupt that says
136  // it's done.
137  ATOMIC_WAIT_FOR_INTERRUPT(plic_peripheral != UINT32_MAX);
138 
139  CHECK(plic_peripheral == kTopEarlgreyPlicPeripheralOtbn,
140  "Interrupt from incorrect peripheral: (exp: %d, obs: %s)",
141  kTopEarlgreyPlicPeripheralOtbn, plic_peripheral);
142 
143  // Check this is the interrupt we expected.
144  CHECK(irq_id == kTopEarlgreyPlicIrqIdOtbnDone);
145 
146  // Disable Done interrupt.
147  CHECK_DIF_OK(
148  dif_otbn_irq_set_enabled(otbn, kDifOtbnIrqDone, kDifToggleDisabled));
149 
150  // Acknowledge Done interrupt. This clears INTR_STATE.done back to 0.
151  CHECK_DIF_OK(dif_otbn_irq_acknowledge(otbn, kDifOtbnIrqDone));
152 }
153 
154 static void otbn_init_irq(void) {
155  mmio_region_t plic_base_addr =
157  // Initialize PLIC and configure OTBN interrupt.
158  CHECK_DIF_OK(dif_rv_plic_init(plic_base_addr, &plic));
159 
160  // Set interrupt priority to be positive.
162  CHECK_DIF_OK(dif_rv_plic_irq_set_priority(&plic, irq_id, 0x1));
163 
164  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
166 
167  // Set the threshold for Ibex to 0.
169  &plic, kTopEarlgreyPlicTargetIbex0, 0x0));
170 
171  // Enable the external IRQ (so that we see the interrupt from the PLIC).
172  irq_global_ctrl(true);
173  irq_external_ctrl(true);
174 }
175 
176 /**
177  * Securely wipes OTBN DMEM and waits for Done interrupt.
178  *
179  * @param otbn The OTBN context object.
180  */
181 static void otbn_wipe_dmem(dif_otbn_t *otbn) {
182  CHECK_DIF_OK(dif_otbn_write_cmd(otbn, kDifOtbnCmdSecWipeDmem));
183  otbn_wait_for_done_irq(otbn);
184 }
185 
186 /**
187  * CHECK()s that the actual data matches the expected data.
188  *
189  * @param actual The actual data.
190  * @param expected The expected data.
191  * @param size_bytes The size of the actual/expected data.
192  */
193 static void check_data(const char *msg, const uint8_t *actual,
194  const uint8_t *expected, size_t size_bytes) {
195  for (int i = 0; i < size_bytes; ++i) {
196  CHECK(actual[i] == expected[i],
197  "%s: mismatch at byte %d: 0x%x (actual) != 0x%x (expected)", msg, i,
198  actual[i], expected[i]);
199  }
200 }
201 
202 /**
203  * Signs a message with ECDSA using the P-256 curve.
204  *
205  * @param otbn The OTBN context object.
206  * @param msg The message to sign (32B).
207  * @param private_key_d The private key (32B).
208  * @param[out] signature_r Signature component r (the x-coordinate of R).
209  * Provide a pre-allocated 32B buffer.
210  * @param[out] signature_s Signature component s (the proof).
211  * Provide a pre-allocated 32B buffer.
212  */
213 static void p256_ecdsa_sign(dif_otbn_t *otbn, const uint8_t *msg,
214  const uint8_t *private_key_d, uint8_t *signature_r,
215  uint8_t *signature_s) {
216  CHECK(otbn != NULL);
217 
218  // Write input arguments.
219  uint32_t mode = kModeSign;
220  CHECK_STATUS_OK(
221  otbn_testutils_write_data(otbn, sizeof(uint32_t), &mode, kOtbnVarMode));
222  CHECK_STATUS_OK(
223  otbn_testutils_write_data(otbn, /*len_bytes=*/32, msg, kOtbnVarMsg));
224  CHECK_STATUS_OK(otbn_testutils_write_data(otbn, /*len_bytes=*/32,
225  private_key_d, kOtbnVarD0));
226 
227  // Write redundant upper bits of d (all-zero for this test).
228  uint8_t d0_high[32] = {0};
229  CHECK_STATUS_OK(otbn_testutils_write_data(otbn, /*len_bytes=*/32, d0_high,
230  kOtbnVarD0 + 32));
231 
232  // Write second share of d (all-zero for this test).
233  uint8_t d1[64] = {0};
234  CHECK_STATUS_OK(
235  otbn_testutils_write_data(otbn, /*len_bytes=*/64, d1, kOtbnVarD1));
236 
237  // Call OTBN to perform operation, and wait for it to complete.
238  CHECK_STATUS_OK(otbn_testutils_execute(otbn));
239  otbn_wait_for_done_irq(otbn);
240 
241  // Read back results.
242  CHECK_STATUS_OK(
243  otbn_testutils_read_data(otbn, /*len_bytes=*/32, kOtbnVarR, signature_r));
244  CHECK_STATUS_OK(
245  otbn_testutils_read_data(otbn, /*len_bytes=*/32, kOtbnVarS, signature_s));
246 }
247 
248 /**
249  * Verifies a message with ECDSA using the P-256 curve.
250  *
251  * @param otbn The OTBN context object.
252  * @param msg The message to verify (32B).
253  * @param signature_r The signature component r (the proof) (32B).
254  * @param signature_s The signature component s (the proof) (32B).
255  * @param public_key_x The public key x-coordinate (32B).
256  * @param public_key_y The public key y-coordinate (32B).
257  * @param[out] signature_x_r Recovered point x_r (== R'.x). Provide a
258  * pre-allocated 32B buffer.
259  */
260 static void p256_ecdsa_verify(dif_otbn_t *otbn, const uint8_t *msg,
261  const uint8_t *signature_r,
262  const uint8_t *signature_s,
263  const uint8_t *public_key_x,
264  const uint8_t *public_key_y,
265  uint8_t *signature_x_r) {
266  CHECK(otbn != NULL);
267 
268  // Write input arguments.
269  uint32_t mode = kModeVerify;
270  CHECK_STATUS_OK(
271  otbn_testutils_write_data(otbn, sizeof(uint32_t), &mode, kOtbnVarMode));
272  CHECK_STATUS_OK(
273  otbn_testutils_write_data(otbn, /*len_bytes=*/32, msg, kOtbnVarMsg));
274  CHECK_STATUS_OK(otbn_testutils_write_data(otbn, /*len_bytes=*/32, signature_r,
275  kOtbnVarR));
276  CHECK_STATUS_OK(otbn_testutils_write_data(otbn, /*len_bytes=*/32, signature_s,
277  kOtbnVarS));
278  CHECK_STATUS_OK(otbn_testutils_write_data(otbn, /*len_bytes=*/32,
279  public_key_x, kOtbnVarX));
280  CHECK_STATUS_OK(otbn_testutils_write_data(otbn, /*len_bytes=*/32,
281  public_key_y, kOtbnVarY));
282 
283  // Call OTBN to perform operation, and wait for it to complete.
284  CHECK_STATUS_OK(otbn_testutils_execute(otbn));
285  otbn_wait_for_done_irq(otbn);
286 
287  // Read back results.
288  CHECK_STATUS_OK(otbn_testutils_read_data(otbn, /*len_bytes=*/32, kOtbnVarXR,
289  signature_x_r));
290 }
291 
292 /**
293  * Performs a ECDSA roundtrip test using the NIST P-256 curve.
294  *
295  * A roundtrip consists of three steps: Initialize OTBN, sign, and verify.
296  */
297 static void test_ecdsa_p256_roundtrip(void) {
298  // Message
299  static const uint8_t kIn[32] = {"Hello OTBN."};
300 
301  // Public key x-coordinate (Q.x)
302  static const uint8_t kPublicKeyQx[32] = {
303  0x4e, 0xb2, 0x8b, 0x55, 0xeb, 0x88, 0x62, 0x24, 0xf2, 0xbf, 0x1b,
304  0x9e, 0xd8, 0x4a, 0x09, 0xa7, 0x86, 0x67, 0x92, 0xcd, 0xca, 0x07,
305  0x5d, 0x07, 0x82, 0xe7, 0x2d, 0xac, 0x31, 0x14, 0x79, 0x1f};
306 
307  // Public key y-coordinate (Q.y)
308  static const uint8_t kPublicKeyQy[32] = {
309  0x27, 0x9c, 0xe4, 0x23, 0x24, 0x10, 0xa2, 0xfa, 0xbd, 0x53, 0x73,
310  0xf1, 0xa5, 0x08, 0xf0, 0x40, 0x9e, 0xc0, 0x55, 0x21, 0xa4, 0xf0,
311  0x54, 0x59, 0x00, 0x3e, 0x5f, 0x15, 0x3c, 0xc6, 0x4b, 0x87};
312 
313  // Private key (d)
314  static const uint8_t kPrivateKeyD[32] = {
315  0xcd, 0xb4, 0x57, 0xaf, 0x1c, 0x9f, 0x4c, 0x74, 0x02, 0x0c, 0x7e,
316  0x8b, 0xe9, 0x93, 0x3e, 0x28, 0x0c, 0xf0, 0x18, 0x0d, 0xf4, 0x6c,
317  0x0b, 0xda, 0x7a, 0xbb, 0xe6, 0x8f, 0xb7, 0xa0, 0x45, 0x55};
318 
319  // Initialize
320  uint64_t t_start_init = profile_start();
321  CHECK_DIF_OK(
322  dif_otbn_init(mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), &otbn));
323  otbn_init_irq();
324  CHECK_STATUS_OK(otbn_testutils_load_app(&otbn, kOtbnAppP256Ecdsa));
325  profile_end_and_print(t_start_init, "Initialization");
326 
327  // Sign
328  uint8_t signature_r[32] = {0};
329  uint8_t signature_s[32] = {0};
330 
331  LOG_INFO("Signing");
332  uint64_t t_start_sign = profile_start();
333  p256_ecdsa_sign(&otbn, kIn, kPrivateKeyD, signature_r, signature_s);
334  profile_end_and_print(t_start_sign, "Sign");
335 
336  // Securely wipe OTBN data memory and reload app
337  LOG_INFO("Wiping OTBN DMEM and reloading app");
338  otbn_wipe_dmem(&otbn);
339  CHECK_STATUS_OK(otbn_testutils_load_app(&otbn, kOtbnAppP256Ecdsa));
340 
341  // Verify
342  uint8_t signature_x_r[32] = {0};
343 
344  LOG_INFO("Verifying");
345  uint64_t t_start_verify = profile_start();
346  p256_ecdsa_verify(&otbn, kIn, signature_r, signature_s, kPublicKeyQx,
347  kPublicKeyQy, signature_x_r);
348 
349  // Include the r =? x_r comparison in the profiling as this is something
350  // either OTBN or the host CPU needs to do as part of the signature
351  // verification.
352  check_data("signature_x_r", signature_r, signature_x_r, 32);
353  profile_end_and_print(t_start_verify, "Verify");
354 
355  // Securely wipe OTBN data memory
356  LOG_INFO("Wiping OTBN DMEM");
357  otbn_wipe_dmem(&otbn);
358 }
359 
360 bool test_main(void) {
361  CHECK_STATUS_OK(entropy_testutils_auto_mode_init());
362 
363  test_ecdsa_p256_roundtrip();
364 
365  return true;
366 }