blob: c76280525fac3a91f1e0e5c4405dfb7dfbc13a02 [file] [log] [blame]
// 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/memory.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_otbn.h"
#include "sw/device/lib/runtime/ibex.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/runtime/otbn.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"
/**
* OpenTitan program for OTBN ECDSA-P384 side-channel analysis.
*
* This program implements the following simple serial commands:
* - Set ephemeral secret key and sign ('p')*,
* - Set private key ('d')*,
* - Set message ('n')*,
* - Version ('v')+,
* - Seed PRNG ('s')+,
* See https://wiki.newae.com/SimpleSerial for details on the protocol.
*
* The OTBN-related code was developed based on
* https://github.com/lowRISC/opentitan/tree/master/sw/device/lib/crypto/ecdsa_p256
* and
* https://github.com/lowRISC/opentitan/blob/master/sw/device/tests/crypto/ecdsa_p256_functest.c
*
*/
OTTF_DEFINE_TEST_CONFIG();
/* The start of global ECDSA variables to get data from simpleserial UART */
/**
* Private key d
* This is the default value used by NewAE in their experiments
* https://github.com/newaetech/ot-sca/blob/ecc-analysis/reports/ecc/REPORT.md
* https://github.com/newaetech/opentitan/tree/sca_otbninst
*
* The endiannes may need to be fixed.
*
* The value of this variable can be overwritten via
* the simpleserial command `d` (see ecc256_set_private_key_d)
*/
uint8_t ecc256_private_key_d[32] = {
0xcd, 0xb4, 0x57, 0xaf, 0x1c, 0x9f, 0x4c, 0x74, 0x02, 0x0c, 0x7e,
0x8b, 0xe9, 0x93, 0x3e, 0x28, 0x0c, 0xf0, 0x18, 0x0d, 0xf4, 0x6c,
0x0b, 0xda, 0x7a, 0xbb, 0xe6, 0x8f, 0xb7, 0xa0, 0x45, 0x55};
/**
* Message to sign
* The value of this variable can be overwritten via
* the simpleserial command `n` (see ecc384_set_msg)
*
*/
uint8_t ecc256_msg[32] = {"Hello OTBN."};
/* The end of the global ecdsa variables coming from simpleserial UART */
/* p256_ecdsa_sca has randomnization removed */
OTBN_DECLARE_APP_SYMBOLS(p256_ecdsa_sca);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, dptr_msg);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, dptr_r);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, dptr_s);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, dptr_x);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, dptr_y);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, dptr_d);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, dptr_x_r);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, dptr_k);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, mode);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, msg);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, r);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, s);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, x);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, y);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, d);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, k);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, x_r);
static const otbn_app_t kOtbnAppP256Ecdsa = OTBN_APP_T_INIT(p256_ecdsa_sca);
static const otbn_addr_t kOtbnVarDptrMsg =
OTBN_ADDR_T_INIT(p256_ecdsa_sca, dptr_msg);
static const otbn_addr_t kOtbnVarDptrR =
OTBN_ADDR_T_INIT(p256_ecdsa_sca, dptr_r);
static const otbn_addr_t kOtbnVarDptrS =
OTBN_ADDR_T_INIT(p256_ecdsa_sca, dptr_s);
static const otbn_addr_t kOtbnVarDptrX =
OTBN_ADDR_T_INIT(p256_ecdsa_sca, dptr_x);
static const otbn_addr_t kOtbnVarDptrY =
OTBN_ADDR_T_INIT(p256_ecdsa_sca, dptr_y);
static const otbn_addr_t kOtbnVarDptrD =
OTBN_ADDR_T_INIT(p256_ecdsa_sca, dptr_d);
static const otbn_addr_t kOtbnVarDptrXR =
OTBN_ADDR_T_INIT(p256_ecdsa_sca, dptr_x_r);
static const otbn_addr_t kOtbnVarDptrK =
OTBN_ADDR_T_INIT(p256_ecdsa_sca, dptr_k);
static const otbn_addr_t kOtbnVarMode = OTBN_ADDR_T_INIT(p256_ecdsa_sca, mode);
static const otbn_addr_t kOtbnVarMsg = OTBN_ADDR_T_INIT(p256_ecdsa_sca, msg);
static const otbn_addr_t kOtbnVarR = OTBN_ADDR_T_INIT(p256_ecdsa_sca, r);
static const otbn_addr_t kOtbnVarS = OTBN_ADDR_T_INIT(p256_ecdsa_sca, s);
static const otbn_addr_t kOtbnVarX = OTBN_ADDR_T_INIT(p256_ecdsa_sca, x);
static const otbn_addr_t kOtbnVarY = OTBN_ADDR_T_INIT(p256_ecdsa_sca, y);
static const otbn_addr_t kOtbnVarD = OTBN_ADDR_T_INIT(p256_ecdsa_sca, d);
static const otbn_addr_t kOtbnVarXR = OTBN_ADDR_T_INIT(p256_ecdsa_sca, x_r);
static const otbn_addr_t kOtbnVarK = OTBN_ADDR_T_INIT(p256_ecdsa_sca, k);
/**
* Makes a single dptr in the P256 library point to where its value is stored.
*/
static void setup_data_pointer(otbn_t *otbn_ctx, const otbn_addr_t dptr,
const otbn_addr_t value) {
SS_CHECK(otbn_copy_data_to_otbn(otbn_ctx, sizeof(value), &value, dptr) ==
kOtbnOk);
}
/**
* Sets up all data pointers used by the P256 library to point to DMEM.
*
* The ECDSA P256 OTBN library makes use of "named" data pointers as part of
* its calling convention, which are exposed as symbol starting with `dptr_`.
* The DMEM locations these pointers refer to is not mandated by the P256
* calling convention; the values can be placed anywhere in OTBN DMEM.
*
* As convenience, `ecdsa_p256.s` pre-allocates space for the data values.
*
* This function makes the data pointers refer to the pre-allocated DMEM
* regions to store the actual values.
*/
static void setup_data_pointers(otbn_t *otbn_ctx) {
setup_data_pointer(otbn_ctx, kOtbnVarDptrMsg, kOtbnVarMsg);
setup_data_pointer(otbn_ctx, kOtbnVarDptrR, kOtbnVarR);
setup_data_pointer(otbn_ctx, kOtbnVarDptrS, kOtbnVarS);
setup_data_pointer(otbn_ctx, kOtbnVarDptrX, kOtbnVarX);
setup_data_pointer(otbn_ctx, kOtbnVarDptrY, kOtbnVarY);
setup_data_pointer(otbn_ctx, kOtbnVarDptrD, kOtbnVarD);
setup_data_pointer(otbn_ctx, kOtbnVarDptrXR, kOtbnVarXR);
setup_data_pointer(otbn_ctx, kOtbnVarDptrK, kOtbnVarK);
}
/**
* Simple serial 'd' (set private key) command handler.
*
* This function does not use key shares to simplify side-channel analysis.
* The key must be `kAesKeyLength` bytes long.
*
* @param key_d Key.
* @param key_d_len Key length.
*/
static void ecc256_serial_set_private_key_d(const uint8_t *key_d,
size_t key_d_len) {
SS_CHECK(key_d_len == 32);
memcpy(ecc256_private_key_d, key_d, key_d_len);
}
/**
* Simple serial 'n' (set message) command handler.
*
* This function does not use key shares to simplify side-channel analysis.
* The key must be `kAesKeyLength` bytes long.
*
* @param msg Message to sign.
* @param msg_len Message length.
*/
static void ecc256_serial_set_msg(const uint8_t *msg, size_t msg_len) {
SS_CHECK(msg_len <= 32);
memcpy(ecc256_msg, msg, msg_len);
}
/**
* Signs a message with ECDSA using the P-256 curve.
*
* R = k*G
* r = x-coordinate of R
* s = k^(-1)(msg + r*d) mod n
*
* @param otbn_ctx The OTBN context object.
* @param msg The message to sign, msg (32B).
* @param private_key_d The private key, d (32B).
* @param k The ephemeral key, k (random scalar) (32B).
* @param[out] signature_r Signature component r (the x-coordinate of R).
* Provide a pre-allocated 32B buffer.
* @param[out] signature_s Signature component s (the proof).
* Provide a pre-allocated 32B buffer.
*/
static void p256_ecdsa_sign(otbn_t *otbn_ctx, const uint8_t *msg,
const uint8_t *private_key_d, uint8_t *signature_r,
uint8_t *signature_s, const uint8_t *k) {
SS_CHECK(otbn_ctx != NULL);
// Set pointers to input arguments.
LOG_INFO("Setup data pointers");
setup_data_pointers(otbn_ctx);
// Write input arguments.
uint32_t mode = 1; // mode 1 => sign
LOG_INFO("Copy data");
SS_CHECK(otbn_copy_data_to_otbn(otbn_ctx, sizeof(mode), &mode,
kOtbnVarMode) == kOtbnOk);
SS_CHECK(otbn_copy_data_to_otbn(otbn_ctx, /*len_bytes=*/32, msg,
kOtbnVarMsg) == kOtbnOk);
SS_CHECK(otbn_copy_data_to_otbn(otbn_ctx, /*len_bytes=*/32, private_key_d,
kOtbnVarD) == kOtbnOk);
SS_CHECK(otbn_copy_data_to_otbn(otbn_ctx, /*len_bytes=*/32, k, kOtbnVarK) ==
kOtbnOk);
// Call OTBN to perform operation, and wait for it to complete.
LOG_INFO("Execute");
SS_CHECK(otbn_execute(otbn_ctx) == kOtbnOk);
LOG_INFO("Wait for done");
SS_CHECK(otbn_busy_wait_for_done(otbn_ctx) == kOtbnOk);
// Read back results.
LOG_INFO("Get results");
SS_CHECK(otbn_copy_data_from_otbn(otbn_ctx, /*len_bytes=*/32, kOtbnVarR,
signature_r) == kOtbnOk);
SS_CHECK(otbn_copy_data_from_otbn(otbn_ctx, /*len_bytes=*/32, kOtbnVarS,
signature_s) == kOtbnOk);
LOG_INFO("r[0]: 0x%02x", signature_r[0]);
LOG_INFO("s[0]: 0x%02x", signature_s[0]);
}
/**
* Simple serial 'p' (sign) command handler.
*
* Takes the scalar value from the simple serial UART
* and triggers OTBN_P256_sign operation.
* Uses a fixed message and private key value
*
* To overwrite the message, use the simpleserial command 'n'
* To overwrite the private key value, use the simpleserial command 'd'
*
* @param ecc256_secret_k the ephemeral key/scalar provided via simpleserial
* UART.
* @param secret_k_len Length of the ephemeral key.
*/
static void ecc256_simpleserial_ecdsa(const uint8_t *ecc256_secret_k,
size_t secret_k_len)
// static void simpleserial_ecdsa()
{
otbn_t otbn_ctx;
if (secret_k_len != 32) {
LOG_INFO("Invalid data length %hu", (uint8_t)secret_k_len);
return;
}
LOG_INFO("SSECDSA starting...");
SS_CHECK(otbn_init(&otbn_ctx, mmio_region_from_addr(
TOP_EARLGREY_OTBN_BASE_ADDR)) == kOtbnOk);
SS_CHECK(otbn_zero_data_memory(&otbn_ctx) == kOtbnOk);
SS_CHECK(otbn_load_app(&otbn_ctx, kOtbnAppP256Ecdsa) == kOtbnOk);
LOG_INFO("otbn_status: 0x%08x",
mmio_region_read32(
mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), 0x18));
// Sign
uint8_t ecc256_signature_r[32] = {0};
uint8_t ecc256_signature_s[32] = {0};
LOG_INFO("Signing");
sca_set_trigger_high();
p256_ecdsa_sign(&otbn_ctx, ecc256_msg, ecc256_private_key_d,
ecc256_signature_r, ecc256_signature_s, ecc256_secret_k);
sca_set_trigger_low();
/**
* Send the signature_r and signature_s back to host
*
* TODO: Remove them if they are not necessary for the side-channel analysis.
*/
simple_serial_send_packet('r', (uint8_t *)ecc256_signature_r, 32);
simple_serial_send_packet('r', (uint8_t *)ecc256_signature_s, 32);
LOG_INFO("Clearing OTBN memory");
SS_CHECK(otbn_zero_data_memory(&otbn_ctx) == kOtbnOk);
}
/**
* Main function.
*
* Initializes peripherals and processes simple serial packets received over
* UART.
*
* TODO: Decide which function name to use after discussing with the team.
* If we use `_ottf_main()`, the code compiles but I don't get
* serial communication from UART. To use `test_main` we need to include
* the header file `sw/device/lib/testing/test_framework/ottf_main.h`
*
*/
bool test_main(void) {
// void _ottf_main(void) {
entropy_testutils_boot_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());
simple_serial_result_t err;
/* Register the simple serial command handlers */
if (err = simple_serial_register_handler('p', ecc256_simpleserial_ecdsa),
err != kSimpleSerialOk) {
LOG_INFO("Register handler failed with return %hu",
(short unsigned int)err);
while (1)
;
}
if (err =
simple_serial_register_handler('d', ecc256_serial_set_private_key_d),
err != kSimpleSerialOk) {
LOG_INFO("Register handler failed with return %hu",
(short unsigned int)err);
while (1)
;
}
if (err = simple_serial_register_handler('n', ecc256_serial_set_msg),
err != kSimpleSerialOk) {
LOG_INFO("Register handler failed with return %hu",
(short unsigned int)err);
while (1)
;
}
LOG_INFO("Starting simple serial packet handling.");
while (true) {
simple_serial_process_packet();
}
// return true;
}