diff --git a/components/esp_hw_support/include/soc/esp32s3/esp_crypto_lock.h b/components/esp_hw_support/include/soc/esp32s3/esp_crypto_lock.h index d45cc2e322..074754a86b 100644 --- a/components/esp_hw_support/include/soc/esp32s3/esp_crypto_lock.h +++ b/components/esp_hw_support/include/soc/esp32s3/esp_crypto_lock.h @@ -18,6 +18,20 @@ extern "C" { * Other unrelated components must not use it. */ +/** + * @brief Acquire lock for Digital Signature(DS) cryptography peripheral + * + * Internally also takes the HMAC lock, as the DS depends on the HMAC peripheral + */ +void esp_crypto_ds_lock_acquire(void); + +/** + * @brief Release lock for Digital Signature(DS) cryptography peripheral + * + * Internally also releases the HMAC lock, as the DS depends on the HMAC peripheral + */ +void esp_crypto_ds_lock_release(void); + /** * @brief Acquire lock for HMAC cryptography peripheral * diff --git a/components/esp_hw_support/include/soc/esp32s3/esp_ds.h b/components/esp_hw_support/include/soc/esp32s3/esp_ds.h new file mode 100644 index 0000000000..46a1d22a54 --- /dev/null +++ b/components/esp_hw_support/include/soc/esp32s3/esp_ds.h @@ -0,0 +1,192 @@ +/* + * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include + +#include "esp_hmac.h" +#include "esp_err.h" +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP32S3_ERR_HW_CRYPTO_DS_HMAC_FAIL ESP_ERR_HW_CRYPTO_BASE + 0x1 /*!< HMAC peripheral problem */ +#define ESP32S3_ERR_HW_CRYPTO_DS_INVALID_KEY ESP_ERR_HW_CRYPTO_BASE + 0x2 /*!< given HMAC key isn't correct, + HMAC peripheral problem */ +#define ESP32S3_ERR_HW_CRYPTO_DS_INVALID_DIGEST ESP_ERR_HW_CRYPTO_BASE + 0x4 /*!< message digest check failed, + result is invalid */ +#define ESP32S3_ERR_HW_CRYPTO_DS_INVALID_PADDING ESP_ERR_HW_CRYPTO_BASE + 0x5 /*!< padding check failed, but result + is produced anyway and can be read*/ + +#define ESP_DS_IV_LEN 16 + +/* Length of parameter 'C' stored in flash */ +#define ESP_DS_C_LEN (12672 / 8) + +typedef struct esp_ds_context esp_ds_context_t; + +typedef enum { + ESP_DS_RSA_1024 = (1024 / 32) - 1, + ESP_DS_RSA_2048 = (2048 / 32) - 1, + ESP_DS_RSA_3072 = (3072 / 32) - 1, + ESP_DS_RSA_4096 = (4096 / 32) - 1 +} esp_digital_signature_length_t; + +/** + * Encrypted private key data. Recommended to store in flash in this format. + * + * @note This struct has to match to one from the ROM code! This documentation is mostly taken from there. + */ +typedef struct esp_digital_signature_data { + /** + * RSA LENGTH register parameters + * (number of words in RSA key & operands, minus one). + * + * Max value 127 (for RSA 4096). + * + * This value must match the length field encrypted and stored in 'c', + * or invalid results will be returned. (The DS peripheral will + * always use the value in 'c', not this value, so an attacker can't + * alter the DS peripheral results this way, it will just truncate or + * extend the message and the resulting signature in software.) + * + * @note In IDF, the enum type length is the same as of type unsigned, so they can be used interchangably. + * See the ROM code for the original declaration of struct \c ets_ds_data_t. + */ + esp_digital_signature_length_t rsa_length; + + /** + * IV value used to encrypt 'c' + */ + uint8_t iv[ESP_DS_IV_LEN]; + + /** + * Encrypted Digital Signature parameters. Result of AES-CBC encryption + * of plaintext values. Includes an encrypted message digest. + */ + uint8_t c[ESP_DS_C_LEN]; +} esp_ds_data_t; + +/** Plaintext parameters used by Digital Signature. + * + * Not used for signing with DS peripheral, but can be encrypted + * in-device by calling esp_ds_encrypt_params() + * + * @note This documentation is mostly taken from the ROM code. + */ +typedef struct { + uint32_t Y[SOC_RSA_MAX_BIT_LEN / 32]; //!< RSA exponent + uint32_t M[SOC_RSA_MAX_BIT_LEN / 32]; //!< RSA modulus + uint32_t Rb[SOC_RSA_MAX_BIT_LEN / 32]; //!< RSA r inverse operand + uint32_t M_prime; //!< RSA M prime operand + esp_digital_signature_length_t length; //!< RSA length +} esp_ds_p_data_t; + +/** + * Sign the message. + * + * This function is a wrapper around \c esp_ds_finish_sign() and \c esp_ds_start_sign(), so do not use them + * in parallel. + * It blocks until the signing is finished and then returns the signature. + * + * @note This function locks the HMAC, SHA, AES and RSA components during its entire execution time. + * + * @param message the message to be signed; its length is determined by data->rsa_length + * @param data the encrypted signing key data (AES encrypted RSA key + IV) + * @param key_id the HMAC key ID determining the HMAC key of the HMAC which will be used to decrypt the + * signing key data + * @param signature the destination of the signature, should be (data->rsa_length + 1)*4 bytes long + * + * @return + * - ESP_OK if successful, the signature was written to the parameter \c signature. + * - ESP_ERR_INVALID_ARG if one of the parameters is NULL or data->rsa_length is too long or 0 + * - ESP_ERR_HW_CRYPTO_DS_HMAC_FAIL if there was an HMAC failure during retrieval of the decryption key + * - ESP_ERR_NO_MEM if there hasn't been enough memory to allocate the context object + * - ESP_ERR_HW_CRYPTO_DS_INVALID_KEY if there's a problem with passing the HMAC key to the DS component + * - ESP_ERR_HW_CRYPTO_DS_INVALID_DIGEST if the message digest didn't match; the signature is invalid. + * - ESP_ERR_HW_CRYPTO_DS_INVALID_PADDING if the message padding is incorrect, the signature can be read though + * since the message digest matches. + */ +esp_err_t esp_ds_sign(const void *message, + const esp_ds_data_t *data, + hmac_key_id_t key_id, + void *signature); + +/** + * Start the signing process. + * + * This function yields a context object which needs to be passed to \c esp_ds_finish_sign() to finish the signing + * process. + * + * @note This function locks the HMAC, SHA, AES and RSA components, so the user has to ensure to call + * \c esp_ds_finish_sign() in a timely manner. + * + * @param message the message to be signed; its length is determined by data->rsa_length + * @param data the encrypted signing key data (AES encrypted RSA key + IV) + * @param key_id the HMAC key ID determining the HMAC key of the HMAC which will be used to decrypt the + * signing key data + * @param esp_ds_ctx the context object which is needed for finishing the signing process later + * + * @return + * - ESP_OK if successful, the ds operation was started now and has to be finished with \c esp_ds_finish_sign() + * - ESP_ERR_INVALID_ARG if one of the parameters is NULL or data->rsa_length is too long or 0 + * - ESP_ERR_HW_CRYPTO_DS_HMAC_FAIL if there was an HMAC failure during retrieval of the decryption key + * - ESP_ERR_NO_MEM if there hasn't been enough memory to allocate the context object + * - ESP_ERR_HW_CRYPTO_DS_INVALID_KEY if there's a problem with passing the HMAC key to the DS component + */ +esp_err_t esp_ds_start_sign(const void *message, + const esp_ds_data_t *data, + hmac_key_id_t key_id, + esp_ds_context_t **esp_ds_ctx); + +/** + * Return true if the DS peripheral is busy, otherwise false. + * + * @note Only valid if \c esp_ds_start_sign() was called before. + */ +bool esp_ds_is_busy(void); + +/** + * Finish the signing process. + * + * @param signature the destination of the signature, should be (data->rsa_length + 1)*4 bytes long + * @param esp_ds_ctx the context object retreived by \c esp_ds_start_sign() + * + * @return + * - ESP_OK if successful, the ds operation has been finished and the result is written to signature. + * - ESP_ERR_INVALID_ARG if one of the parameters is NULL + * - ESP_ERR_HW_CRYPTO_DS_INVALID_DIGEST if the message digest didn't match; the signature is invalid. + * - ESP_ERR_HW_CRYPTO_DS_INVALID_PADDING if the message padding is incorrect, the signature can be read though + * since the message digest matches. + */ +esp_err_t esp_ds_finish_sign(void *signature, esp_ds_context_t *esp_ds_ctx); + +/** + * Encrypt the private key parameters. + * + * @param data Output buffer to store encrypted data, suitable for later use generating signatures. + * The allocated memory must be in internal memory and word aligned since it's filled by DMA. Both is asserted + * at run time. + * @param iv Pointer to 16 byte IV buffer, will be copied into 'data'. Should be randomly generated bytes each time. + * @param p_data Pointer to input plaintext key data. The expectation is this data will be deleted after this process + * is done and 'data' is stored. + * @param key Pointer to 32 bytes of key data. Type determined by key_type parameter. The expectation is the + * corresponding HMAC key will be stored to efuse and then permanently erased. + * + * @return + * - ESP_OK if successful, the ds operation has been finished and the result is written to signature. + * - ESP_ERR_INVALID_ARG if one of the parameters is NULL or p_data->rsa_length is too long + */ +esp_err_t esp_ds_encrypt_params(esp_ds_data_t *data, + const void *iv, + const esp_ds_p_data_t *p_data, + const void *key); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/port/esp32s3/CMakeLists.txt b/components/esp_hw_support/port/esp32s3/CMakeLists.txt index e607f84f04..e375c6df6d 100644 --- a/components/esp_hw_support/port/esp32s3/CMakeLists.txt +++ b/components/esp_hw_support/port/esp32s3/CMakeLists.txt @@ -10,15 +10,16 @@ set(srcs "rtc_time.c" "rtc_wdt.c" "chip_info.c" - ) + ) if(NOT BOOTLOADER_BUILD) list(APPEND srcs "../async_memcpy_impl_gdma.c" - "dport_access.c" - "esp_hmac.c" - "esp_crypto_lock.c" - "memprot.c" - "spiram.c") + "dport_access.c" + "esp_hmac.c" + "esp_ds.c" + "esp_crypto_lock.c" + "memprot.c" + "spiram.c") if(CONFIG_SPIRAM_MODE_QUAD) list(APPEND srcs "spiram_psram.c") diff --git a/components/esp_hw_support/port/esp32s3/esp_crypto_lock.c b/components/esp_hw_support/port/esp32s3/esp_crypto_lock.c index 3e99a08ea4..a8ca5212f1 100644 --- a/components/esp_hw_support/port/esp32s3/esp_crypto_lock.c +++ b/components/esp_hw_support/port/esp32s3/esp_crypto_lock.c @@ -26,6 +26,16 @@ static _lock_t s_crypto_sha_aes_hmac_ds_lock; /* Lock for the MPI/RSA peripheral, also used by the DS peripheral */ static _lock_t s_crypto_mpi_lock; +void esp_crypto_ds_lock_acquire(void) +{ + _lock_acquire_recursive(&s_crypto_sha_aes_hmac_ds_lock); +} + +void esp_crypto_ds_lock_release(void) +{ + _lock_release_recursive(&s_crypto_sha_aes_hmac_ds_lock); +} + void esp_crypto_hmac_lock_acquire(void) { _lock_acquire_recursive(&s_crypto_sha_aes_hmac_ds_lock); diff --git a/components/esp_hw_support/port/esp32s3/esp_ds.c b/components/esp_hw_support/port/esp32s3/esp_ds.c new file mode 100644 index 0000000000..90385de752 --- /dev/null +++ b/components/esp_hw_support/port/esp32s3/esp_ds.c @@ -0,0 +1,228 @@ +/* + * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/periph_ctrl.h" +#include "esp_crypto_lock.h" +#include "hal/ds_hal.h" +#include "hal/ds_ll.h" +#include "hal/hmac_hal.h" +#include "esp32s3/rom/digital_signature.h" +#include "esp_timer.h" +#include "esp_ds.h" + +struct esp_ds_context { + const esp_ds_data_t *data; +}; + +/** + * The vtask delay \c esp_ds_sign() is using while waiting for completion of the signing operation. + */ +#define ESP_DS_SIGN_TASK_DELAY_MS 10 + +#define RSA_LEN_MAX 127 + +/* + * esp_digital_signature_length_t is used in esp_ds_data_t in contrast to ets_ds_data_t, where unsigned is used. + * Check esp_digital_signature_length_t's width here because it's converted to unsigned using raw casts. + */ +_Static_assert(sizeof(esp_digital_signature_length_t) == sizeof(unsigned), + "The size of esp_digital_signature_length_t and unsigned has to be the same"); + +/* + * esp_ds_data_t is used in the encryption function but casted to ets_ds_data_t. + * Check esp_ds_data_t's width here because it's converted using raw casts. + */ +_Static_assert(sizeof(esp_ds_data_t) == sizeof(ets_ds_data_t), + "The size of esp_ds_data_t and ets_ds_data_t has to be the same"); + +static void ds_acquire_enable(void) +{ + esp_crypto_ds_lock_acquire(); + esp_crypto_mpi_lock_acquire(); + + // We also enable SHA and HMAC here. SHA is used by HMAC, HMAC is used by DS. + periph_module_enable(PERIPH_HMAC_MODULE); + periph_module_enable(PERIPH_SHA_MODULE); + periph_module_enable(PERIPH_DS_MODULE); + + hmac_hal_start(); +} + +static void ds_disable_release(void) +{ + ds_hal_finish(); + + periph_module_disable(PERIPH_DS_MODULE); + periph_module_disable(PERIPH_SHA_MODULE); + periph_module_disable(PERIPH_HMAC_MODULE); + + esp_crypto_mpi_lock_release(); + esp_crypto_ds_lock_release(); +} + +esp_err_t esp_ds_sign(const void *message, + const esp_ds_data_t *data, + hmac_key_id_t key_id, + void *signature) +{ + // Need to check signature here, otherwise the signature is only checked when the signing has finished and fails + // but the signing isn't uninitialized and the mutex is still locked. + if (!signature) { + return ESP_ERR_INVALID_ARG; + } + + esp_ds_context_t *context; + esp_err_t result = esp_ds_start_sign(message, data, key_id, &context); + if (result != ESP_OK) { + return result; + } + + while (esp_ds_is_busy()) { + vTaskDelay(ESP_DS_SIGN_TASK_DELAY_MS / portTICK_PERIOD_MS); + } + + return esp_ds_finish_sign(signature, context); +} + +esp_err_t esp_ds_start_sign(const void *message, + const esp_ds_data_t *data, + hmac_key_id_t key_id, + esp_ds_context_t **esp_ds_ctx) +{ + if (!message || !data || !esp_ds_ctx) { + return ESP_ERR_INVALID_ARG; + } + + if (key_id >= HMAC_KEY_MAX) { + return ESP_ERR_INVALID_ARG; + } + + if (!(data->rsa_length == ESP_DS_RSA_1024 + || data->rsa_length == ESP_DS_RSA_2048 + || data->rsa_length == ESP_DS_RSA_3072 + || data->rsa_length == ESP_DS_RSA_4096)) { + return ESP_ERR_INVALID_ARG; + } + + ds_acquire_enable(); + + // initiate hmac + uint32_t conf_error = hmac_hal_configure(HMAC_OUTPUT_DS, key_id); + if (conf_error) { + ds_disable_release(); + return ESP32S3_ERR_HW_CRYPTO_DS_HMAC_FAIL; + } + + ds_hal_start(); + + // check encryption key from HMAC + int64_t start_time = esp_timer_get_time(); + while (ds_ll_busy() != 0) { + if ((esp_timer_get_time() - start_time) > SOC_DS_KEY_CHECK_MAX_WAIT_US) { + ds_disable_release(); + return ESP32S3_ERR_HW_CRYPTO_DS_INVALID_KEY; + } + } + + esp_ds_context_t *context = malloc(sizeof(esp_ds_context_t)); + if (!context) { + ds_disable_release(); + return ESP_ERR_NO_MEM; + } + + size_t rsa_len = (data->rsa_length + 1) * 4; + ds_hal_write_private_key_params(data->c); + ds_hal_configure_iv((uint32_t *)data->iv); + ds_hal_write_message(message, rsa_len); + + // initiate signing + ds_hal_start_sign(); + + context->data = data; + *esp_ds_ctx = context; + + return ESP_OK; +} + +bool esp_ds_is_busy(void) +{ + return ds_hal_busy(); +} + +esp_err_t esp_ds_finish_sign(void *signature, esp_ds_context_t *esp_ds_ctx) +{ + if (!signature || !esp_ds_ctx) { + return ESP_ERR_INVALID_ARG; + } + + const esp_ds_data_t *data = esp_ds_ctx->data; + unsigned rsa_len = (data->rsa_length + 1) * 4; + + while (ds_hal_busy()) { } + + ds_signature_check_t sig_check_result = ds_hal_read_result((uint8_t *) signature, (size_t) rsa_len); + + esp_err_t return_value = ESP_OK; + + if (sig_check_result == DS_SIGNATURE_MD_FAIL || sig_check_result == DS_SIGNATURE_PADDING_AND_MD_FAIL) { + return_value = ESP32S3_ERR_HW_CRYPTO_DS_INVALID_DIGEST; + } + + if (sig_check_result == DS_SIGNATURE_PADDING_FAIL) { + return_value = ESP32S3_ERR_HW_CRYPTO_DS_INVALID_PADDING; + } + + free(esp_ds_ctx); + + hmac_hal_clean(); + + ds_disable_release(); + + return return_value; +} + +esp_err_t esp_ds_encrypt_params(esp_ds_data_t *data, + const void *iv, + const esp_ds_p_data_t *p_data, + const void *key) +{ + if (!p_data) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t result = ESP_OK; + + esp_crypto_ds_lock_acquire(); + periph_module_enable(PERIPH_AES_MODULE); + periph_module_enable(PERIPH_DS_MODULE); + periph_module_enable(PERIPH_SHA_MODULE); + periph_module_enable(PERIPH_HMAC_MODULE); + periph_module_enable(PERIPH_RSA_MODULE); + + ets_ds_data_t *ds_data = (ets_ds_data_t *) data; + const ets_ds_p_data_t *ds_plain_data = (const ets_ds_p_data_t *) p_data; + + ets_ds_result_t ets_result = ets_ds_encrypt_params(ds_data, iv, ds_plain_data, key, ETS_DS_KEY_HMAC); + + if (ets_result == ETS_DS_INVALID_PARAM) { + result = ESP_ERR_INVALID_ARG; + } + + periph_module_disable(PERIPH_RSA_MODULE); + periph_module_disable(PERIPH_HMAC_MODULE); + periph_module_disable(PERIPH_SHA_MODULE); + periph_module_disable(PERIPH_DS_MODULE); + periph_module_disable(PERIPH_AES_MODULE); + esp_crypto_ds_lock_release(); + + return result; +} diff --git a/components/esp_hw_support/test/test_ds.c b/components/esp_hw_support/test/test_ds.c index cd08702f26..2a69e1e8dc 100644 --- a/components/esp_hw_support/test/test_ds.c +++ b/components/esp_hw_support/test/test_ds.c @@ -19,13 +19,18 @@ #include "esp32c3/rom/efuse.h" #include "esp32c3/rom/digital_signature.h" #include "esp32c3/rom/hmac.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "esp32s3/rom/efuse.h" +#include "esp32s3/rom/digital_signature.h" +#include "esp32s3/rom/aes.h" +#include "esp32s3/rom/sha.h" #endif #include "esp_ds.h" #define NUM_RESULTS 10 -#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 #define DS_MAX_BITS (4096) #elif CONFIG_IDF_TARGET_ESP32C3 #define DS_MAX_BITS (ETS_DS_MAX_BITS) @@ -36,17 +41,17 @@ typedef struct { ets_ds_p_data_t p_data; uint8_t expected_c[ETS_DS_C_LEN]; uint8_t hmac_key_idx; - uint32_t expected_results[NUM_RESULTS][DS_MAX_BITS/32]; + uint32_t expected_results[NUM_RESULTS][DS_MAX_BITS / 32]; } encrypt_testcase_t; // Generated header digital_signature_test_cases_.h (by gen_digital_signature_tests.py) defines // NUM_HMAC_KEYS, test_hmac_keys, NUM_MESSAGES, NUM_CASES, test_messages[], test_cases[] // Some adaptations were made: removed the 512 bit case and changed RSA lengths to the enums from esp_ds.h #if DS_MAX_BITS == 4096 -#define RSA_LEN (ESP_DS_RSA_4096) +#define RSA_LEN (ESP_DS_RSA_4096) #include "digital_signature_test_cases_4096.h" #elif DS_MAX_BITS == 3072 -#define RSA_LEN (ESP_DS_RSA_3072) +#define RSA_LEN (ESP_DS_RSA_3072) #include "digital_signature_test_cases_3072.h" #endif @@ -96,14 +101,14 @@ TEST_CASE("Digital Signature Parameter Encryption", "[hw_crypto] [ds]") esp_ds_data_t result = { }; esp_ds_p_data_t p_data; - memcpy(p_data.Y, t->p_data.Y, DS_MAX_BITS/8); - memcpy(p_data.M, t->p_data.M, DS_MAX_BITS/8); - memcpy(p_data.Rb, t->p_data.Rb, DS_MAX_BITS/8); + memcpy(p_data.Y, t->p_data.Y, DS_MAX_BITS / 8); + memcpy(p_data.M, t->p_data.M, DS_MAX_BITS / 8); + memcpy(p_data.Rb, t->p_data.Rb, DS_MAX_BITS / 8); p_data.M_prime = t->p_data.M_prime; p_data.length = t->p_data.length; esp_err_t r = esp_ds_encrypt_params(&result, t->iv, &p_data, - test_hmac_keys[t->hmac_key_idx]); + test_hmac_keys[t->hmac_key_idx]); printf("Encrypting test case %d done\n", i); TEST_ASSERT_EQUAL(ESP_OK, r); TEST_ASSERT_EQUAL(t->p_data.length, result.rsa_length); @@ -249,9 +254,10 @@ static void burn_hmac_keys(void) // starting from block 1, block 0 occupied with HMAC upstream test key int __attribute__((unused)) ets_status = ets_efuse_write_key(ETS_EFUSE_BLOCK_KEY1 + i, - purpose, - test_hmac_keys[i], 32); -#if CONFIG_IDF_TARGET_ESP32S2 + purpose, + test_hmac_keys[i], 32); + +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 if (ets_status == ESP_OK) { printf("written DS test key to block [%d]!\n", ETS_EFUSE_BLOCK_KEY1 + i); } else { @@ -280,7 +286,7 @@ TEST_CASE("Digital Signature wrong HMAC key purpose (FPGA only)", "[hw_crypto] [ const char *message = "test"; // HMAC fails in that case because it checks for the correct purpose -#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 TEST_ASSERT_EQUAL(ESP_ERR_HW_CRYPTO_DS_HMAC_FAIL, esp_ds_start_sign(message, &ds_data, HMAC_KEY0, &ctx)); #elif CONFIG_IDF_TARGET_ESP32C3 TEST_ASSERT_EQUAL(ESP32C3_ERR_HW_CRYPTO_DS_HMAC_FAIL, esp_ds_start_sign(message, &ds_data, HMAC_KEY0, &ctx)); @@ -297,7 +303,7 @@ TEST_CASE("Digital Signature Blocking wrong HMAC key purpose (FPGA only)", "[hw_ uint8_t signature_data [128 * 4]; // HMAC fails in that case because it checks for the correct purpose -#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 TEST_ASSERT_EQUAL(ESP_ERR_HW_CRYPTO_DS_HMAC_FAIL, esp_ds_sign(message, &ds_data, HMAC_KEY0, signature_data)); #elif CONFIG_IDF_TARGET_ESP32C3 TEST_ASSERT_EQUAL(ESP32C3_ERR_HW_CRYPTO_DS_HMAC_FAIL, esp_ds_sign(message, &ds_data, HMAC_KEY0, signature_data)); @@ -319,15 +325,15 @@ TEST_CASE("Digital Signature Operation (FPGA only)", "[hw_crypto] [ds]") ds_data.rsa_length = t->p_data.length; for (int j = 0; j < NUM_MESSAGES; j++) { - uint8_t signature[DS_MAX_BITS/8] = { 0 }; + uint8_t signature[DS_MAX_BITS / 8] = { 0 }; printf(" ... message %d\n", j); esp_ds_context_t *esp_ds_ctx; esp_err_t ds_r = esp_ds_start_sign(test_messages[j], - &ds_data, - t->hmac_key_idx + 1, - &esp_ds_ctx); + &ds_data, + t->hmac_key_idx + 1, + &esp_ds_ctx); TEST_ASSERT_EQUAL(ESP_OK, ds_r); ds_r = esp_ds_finish_sign(signature, esp_ds_ctx); @@ -355,23 +361,23 @@ TEST_CASE("Digital Signature Blocking Operation (FPGA only)", "[hw_crypto] [ds]" memcpy(ds_data.c, t->expected_c, ETS_DS_C_LEN); ds_data.rsa_length = t->p_data.length; - uint8_t signature[DS_MAX_BITS/8] = { 0 }; -#if CONFIG_IDF_TARGET_ESP32S2 + uint8_t signature[DS_MAX_BITS / 8] = { 0 }; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 esp_ds_context_t *esp_ds_ctx; esp_err_t ds_r = esp_ds_start_sign(test_messages[0], - &ds_data, - t->hmac_key_idx + 1, - &esp_ds_ctx); + &ds_data, + t->hmac_key_idx + 1, + &esp_ds_ctx); TEST_ASSERT_EQUAL(ESP_OK, ds_r); ds_r = esp_ds_finish_sign(signature, esp_ds_ctx); TEST_ASSERT_EQUAL(ESP_OK, ds_r); #elif CONFIG_IDF_TARGET_ESP32C3 esp_err_t ds_r = esp_ds_sign(test_messages[0], - &ds_data, - t->hmac_key_idx + 1, - signature); + &ds_data, + t->hmac_key_idx + 1, + signature); TEST_ASSERT_EQUAL(ESP_OK, ds_r); #endif @@ -390,8 +396,8 @@ TEST_CASE("Digital Signature Invalid Data (FPGA only)", "[hw_crypto] [ds]") memcpy(ds_data.c, t->expected_c, ETS_DS_C_LEN); ds_data.rsa_length = t->p_data.length; - uint8_t signature[DS_MAX_BITS/8] = { 0 }; - const uint8_t zero[DS_MAX_BITS/8] = { 0 }; + uint8_t signature[DS_MAX_BITS / 8] = { 0 }; + const uint8_t zero[DS_MAX_BITS / 8] = { 0 }; // Corrupt the IV one bit at a time, rerun and expect failure for (int bit = 0; bit < 128; bit++) { @@ -402,12 +408,12 @@ TEST_CASE("Digital Signature Invalid Data (FPGA only)", "[hw_crypto] [ds]") esp_err_t ds_r = esp_ds_start_sign(test_messages[0], &ds_data, t->hmac_key_idx + 1, &esp_ds_ctx); TEST_ASSERT_EQUAL(ESP_OK, ds_r); ds_r = esp_ds_finish_sign(signature, esp_ds_ctx); -#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 TEST_ASSERT_EQUAL(ESP_ERR_HW_CRYPTO_DS_INVALID_DIGEST, ds_r); #elif CONFIG_IDF_TARGET_ESP32C3 TEST_ASSERT_EQUAL(ESP32C3_ERR_HW_CRYPTO_DS_INVALID_DIGEST, ds_r); #endif - TEST_ASSERT_EQUAL_HEX8_ARRAY(zero, signature, DS_MAX_BITS/8); + TEST_ASSERT_EQUAL_HEX8_ARRAY(zero, signature, DS_MAX_BITS / 8); ds_data.iv[bit / 8] ^= 1 << (bit % 8); } @@ -422,12 +428,12 @@ TEST_CASE("Digital Signature Invalid Data (FPGA only)", "[hw_crypto] [ds]") esp_err_t ds_r = esp_ds_start_sign(test_messages[0], &ds_data, t->hmac_key_idx + 1, &esp_ds_ctx); TEST_ASSERT_EQUAL(ESP_OK, ds_r); ds_r = esp_ds_finish_sign(signature, esp_ds_ctx); -#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 TEST_ASSERT_EQUAL(ESP_ERR_HW_CRYPTO_DS_INVALID_DIGEST, ds_r); #elif CONFIG_IDF_TARGET_ESP32C3 TEST_ASSERT_EQUAL(ESP32C3_ERR_HW_CRYPTO_DS_INVALID_DIGEST, ds_r); #endif - TEST_ASSERT_EQUAL_HEX8_ARRAY(zero, signature, DS_MAX_BITS/8); + TEST_ASSERT_EQUAL_HEX8_ARRAY(zero, signature, DS_MAX_BITS / 8); ds_data.c[bit / 8] ^= 1 << (bit % 8); } diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index b5d8de8a9b..ecee471ee7 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -1,52 +1,52 @@ idf_build_get_property(target IDF_TARGET) -set(srcs "wdt_hal_iram.c" - "mpu_hal.c" - "cpu_hal.c") +set(srcs "wdt_hal_iram.c" + "mpu_hal.c" + "cpu_hal.c") set(includes "${target}/include" "include" "platform_port/include") if(NOT BOOTLOADER_BUILD) list(APPEND srcs - "rmt_hal.c" - "rtc_io_hal.c" - "spi_hal.c" - "spi_hal_iram.c" - "spi_slave_hal.c" - "spi_slave_hal_iram.c" - "i2s_hal.c" - "sigmadelta_hal.c" - "timer_hal.c" - "ledc_hal.c" - "ledc_hal_iram.c" - "i2c_hal.c" - "i2c_hal_iram.c" - "gpio_hal.c" - "uart_hal.c" - "uart_hal_iram.c" - "spi_flash_hal.c" - "spi_flash_hal_iram.c" - "spi_flash_encrypt_hal_iram.c" - "soc_hal.c" - "interrupt_controller_hal.c" - "sha_hal.c" - "aes_hal.c" - "twai_hal.c" - "twai_hal_iram.c" - "adc_hal.c") + "rmt_hal.c" + "rtc_io_hal.c" + "spi_hal.c" + "spi_hal_iram.c" + "spi_slave_hal.c" + "spi_slave_hal_iram.c" + "i2s_hal.c" + "sigmadelta_hal.c" + "timer_hal.c" + "ledc_hal.c" + "ledc_hal_iram.c" + "i2c_hal.c" + "i2c_hal_iram.c" + "gpio_hal.c" + "uart_hal.c" + "uart_hal_iram.c" + "spi_flash_hal.c" + "spi_flash_hal_iram.c" + "spi_flash_encrypt_hal_iram.c" + "soc_hal.c" + "interrupt_controller_hal.c" + "sha_hal.c" + "aes_hal.c" + "twai_hal.c" + "twai_hal_iram.c" + "adc_hal.c") if(${target} STREQUAL "esp32") list(APPEND srcs - "dac_hal.c" - "mcpwm_hal.c" - "pcnt_hal.c" - "sdio_slave_hal.c" - "touch_sensor_hal.c" - "esp32/adc_hal.c" - "esp32/brownout_hal.c" - "esp32/interrupt_descriptor_table.c" - "esp32/touch_sensor_hal.c" - "esp32/gpio_hal_workaround.c") + "dac_hal.c" + "mcpwm_hal.c" + "pcnt_hal.c" + "sdio_slave_hal.c" + "touch_sensor_hal.c" + "esp32/adc_hal.c" + "esp32/brownout_hal.c" + "esp32/interrupt_descriptor_table.c" + "esp32/touch_sensor_hal.c" + "esp32/gpio_hal_workaround.c") if(NOT BOOTLOADER_BUILD AND CONFIG_ETH_USE_ESP32_EMAC) list(APPEND srcs "emac_hal.c") endif() @@ -73,6 +73,7 @@ if(NOT BOOTLOADER_BUILD) if(${target} STREQUAL "esp32s3") list(APPEND srcs + "ds_hal.c" "gdma_hal.c" "lcd_hal.c" "mcpwm_hal.c" @@ -106,15 +107,15 @@ if(NOT BOOTLOADER_BUILD) if(${target} STREQUAL "esp32h2") list(APPEND srcs - "ds_hal.c" - "gdma_hal.c" - "spi_flash_hal_gpspi.c" - "spi_slave_hd_hal.c" - "systimer_hal.c" - "esp32h2/adc_hal.c" - "esp32h2/brownout_hal.c" - "esp32h2/hmac_hal.c" - "esp32h2/rtc_cntl_hal.c") + "ds_hal.c" + "gdma_hal.c" + "spi_flash_hal_gpspi.c" + "spi_slave_hd_hal.c" + "systimer_hal.c" + "esp32h2/adc_hal.c" + "esp32h2/brownout_hal.c" + "esp32h2/hmac_hal.c" + "esp32h2/rtc_cntl_hal.c") endif() endif() diff --git a/components/hal/esp32s3/include/hal/clk_gate_ll.h b/components/hal/esp32s3/include/hal/clk_gate_ll.h index 3f3fe70fd6..01f684732f 100644 --- a/components/hal/esp32s3/include/hal/clk_gate_ll.h +++ b/components/hal/esp32s3/include/hal/clk_gate_ll.h @@ -157,6 +157,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en return SYSTEM_DMA_RST; case PERIPH_HMAC_MODULE: return SYSTEM_CRYPTO_HMAC_RST; + case PERIPH_DS_MODULE: + return SYSTEM_CRYPTO_DS_RST; case PERIPH_AES_MODULE: if (enable == true) { // Clear reset on digital signature, otherwise AES unit is held in reset also. @@ -203,6 +205,7 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) case PERIPH_LCD_CAM_MODULE: case PERIPH_GDMA_MODULE: case PERIPH_HMAC_MODULE: + case PERIPH_DS_MODULE: case PERIPH_AES_MODULE: case PERIPH_SHA_MODULE: case PERIPH_RSA_MODULE: @@ -229,6 +232,7 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) case PERIPH_LCD_CAM_MODULE: case PERIPH_GDMA_MODULE: case PERIPH_HMAC_MODULE: + case PERIPH_DS_MODULE: case PERIPH_AES_MODULE: case PERIPH_SHA_MODULE: case PERIPH_RSA_MODULE: diff --git a/components/hal/esp32s3/include/hal/ds_ll.h b/components/hal/esp32s3/include/hal/ds_ll.h new file mode 100644 index 0000000000..f5ae99074c --- /dev/null +++ b/components/hal/esp32s3/include/hal/ds_ll.h @@ -0,0 +1,162 @@ +/* + * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include + +#include "soc/hwcrypto_reg.h" +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static inline void ds_ll_start(void) +{ + REG_WRITE(DS_SET_START_REG, 1); +} + +/** + * @brief Wait until DS peripheral has finished any outstanding operation. + */ +static inline bool ds_ll_busy(void) +{ + return (REG_READ(DS_QUERY_BUSY_REG) > 0) ? true : false; +} + +/** + * @brief Busy wait until the hardware is ready. + */ +static inline void ds_ll_wait_busy(void) +{ + while (ds_ll_busy()); +} + +/** + * @brief In case of a key error, check what caused it. + */ +static inline ds_key_check_t ds_ll_key_error_source(void) +{ + uint32_t key_error = REG_READ(DS_QUERY_KEY_WRONG_REG); + if (key_error == 0) { + return DS_NO_KEY_INPUT; + } else { + return DS_OTHER_WRONG; + } +} + +/** + * @brief Write the initialization vector to the corresponding register field. + */ +static inline void ds_ll_configure_iv(const uint32_t *iv) +{ + for (size_t i = 0; i < (SOC_DS_KEY_PARAM_MD_IV_LENGTH / sizeof(uint32_t)); i++) { + REG_WRITE(DS_IV_BASE + (i * 4), iv[i]); + } +} + +/** + * @brief Write the message which should be signed. + * + * @param msg Pointer to the message. + * @param size Length of msg in bytes. It is the RSA signature length in bytes. + */ +static inline void ds_ll_write_message(const uint8_t *msg, size_t size) +{ + memcpy((uint8_t *) DS_X_BASE, msg, size); +} + +/** + * @brief Write the encrypted private key parameters. + */ +static inline void ds_ll_write_private_key_params(const uint8_t *encrypted_key_params) +{ + /* Note: as the internal peripheral still has RSA 4096 structure, + but C is encrypted based on the actual max RSA length (ETS_DS_MAX_BITS), need to fragment it + when copying to hardware... + + (note if ETS_DS_MAX_BITS == 4096, this should be the same as copying data->c to hardware in one fragment) + */ + typedef struct { + uint32_t addr; + size_t len; + } frag_t; + const frag_t frags[] = { + {DS_C_Y_BASE, SOC_DS_SIGNATURE_MAX_BIT_LEN / 8}, + {DS_C_M_BASE, SOC_DS_SIGNATURE_MAX_BIT_LEN / 8}, + {DS_C_RB_BASE, SOC_DS_SIGNATURE_MAX_BIT_LEN / 8}, + {DS_C_BOX_BASE, DS_IV_BASE - DS_C_BOX_BASE}, + }; + const size_t NUM_FRAGS = sizeof(frags) / sizeof(frag_t); + const uint8_t *from = encrypted_key_params; + + for (int i = 0; i < NUM_FRAGS; i++) { + memcpy((uint8_t *)frags[i].addr, from, frags[i].len); + from += frags[i].len; + } +} + +/** + * @brief Begin signing procedure. + */ +static inline void ds_ll_start_sign(void) +{ + REG_WRITE(DS_SET_ME_REG, 1); +} + +/** + * @brief check the calculated signature. + * + * @return + * - DS_SIGNATURE_OK if no issue is detected with the signature. + * - DS_SIGNATURE_PADDING_FAIL if the padding of the private key parameters is wrong. + * - DS_SIGNATURE_MD_FAIL if the message digest check failed. This means that the message digest calculated using + * the private key parameters fails, i.e., the integrity of the private key parameters is not protected. + * - DS_SIGNATURE_PADDING_AND_MD_FAIL if both padding and message digest check fail. + */ +static inline ds_signature_check_t ds_ll_check_signature(void) +{ + uint32_t result = REG_READ(DS_QUERY_CHECK_REG); + switch (result) { + case 0: + return DS_SIGNATURE_OK; + case 1: + return DS_SIGNATURE_MD_FAIL; + case 2: + return DS_SIGNATURE_PADDING_FAIL; + default: + return DS_SIGNATURE_PADDING_AND_MD_FAIL; + } +} + +/** + * @brief Read the signature from the hardware. + * + * @param result The signature result. + * @param size Length of signature result in bytes. It is the RSA signature length in bytes. + */ +static inline void ds_ll_read_result(uint8_t *result, size_t size) +{ + memcpy(result, (uint8_t *) DS_Z_BASE, size); +} + +/** + * @brief Exit the signature operation. + * + * @note This does not deactivate the module. Corresponding clock/reset bits have to be triggered for deactivation. + */ +static inline void ds_ll_finish(void) +{ + REG_WRITE(DS_SET_FINISH_REG, 1); + ds_ll_wait_busy(); +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/ds_hal.h b/components/hal/include/hal/ds_hal.h index 253fab9f96..40d5c35893 100644 --- a/components/hal/include/hal/ds_hal.h +++ b/components/hal/include/hal/ds_hal.h @@ -21,7 +21,7 @@ #pragma once #if CONFIG_IDF_TARGET_ESP32 - #error "ESP32 doesn't have a DS peripheral" +#error "ESP32 doesn't have a DS peripheral" #endif #include diff --git a/components/soc/esp32s3/include/soc/hwcrypto_reg.h b/components/soc/esp32s3/include/soc/hwcrypto_reg.h index 9e16b742e7..09011285fd 100644 --- a/components/soc/esp32s3/include/soc/hwcrypto_reg.h +++ b/components/soc/esp32s3/include/soc/hwcrypto_reg.h @@ -1,16 +1,9 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + #pragma once #include "soc.h" @@ -129,6 +122,10 @@ /* Digital Signature registers*/ #define DS_C_BASE ((DR_REG_DIGITAL_SIGNATURE_BASE) + 0x000 ) +#define DS_C_Y_BASE ((DR_REG_DIGITAL_SIGNATURE_BASE) + 0x000 ) +#define DS_C_M_BASE ((DR_REG_DIGITAL_SIGNATURE_BASE) + 0x200 ) +#define DS_C_RB_BASE ((DR_REG_DIGITAL_SIGNATURE_BASE) + 0x400 ) +#define DS_C_BOX_BASE ((DR_REG_DIGITAL_SIGNATURE_BASE) + 0x600 ) #define DS_IV_BASE ((DR_REG_DIGITAL_SIGNATURE_BASE) + 0x630 ) #define DS_X_BASE ((DR_REG_DIGITAL_SIGNATURE_BASE) + 0x800 ) #define DS_Z_BASE ((DR_REG_DIGITAL_SIGNATURE_BASE) + 0xA00 ) diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 0ad56c7791..2260b176c6 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -18,7 +18,7 @@ #define SOC_USB_OTG_SUPPORTED 1 #define SOC_RTC_SLOW_MEM_SUPPORTED 1 #define SOC_CCOMP_TIMER_SUPPORTED 1 -#define SOC_DIG_SIGN_SUPPORTED 0 +#define SOC_DIG_SIGN_SUPPORTED 1 #define SOC_HMAC_SUPPORTED 1 #define SOC_ASYNC_MEMCPY_SUPPORTED 1 #define SOC_SUPPORTS_SECURE_DL_MODE 1 @@ -51,6 +51,17 @@ /*-------------------------- CPU CAPS ----------------------------------------*/ #include "cpu_caps.h" +/*-------------------------- DIGITAL SIGNATURE CAPS ----------------------------------------*/ +/** The maximum length of a Digital Signature in bits. */ +#define SOC_DS_SIGNATURE_MAX_BIT_LEN (4096) + +/** Initialization vector (IV) length for the RSA key parameter message digest (MD) in bytes. */ +#define SOC_DS_KEY_PARAM_MD_IV_LENGTH (16) + +/** Maximum wait time for DS parameter decryption key. If overdue, then key error. + See TRM DS chapter for more details */ +#define SOC_DS_KEY_CHECK_MAX_WAIT_US (1100) + /*-------------------------- GDMA CAPS ---------------------------------------*/ #define SOC_GDMA_GROUPS (1) // Number of GDMA groups #define SOC_GDMA_PAIRS_PER_GROUP (5) // Number of GDMA pairs in each group diff --git a/docs/doxygen/Doxyfile_esp32s3 b/docs/doxygen/Doxyfile_esp32s3 index 021e46ae76..54152bd320 100644 --- a/docs/doxygen/Doxyfile_esp32s3 +++ b/docs/doxygen/Doxyfile_esp32s3 @@ -1,6 +1,7 @@ INPUT += \ $(PROJECT_PATH)/components/ulp/include/$(IDF_TARGET)/ulp.h \ $(PROJECT_PATH)/components/esp_hw_support/include/soc/$(IDF_TARGET)/esp_hmac.h \ + $(PROJECT_PATH)/components/esp_hw_support/include/soc/$(IDF_TARGET)/esp_ds.h \ $(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \ $(PROJECT_PATH)/components/driver/include/driver/mcpwm.h \ $(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \