Merge branch 'fix/ecdsa_s31_deterministic_rfc6979' into 'master'

fix(mbedtls): fixes deterministic ECDSA for S31 hardware operation

See merge request espressif/esp-idf!53204
This commit is contained in:
Mahavir Jain
2026-10-16 10:01:46 +05:30
4 changed files with 159 additions and 16 deletions
+4 -2
View File
@@ -1347,8 +1347,10 @@ menu "mbedTLS"
Note that deterministic signatures generated by the peripheral use its own
deterministic K derivation, which is a variant of RFC 6979. On SECP384R1
they do not match the RFC 6979 signatures of a software implementation,
although both are valid. Disable this option if strict RFC 6979
deterministic signatures are required on SECP384R1.
although both are valid. For a plaintext key pair, deterministic signing
on SECP384R1 therefore uses the software implementation, which follows
RFC 6979. A key in an eFuse key block or in the Key Manager is always
signed by the peripheral.
On a chip without a software key source the private key has to be burnt
in an efuse key block with key purpose set to ECDSA_KEY.
@@ -44,6 +44,21 @@ static const char *TAG = "psa_crypto_driver_esp_ecdsa";
* failing for a given input */
#define ESP_ECDSA_SIGN_MAX_ATTEMPTS 16
/* The peripheral derives the deterministic nonce with its own variant of
* RFC 6979. The signatures match RFC 6979 on SECP256R1 (checked against the
* RFC 6979 test vectors), but not on SECP384R1. Only SECP256R1 is accepted, so
* that deterministic requests on any other curve fall back to the builtin
* RFC 6979 implementation. */
static bool esp_ecdsa_transparent_det_sign_supported(esp_ecdsa_curve_t curve)
{
#if SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE && SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP
return ecdsa_ll_is_deterministic_mode_supported() && curve == ESP_ECDSA_CURVE_SECP256R1;
#else
(void) curve;
return false;
#endif /* SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE && SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP */
}
psa_status_t esp_ecdsa_transparent_sign_hash_start(
esp_ecdsa_transparent_sign_hash_operation_t *operation,
const psa_key_attributes_t *attributes,
@@ -81,22 +96,16 @@ psa_status_t esp_ecdsa_transparent_sign_hash_start(
return PSA_ERROR_NOT_SUPPORTED;
}
if (PSA_ALG_ECDSA_IS_DETERMINISTIC(alg)) {
#if SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE && SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP
if (!ecdsa_ll_is_deterministic_mode_supported()) {
return PSA_ERROR_NOT_SUPPORTED;
}
#else
return PSA_ERROR_NOT_SUPPORTED;
#endif /* SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE && SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP */
}
size_t key_len = PSA_BITS_TO_BYTES(psa_get_key_bits(attributes));
esp_ecdsa_curve_t curve = esp_ecdsa_bits_to_curve(key_len);
if (curve == ESP_ECDSA_CURVE_MAX) {
return PSA_ERROR_NOT_SUPPORTED;
}
if (PSA_ALG_ECDSA_IS_DETERMINISTIC(alg) && !esp_ecdsa_transparent_det_sign_supported(curve)) {
return PSA_ERROR_NOT_SUPPORTED;
}
/* Hash algorithm, eFuse curve and digest length checks shared with the opaque
* driver. Every failure falls back to the builtin implementation. */
if (esp_ecdsa_check_sign_request(alg, curve, hash_length) != PSA_SUCCESS) {
@@ -1134,6 +1134,135 @@ TEST_CASE("mbedtls ECDSA signature generation with software key on SECP384R1", "
}
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
#if SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE
/* RFC 6979 Appendix A.2.5: P-256, SHA-256, message "sample" (big endian) */
static const uint8_t rfc6979_p256_priv[32] = {
0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16,
0x6b, 0x5c, 0x21, 0x57, 0x67, 0xb1, 0xd6, 0x93,
0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12,
0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21,
};
static const uint8_t rfc6979_p256_hash[32] = {
0xaf, 0x2b, 0xdb, 0xe1, 0xaa, 0x9b, 0x6e, 0xc1,
0xe2, 0xad, 0xe1, 0xd6, 0x94, 0xf4, 0x1f, 0xc7,
0x1a, 0x83, 0x1d, 0x02, 0x68, 0xe9, 0x89, 0x15,
0x62, 0x11, 0x3d, 0x8a, 0x62, 0xad, 0xd1, 0xbf,
};
static const uint8_t rfc6979_p256_r[32] = {
0xef, 0xd4, 0x8b, 0x2a, 0xac, 0xb6, 0xa8, 0xfd,
0x11, 0x40, 0xdd, 0x9c, 0xd4, 0x5e, 0x81, 0xd6,
0x9d, 0x2c, 0x87, 0x7b, 0x56, 0xaa, 0xf9, 0x91,
0xc3, 0x4d, 0x0e, 0xa8, 0x4e, 0xaf, 0x37, 0x16,
};
static const uint8_t rfc6979_p256_s[32] = {
0xf7, 0xcb, 0x1c, 0x94, 0x2d, 0x65, 0x7c, 0x41,
0xd4, 0x36, 0xc7, 0xa1, 0xb6, 0xe2, 0x9f, 0x65,
0xf3, 0xe9, 0x00, 0xdb, 0xb9, 0xaf, 0xf4, 0x06,
0x4d, 0xc4, 0xab, 0x2f, 0x84, 0x3a, 0xcd, 0xa8,
};
/* RFC 6979 Appendix A.2.5: P-256, SHA-256, message "test" (big endian, same key) */
static const uint8_t rfc6979_p256_test_hash[32] = {
0x9f, 0x86, 0xd0, 0x81, 0x88, 0x4c, 0x7d, 0x65,
0x9a, 0x2f, 0xea, 0xa0, 0xc5, 0x5a, 0xd0, 0x15,
0xa3, 0xbf, 0x4f, 0x1b, 0x2b, 0x0b, 0x82, 0x2c,
0xd1, 0x5d, 0x6c, 0x15, 0xb0, 0xf0, 0x0a, 0x08,
};
static const uint8_t rfc6979_p256_test_r[32] = {
0xf1, 0xab, 0xb0, 0x23, 0x51, 0x83, 0x51, 0xcd,
0x71, 0xd8, 0x81, 0x56, 0x7b, 0x1e, 0xa6, 0x63,
0xed, 0x3e, 0xfc, 0xf6, 0xc5, 0x13, 0x2b, 0x35,
0x4f, 0x28, 0xd3, 0xb0, 0xb7, 0xd3, 0x83, 0x67,
};
static const uint8_t rfc6979_p256_test_s[32] = {
0x01, 0x9f, 0x41, 0x13, 0x74, 0x2a, 0x2b, 0x14,
0xbd, 0x25, 0x92, 0x6b, 0x49, 0xc6, 0x49, 0x15,
0x5f, 0x26, 0x7e, 0x60, 0xd3, 0x81, 0x4b, 0x4c,
0x0c, 0xc8, 0x42, 0x50, 0xe4, 0x6f, 0x00, 0x83,
};
#if SOC_ECDSA_SUPPORT_CURVE_P384
/* RFC 6979 Appendix A.2.6: P-384, SHA-384, message "sample" (big endian) */
static const uint8_t rfc6979_p384_priv[48] = {
0x6b, 0x9d, 0x3d, 0xad, 0x2e, 0x1b, 0x8c, 0x1c,
0x05, 0xb1, 0x98, 0x75, 0xb6, 0x65, 0x9f, 0x4d,
0xe2, 0x3c, 0x3b, 0x66, 0x7b, 0xf2, 0x97, 0xba,
0x9a, 0xa4, 0x77, 0x40, 0x78, 0x71, 0x37, 0xd8,
0x96, 0xd5, 0x72, 0x4e, 0x4c, 0x70, 0xa8, 0x25,
0xf8, 0x72, 0xc9, 0xea, 0x60, 0xd2, 0xed, 0xf5,
};
static const uint8_t rfc6979_p384_hash[48] = {
0x9a, 0x90, 0x83, 0x50, 0x5b, 0xc9, 0x22, 0x76,
0xae, 0xc4, 0xbe, 0x31, 0x26, 0x96, 0xef, 0x7b,
0xf3, 0xbf, 0x60, 0x3f, 0x4b, 0xbd, 0x38, 0x11,
0x96, 0xa0, 0x29, 0xf3, 0x40, 0x58, 0x53, 0x12,
0x31, 0x3b, 0xca, 0x4a, 0x9b, 0x5b, 0x89, 0x0e,
0xfe, 0xe4, 0x2c, 0x77, 0xb1, 0xee, 0x25, 0xfe,
};
static const uint8_t rfc6979_p384_r[48] = {
0x94, 0xed, 0xbb, 0x92, 0xa5, 0xec, 0xb8, 0xaa,
0xd4, 0x73, 0x6e, 0x56, 0xc6, 0x91, 0x91, 0x6b,
0x3f, 0x88, 0x14, 0x06, 0x66, 0xce, 0x9f, 0xa7,
0x3d, 0x64, 0xc4, 0xea, 0x95, 0xad, 0x13, 0x3c,
0x81, 0xa6, 0x48, 0x15, 0x2e, 0x44, 0xac, 0xf9,
0x6e, 0x36, 0xdd, 0x1e, 0x80, 0xfa, 0xbe, 0x46,
};
static const uint8_t rfc6979_p384_s[48] = {
0x99, 0xef, 0x4a, 0xeb, 0x15, 0xf1, 0x78, 0xce,
0xa1, 0xfe, 0x40, 0xdb, 0x26, 0x03, 0x13, 0x8f,
0x13, 0x0e, 0x74, 0x0a, 0x19, 0x62, 0x45, 0x26,
0x20, 0x3b, 0x63, 0x51, 0xd0, 0xa3, 0xa9, 0x4f,
0xa3, 0x29, 0xc1, 0x45, 0x78, 0x6e, 0x67, 0x9e,
0x7b, 0x82, 0xc7, 0x1a, 0x38, 0x62, 0x8a, 0xc8,
};
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
/* Sign with a deterministic ECDSA software key and compare the signature byte
* for byte with the RFC 6979 value. A valid signature is not enough here: the
* test must fail if any path uses a nonce that is not the RFC 6979 one. */
static void test_ecdsa_sign_rfc6979_kat(size_t bits, psa_algorithm_t hash_alg, const uint8_t *priv,
const uint8_t *hash, const uint8_t *r, const uint8_t *s)
{
size_t len = PSA_BITS_TO_BYTES(bits);
uint8_t signature[2 * MAX_ECDSA_COMPONENT_LEN] = {0};
size_t signature_len = 0;
psa_key_id_t key_id = 0;
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
psa_algorithm_t alg = PSA_ALG_DETERMINISTIC_ECDSA(hash_alg);
psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&attr, bits);
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&attr, alg);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, psa_import_key(&attr, priv, len, &key_id));
psa_reset_key_attributes(&attr);
psa_status_t status = psa_sign_hash(key_id, alg, hash, PSA_HASH_LENGTH(hash_alg),
signature, sizeof(signature), &signature_len);
psa_destroy_key(key_id);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status);
TEST_ASSERT_EQUAL(2 * len, signature_len);
TEST_ASSERT_EQUAL_HEX8_ARRAY(r, signature, len);
TEST_ASSERT_EQUAL_HEX8_ARRAY(s, signature + len, len);
}
TEST_CASE("mbedtls ECDSA deterministic signature with software key matches RFC 6979 on SECP256R1", "[mbedtls][ecdsa_sw_key]")
{
test_ecdsa_sign_rfc6979_kat(256, PSA_ALG_SHA_256, rfc6979_p256_priv, rfc6979_p256_hash,
rfc6979_p256_r, rfc6979_p256_s);
test_ecdsa_sign_rfc6979_kat(256, PSA_ALG_SHA_256, rfc6979_p256_priv, rfc6979_p256_test_hash,
rfc6979_p256_test_r, rfc6979_p256_test_s);
}
#if SOC_ECDSA_SUPPORT_CURVE_P384
TEST_CASE("mbedtls ECDSA deterministic signature with software key matches RFC 6979 on SECP384R1", "[mbedtls][ecdsa_sw_key]")
{
test_ecdsa_sign_rfc6979_kat(384, PSA_ALG_SHA_384, rfc6979_p384_priv, rfc6979_p384_hash,
rfc6979_p384_r, rfc6979_p384_s);
}
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
#endif /* SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE */
TEST_CASE("mbedtls ECDSA software fallback for combinations unsupported by the peripheral", "[mbedtls][ecdsa_sw_key]")
{
/* P-256 key with a SHA-384 sized hash is not supported by the transparent
@@ -136,9 +136,8 @@ static void run_ecdsa_perf(size_t bits, bool deterministic)
int64_t sw_us = time_sign(&ctx, mbedtls_psa_ecdsa_sign_hash, sw_sig);
log_result(deterministic ? "deterministic sign" : "sign", bits, hw_us, sw_us);
/* Deterministic signatures are not compared byte for byte: on P-384 the
* peripheral derives the nonce with a variant of RFC 6979.
* Each implementation must accept the signature of the other one. */
/* Each implementation must accept the signature of the other one. The
* RFC 6979 known-answer tests check deterministic signatures byte for byte. */
TEST_ASSERT_EQUAL(PSA_SUCCESS, mbedtls_psa_ecdsa_verify_hash(&ctx.pub_attr, ctx.pub, 2 * ctx.len + 1,
ctx.alg, ctx.hash, ctx.len,
hw_sig, 2 * ctx.len));
@@ -164,7 +163,11 @@ static void run_ecdsa_perf_on_curve(size_t bits)
run_ecdsa_perf(bits, false);
#if SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE
run_ecdsa_perf(bits, true);
/* The driver signs deterministic requests on P-256 only. On P-384 they
* fall back to the software implementation, so there is nothing to compare. */
if (bits == 256) {
run_ecdsa_perf(bits, true);
}
#endif /* SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE */
}