From 372a6306e00fe3e2663fc79dca0f0b2978273af6 Mon Sep 17 00:00:00 2001 From: Pascal Nasahl Date: Fri, 23 Aug 2024 11:09:49 +0200 Subject: [PATCH] [pentest] Add HMAC SCA tests This commit pulls over the HMAC SCA tests from the pentest branch of nasahlpa/opentitan that has been used for the penetration testing. Signed-off-by: Pascal Nasahl --- .../tests/penetrationtests/firmware/BUILD | 2 + .../penetrationtests/firmware/firmware.c | 5 + .../penetrationtests/firmware/firmware_sca.c | 5 + .../tests/penetrationtests/firmware/sca/BUILD | 20 ++ .../penetrationtests/firmware/sca/hmac_sca.c | 227 ++++++++++++++++++ .../penetrationtests/firmware/sca/hmac_sca.h | 71 ++++++ sw/device/tests/penetrationtests/json/BUILD | 8 + .../tests/penetrationtests/json/commands.h | 1 + .../penetrationtests/json/hmac_sca_commands.c | 6 + .../penetrationtests/json/hmac_sca_commands.h | 47 ++++ 10 files changed, 392 insertions(+) create mode 100644 sw/device/tests/penetrationtests/firmware/sca/hmac_sca.c create mode 100644 sw/device/tests/penetrationtests/firmware/sca/hmac_sca.h create mode 100644 sw/device/tests/penetrationtests/json/hmac_sca_commands.c create mode 100644 sw/device/tests/penetrationtests/json/hmac_sca_commands.h diff --git a/sw/device/tests/penetrationtests/firmware/BUILD b/sw/device/tests/penetrationtests/firmware/BUILD index 1e421f70fdfd7..7c898a4d31be5 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/fi:rng_fi", "//sw/device/tests/penetrationtests/firmware/fi:rom_fi", "//sw/device/tests/penetrationtests/firmware/sca:aes_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:prng_sca", @@ -59,6 +60,7 @@ FIRMWARE_DEPS_FI = [ FIRMWARE_DEPS_SCA = [ "//sw/device/tests/penetrationtests/firmware/sca:aes_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:prng_sca", diff --git a/sw/device/tests/penetrationtests/firmware/firmware.c b/sw/device/tests/penetrationtests/firmware/firmware.c index 70d5bef8f2b7c..65c4ec16af1c8 100644 --- a/sw/device/tests/penetrationtests/firmware/firmware.c +++ b/sw/device/tests/penetrationtests/firmware/firmware.c @@ -15,6 +15,7 @@ #include "sw/device/tests/penetrationtests/json/commands.h" #include "sw/device/tests/penetrationtests/json/crypto_fi_commands.h" #include "sw/device/tests/penetrationtests/json/extclk_sca_fi_commands.h" +#include "sw/device/tests/penetrationtests/json/hmac_sca_commands.h" #include "sw/device/tests/penetrationtests/json/ibex_fi_commands.h" #include "sw/device/tests/penetrationtests/json/ibex_sca_commands.h" #include "sw/device/tests/penetrationtests/json/kmac_sca_commands.h" @@ -35,6 +36,7 @@ #include "fi/rom_fi.h" #include "lib/extclk_sca_fi.h" #include "sca/aes_sca.h" +#include "sca/hmac_sca.h" #include "sca/ibex_sca.h" #include "sca/kmac_sca.h" #include "sca/prng_sca.h" @@ -57,6 +59,9 @@ status_t process_cmd(ujson_t *uj) { case kPenetrationtestCommandExtClkScaFi: RESP_ERR(uj, handle_extclk_sca_fi(uj)); break; + case kPenetrationtestCommandHmacSca: + RESP_ERR(uj, handle_hmac_sca(uj)); + break; case kPenetrationtestCommandIbexFi: RESP_ERR(uj, handle_ibex_fi(uj)); break; diff --git a/sw/device/tests/penetrationtests/firmware/firmware_sca.c b/sw/device/tests/penetrationtests/firmware/firmware_sca.c index 21c50611cf4bd..f81237718ba7e 100644 --- a/sw/device/tests/penetrationtests/firmware/firmware_sca.c +++ b/sw/device/tests/penetrationtests/firmware/firmware_sca.c @@ -13,6 +13,7 @@ // Include commands #include "sw/device/tests/penetrationtests/json/aes_sca_commands.h" #include "sw/device/tests/penetrationtests/json/commands.h" +#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/prng_sca_commands.h" @@ -22,6 +23,7 @@ // Include handlers #include "lib/extclk_sca_fi.h" #include "sca/aes_sca.h" +#include "sca/hmac_sca.h" #include "sca/ibex_sca.h" #include "sca/kmac_sca.h" #include "sca/prng_sca.h" @@ -41,6 +43,9 @@ status_t process_cmd(ujson_t *uj) { case kPenetrationtestCommandExtClkScaFi: RESP_ERR(uj, handle_extclk_sca_fi(uj)); break; + case kPenetrationtestCommandHmacSca: + RESP_ERR(uj, handle_hmac_sca(uj)); + break; case kPenetrationtestCommandIbexSca: RESP_ERR(uj, handle_ibex_sca(uj)); break; diff --git a/sw/device/tests/penetrationtests/firmware/sca/BUILD b/sw/device/tests/penetrationtests/firmware/sca/BUILD index 444d85192b0cc..825dd9ad58efa 100644 --- a/sw/device/tests/penetrationtests/firmware/sca/BUILD +++ b/sw/device/tests/penetrationtests/firmware/sca/BUILD @@ -42,6 +42,26 @@ cc_library( ], ) +cc_library( + name = "hmac_sca", + srcs = ["hmac_sca.c"], + hdrs = ["hmac_sca.h"], + deps = [ + "//sw/device/lib/base:memory", + "//sw/device/lib/base:status", + "//sw/device/lib/crypto/impl:keyblob", + "//sw/device/lib/crypto/impl:mac", + "//sw/device/lib/crypto/include:datatypes", + "//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:hmac_sca_commands", + ], +) + cc_library( name = "ibex_sca", srcs = ["ibex_sca.c"], diff --git a/sw/device/tests/penetrationtests/firmware/sca/hmac_sca.c b/sw/device/tests/penetrationtests/firmware/sca/hmac_sca.c new file mode 100644 index 0000000000000..047cad2f9ac32 --- /dev/null +++ b/sw/device/tests/penetrationtests/firmware/sca/hmac_sca.c @@ -0,0 +1,227 @@ +// 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/hmac_sca.h" + +#include "sw/device/lib/base/memory.h" +#include "sw/device/lib/base/status.h" +#include "sw/device/lib/crypto/impl/keyblob.h" +#include "sw/device/lib/crypto/include/datatypes.h" +#include "sw/device/lib/crypto/include/mac.h" +#include "sw/device/lib/runtime/log.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/hmac_sca_commands.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" + +enum { + /** + * Key length in bytes. + */ + kKeyLength = HMACSCA_CMD_MAX_KEY_BYTES, + /** + * Message length in bytes. + */ + kMessageLength = HMACSCA_CMD_MAX_MESSAGE_BYTES, + /** + * Tag length in bytes. + */ + kTagLength = HMACSCA_CMD_MAX_TAG_BYTES, + /** + * Tag length in words. + */ + kTagLengthWord = kTagLength / sizeof(uint32_t), + /** + * Max number of traces per batch. + */ + kNumBatchOpsMax = 128, +}; + +static status_t trigger_hmac(uint8_t key_buf[], uint8_t mask_buf[], + uint8_t msg_buf[], uint32_t tag_buf[]) { + // Build the key configuration + otcrypto_key_config_t config = { + .version = kOtcryptoLibVersion1, + .key_mode = kOtcryptoKeyModeHmacSha256, + .key_length = kKeyLength, + .hw_backed = kHardenedBoolFalse, + .security_level = kOtcryptoKeySecurityLevelLow, + }; + + // Create keyblob. + uint32_t keyblob[keyblob_num_words(config)]; + + // Create blinded key. + TRY(keyblob_from_key_and_mask((uint32_t *)key_buf, (uint32_t *)mask_buf, + config, keyblob)); + otcrypto_blinded_key_t key = { + .config = config, + .keyblob_length = sizeof(keyblob), + .keyblob = keyblob, + }; + + // Create input message. + otcrypto_const_byte_buf_t input_message = { + .len = kMessageLength, + .data = msg_buf, + }; + + // Create tag. + otcrypto_word32_buf_t tag = { + .len = kTagLengthWord, + .data = tag_buf, + }; + + sca_set_trigger_high(); + TRY(otcrypto_hmac(&key, input_message, tag)); + sca_set_trigger_low(); + return OK_STATUS(); +} + +status_t handle_hmac_sca_init(ujson_t *uj) { + // Setup trigger and enable peripherals needed for the test. + sca_select_trigger_type(kScaTriggerTypeSw); + // Enable the HMAC module and disable unused IP blocks to improve + // SCA measurements. + sca_init(kScaTriggerSourceHmac, kScaPeripheralEntropy | kScaPeripheralIoDiv4 | + kScaPeripheralOtbn | kScaPeripheralCsrng | + kScaPeripheralEdn | kScaPeripheralHmac); + + // Disable the instruction cache and dummy instructions for SCA. + sca_configure_cpu(); + + // Read device ID and return to host. + penetrationtest_device_id_t uj_output; + TRY(sca_read_device_id(uj_output.device_id)); + RESP_OK(ujson_serialize_penetrationtest_device_id_t, uj, &uj_output); + + return OK_STATUS(); +} + +status_t handle_hmac_sca_batch_fvsr(ujson_t *uj) { + penetrationtest_hmac_sca_key_t uj_key; + penetrationtest_hmac_sca_num_it_t uj_it; + + TRY(ujson_deserialize_penetrationtest_hmac_sca_key_t(uj, &uj_key)); + TRY(ujson_deserialize_penetrationtest_hmac_sca_num_it_t(uj, &uj_it)); + + uint8_t batch_messages[kNumBatchOpsMax][kMessageLength]; + uint8_t batch_keys[kNumBatchOpsMax][kKeyLength]; + uint8_t batch_masks[kNumBatchOpsMax][kKeyLength]; + + // First generate all FvsR data sets. When sample_fixed, + // the provided key/masks is used and the message is random. When + // not sample_fixed, a random key/mask and a random message is + // generated. + bool sample_fixed = true; + for (size_t it = 0; it < uj_it.num_iterations; it++) { + if (sample_fixed) { + memcpy(batch_keys[it], uj_key.key, kKeyLength); + memcpy(batch_masks[it], uj_key.mask, kKeyLength); + } else { + prng_rand_bytes(batch_keys[it], kKeyLength); + prng_rand_bytes(batch_masks[it], kKeyLength); + } + prng_rand_bytes(batch_messages[it], kMessageLength); + sample_fixed = batch_messages[it][0] & 0x1; + } + + // Invoke HMAC for each data set. + uint32_t tag_buf[kTagLengthWord]; + for (size_t it = 0; it < uj_it.num_iterations; it++) { + TRY(trigger_hmac(batch_keys[it], batch_masks[it], batch_messages[it], + tag_buf)); + } + + // Send the last tag to host via UART. + penetrationtest_hmac_sca_tag_t uj_tag; + memcpy(uj_tag.tag, tag_buf, kTagLength); + RESP_OK(ujson_serialize_penetrationtest_hmac_sca_tag_t, uj, &uj_tag); + + return OK_STATUS(); +} + +status_t handle_hmac_sca_batch_random(ujson_t *uj) { + penetrationtest_hmac_sca_num_it_t uj_it; + + TRY(ujson_deserialize_penetrationtest_hmac_sca_num_it_t(uj, &uj_it)); + + uint8_t batch_messages[kNumBatchOpsMax][kMessageLength]; + uint8_t batch_keys[kNumBatchOpsMax][kKeyLength]; + uint8_t batch_masks[kNumBatchOpsMax][kKeyLength]; + + // Generate random keys and messages. + for (size_t it = 0; it < uj_it.num_iterations; it++) { + prng_rand_bytes(batch_keys[it], kKeyLength); + prng_rand_bytes(batch_masks[it], kKeyLength); + prng_rand_bytes(batch_messages[it], kMessageLength); + } + + // Invoke HMAC for each data set. + uint32_t tag_buf[kTagLengthWord]; + for (size_t it = 0; it < uj_it.num_iterations; it++) { + TRY(trigger_hmac(batch_keys[it], batch_masks[it], batch_messages[it], + tag_buf)); + } + + // Send the last tag to host via UART. + penetrationtest_hmac_sca_tag_t uj_tag; + memcpy(uj_tag.tag, tag_buf, kTagLength); + RESP_OK(ujson_serialize_penetrationtest_hmac_sca_tag_t, uj, &uj_tag); + + return OK_STATUS(); +} + +status_t handle_hmac_sca_single(ujson_t *uj) { + penetrationtest_hmac_sca_key_t uj_key; + penetrationtest_hmac_sca_message_t uj_message; + + TRY(ujson_deserialize_penetrationtest_hmac_sca_key_t(uj, &uj_key)); + TRY(ujson_deserialize_penetrationtest_hmac_sca_message_t(uj, &uj_message)); + + // Create buffer to store key, mask, message, and tag. + uint8_t key_buf[kKeyLength]; + memcpy(key_buf, uj_key.key, kKeyLength); + uint8_t mask_buf[kKeyLength]; + memcpy(mask_buf, uj_key.mask, kKeyLength); + uint8_t msg_buf[kMessageLength]; + memcpy(msg_buf, uj_message.message, kMessageLength); + uint32_t tag_buf[kTagLengthWord]; + + // Trigger HMAC operation. + TRY(trigger_hmac(key_buf, mask_buf, msg_buf, tag_buf)); + + // Copy tag to uJSON type. + penetrationtest_hmac_sca_tag_t uj_tag; + memcpy(uj_tag.tag, tag_buf, kTagLength); + + // Send tag to host via UART. + RESP_OK(ujson_serialize_penetrationtest_hmac_sca_tag_t, uj, &uj_tag); + + return OK_STATUS(); +} + +status_t handle_hmac_sca(ujson_t *uj) { + hmac_sca_subcommand_t cmd; + TRY(ujson_deserialize_hmac_sca_subcommand_t(uj, &cmd)); + switch (cmd) { + case kHmacScaSubcommandInit: + return handle_hmac_sca_init(uj); + case kHmacScaSubcommandBatchFvsr: + return handle_hmac_sca_batch_fvsr(uj); + case kHmacScaSubcommandBatchRandom: + return handle_hmac_sca_batch_random(uj); + case kHmacScaSubcommandSingle: + return handle_hmac_sca_single(uj); + default: + LOG_ERROR("Unrecognized HMAC SCA subcommand: %d", cmd); + return INVALID_ARGUMENT(); + } + return OK_STATUS(); +} diff --git a/sw/device/tests/penetrationtests/firmware/sca/hmac_sca.h b/sw/device/tests/penetrationtests/firmware/sca/hmac_sca.h new file mode 100644 index 0000000000000..8309bb17f2221 --- /dev/null +++ b/sw/device/tests/penetrationtests/firmware/sca/hmac_sca.h @@ -0,0 +1,71 @@ +// 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_HMAC_SCA_H_ +#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_SCA_HMAC_SCA_H_ + +#include "sw/device/lib/base/status.h" +#include "sw/device/lib/ujson/ujson.h" + +/** + * Initializes the trigger and configures the device for the HMAC SCA test. + * + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_hmac_sca_init(ujson_t *uj); + +/** + * hmac.sca.batch_fvsr test + * + * This SCA penetration test triggers num_iterations HMAC-SHA256 operations + * using a Fixed vs Random (FvsR) dataset. This dataset is generated on the + * device using the PRNG from the SCA library. + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_hmac_sca_batch_fvsr(ujson_t *uj); + +/** + * hmac.sca.batch_random test + * + * This SCA penetration test triggers num_iterations HMAC-SHA256 operations + * using a random dataset. This dataset is generated on the device using the + * PRNG from the SCA library. + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_hmac_sca_batch_random(ujson_t *uj); + +/** + * hmac.sca.single test + * + * This SCA penetration test triggers a single HMAC-SHA256 operation using the + * provided key, mask, and message. The tag is returend to the host. + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_hmac_sca_single(ujson_t *uj); + +/** + * HMAC SCA command handler. + * + * Command handler for the HMAC SCA command. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_hmac_sca(ujson_t *uj); + +#endif // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_SCA_HMAC_SCA_H_ diff --git a/sw/device/tests/penetrationtests/json/BUILD b/sw/device/tests/penetrationtests/json/BUILD index 9724543397043..4bc0b2f598f4a 100644 --- a/sw/device/tests/penetrationtests/json/BUILD +++ b/sw/device/tests/penetrationtests/json/BUILD @@ -12,6 +12,7 @@ cc_library( ":aes_sca_commands", ":crypto_fi_commands", ":extclk_sca_fi_commands", + ":hmac_sca_commands", ":ibex_fi_commands", ":kmac_sca_commands", ":lc_ctrl_fi_commands", @@ -45,6 +46,13 @@ cc_library( deps = ["//sw/device/lib/ujson"], ) +cc_library( + name = "hmac_sca_commands", + srcs = ["hmac_sca_commands.c"], + hdrs = ["hmac_sca_commands.h"], + deps = ["//sw/device/lib/ujson"], +) + cc_library( name = "ibex_fi_commands", srcs = ["ibex_fi_commands.c"], diff --git a/sw/device/tests/penetrationtests/json/commands.h b/sw/device/tests/penetrationtests/json/commands.h index 0d2c6fa90d53f..fc5b09f290916 100644 --- a/sw/device/tests/penetrationtests/json/commands.h +++ b/sw/device/tests/penetrationtests/json/commands.h @@ -15,6 +15,7 @@ extern "C" { value(_, AesSca) \ value(_, CryptoFi) \ value(_, ExtClkScaFi) \ + value(_, HmacSca) \ value(_, IbexFi) \ value(_, IbexSca) \ value(_, KmacSca) \ diff --git a/sw/device/tests/penetrationtests/json/hmac_sca_commands.c b/sw/device/tests/penetrationtests/json/hmac_sca_commands.c new file mode 100644 index 0000000000000..656cfedae3fce --- /dev/null +++ b/sw/device/tests/penetrationtests/json/hmac_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 "hmac_sca_commands.h" diff --git a/sw/device/tests/penetrationtests/json/hmac_sca_commands.h b/sw/device/tests/penetrationtests/json/hmac_sca_commands.h new file mode 100644 index 0000000000000..d36cc740b2fc8 --- /dev/null +++ b/sw/device/tests/penetrationtests/json/hmac_sca_commands.h @@ -0,0 +1,47 @@ +// 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_HMAC_SCA_COMMANDS_H_ +#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_JSON_HMAC_SCA_COMMANDS_H_ +#include "sw/device/lib/ujson/ujson_derive.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define HMACSCA_CMD_MAX_MESSAGE_BYTES 16 +#define HMACSCA_CMD_MAX_KEY_BYTES 32 +#define HMACSCA_CMD_MAX_TAG_BYTES 32 + +// clang-format off + +#define HMACSCA_SUBCOMMAND(_, value) \ + value(_, Init) \ + value(_, BatchFvsr) \ + value(_, BatchRandom) \ + value(_, Single) +UJSON_SERDE_ENUM(HmacScaSubcommand, hmac_sca_subcommand_t, HMACSCA_SUBCOMMAND); + +#define HMACSCA_MESSAGE(field, string) \ + field(message, uint8_t, HMACSCA_CMD_MAX_MESSAGE_BYTES) +UJSON_SERDE_STRUCT(PenetrationtestHmacScaMessage, penetrationtest_hmac_sca_message_t, HMACSCA_MESSAGE); + +#define HMACSCA_KEY(field, string) \ + field(key, uint8_t, HMACSCA_CMD_MAX_KEY_BYTES) \ + field(mask, uint8_t, HMACSCA_CMD_MAX_KEY_BYTES) +UJSON_SERDE_STRUCT(PenetrationtestHmacScaKey, penetrationtest_hmac_sca_key_t, HMACSCA_KEY); + +#define HMACSCA_TAG(field, string) \ + field(tag, uint8_t, HMACSCA_CMD_MAX_TAG_BYTES) +UJSON_SERDE_STRUCT(PenetrationtestHmacScaTag, penetrationtest_hmac_sca_tag_t, HMACSCA_TAG); + +#define HMACSCA_NUM_IT(field, string) \ + field(num_iterations, uint32_t) +UJSON_SERDE_STRUCT(PenetrationtestHmacScaNumIt, penetrationtest_hmac_sca_num_it_t, HMACSCA_NUM_IT); + +// clang-format on + +#ifdef __cplusplus +} +#endif +#endif // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_JSON_HMAC_SCA_COMMANDS_H_