From 9a26c5d76bb63e12d23de0aceb4309dc61e3352c Mon Sep 17 00:00:00 2001 From: Pascal Nasahl Date: Fri, 22 Mar 2024 13:11:15 +0100 Subject: [PATCH] [pentest] Add ECC256 Keygen SCA test This commit adds the ECC256 key generation side-channel penetration test to the codebase. The host code is located in lowRISC/ot-sca#347 Signed-off-by: Pascal Nasahl --- sw/device/sca/aes_serial.c | 2 +- sw/device/sca/kmac_serial.c | 2 +- sw/device/sca/lib/BUILD | 1 + sw/device/sca/lib/sca.c | 7 +- sw/device/sca/lib/sca.h | 3 +- .../sca/otbn_vertical/ecc256_keygen_serial.c | 2 +- .../sca/otbn_vertical/ecc256_modinv_serial.c | 2 +- sw/device/sca/sha3_serial.c | 2 +- .../tests/penetrationtests/firmware/BUILD | 2 + .../penetrationtests/firmware/firmware.c | 5 + .../penetrationtests/firmware/firmware_sca.c | 5 + .../tests/penetrationtests/firmware/sca/BUILD | 40 ++ .../penetrationtests/firmware/sca/aes_sca.c | 4 +- .../firmware/sca/ecc256_keygen_sca.c | 353 ++++++++++++++++++ .../firmware/sca/ecc256_keygen_sca.h | 31 ++ .../penetrationtests/firmware/sca/kmac_sca.c | 7 +- .../penetrationtests/firmware/sca/otbn_sca.c | 63 ++++ .../penetrationtests/firmware/sca/otbn_sca.h | 95 +++++ .../penetrationtests/firmware/sca/sha3_sca.c | 6 +- sw/device/tests/penetrationtests/json/BUILD | 8 + .../tests/penetrationtests/json/commands.h | 1 + .../penetrationtests/json/otbn_sca_commands.c | 6 + .../penetrationtests/json/otbn_sca_commands.h | 53 +++ 23 files changed, 686 insertions(+), 14 deletions(-) create mode 100644 sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.c create mode 100644 sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.h create mode 100644 sw/device/tests/penetrationtests/firmware/sca/otbn_sca.c create mode 100644 sw/device/tests/penetrationtests/firmware/sca/otbn_sca.h create mode 100644 sw/device/tests/penetrationtests/json/otbn_sca_commands.c create mode 100644 sw/device/tests/penetrationtests/json/otbn_sca_commands.h diff --git a/sw/device/sca/aes_serial.c b/sw/device/sca/aes_serial.c index 38fcecf25744a..aa0cce80eddd7 100644 --- a/sw/device/sca/aes_serial.c +++ b/sw/device/sca/aes_serial.c @@ -245,7 +245,7 @@ static void aes_encrypt(const uint8_t *plaintext, size_t plaintext_len) { // Using the SecAesStartTriggerDelay hardware parameter, the AES unit is // configured to start operation 40 cycles after receiving the start trigger. // This allows Ibex to go to sleep in order to not disturb the capture. - sca_call_and_sleep(aes_manual_trigger, kIbexAesSleepCycles, false); + sca_call_and_sleep(aes_manual_trigger, kIbexAesSleepCycles, false, false); } /** diff --git a/sw/device/sca/kmac_serial.c b/sw/device/sca/kmac_serial.c index 79edbb7de40cc..0ade42be0afb7 100644 --- a/sw/device/sca/kmac_serial.c +++ b/sw/device/sca/kmac_serial.c @@ -480,7 +480,7 @@ static void sha3_serial_absorb(const uint8_t *msg, size_t msg_len) { // configured to start operation 40 cycles after receiving the START and PROC // commands. This allows Ibex to go to sleep in order to not disturb the // capture. - sca_call_and_sleep(kmac_msg_proc, kIbexSha3SleepCycles, false); + sca_call_and_sleep(kmac_msg_proc, kIbexSha3SleepCycles, false, false); } /** diff --git a/sw/device/sca/lib/BUILD b/sw/device/sca/lib/BUILD index 794249a15fa9c..991d83d93c5ff 100644 --- a/sw/device/sca/lib/BUILD +++ b/sw/device/sca/lib/BUILD @@ -55,6 +55,7 @@ cc_library( "//sw/device/lib/arch:device", "//sw/device/lib/base:bitfield", "//sw/device/lib/base:macros", + "//sw/device/lib/crypto/drivers:otbn", "//sw/device/lib/dif:clkmgr", "//sw/device/lib/dif:csrng", "//sw/device/lib/dif:edn", diff --git a/sw/device/sca/lib/sca.c b/sw/device/sca/lib/sca.c index 5781dbb51bf09..84f8bccdcc185 100644 --- a/sw/device/sca/lib/sca.c +++ b/sw/device/sca/lib/sca.c @@ -8,6 +8,7 @@ #include "sw/device/lib/arch/device.h" #include "sw/device/lib/base/bitfield.h" #include "sw/device/lib/base/macros.h" +#include "sw/device/lib/crypto/drivers/otbn.h" #include "sw/device/lib/dif/dif_clkmgr.h" #include "sw/device/lib/dif/dif_entropy_src.h" #include "sw/device/lib/dif/dif_gpio.h" @@ -310,7 +311,7 @@ void sca_set_trigger_low(void) { } void sca_call_and_sleep(sca_callee callee, uint32_t sleep_cycles, - bool sw_trigger) { + bool sw_trigger, bool otbn) { // Disable the IO_DIV4_PERI clock to reduce noise during the actual capture. // This also disables the UART(s) and GPIO modules required for // communication with the scope. Therefore, it has to be re-enabled after @@ -337,6 +338,10 @@ void sca_call_and_sleep(sca_callee callee, uint32_t sleep_cycles, wait_for_interrupt(); + if (otbn) { + otbn_busy_wait_for_done(); + } + if (sw_trigger) { sca_set_trigger_low(); } diff --git a/sw/device/sca/lib/sca.h b/sw/device/sca/lib/sca.h index 57e0fd084e6f4..ceb4b6a9698eb 100644 --- a/sw/device/sca/lib/sca.h +++ b/sw/device/sca/lib/sca.h @@ -197,9 +197,10 @@ typedef void (*sca_callee)(void); * @param callee Function to call before putting Ibex to sleep. * @param sleep_cycles Number of cycles to sleep. * @param sw_trigger Raise trigger before calling the target function. + * @param otbn Wait until OTBN execution has finished. */ void sca_call_and_sleep(sca_callee callee, uint32_t sleep_cycles, - bool sw_trigger); + bool sw_trigger, bool otbn); /** * Seeds the software LFSR usable e.g. for key masking. diff --git a/sw/device/sca/otbn_vertical/ecc256_keygen_serial.c b/sw/device/sca/otbn_vertical/ecc256_keygen_serial.c index e8daef5cc37ce..c9a41f94c8b88 100644 --- a/sw/device/sca/otbn_vertical/ecc256_keygen_serial.c +++ b/sw/device/sca/otbn_vertical/ecc256_keygen_serial.c @@ -204,7 +204,7 @@ static void p256_run_keygen(uint32_t mode, const uint32_t *share0, // Execute program. sca_set_trigger_high(); - sca_call_and_sleep(otbn_manual_trigger, kIbexOtbnSleepCycles, false); + sca_call_and_sleep(otbn_manual_trigger, kIbexOtbnSleepCycles, false, false); SS_CHECK_STATUS_OK(otbn_busy_wait_for_done()); sca_set_trigger_low(); } diff --git a/sw/device/sca/otbn_vertical/ecc256_modinv_serial.c b/sw/device/sca/otbn_vertical/ecc256_modinv_serial.c index 18fe0be6ae5fd..7ab76b6b697a1 100644 --- a/sw/device/sca/otbn_vertical/ecc256_modinv_serial.c +++ b/sw/device/sca/otbn_vertical/ecc256_modinv_serial.c @@ -92,7 +92,7 @@ static void p256_run_modinv(uint32_t *k0, uint32_t *k1) { // Execute program. sca_set_trigger_high(); - sca_call_and_sleep(otbn_manual_trigger, kIbexOtbnSleepCycles, false); + sca_call_and_sleep(otbn_manual_trigger, kIbexOtbnSleepCycles, false, false); otbn_busy_wait_for_done(); sca_set_trigger_low(); } diff --git a/sw/device/sca/sha3_serial.c b/sw/device/sca/sha3_serial.c index 10046c1cc0dc0..da366e96e9f15 100644 --- a/sw/device/sca/sha3_serial.c +++ b/sw/device/sca/sha3_serial.c @@ -405,7 +405,7 @@ static void sha3_serial_absorb(const uint8_t *msg, size_t msg_len) { // configured to start operation 40 cycles after receiving the START and PROC // commands. This allows Ibex to go to sleep in order to not disturb the // capture. - sca_call_and_sleep(kmac_msg_proc, kIbexSha3SleepCycles, true); + sca_call_and_sleep(kmac_msg_proc, kIbexSha3SleepCycles, true, false); } /** diff --git a/sw/device/tests/penetrationtests/firmware/BUILD b/sw/device/tests/penetrationtests/firmware/BUILD index bbbe66712ade4..cf26baa8d3aac 100644 --- a/sw/device/tests/penetrationtests/firmware/BUILD +++ b/sw/device/tests/penetrationtests/firmware/BUILD @@ -19,6 +19,7 @@ FIRMWARE_DEPS_FPGA = [ "//sw/device/tests/penetrationtests/firmware/sca:hmac_sca", "//sw/device/tests/penetrationtests/firmware/sca:ibex_sca", "//sw/device/tests/penetrationtests/firmware/sca:kmac_sca", + "//sw/device/tests/penetrationtests/firmware/sca:otbn_sca", "//sw/device/tests/penetrationtests/firmware/sca:prng_sca", "//sw/device/tests/penetrationtests/firmware/sca:sha3_sca", "//sw/device/tests/penetrationtests/firmware/sca:trigger_sca", @@ -57,6 +58,7 @@ FIRMWARE_DEPS_SCA = [ "//sw/device/tests/penetrationtests/firmware/sca:hmac_sca", "//sw/device/tests/penetrationtests/firmware/sca:ibex_sca", "//sw/device/tests/penetrationtests/firmware/sca:kmac_sca", + "//sw/device/tests/penetrationtests/firmware/sca:otbn_sca", "//sw/device/tests/penetrationtests/firmware/sca:prng_sca", "//sw/device/tests/penetrationtests/firmware/sca:sha3_sca", "//sw/device/tests/penetrationtests/firmware/sca:trigger_sca", diff --git a/sw/device/tests/penetrationtests/firmware/firmware.c b/sw/device/tests/penetrationtests/firmware/firmware.c index 2017c81f74cb5..fc45ca43d5bba 100644 --- a/sw/device/tests/penetrationtests/firmware/firmware.c +++ b/sw/device/tests/penetrationtests/firmware/firmware.c @@ -20,6 +20,7 @@ #include "sw/device/tests/penetrationtests/json/ibex_sca_commands.h" #include "sw/device/tests/penetrationtests/json/kmac_sca_commands.h" #include "sw/device/tests/penetrationtests/json/otbn_fi_commands.h" +#include "sw/device/tests/penetrationtests/json/otbn_sca_commands.h" #include "sw/device/tests/penetrationtests/json/prng_sca_commands.h" #include "sw/device/tests/penetrationtests/json/sha3_sca_commands.h" #include "sw/device/tests/penetrationtests/json/trigger_sca_commands.h" @@ -33,6 +34,7 @@ #include "sca/hmac_sca.h" #include "sca/ibex_sca.h" #include "sca/kmac_sca.h" +#include "sca/otbn_sca.h" #include "sca/prng_sca.h" #include "sca/sha3_sca.h" #include "sca/trigger_sca.h" @@ -65,6 +67,9 @@ status_t process_cmd(ujson_t *uj) { case kPenetrationtestCommandKmacSca: RESP_ERR(uj, handle_kmac_sca(uj)); break; + case kPenetrationtestCommandOtbnSca: + RESP_ERR(uj, handle_otbn_sca(uj)); + break; case kPenetrationtestCommandOtbnFi: RESP_ERR(uj, handle_otbn_fi(uj)); break; diff --git a/sw/device/tests/penetrationtests/firmware/firmware_sca.c b/sw/device/tests/penetrationtests/firmware/firmware_sca.c index de3e7d68ae5e0..46f80e51ed130 100644 --- a/sw/device/tests/penetrationtests/firmware/firmware_sca.c +++ b/sw/device/tests/penetrationtests/firmware/firmware_sca.c @@ -16,6 +16,7 @@ #include "sw/device/tests/penetrationtests/json/hmac_sca_commands.h" #include "sw/device/tests/penetrationtests/json/ibex_sca_commands.h" #include "sw/device/tests/penetrationtests/json/kmac_sca_commands.h" +#include "sw/device/tests/penetrationtests/json/otbn_sca_commands.h" #include "sw/device/tests/penetrationtests/json/prng_sca_commands.h" #include "sw/device/tests/penetrationtests/json/sha3_sca_commands.h" #include "sw/device/tests/penetrationtests/json/trigger_sca_commands.h" @@ -26,6 +27,7 @@ #include "sca/hmac_sca.h" #include "sca/ibex_sca.h" #include "sca/kmac_sca.h" +#include "sca/otbn_sca.h" #include "sca/prng_sca.h" #include "sca/sha3_sca.h" #include "sca/trigger_sca.h" @@ -52,6 +54,9 @@ status_t process_cmd(ujson_t *uj) { case kPenetrationtestCommandKmacSca: RESP_ERR(uj, handle_kmac_sca(uj)); break; + case kPenetrationtestCommandOtbnSca: + RESP_ERR(uj, handle_otbn_sca(uj)); + break; case kPenetrationtestCommandPrngSca: RESP_ERR(uj, handle_prng_sca(uj)); break; diff --git a/sw/device/tests/penetrationtests/firmware/sca/BUILD b/sw/device/tests/penetrationtests/firmware/sca/BUILD index 40c608e13869b..15022d9bd0227 100644 --- a/sw/device/tests/penetrationtests/firmware/sca/BUILD +++ b/sw/device/tests/penetrationtests/firmware/sca/BUILD @@ -24,6 +24,46 @@ cc_library( ], ) +cc_library( + name = "ecc256_keygen_sca", + srcs = ["ecc256_keygen_sca.c"], + hdrs = ["ecc256_keygen_sca.h"], + 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/test_framework:ottf_main", + "//sw/device/lib/testing/test_framework:ujson_ottf", + "//sw/device/lib/ujson", + "//sw/device/sca/lib:prng", + "//sw/device/sca/lib:sca", + "//sw/device/tests/penetrationtests/json:otbn_sca_commands", + "//sw/otbn/crypto:p256_key_from_seed_sca", + ], +) + +cc_library( + name = "otbn_sca", + srcs = ["otbn_sca.c"], + hdrs = ["otbn_sca.h"], + deps = [ + ":ecc256_keygen_sca", + "//sw/device/lib/base:memory", + "//sw/device/lib/base:status", + "//sw/device/lib/crypto/impl:status", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing/test_framework:ujson_ottf", + "//sw/device/lib/ujson", + "//sw/device/sca/lib:prng", + "//sw/device/sca/lib:sca", + "//sw/device/tests/penetrationtests/firmware/lib:sca_lib", + "//sw/device/tests/penetrationtests/json:otbn_sca_commands", + ], +) + cc_library( name = "hmac_sca", srcs = ["hmac_sca.c"], diff --git a/sw/device/tests/penetrationtests/firmware/sca/aes_sca.c b/sw/device/tests/penetrationtests/firmware/sca/aes_sca.c index 0fd91f45d3866..88e57309def12 100644 --- a/sw/device/tests/penetrationtests/firmware/sca/aes_sca.c +++ b/sw/device/tests/penetrationtests/firmware/sca/aes_sca.c @@ -250,12 +250,12 @@ static aes_sca_error_t aes_encrypt(const uint8_t *plaintext, // Start AES operation (this triggers the capture) and go to sleep. if (fpga_mode) { // On the FPGA, the AES block automatically sets and unsets the trigger. - sca_call_and_sleep(aes_manual_trigger, kIbexAesSleepCycles, false); + sca_call_and_sleep(aes_manual_trigger, kIbexAesSleepCycles, false, false); } else { // On the chip, we need to manually set and unset the trigger. This is done // in this function to have the trigger as close as possible to the AES // operation. - sca_call_and_sleep(aes_manual_trigger, kIbexAesSleepCycles, true); + sca_call_and_sleep(aes_manual_trigger, kIbexAesSleepCycles, true, false); } return aesScaOk; diff --git a/sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.c b/sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.c new file mode 100644 index 0000000000000..c3fbd8f727ac7 --- /dev/null +++ b/sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.c @@ -0,0 +1,353 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.h" + +#include "sw/device/lib/base/abs_mmio.h" +#include "sw/device/lib/base/memory.h" +#include "sw/device/lib/runtime/ibex.h" +#include "sw/device/lib/runtime/log.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/lib/testing/test_framework/ujson_ottf.h" +#include "sw/device/lib/ujson/ujson.h" +#include "sw/device/sca/lib/prng.h" +#include "sw/device/sca/lib/sca.h" +#include "sw/device/tests/penetrationtests/json/otbn_sca_commands.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" +#include "otbn_regs.h" + +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, + /** + * Max number of traces per batch. + */ + kNumBatchOpsMax = 256, + /** + * Number of cycles that Ibex should sleep to minimize noise during OTBN + * operations. Caution: This number should be chosen to provide enough time + * to complete the operation. Otherwise, Ibex might wake up while OTBN is + * still busy and disturb the capture. This was measured using mcycle_read + * when otbn_manual_trigger and otbn_busy_wait_for_done get executed. + */ + kIbexOtbnSleepCycles = 1100, +}; + +/** + * App configuration for p256_key_from_seed_sca + */ +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); + +/** + * An array of seeds to be used in a batch + */ +uint32_t batch_share0[kNumBatchOpsMax][kEcc256SeedNumWords]; + +/** + * An array of masks to be used in a batch + */ +uint32_t batch_share1[kNumBatchOpsMax][kEcc256SeedNumWords]; + +/** + * Arrays for first and second share of masked private key d to be used in a + * batch + */ +uint32_t d0_batch[kEcc256SeedNumWords]; +uint32_t d1_batch[kEcc256SeedNumWords]; + +/** + * Fixed-message indicator. + * + * Used in the 'b' (batch capture) command for indicating whether to use fixed + * or random message. + */ +static bool run_fixed = true; + +/** + * Masking indicator. + * + * Used in the 'b' (batch capture) command for indicating whether to use masks. + */ +static bool en_masks = false; + +uint32_t ecc256_seed[kEcc256SeedNumWords]; + +uint32_t ecc256_C[kEcc256SeedNumWords]; + +uint32_t random_number[kEcc256CoordNumWords]; + +uint32_t ecc256_fixed_number[kEcc256CoordNumWords]; + +/** + * Adds two integers storred in byte arrays. + * + * Adds the integer stored in source array to the integer stored in + * destination aray. + * The user needs to ensure that dest_len is enough to store the result + * without overflow. + * + * @param[in] dest Location of the first input array and the result. + * @param[in] source Location of the second input array. + * @param[in] dest_len Length of the dest array in bytes. + * @param[in] source_len Length of the source array in bytes. + */ +static void add_arrays(uint8_t *dest, uint8_t *source, size_t dest_len, + size_t source_len) { + uint16_t temp = 0; + + for (size_t i = 0; i < source_len; i++) { + temp += (uint16_t)source[i] + dest[i]; + dest[i] = (uint8_t)(temp & 0x00FF); + temp >>= 8; + } + + for (size_t i = source_len; i < dest_len; i++) { + temp += (uint16_t)dest[i]; + dest[i] = (uint8_t)(temp & 0x00FF); + temp >>= 8; + } +} + +/** + * Callback wrapper for OTBN manual trigger function. + */ +static void otbn_manual_trigger(void) { otbn_execute(); } + +/** + * 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. + * @return OK or error. + */ +static status_t p256_run_keygen(uint32_t mode, const uint32_t *share0, + const uint32_t *share1) { + // Secure wipe to scramble DMEM. + TRY(otbn_load_app(kOtbnAppP256KeyFromSeed)); + + // Write mode. + TRY(otbn_dmem_write(/*num_words=*/1, &mode, kOtbnVarMode)); + + // Write seed shares. + TRY(otbn_dmem_write(kEcc256SeedNumWords, share0, kOtbnVarSeed0)); + TRY(otbn_dmem_write(kEcc256SeedNumWords, share1, kOtbnVarSeed1)); + + // Execute program. Trigger is set inside this function. + sca_call_and_sleep(otbn_manual_trigger, kIbexOtbnSleepCycles, true, true); + + return OK_STATUS(); +} + +status_t handle_otbn_sca_ecc256_en_masks(ujson_t *uj) { + penetrationtest_otbn_sca_en_masks_t uj_data; + TRY(ujson_deserialize_penetrationtest_otbn_sca_en_masks_t(uj, &uj_data)); + if (uj_data.en_masks) { + en_masks = true; + } else { + en_masks = false; + } + return OK_STATUS(); +} + +status_t handle_otbn_sca_ecc256_set_seed(ujson_t *uj) { + penetrationtest_otbn_sca_seed_t uj_data; + TRY(ujson_deserialize_penetrationtest_otbn_sca_seed_t(uj, &uj_data)); + + memcpy(ecc256_seed, uj_data.seed, kEcc256SeedNumBytes); + + return OK_STATUS(); +} + +status_t handle_otbn_sca_ecc256_set_c(ujson_t *uj) { + penetrationtest_otbn_sca_constant_t uj_data; + TRY(ujson_deserialize_penetrationtest_otbn_sca_constant_t(uj, &uj_data)); + + memcpy(ecc256_C, uj_data.constant, kEcc256SeedNumBytes); + + return OK_STATUS(); +} + +status_t handle_otbn_sca_ecc256_ecdsa_keygen_fvsr_seed_batch(ujson_t *uj) { + penetrationtest_otbn_sca_num_traces_t uj_data; + TRY(ujson_deserialize_penetrationtest_otbn_sca_num_traces_t(uj, &uj_data)); + + uint32_t num_traces = uj_data.num_traces; + uint32_t batch_digest[kEcc256SeedNumWords]; + + if (num_traces > kNumBatchOpsMax) { + return OUT_OF_RANGE(); + } + + // Zero the batch digest. + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_digest[j] = 0; + } + + for (size_t i = 0; i < num_traces; ++i) { + // Set mask to a random mask (en_masks = true) or to 0 (en_masks = false). + if (en_masks) { + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_share1[i][j] = prng_rand_uint32(); + } + } else { + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_share1[i][j] = 0; + } + } + + // If the run is fixed, take the fixed seed received from the host. Else, + // generate a random seed. + if (run_fixed) { + memcpy(batch_share0[i], ecc256_seed, kEcc256SeedNumBytes); + } else { + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_share0[i][j] = prng_rand_uint32(); + } + } + + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_share0[i][j] ^= batch_share1[i][j]; + } + run_fixed = prng_rand_uint32() & 0x1; + } + + for (size_t i = 0; i < num_traces; ++i) { + TRY(p256_run_keygen(kEcc256ModePrivateKeyOnly, batch_share0[i], + batch_share1[i])); + + // Read results. + TRY(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD0, d0_batch)); + TRY(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD1, d1_batch)); + + // The correctness of each batch is verified by computing and sending + // the batch digest. This digest is computed by XORing all d0 shares of + // the batch. + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_digest[j] ^= d0_batch[j]; + } + } + + // Send the batch digest to the host for verification. + penetrationtest_otbn_sca_batch_digest_t uj_output; + memcpy(uj_output.batch_digest, (uint8_t *)batch_digest, + kEcc256SeedNumWords * 4); + RESP_OK(ujson_serialize_penetrationtest_otbn_sca_batch_digest_t, uj, + &uj_output); + + return OK_STATUS(); +} + +status_t handle_otbn_sca_ecc256_ecdsa_keygen_fvsr_key_batch(ujson_t *uj) { + penetrationtest_otbn_sca_num_traces_t uj_data; + TRY(ujson_deserialize_penetrationtest_otbn_sca_num_traces_t(uj, &uj_data)); + + uint32_t num_traces = uj_data.num_traces; + uint32_t batch_digest[kEcc256SeedNumWords]; + + if (num_traces > kNumBatchOpsMax) { + return OUT_OF_RANGE(); + } + + // Zero the batch digest. + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_digest[j] = 0; + } + + for (size_t i = 0; i < num_traces; ++i) { + // Set mask to a random mask (en_masks = true) or to 0 (en_masks = false). + if (en_masks) { + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_share1[i][j] = prng_rand_uint32(); + } + } else { + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_share1[i][j] = 0; + } + } + + // If the run is fixed, take the fixed seed received from the host. Else, + // generate a random seed. + if (run_fixed) { + memcpy(batch_share0[i], ecc256_seed, kEcc256SeedNumBytes); + } else { + // Here change to random_number + C. + // It is necessary to set the C first. + memcpy(batch_share0[i], ecc256_C, kEcc256SeedNumBytes); + for (size_t j = 0; j < kEcc256CoordNumWords; ++j) { + random_number[j] = prng_rand_uint32(); + } + add_arrays((unsigned char *)batch_share0[i], + (unsigned char *)random_number, kEcc256SeedNumBytes, + kEcc256CoordNumBytes); + } + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_share0[i][j] ^= batch_share1[i][j]; + } + // Another PRNG run to determine 'run_fixed' for the next cycle. + run_fixed = prng_rand_uint32() & 0x1; + } + + for (size_t i = 0; i < num_traces; ++i) { + TRY(p256_run_keygen(kEcc256ModePrivateKeyOnly, batch_share0[i], + batch_share1[i])); + + // Read results. + TRY(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD0, d0_batch)); + TRY(otbn_dmem_read(kEcc256SeedNumWords, kOtbnVarD1, d1_batch)); + + // The correctness of each batch is verified by computing and sending + // the batch digest. This digest is computed by XORing all d0 shares of + // the batch. + for (size_t j = 0; j < kEcc256SeedNumWords; ++j) { + batch_digest[j] ^= d0_batch[j]; + } + } + // Send the batch digest to the host for verification. + penetrationtest_otbn_sca_batch_digest_t uj_output; + memcpy(uj_output.batch_digest, (uint8_t *)batch_digest, + kEcc256SeedNumWords * 4); + RESP_OK(ujson_serialize_penetrationtest_otbn_sca_batch_digest_t, uj, + &uj_output); + + return OK_STATUS(); +} diff --git a/sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.h b/sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.h new file mode 100644 index 0000000000000..70e7d076ad35f --- /dev/null +++ b/sw/device/tests/penetrationtests/firmware/sca/ecc256_keygen_sca.h @@ -0,0 +1,31 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_SCA_ECC256_KEYGEN_SCA_H_ +#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_SCA_ECC256_KEYGEN_SCA_H_ + +#include "sw/device/lib/crypto/drivers/otbn.h" + +/** + * App configuration for p256_key_from_seed_sca + */ +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); + +extern const otbn_app_t kOtbnAppP256KeyFromSeed; + +static const otbn_addr_t kOtbnVarMode; +static const otbn_addr_t kOtbnVarSeed0; +static const otbn_addr_t kOtbnVarSeed1; +static const otbn_addr_t kOtbnVarD0; +static const otbn_addr_t kOtbnVarD1; + +#endif // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_SCA_ECC256_KEYGEN_SCA_H_ diff --git a/sw/device/tests/penetrationtests/firmware/sca/kmac_sca.c b/sw/device/tests/penetrationtests/firmware/sca/kmac_sca.c index 15813cf8a9178..aa8e77b72e52a 100644 --- a/sw/device/tests/penetrationtests/firmware/sca/kmac_sca.c +++ b/sw/device/tests/penetrationtests/firmware/sca/kmac_sca.c @@ -543,7 +543,7 @@ static kmac_sca_error_t sha3_ujson_absorb(const uint8_t *msg, size_t msg_len) { if (fpga_mode == false) { // Start command. On the chip, we need to first issue a START command // before writing to the message FIFO. - sca_call_and_sleep(kmac_start_cmd, kIbexLoadHashPrefixKeySleepCycles, + sca_call_and_sleep(kmac_start_cmd, kIbexLoadHashPrefixKeySleepCycles, false, false); } @@ -558,11 +558,12 @@ static kmac_sca_error_t sha3_ujson_absorb(const uint8_t *msg, size_t msg_len) { // configured to start operation 320 cycles after receiving the START and // PROC commands. This allows Ibex to go to sleep in order to not disturb // the capture. - sca_call_and_sleep(kmac_start_process_cmd, kIbexSha3SleepCycles, false); + sca_call_and_sleep(kmac_start_process_cmd, kIbexSha3SleepCycles, false, + false); } else { // On the chip, issue a PROCESS command to start operation and put Ibex // into sleep. - sca_call_and_sleep(kmac_process_cmd, kIbexLoadHashMessageSleepCycles, + sca_call_and_sleep(kmac_process_cmd, kIbexLoadHashMessageSleepCycles, false, false); } diff --git a/sw/device/tests/penetrationtests/firmware/sca/otbn_sca.c b/sw/device/tests/penetrationtests/firmware/sca/otbn_sca.c new file mode 100644 index 0000000000000..47d7ce453583b --- /dev/null +++ b/sw/device/tests/penetrationtests/firmware/sca/otbn_sca.c @@ -0,0 +1,63 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/tests/penetrationtests/firmware/sca/otbn_sca.h" + +#include "ecc256_keygen_sca.h" +#include "sw/device/lib/base/memory.h" +#include "sw/device/lib/base/status.h" +#include "sw/device/lib/crypto/impl/status.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/entropy_testutils.h" +#include "sw/device/lib/testing/test_framework/ottf_test_config.h" +#include "sw/device/lib/testing/test_framework/ujson_ottf.h" +#include "sw/device/lib/ujson/ujson.h" +#include "sw/device/sca/lib/prng.h" +#include "sw/device/sca/lib/sca.h" +#include "sw/device/tests/penetrationtests/firmware//lib/sca_lib.h" +#include "sw/device/tests/penetrationtests/json/otbn_sca_commands.h" + +status_t handle_otbn_sca_init(ujson_t *uj) { + // Configure the entropy complex for OTBN. Set the reseed interval to max + // to avoid a non-constant trigger window. + TRY(sca_configure_entropy_source_max_reseed_interval()); + + sca_init(kScaTriggerSourceOtbn, kScaPeripheralEntropy | kScaPeripheralIoDiv4 | + kScaPeripheralOtbn | kScaPeripheralCsrng | + kScaPeripheralEdn | kScaPeripheralHmac); + + // Load p256 keygen from seed app into OTBN. + if (otbn_load_app(kOtbnAppP256KeyFromSeed).value != OTCRYPTO_OK.value) { + return ABORTED(); + } + + // Disable the instruction cache and dummy instructions for better SCA + // measurements. + sca_configure_cpu(); + + return OK_STATUS(); +} + +status_t handle_otbn_sca(ujson_t *uj) { + otbn_sca_subcommand_t cmd; + TRY(ujson_deserialize_otbn_sca_subcommand_t(uj, &cmd)); + switch (cmd) { + case kOtbnScaSubcommandInit: + return handle_otbn_sca_init(uj); + case kOtbnScaSubcommandEcc256EcdsaKeygenFvsrSeedBatch: + return handle_otbn_sca_ecc256_ecdsa_keygen_fvsr_seed_batch(uj); + case kOtbnScaSubcommandEcc256EcdsaKeygenFvsrKeyBatch: + return handle_otbn_sca_ecc256_ecdsa_keygen_fvsr_key_batch(uj); + case kOtbnScaSubcommandEcc256SetSeed: + return handle_otbn_sca_ecc256_set_seed(uj); + case kOtbnScaSubcommandEcc256SetC: + return handle_otbn_sca_ecc256_set_c(uj); + case kOtbnScaSubcommandEcc256EnMasks: + return handle_otbn_sca_ecc256_en_masks(uj); + default: + LOG_ERROR("Unrecognized OTBN SCA subcommand: %d", cmd); + return INVALID_ARGUMENT(); + } + return OK_STATUS(); +} diff --git a/sw/device/tests/penetrationtests/firmware/sca/otbn_sca.h b/sw/device/tests/penetrationtests/firmware/sca/otbn_sca.h new file mode 100644 index 0000000000000..27a4098fe88bc --- /dev/null +++ b/sw/device/tests/penetrationtests/firmware/sca/otbn_sca.h @@ -0,0 +1,95 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_SCA_OTBN_SCA_H_ +#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_SCA_OTBN_SCA_H_ + +#include "sw/device/lib/base/status.h" +#include "sw/device/lib/ujson/ujson.h" + +/** + * Select the OTBN app to load. + * + * Currently, only the P256KeyFromSeed application is supported. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_otbn_sca_ecc256_app_select(ujson_t *uj); + +/** + * Enable or disable masking. + * + * This command handler allows to enable or disable the masking. When masking is + * turned on, a random 320-bit mask is generated for the seed share 1. The mask + * is 0 when masking is turned off. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_otbn_sca_ecc256_en_masks(ujson_t *uj); + +/** + * Set the constant C. + * + * This command handler allows the host to set the constant C to generate the + * random key. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_otbn_sca_ecc256_set_c(ujson_t *uj); + +/** + * Set the seed share 0. + * + * Allows the host to set the 320-bit seed share 0 that is used for the key + * generation. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_otbn_sca_ecc256_set_seed(ujson_t *uj); + +/** + * Starts the P-256 ECDSA Key Generation from a key in batch mode. + * + * Num_traces fixed vs random keys are generated using the SCA PRNG and + * for each key the key generation operation on OTBN is started. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_otbn_sca_ecc256_ecdsa_keygen_fvsr_key_batch(ujson_t *uj); + +/** + * Starts the P-256 ECDSA Key Generation from a seed in batch mode. + * + * Num_traces fixed vs random seeds are generated using the SCA PRNG and + * for each seed the key generation operation on OTBN is started. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_otbn_sca_ecc256_ecdsa_keygen_fvsr_seed_batch(ujson_t *uj); + +/** + * Initializes the OTBN SCA test on the device. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_otbn_sca_init(ujson_t *uj); + +/** + * OTBN SCA command handler. + * + * Command handler for the OTBN SCA command. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_otbn_sca(ujson_t *uj); + +#endif // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_SCA_OTBN_SCA_H_ diff --git a/sw/device/tests/penetrationtests/firmware/sca/sha3_sca.c b/sw/device/tests/penetrationtests/firmware/sca/sha3_sca.c index eb01663486875..db30803043b50 100644 --- a/sw/device/tests/penetrationtests/firmware/sca/sha3_sca.c +++ b/sw/device/tests/penetrationtests/firmware/sca/sha3_sca.c @@ -416,11 +416,13 @@ sha3_sca_error_t sha3_serial_absorb(const uint8_t *msg, size_t msg_len) { // configured to start operation 320 cycles after receiving the START and // PROC commands. This allows Ibex to go to sleep in order to not disturb // the capture. - sca_call_and_sleep(kmac_start_process_cmd, kIbexSha3SleepCycles, true); + sca_call_and_sleep(kmac_start_process_cmd, kIbexSha3SleepCycles, true, + false); } else { // On the chip, issue a PROCESS command to start operation and put Ibex // into sleep. - sca_call_and_sleep(kmac_process_cmd, kIbexLoadHashMessageSleepCycles, true); + sca_call_and_sleep(kmac_process_cmd, kIbexLoadHashMessageSleepCycles, true, + false); } return sha3ScaOk; diff --git a/sw/device/tests/penetrationtests/json/BUILD b/sw/device/tests/penetrationtests/json/BUILD index 29a47b6bc176c..9097292616080 100644 --- a/sw/device/tests/penetrationtests/json/BUILD +++ b/sw/device/tests/penetrationtests/json/BUILD @@ -17,6 +17,7 @@ cc_library( ":ibex_sca_commands", ":kmac_sca_commands", ":otbn_fi_commands", + ":otbn_sca_commands", ":prng_sca_commands", ":sha3_sca_commands", ":trigger_sca_commands", @@ -73,6 +74,13 @@ cc_library( deps = ["//sw/device/lib/ujson"], ) +cc_library( + name = "otbn_sca_commands", + srcs = ["otbn_sca_commands.c"], + hdrs = ["otbn_sca_commands.h"], + deps = ["//sw/device/lib/ujson"], +) + cc_library( name = "otbn_fi_commands", srcs = ["otbn_fi_commands.c"], diff --git a/sw/device/tests/penetrationtests/json/commands.h b/sw/device/tests/penetrationtests/json/commands.h index 920bcef51cc18..929dee55db913 100644 --- a/sw/device/tests/penetrationtests/json/commands.h +++ b/sw/device/tests/penetrationtests/json/commands.h @@ -19,6 +19,7 @@ extern "C" { value(_, IbexFi) \ value(_, IbexSca) \ value(_, KmacSca) \ + value(_, OtbnSca) \ value(_, OtbnFi) \ value(_, PrngSca) \ value(_, Sha3Sca) \ diff --git a/sw/device/tests/penetrationtests/json/otbn_sca_commands.c b/sw/device/tests/penetrationtests/json/otbn_sca_commands.c new file mode 100644 index 0000000000000..3647bf0cc04a1 --- /dev/null +++ b/sw/device/tests/penetrationtests/json/otbn_sca_commands.c @@ -0,0 +1,6 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#define UJSON_SERDE_IMPL 1 +#include "otbn_sca_commands.h" diff --git a/sw/device/tests/penetrationtests/json/otbn_sca_commands.h b/sw/device/tests/penetrationtests/json/otbn_sca_commands.h new file mode 100644 index 0000000000000..c325db8129c35 --- /dev/null +++ b/sw/device/tests/penetrationtests/json/otbn_sca_commands.h @@ -0,0 +1,53 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_JSON_OTBN_SCA_COMMANDS_H_ +#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_JSON_OTBN_SCA_COMMANDS_H_ +#include "sw/device/lib/ujson/ujson_derive.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define OTBNSCA_CMD_MAX_BATCH_DIGEST_BYTES 40 +#define OTBNSCA_CMD_MAX_SEED_BYTES 40 + +// clang-format off + +// OTBN SCA arguments + +#define OTBNSCA_SUBCOMMAND(_, value) \ + value(_, Init) \ + value(_, Ecc256EcdsaKeygenFvsrSeedBatch) \ + value(_, Ecc256EcdsaKeygenFvsrKeyBatch) \ + value(_, Ecc256SetSeed) \ + value(_, Ecc256SetC) \ + value(_, Ecc256EnMasks) +UJSON_SERDE_ENUM(OtbnScaSubcommand, otbn_sca_subcommand_t, OTBNSCA_SUBCOMMAND); + +#define OTBN_SCA_EN_MASKS(field, string) \ + field(en_masks, bool) +UJSON_SERDE_STRUCT(PenetrationtestOtbnScaEnMasks, penetrationtest_otbn_sca_en_masks_t, OTBN_SCA_EN_MASKS); + +#define OTBN_SCA_NUM_TRACES(field, string) \ + field(num_traces, uint32_t) +UJSON_SERDE_STRUCT(PenetrationtestOtbnScaNumTraces, penetrationtest_otbn_sca_num_traces_t, OTBN_SCA_NUM_TRACES); + +#define OTBN_SCA_BATCH_DIGEST(field, string) \ + field(batch_digest, uint8_t, OTBNSCA_CMD_MAX_BATCH_DIGEST_BYTES) +UJSON_SERDE_STRUCT(PenetrationtestOtbnScaBatchDigest, penetrationtest_otbn_sca_batch_digest_t, OTBN_SCA_BATCH_DIGEST); + +#define OTBN_SCA_SEED(field, string) \ + field(seed, uint8_t, OTBNSCA_CMD_MAX_SEED_BYTES) +UJSON_SERDE_STRUCT(PenetrationtestOtbnScaSeed, penetrationtest_otbn_sca_seed_t, OTBN_SCA_SEED); + +#define OTBN_SCA_CONSTANT(field, string) \ + field(constant, uint8_t, OTBNSCA_CMD_MAX_SEED_BYTES) +UJSON_SERDE_STRUCT(PenetrationtestOtbnScaConstant, penetrationtest_otbn_sca_constant_t, OTBN_SCA_CONSTANT); + +// clang-format on + +#ifdef __cplusplus +} +#endif +#endif // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_JSON_OTBN_SCA_COMMANDS_H_