[sca] Add serial program for ECDSA-P256 key generation.

This interface allows using the ChipWhisperer simple-serial tools to set
the seed and mask for ECDSA-P256 private key and keypair generation.

Signed-off-by: Jade Philipoom <jadep@google.com>
diff --git a/sw/device/sca/BUILD b/sw/device/sca/BUILD
index 8c192f3..3f529e5 100644
--- a/sw/device/sca/BUILD
+++ b/sw/device/sca/BUILD
@@ -85,8 +85,28 @@
 )
 
 opentitan_flash_binary(
-    name = "ecc_serial",
-    srcs = ["ecc_serial.c"],
+    name = "ecc256_keygen_serial",
+    srcs = ["ecc256_keygen_serial.c"],
+    deps = [
+        "//hw/top_earlgrey/sw/autogen:top_earlgrey",
+        "//sw/device/lib/base:abs_mmio",
+        "//sw/device/lib/base:memory",
+        "//sw/device/lib/crypto/drivers:otbn",
+        "//sw/device/lib/runtime:ibex",
+        "//sw/device/lib/runtime:log",
+        "//sw/device/lib/testing:entropy_testutils",
+        "//sw/device/lib/testing/test_framework:ottf_ld_silicon_creator_slot_a",
+        "//sw/device/lib/testing/test_framework:ottf_main",
+        "//sw/device/sca/lib:prng",
+        "//sw/device/sca/lib:sca",
+        "//sw/device/sca/lib:simple_serial",
+        "//sw/otbn/crypto:p256_key_from_seed_sca",
+    ],
+)
+
+opentitan_flash_binary(
+    name = "ecc256_sign_serial",
+    srcs = ["ecc256_sign_serial.c"],
     deps = [
         "//hw/top_earlgrey/sw/autogen:top_earlgrey",
         "//sw/device/lib/base:abs_mmio",
diff --git a/sw/device/sca/ecc256_keygen_serial.c b/sw/device/sca/ecc256_keygen_serial.c
new file mode 100644
index 0000000..aaae706
--- /dev/null
+++ b/sw/device/sca/ecc256_keygen_serial.c
@@ -0,0 +1,305 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/base/abs_mmio.h"
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/lib/crypto/drivers/otbn.h"
+#include "sw/device/lib/runtime/ibex.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/testing/entropy_testutils.h"
+#include "sw/device/lib/testing/test_framework/ottf_main.h"
+#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
+#include "sw/device/sca/lib/sca.h"
+#include "sw/device/sca/lib/simple_serial.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "otbn_regs.h"
+
+/**
+ * OpenTitan program for OTBN ECDSA-P256 side-channel analysis.
+ *
+ * This program implements the following simple serial commands:
+ *   - Set seed ('x'),
+ *   - Secret key generation ('k'),
+ *   - Keypair generation ('p'),
+ *   - Get version ('v') (implemented in simpleserial library),
+ *   - Seed PRNG ('s') (implemented in simpleserial library),
+ * See https://wiki.newae.com/SimpleSerial for details on the protocol.
+ */
+
+OTTF_DEFINE_TEST_CONFIG();
+
+enum {
+  /**
+   * Number of bytes for ECDSA P-256 seeds and masked private keys.
+   */
+  kEcc256SeedNumBytes = 320 / 8,
+  /**
+   * Number of 32b words for ECDSA P-256 seeds and masked private keys.
+   */
+  kEcc256SeedNumWords = kEcc256SeedNumBytes / sizeof(uint32_t),
+  /**
+   * Number of bytes for ECDSA P-256 point coordinates.
+   */
+  kEcc256CoordNumBytes = 256 / 8,
+  /**
+   * Number of 32b words for ECDSA P-256 point coordinates.
+   */
+  kEcc256CoordNumWords = kEcc256CoordNumBytes / sizeof(uint32_t),
+  /**
+   * Mode option for the ECDSA keygen app (generates the private key only).
+   */
+  kEcc256ModePrivateKeyOnly = 1,
+  /**
+   * Mode option for the ECDSA keygen app (generates the full keypair).
+   */
+  kEcc256ModeKeypair = 2,
+};
+
+OTBN_DECLARE_APP_SYMBOLS(p256_key_from_seed_sca);
+
+OTBN_DECLARE_SYMBOL_ADDR(p256_key_from_seed_sca, mode);
+OTBN_DECLARE_SYMBOL_ADDR(p256_key_from_seed_sca, seed0);
+OTBN_DECLARE_SYMBOL_ADDR(p256_key_from_seed_sca, seed1);
+OTBN_DECLARE_SYMBOL_ADDR(p256_key_from_seed_sca, d0);
+OTBN_DECLARE_SYMBOL_ADDR(p256_key_from_seed_sca, d1);
+OTBN_DECLARE_SYMBOL_ADDR(p256_key_from_seed_sca, x);
+OTBN_DECLARE_SYMBOL_ADDR(p256_key_from_seed_sca, y);
+
+static const otbn_app_t kOtbnAppP256KeyFromSeed =
+    OTBN_APP_T_INIT(p256_key_from_seed_sca);
+
+static const otbn_addr_t kOtbnVarMode =
+    OTBN_ADDR_T_INIT(p256_key_from_seed_sca, mode);
+static const otbn_addr_t kOtbnVarSeed0 =
+    OTBN_ADDR_T_INIT(p256_key_from_seed_sca, seed0);
+static const otbn_addr_t kOtbnVarSeed1 =
+    OTBN_ADDR_T_INIT(p256_key_from_seed_sca, seed1);
+static const otbn_addr_t kOtbnVarD0 =
+    OTBN_ADDR_T_INIT(p256_key_from_seed_sca, d0);
+static const otbn_addr_t kOtbnVarD1 =
+    OTBN_ADDR_T_INIT(p256_key_from_seed_sca, d1);
+static const otbn_addr_t kOtbnVarX =
+    OTBN_ADDR_T_INIT(p256_key_from_seed_sca, x);
+static const otbn_addr_t kOtbnVarY =
+    OTBN_ADDR_T_INIT(p256_key_from_seed_sca, y);
+
+/**
+ * Seed value.
+ *
+ * The default value corresponds to the test data in
+ *   sw/otbn/crypto/test/p256_key_from_seed_test.s
+ *
+ * This default value can be overwritten via the simpleserial command `s`
+ * (see ecc256_set_seed)
+ */
+uint32_t ecc256_seed[kEcc256SeedNumWords] = {
+    0x016064e9, 0x11e3f4d6, 0xac3a6fa7, 0xaba11a1b, 0x8f9271d1,
+    0x22b79d5f, 0x1176f31d, 0xb5ac3a51, 0x99a082d7, 0x484eb366,
+};
+
+/**
+ * Simple serial 'x' (set seed) command handler.
+ *
+ * The key must be `kEcc256SeedNumBytes` bytes long.
+ *
+ * @param seed Value for seed share.
+ * @param seed_len Length of seed share.
+ */
+static void ecc256_set_seed(const uint8_t *seed, size_t seed_len) {
+  SS_CHECK(seed_len == kEcc256SeedNumBytes);
+  memcpy(ecc256_seed, seed, seed_len);
+}
+
+/**
+ * Runs the OTBN key generation program.
+ *
+ * The seed shares must be `kEcc256SeedNumWords` words long.
+ *
+ * @param[in] mode  Mode parameter (private key only or full keypair).
+ * @param[in] seed  Seed for key generation.
+ * @param[in] mask  Mask for seed.
+ */
+static void p256_run_keygen(uint32_t mode, const uint32_t *seed,
+                            const uint32_t *mask) {
+  SS_CHECK_STATUS_OK(otbn_load_app(kOtbnAppP256KeyFromSeed));
+
+  // Write mode.
+  SS_CHECK_STATUS_OK(otbn_dmem_write(/*num_words=*/1, &mode, kOtbnVarMode));
+
+  // Compute first share of seed (seed ^ mask).
+  uint32_t seed0[kEcc256SeedNumWords];
+  for (size_t i = 0; i < kEcc256SeedNumWords; i++) {
+    seed0[i] = seed[i] ^ mask[i];
+  }
+
+  // Write seed shares.
+  SS_CHECK_STATUS_OK(
+      otbn_dmem_write(kEcc256SeedNumWords, seed0, kOtbnVarSeed0));
+  SS_CHECK_STATUS_OK(otbn_dmem_write(kEcc256SeedNumWords, mask, kOtbnVarSeed1));
+
+  // Execute program.
+  sca_set_trigger_high();
+  SS_CHECK_STATUS_OK(otbn_execute());
+  SS_CHECK_STATUS_OK(otbn_busy_wait_for_done());
+  sca_set_trigger_low();
+}
+
+/**
+ * Generates a secret key from a masked seed.
+ *
+ * The seed shares must be `kEcc256SeedNumWords` words long, and the caller
+ * must provide pre-allocated buffers of the same length for the private key
+ * shares.
+ *
+ * @param[in] seed  Seed for key generation.
+ * @param[in] mask  Mask for seed.
+ * @param[out] d0   First share of masked private key d.
+ * @param[out] d1   Second share of masked private key d.
+ */
+static void p256_ecdsa_gen_secret_key(const uint32_t *seed,
+                                      const uint32_t *mask, uint32_t *d0,
+                                      uint32_t *d1) {
+  // Run the key generation program.
+  p256_run_keygen(kEcc256ModePrivateKeyOnly, seed, mask);
+
+  // Read results.
+  SS_CHECK_STATUS_OK(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD0, d0));
+  SS_CHECK_STATUS_OK(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD1, d1));
+}
+
+/**
+ * Generates a keypair from a masked seed.
+ *
+ * The seed shares must be `kEcc256SeedNumWords` words long, and the caller
+ * must provide pre-allocated buffers of the same length for the private key
+ * shares and of length `kEcc256CoordNumWords` for the public key coordinates.
+ *
+ * @param[in] seed  Seed for key generation.
+ * @param[in] mask  Mask for seed.
+ * @param[out] d0   First share of masked private key d.
+ * @param[out] d1   Second share of masked private key d.
+ * @param[out] x    x-coordinate of public key Q.
+ * @param[out] y    y-coordinate of public key Q.
+ */
+static void p256_ecdsa_gen_keypair(const uint32_t *seed, const uint32_t *mask,
+                                   uint32_t *d0, uint32_t *d1, uint32_t *x,
+                                   uint32_t *y) {
+  // Run the key generation program.
+  p256_run_keygen(kEcc256ModeKeypair, seed, mask);
+
+  // Read results.
+  SS_CHECK_STATUS_OK(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD0, d0));
+  SS_CHECK_STATUS_OK(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD1, d1));
+  SS_CHECK_STATUS_OK(otbn_dmem_read(kEcc256CoordNumWords, kOtbnVarX, x));
+  SS_CHECK_STATUS_OK(otbn_dmem_read(kEcc256CoordNumWords, kOtbnVarY, y));
+}
+
+/**
+ * Simple serial 'k' (secret keygen) command handler.
+ *
+ * Takes the mask value from the simple serial UART and triggers an OTBN
+ * secret key generation operation. The mask must be `kEcc256SeedNumBytes`
+ * bytes long.
+ *
+ * Uses a fixed seed. To overwrite the seed, use the simpleserial command 's'.
+ *
+ * @param[in] mask The mask provided by the simpleserial UART.
+ * @param[in] mask_len Length of the mask.
+ */
+static void ecc256_ecdsa_secret_keygen(const uint8_t *mask, size_t mask_len) {
+  if (mask_len != kEcc256SeedNumBytes) {
+    LOG_ERROR("Invalid mask length %hu", (uint8_t)mask_len);
+    return;
+  }
+
+  // Copy mask to an aligned buffer.
+  uint32_t ecc256_mask[kEcc256SeedNumWords];
+  memcpy(ecc256_mask, mask, kEcc256SeedNumBytes);
+
+  uint32_t ecc256_d0[kEcc256SeedNumWords];
+  uint32_t ecc256_d1[kEcc256SeedNumWords];
+  p256_ecdsa_gen_secret_key(ecc256_seed, ecc256_mask, ecc256_d0, ecc256_d1);
+
+  simple_serial_send_packet('r', (unsigned char *) ecc256_d0, kEcc256SeedNumBytes);
+  simple_serial_send_packet('r', (unsigned char *) ecc256_d1, kEcc256SeedNumBytes);
+
+  SS_CHECK_STATUS_OK(otbn_dmem_sec_wipe());
+  SS_CHECK_STATUS_OK(otbn_imem_sec_wipe());
+}
+
+/**
+ * Simple serial 'p' (keypair generation) command handler.
+ *
+ * Takes the mask value from the simple serial UART and triggers an OTBN
+ * secret key generation operation. The mask must be `kEcc256SeedNumBytes`
+ * bytes long.
+ *
+ * Uses a fixed seed. To overwrite the seed, use the simpleserial command 's'.
+ *
+ * @param[in] mask The mask provided by the simpleserial UART.
+ * @param[in] mask_len Length of the mask.
+ */
+static void ecc256_ecdsa_gen_keypair(const uint8_t *mask, size_t mask_len) {
+  if (mask_len != kEcc256SeedNumBytes) {
+    LOG_ERROR("Invalid mask length %hu", (uint8_t)mask_len);
+    return;
+  }
+
+  // Copy mask to an aligned buffer.
+  uint32_t ecc256_mask[kEcc256SeedNumWords];
+  memcpy(ecc256_mask, mask, kEcc256SeedNumBytes);
+
+  uint32_t ecc256_d0[kEcc256SeedNumWords];
+  uint32_t ecc256_d1[kEcc256SeedNumWords];
+  uint32_t ecc256_x[kEcc256CoordNumWords];
+  uint32_t ecc256_y[kEcc256CoordNumWords];
+  p256_ecdsa_gen_keypair(ecc256_seed, ecc256_mask, ecc256_d0, ecc256_d1,
+                         ecc256_x, ecc256_y);
+
+  simple_serial_send_packet('r', (unsigned char *)ecc256_d0,
+                            kEcc256SeedNumBytes);
+  simple_serial_send_packet('r', (unsigned char *)ecc256_d1,
+                            kEcc256SeedNumBytes);
+  simple_serial_send_packet('r', (unsigned char *)ecc256_x,
+                            kEcc256CoordNumBytes);
+  simple_serial_send_packet('r', (unsigned char *)ecc256_y,
+                            kEcc256CoordNumBytes);
+
+  SS_CHECK_STATUS_OK(otbn_dmem_sec_wipe());
+  SS_CHECK_STATUS_OK(otbn_imem_sec_wipe());
+}
+
+/**
+ * Initializes peripherals and processes simple serial packets received over
+ * UART.
+ */
+static void simple_serial_main(void) {
+  entropy_testutils_auto_mode_init();
+
+  sca_init(kScaTriggerSourceOtbn, kScaPeripheralEntropy | kScaPeripheralIoDiv4 |
+                                      kScaPeripheralOtbn | kScaPeripheralCsrng |
+                                      kScaPeripheralEdn | kScaPeripheralHmac);
+
+  LOG_INFO("Running ECC serial");
+  LOG_INFO("Initializing simple serial interface to capture board.");
+
+  simple_serial_init(sca_get_uart());
+  SS_CHECK(simple_serial_register_handler('k', ecc256_ecdsa_secret_keygen) ==
+           kSimpleSerialOk);
+  SS_CHECK(simple_serial_register_handler('p', ecc256_ecdsa_gen_keypair) ==
+           kSimpleSerialOk);
+  SS_CHECK(simple_serial_register_handler('x', ecc256_set_seed) ==
+           kSimpleSerialOk);
+
+  LOG_INFO("Starting simple serial packet handling.");
+  while (true) {
+    simple_serial_process_packet();
+  }
+}
+
+bool test_main(void) {
+  simple_serial_main();
+  return true;
+}
diff --git a/sw/device/sca/ecc_serial.c b/sw/device/sca/ecc256_sign_serial.c
similarity index 100%
rename from sw/device/sca/ecc_serial.c
rename to sw/device/sca/ecc256_sign_serial.c
diff --git a/sw/otbn/crypto/p256_key_from_seed_sca.s b/sw/otbn/crypto/p256_key_from_seed_sca.s
index 2077d03..5a6b7d0 100644
--- a/sw/otbn/crypto/p256_key_from_seed_sca.s
+++ b/sw/otbn/crypto/p256_key_from_seed_sca.s
@@ -27,6 +27,27 @@
   unimp
 
 p256_gen_secret_key:
