blob: 4b80c8cc81cc22aa0727750817c8549e5015eed7 [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/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 ephemeral secret key ('k')*,
* - Set private key ('d')*,
* - Set message ('n')*,
* - Start signing ('p')*
* - Version ('v')+,
* - Seed PRNG ('s')+,
* Commands marked with * are implemented in this file. Those marked with + are
* implemented in the simple serial library.
* 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();
enum {
/**
* Number of bytes for ECDSA P-256 private keys, message digests, and point
* coordinates.
*/
kEcc256NumBytes = 256 / 8,
/**
* Number of 32b words for ECDSA P-256 private keys, message digests, and
* point coordinates.
*/
kEcc256NumWords = kEcc256NumBytes / sizeof(uint32_t),
};
/**
* Two shares of the ephemeral secret key k
* k = k0 + k1
* k0 = ecc256_secret_k[0:7] (0x00000000...ffffffff)
* k1 = ecc256_secret_k[8:15] (0x00000000...00000000)
*
* The default values can be overwritten via
* the simpleserial command `k` (see ecc256_set_private_key_d)
*/
uint32_t ecc256_secret_k[2 * kEcc256NumWords] = {
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
};
/**
* 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 value of this variable can be overwritten via
* the simpleserial command `d` (see ecc256_set_private_key_d)
*/
uint32_t ecc256_private_key_d[2 * kEcc256NumWords] = {
0xaf57b4cd, 0x744c9f1c, 0x8b7e0c02, 0x283e93e9, 0x0d18f00c, 0xda0b6cf4,
0x8fe6bb7a, 0x5545a0b7, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
};
/**
* Message to sign.
*
* The value of this variable can be overwritten via the simpleserial command
* `n` (see ecc256_set_msg).
*/
uint32_t ecc256_msg[kEcc256NumWords] = {
0x48656c6c, // 'Hell'
0x6f204f54, // 'o OT'
0x424e0000, // 'BN'
};
// 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 'k' (set ephemeral key) command handler.
*
* This function sets both shares of the secret scalar k.
* The first 32 bytes (i.e, kEcc256NumBytes) are used as k0, and
* The last 32 bytes (i.e, kEcc256NumBytes) are used as k1.
*
* Any of the shares can be set to all zeros to simplify the SCA.
*
* As this function sets both shares,
* the data length must be `2*kEcc256NumBytes`.
*
* @param secret_k Key.
* @param secret_k_len Key length.
*/
static void ecc256_set_secret_key_k(const uint8_t *secret_k,
size_t secret_k_len) {
SS_CHECK(secret_k_len == 2 * kEcc256NumBytes);
memcpy(ecc256_secret_k, secret_k, secret_k_len);
}
/**
* Simple serial 'd' (set private key) command handler.
*
* This function sets both shares of the private key d.
* The first 32 bytes (i.e, kEcc256NumBytes) are used as d0, and
* The last 32 bytes (i.e, kEcc256NumBytes) are used as d1.
*
* Any of the shares can be set to all zeros to simplify the SCA.
*
* As this function sets both shares,
* the data length must be `2*kEcc256NumBytes`.
*
* @param key_d Key.
* @param key_d_len Key length.
*/
static void ecc256_set_private_key_d(const uint8_t *key_d, size_t key_d_len) {
SS_CHECK(key_d_len == 2 * kEcc256NumBytes);
memcpy(ecc256_private_key_d, key_d, key_d_len);
}
/**
* Simple serial 'n' (set message) command handler.
*
* This implementation skips hashing the message to simplify side-channel
* analysis, so the message must not be longer than `kEcc256NumBytes` bytes
* long.
*
* @param msg Message to sign.
* @param msg_len Message length.
*/
static void ecc256_set_msg(const uint8_t *msg, size_t msg_len) {
SS_CHECK(msg_len <= kEcc256NumBytes);
// Reset to zero before copying.
memset(ecc256_msg, 0, kEcc256NumBytes);
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(const uint32_t *msg, const uint32_t *private_key_d,
uint32_t *signature_r, uint32_t *signature_s,
const uint32_t *k) {
uint32_t mode = 1; // mode 1 => sign
// Send operation mode to OTBN
SS_CHECK_STATUS_OK(otbn_dmem_write(/*num_words=*/1, &mode, kOtbnVarMode));
// Send Msg to OTBN
SS_CHECK_STATUS_OK(otbn_dmem_write(kEcc256NumWords, msg, kOtbnVarMsg));
// Send two shares of private_key_d to OTBN
SS_CHECK_STATUS_OK(
otbn_dmem_write(kEcc256NumWords, private_key_d, kOtbnVarD0));
SS_CHECK_STATUS_OK(otbn_dmem_write(
kEcc256NumWords, private_key_d + kEcc256NumWords, kOtbnVarD1));
// Send two shares of secret_k to OTBN
SS_CHECK_STATUS_OK(otbn_dmem_write(kEcc256NumWords, k, kOtbnVarK0));
SS_CHECK_STATUS_OK(
otbn_dmem_write(kEcc256NumWords, k + kEcc256NumWords, kOtbnVarK1));
// Start OTBN execution
SS_CHECK_STATUS_OK(otbn_execute());
SS_CHECK_STATUS_OK(otbn_busy_wait_for_done());
// Read the results back (sig_r, sig_s)
SS_CHECK_STATUS_OK(otbn_dmem_read(kEcc256NumWords, kOtbnVarR, signature_r));
SS_CHECK_STATUS_OK(otbn_dmem_read(kEcc256NumWords, kOtbnVarS, signature_s));
}
/**
* Simple serial 'p' (sign) command handler.
*
* Triggers OTBN_P256_sign operation.
*
*/
static void ecc256_ecdsa() {
LOG_INFO("SSECDSA starting...");
SS_CHECK_STATUS_OK(otbn_load_app(kOtbnAppP256Ecdsa));
LOG_INFO("otbn_status: 0x%08x", abs_mmio_read32(TOP_EARLGREY_OTBN_BASE_ADDR +
OTBN_STATUS_REG_OFFSET));
uint32_t ecc256_signature_r[kEcc256NumWords];
uint32_t ecc256_signature_s[kEcc256NumWords];
LOG_INFO("Signing");
sca_set_trigger_high();
p256_ecdsa_sign(ecc256_msg, ecc256_private_key_d, ecc256_signature_r,
ecc256_signature_s, ecc256_secret_k);
sca_set_trigger_low();
// Copy r and s into byte buffers to avoid strict-aliasing violations.
uint8_t ecc256_signature_r_bytes[kEcc256NumBytes];
memcpy(ecc256_signature_r_bytes, ecc256_signature_r,
sizeof(ecc256_signature_r));
uint8_t ecc256_signature_s_bytes[kEcc256NumBytes];
memcpy(ecc256_signature_s_bytes, ecc256_signature_s,
sizeof(ecc256_signature_s));
// Send the outputs to the Host (Python) through simpleserial protocol.
simple_serial_send_packet('r', ecc256_signature_r_bytes, kEcc256NumBytes);
simple_serial_send_packet('r', ecc256_signature_s_bytes, kEcc256NumBytes);
// Clear OTBN memory
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('p', ecc256_ecdsa) ==
kSimpleSerialOk);
SS_CHECK(simple_serial_register_handler('k', ecc256_set_secret_key_k) ==
kSimpleSerialOk);
SS_CHECK(simple_serial_register_handler('d', ecc256_set_private_key_d) ==
kSimpleSerialOk);
SS_CHECK(simple_serial_register_handler('n', ecc256_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;
}