blob: 01fa98915d72ab2979e4549fcba988e7701269e4 [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();
/**
* 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."};
// p256_ecdsa_sca has randomnization removed.
OTBN_DECLARE_APP_SYMBOLS(p256_ecdsa_sca);
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, d0);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, d1);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, k0);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa_sca, k1);
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 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 kOtbnVarD0 = OTBN_ADDR_T_INIT(p256_ecdsa_sca, d0);
static const otbn_addr_t kOtbnVarD1 = OTBN_ADDR_T_INIT(p256_ecdsa_sca, d1);
static const otbn_addr_t kOtbnVarXR = OTBN_ADDR_T_INIT(p256_ecdsa_sca, x_r);
static const otbn_addr_t kOtbnVarK0 = OTBN_ADDR_T_INIT(p256_ecdsa_sca, k0);
static const otbn_addr_t kOtbnVarK1 = OTBN_ADDR_T_INIT(p256_ecdsa_sca, k1);
/**
* 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 ecc_256_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 ecc_256_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);
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,
kOtbnVarD0) == kOtbnOk);
SS_CHECK(otbn_copy_data_to_otbn(otbn_ctx, /*len_bytes=*/32, k, kOtbnVarK0) ==
kOtbnOk);
uint8_t zero[32] = {0};
SS_CHECK(otbn_copy_data_to_otbn(otbn_ctx, /*len_bytes=*/32, zero,
kOtbnVarD1) == kOtbnOk);
SS_CHECK(otbn_copy_data_to_otbn(otbn_ctx, /*len_bytes=*/32, zero,
kOtbnVarK1) == kOtbnOk);
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);
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 ecc_256_ecdsa(const uint8_t *ecc256_secret_k, size_t secret_k_len) {
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));
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();
// 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);
}
/**
* 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('p', ecc_256_ecdsa) ==
kSimpleSerialOk);
SS_CHECK(simple_serial_register_handler('d', ecc_256_set_private_key_d) ==
kSimpleSerialOk);
SS_CHECK(simple_serial_register_handler('n', ecc_256_set_msg) ==
kSimpleSerialOk);
LOG_INFO("Starting simple serial packet handling.");
while (true) {
simple_serial_process_packet();
}
}
bool test_main(void) {
simple_serial_main();
return true;
}