+  /* First, generate the masked secret key d and write to DMEM.
+       dmem[d0] <= d0
+       dmem[d1] <= d1 */
+  jal       x1, run_gen_secret_key
+
+  ecall
+
+p256_gen_keypair:
+  /* First, generate the masked secret key d and write to DMEM.
+       dmem[d0] <= d0
+       dmem[d1] <= d1 */
+  jal       x1, run_gen_secret_key
+
+  /* Generate the public key Q = d*G.
+       dmem[x] <= Q.x
+       dmem[y] <= Q.y */
+  jal       x1, p256_base_mult
+
+  ecall
+
+run_gen_secret_key:
   /* Init all-zero register. */
   bn.xor    w31, w31, w31
 
@@ -61,21 +82,7 @@
   li        x2, 23
   bn.sid    x2, 0(x3)
 
-  ecall
-
-p256_gen_keypair:
-  /* First, generate the masked secret key d and write to DMEM.
-       dmem[d0] <= d0
-       dmem[d1] <= d1 */
-  jal       x1, p256_gen_secret_key
-
-  /* Generate the public key Q = d*G.
-       dmem[x] <= Q.x
-       dmem[y] <= Q.y */
-  jal       x1, p256_base_mult
-
-  ecall
-
+  ret
 
 /**
  * Note: Technically this could be a .bss section, but it is convenient for