blob: 898f5fdacdd008279b0e5731770e93cb4c5e8b13 [file]
/*
*
* Copyright (c) 2023-2026 Project CHIP Authors
*
* 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.
*/
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPSafeCasts.h>
#include <platform/ESP32/ESP32CHIPCryptoPAL.h>
#include <esp_idf_version.h>
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
// ESP-IDF v6.0+ uses PSA Crypto for hardware ECDSA operations
#include <psa/crypto.h>
#include <psa_crypto_driver_esp_ecdsa.h>
#include <rom/efuse.h>
namespace {
struct PsaEcdsaContext
{
psa_key_id_t key_id;
};
static_assert(sizeof(PsaEcdsaContext) <= chip::Crypto::kMAX_P256Keypair_Context_Size,
"PsaEcdsaContext exceeds P256KeypairContext size");
static inline PsaEcdsaContext * to_psa_ctx(chip::Crypto::P256KeypairContext * context)
{
return chip::SafePointerCast<PsaEcdsaContext *>(context);
}
static inline const PsaEcdsaContext * to_const_psa_ctx(const chip::Crypto::P256KeypairContext * context)
{
return chip::SafePointerCast<const PsaEcdsaContext *>(context);
}
} // anonymous namespace
namespace chip {
namespace Crypto {
ESP32P256Keypair::~ESP32P256Keypair()
{
// Destroy PSA key handle before base destructor runs P256Keypair::Clear().
// Base Clear() checks mInitialized and skips mbedTLS free once we set it false.
if (mInitialized)
{
PsaEcdsaContext * ctx = to_psa_ctx(&mKeypair);
psa_destroy_key(ctx->key_id);
mInitialized = false;
}
}
CHIP_ERROR ESP32P256Keypair::Initialize(ECPKeyTarget keyTarget, int efuseBlock)
{
VerifyOrReturnError(efuseBlock >= 0 && efuseBlock < ETS_EFUSE_BLOCK_MAX, CHIP_ERROR_INVALID_ARGUMENT);
// Destroy existing PSA key handle if re-initializing
if (mInitialized)
{
PsaEcdsaContext * ctx = to_psa_ctx(&mKeypair);
psa_destroy_key(ctx->key_id);
mInitialized = false;
}
psa_status_t status = PSA_SUCCESS;
size_t key_size_in_bits = 256;
// opaque reference for the efuse-stored ec key
esp_ecdsa_opaque_key_t opaque_key = {};
opaque_key.curve = ESP_ECDSA_CURVE_SECP256R1;
opaque_key.efuse_block = static_cast<uint8_t>(efuseBlock);
psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&key_attr, key_size_in_bits);
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
psa_set_key_lifetime(&key_attr, PSA_KEY_LIFETIME_ESP_ECDSA_VOLATILE);
// validate efuse block exists and has correct key purpose
psa_key_id_t key_id = PSA_KEY_ID_NULL;
status = psa_import_key(&key_attr, reinterpret_cast<const uint8_t *>(&opaque_key), sizeof(opaque_key), &key_id);
psa_reset_key_attributes(&key_attr);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL,
ChipLogError(Crypto, "psa_import_key failed, status:%d", static_cast<int>(status)));
// store key id in the keypair context
PsaEcdsaContext * ctx = to_psa_ctx(&mKeypair);
ctx->key_id = key_id;
mInitialized = true;
return CHIP_NO_ERROR;
}
CHIP_ERROR ESP32P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_length, P256ECDSASignature & out_signature) const
{
VerifyOrReturnError(mInitialized, CHIP_ERROR_UNINITIALIZED);
VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT);
// Compute SHA256 hash
uint8_t digest[kSHA256_Hash_Length];
ReturnErrorOnFailure(Hash_SHA256(msg, msg_length, digest));
const PsaEcdsaContext * ctx = to_const_psa_ctx(&mKeypair);
uint8_t signature[kP256_ECDSA_Signature_Length_Raw];
size_t sig_len = 0;
psa_status_t status =
psa_sign_hash(ctx->key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256), digest, sizeof(digest), signature, sizeof(signature), &sig_len);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL,
ChipLogError(Crypto, "psa_sign_hash failed, status:%d", static_cast<int>(status)));
VerifyOrReturnError(sig_len == kP256_ECDSA_Signature_Length_Raw, CHIP_ERROR_INTERNAL);
memcpy(out_signature.Bytes(), signature, sig_len);
return out_signature.SetLength(kP256_ECDSA_Signature_Length_Raw);
}
} // namespace Crypto
} // namespace chip
#else // ESP_IDF_VERSION < 6.0.0
// Legacy implementation using mbedTLS ECDSA alt for IDF < 6.0
#include <ecdsa/ecdsa_alt.h>
#include <mbedtls/bignum.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/ecdh.h>
#include <mbedtls/ecdsa.h>
#include <mbedtls/ecp.h>
#include <mbedtls/error.h>
// In mbedTLS 3.0.0 direct access to structure fields was replaced with using MBEDTLS_PRIVATE macro.
#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
#define CHIP_CRYPTO_PAL_PRIVATE(x) MBEDTLS_PRIVATE(x)
#else
#define CHIP_CRYPTO_PAL_PRIVATE(x) x
#endif
#define MAX_ERROR_STR_LEN 128
namespace {
static int CryptoRNG(void * ctxt, uint8_t * out_buffer, size_t out_length)
{
return (chip::Crypto::DRBG_get_bytes(out_buffer, out_length) == CHIP_NO_ERROR) ? 0 : 1;
}
static inline mbedtls_ecdsa_context * to_ecdsa_ctx(chip::Crypto::P256KeypairContext * context)
{
return chip::SafePointerCast<mbedtls_ecdsa_context *>(context);
}
static void _log_mbedTLS_error(int error_code)
{
if (error_code != 0)
{
#if defined(MBEDTLS_ERROR_C)
char error_str[MAX_ERROR_STR_LEN];
mbedtls_strerror(error_code, error_str, sizeof(error_str));
ChipLogError(Crypto, "mbedTLS error: %s", error_str);
#else
// Error codes defined in 16-bit negative hex numbers. Ease lookup by printing likewise
ChipLogError(Crypto, "mbedTLS error: -0x%04X", -static_cast<uint16_t>(error_code));
#endif
}
}
} // anonymous namespace
namespace chip {
namespace Crypto {
ESP32P256Keypair::~ESP32P256Keypair()
{
// Base class destructor calls Clear() which handles mbedtls_ecp_keypair_free
}
CHIP_ERROR ESP32P256Keypair::Initialize(ECPKeyTarget keyTarget, int efuseBlock)
{
Clear();
CHIP_ERROR error = CHIP_NO_ERROR;
mbedtls_ecdsa_context * ecdsa_ctx = to_ecdsa_ctx(&mKeypair);
mbedtls_ecdsa_init(ecdsa_ctx);
int status = mbedtls_ecp_group_load(&ecdsa_ctx->MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1);
VerifyOrExit(status == 0, error = CHIP_ERROR_INTERNAL);
status = esp_ecdsa_privkey_load_mpi(&ecdsa_ctx->MBEDTLS_PRIVATE(d), efuseBlock);
VerifyOrExit(status == 0, error = CHIP_ERROR_INTERNAL);
mInitialized = true;
ecdsa_ctx = nullptr;
return error;
exit:
if (ecdsa_ctx)
{
mbedtls_ecdsa_free(ecdsa_ctx);
ecdsa_ctx = nullptr;
}
_log_mbedTLS_error(status);
return error;
}
CHIP_ERROR ESP32P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_length, P256ECDSASignature & out_signature) const
{
VerifyOrReturnError(mInitialized, CHIP_ERROR_UNINITIALIZED);
VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT);
uint8_t digest[kSHA256_Hash_Length];
memset(&digest[0], 0, sizeof(digest));
ReturnErrorOnFailure(Hash_SHA256(msg, msg_length, &digest[0]));
#if defined(MBEDTLS_ECDSA_C)
CHIP_ERROR error = CHIP_NO_ERROR;
int result = 0;
mbedtls_mpi r, s;
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
mbedtls_ecdsa_context * ecdsa_ctx = to_ecdsa_ctx(&mKeypair);
result = mbedtls_ecdsa_sign(&ecdsa_ctx->CHIP_CRYPTO_PAL_PRIVATE(grp), &r, &s, &ecdsa_ctx->CHIP_CRYPTO_PAL_PRIVATE(d),
Uint8::to_const_uchar(digest), sizeof(digest), CryptoRNG, nullptr);
VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL);
VerifyOrExit((mbedtls_mpi_size(&r) <= kP256_FE_Length) && (mbedtls_mpi_size(&s) <= kP256_FE_Length),
error = CHIP_ERROR_INTERNAL);
// Concatenate r and s to output. Sizes were checked above.
result = mbedtls_mpi_write_binary(&r, out_signature.Bytes() + 0u, kP256_FE_Length);
VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL);
result = mbedtls_mpi_write_binary(&s, out_signature.Bytes() + kP256_FE_Length, kP256_FE_Length);
VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(out_signature.SetLength(kP256_ECDSA_Signature_Length_Raw) == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL);
exit:
ecdsa_ctx = nullptr;
mbedtls_mpi_free(&s);
mbedtls_mpi_free(&r);
_log_mbedTLS_error(result);
return error;
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif
}
} // namespace Crypto
} // namespace chip
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)