446 lines
16 KiB
C
Raw Permalink Normal View History

2025-08-19 09:49:41 +08:00
/**
* Copyright (c) 2018 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(NRF_BLE_LESC)
#include "nrf_ble_lesc.h"
#include "nrf_crypto.h"
#define NRF_LOG_MODULE_NAME nrf_ble_lesc
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
/**@brief Descriptor of the peer public key. */
typedef struct
{
nrf_crypto_ecc_public_key_t value; /**< Peer public key. */
bool is_requested; /**< Flag indicating that the public key has been requested to compute DH key. */
bool is_valid; /**< Flag indicating that the public key is valid. */
} nrf_ble_lesc_peer_pub_key_t;
/**@brief The maximum number of peripheral and central connections combined.
* This value is based on what is configured in the SoftDevice handler sdk_config.
*/
#define NRF_BLE_LESC_LINK_COUNT (NRF_SDH_BLE_PERIPHERAL_LINK_COUNT + NRF_SDH_BLE_CENTRAL_LINK_COUNT)
__ALIGN(4) static ble_gap_lesc_p256_pk_t m_lesc_public_key; /**< LESC ECC Public Key. */
__ALIGN(4) static ble_gap_lesc_dhkey_t m_lesc_dh_key; /**< LESC ECC DH Key. */
static nrf_crypto_ecdh_context_t m_ecdh_context; /**< Context to do the LESC ECDH calculation */
static bool m_ble_lesc_internal_error; /**< Flag indicating that the module encountered an internal error. */
static bool m_keypair_generated; /**< Flag indicating that the local ECDH key pair was generated. */
static nrf_crypto_ecc_key_pair_generate_context_t m_keygen_context; /**< Context to generate private/public key pair. */
static nrf_crypto_ecc_private_key_t m_private_key; /**< Allocated private key type to use for LESC DH generation. */
static nrf_crypto_ecc_public_key_t m_public_key; /**< Allocated public key type to use for LESC DH generation. */
static nrf_ble_lesc_peer_pub_key_t m_peer_keys[NRF_BLE_LESC_LINK_COUNT]; /**< Array of pointers to peer public keys, used for LESC DH generation. */
static bool m_lesc_oobd_own_generated;
static ble_gap_lesc_oob_data_t m_ble_lesc_oobd_own; /**< LESC OOB data used in LESC OOB pairing mode. */
static nrf_ble_lesc_peer_oob_data_handler m_lesc_oobd_peer_handler;
ret_code_t nrf_ble_lesc_init(void)
{
ret_code_t err_code;
memset((void *) m_peer_keys, 0, sizeof(m_peer_keys));
#if NRF_CRYPTO_ALLOCATOR == NRF_CRYPTO_ALLOCATOR_NRF_MALLOC
// Initialize mem_manager if used by nrf_crypto.
err_code = nrf_mem_init();
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("nrf_mem_init() returned error 0x%x.", err_code);
return err_code;
}
#endif
// Ensure that nrf_crypto has been initialized.
err_code = nrf_crypto_init();
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("nrf_crypto_init() returned error 0x%x.", err_code);
return err_code;
}
NRF_LOG_DEBUG("Initialized nrf_crypto.");
#if defined(NRF_CRYPTO_RNG_AUTO_INIT_ENABLED) && (NRF_CRYPTO_RNG_AUTO_INIT_ENABLED == 1)
// Do nothing. RNG is initialized with nrf_crypto_init call.
#elif defined(NRF_CRYPTO_RNG_AUTO_INIT_ENABLED) && (NRF_CRYPTO_RNG_AUTO_INIT_ENABLED == 0)
// Initialize the RNG.
err_code = nrf_crypto_rng_init(NULL, NULL);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("nrf_crypto_rng_init() returned error 0x%x.", err_code);
return err_code;
}
#else
#error Invalid sdk_config.h (does not contain NRF_CRYPTO_RNG_AUTO_INIT_ENABLED)
#endif // defined(NRF_CRYPTO_RNG_AUTO_INIT_ENABLED) && (NRF_CRYPTO_RNG_AUTO_INIT_ENABLED == 1)
NRF_LOG_DEBUG("Initialized nrf_ble_lesc.");
// Reset module state.
m_ble_lesc_internal_error = false;
m_keypair_generated = false;
// Generate ECC key pair. Only one key pair is automatically generated by this module.
err_code = nrf_ble_lesc_keypair_generate();
return err_code;
}
ret_code_t nrf_ble_lesc_keypair_generate(void)
{
ret_code_t err_code;
size_t public_len = NRF_CRYPTO_ECC_SECP256R1_RAW_PUBLIC_KEY_SIZE;
// Check if any DH computation is pending
for (uint32_t i = 0; i < ARRAY_SIZE(m_peer_keys); i++)
{
if (m_peer_keys[i].is_valid)
{
return NRF_ERROR_BUSY;
}
}
// Update flag to indicate that there is no valid private key.
m_keypair_generated = false;
m_lesc_oobd_own_generated = false;
NRF_LOG_DEBUG("Generating ECC key pair");
err_code = nrf_crypto_ecc_key_pair_generate(&m_keygen_context,
&g_nrf_crypto_ecc_secp256r1_curve_info,
&m_private_key,
&m_public_key);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("nrf_crypto_ecc_key_pair_generate() returned error 0x%x.", err_code);
return err_code;
}
// Convert to a raw type.
err_code = nrf_crypto_ecc_public_key_to_raw(&m_public_key,
m_lesc_public_key.pk,
&public_len);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("nrf_crypto_ecc_public_key_to_raw() returned error 0x%x.", err_code);
return err_code;
}
// Invert the raw type to little-endian (required for BLE).
err_code = nrf_crypto_ecc_byte_order_invert(&g_nrf_crypto_ecc_secp256r1_curve_info,
m_lesc_public_key.pk,
m_lesc_public_key.pk,
NRF_CRYPTO_ECC_SECP256R1_RAW_PUBLIC_KEY_SIZE);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("nrf_crypto_ecc_byte_order_invert() returned error 0x%x.", err_code);
}
else
{
// Set the flag to indicate that there is a valid ECDH key pair generated.
m_keypair_generated = true;
}
return err_code;
}
ret_code_t nrf_ble_lesc_own_oob_data_generate(void)
{
ret_code_t err_code = NRF_ERROR_INVALID_STATE;
m_lesc_oobd_own_generated = false;
if (m_keypair_generated)
{
err_code = sd_ble_gap_lesc_oob_data_get(BLE_CONN_HANDLE_INVALID,
&m_lesc_public_key,
&m_ble_lesc_oobd_own);
if (err_code == NRF_SUCCESS)
{
m_lesc_oobd_own_generated = true;
}
}
return err_code;
}
ble_gap_lesc_p256_pk_t * nrf_ble_lesc_public_key_get(void)
{
ble_gap_lesc_p256_pk_t * p_lesc_pk = NULL;
if (m_keypair_generated)
{
p_lesc_pk = &m_lesc_public_key;
}
else
{
NRF_LOG_ERROR("Trying to access LESC public key that has not been generated yet.");
}
return p_lesc_pk;
}
ble_gap_lesc_oob_data_t * nrf_ble_lesc_own_oob_data_get(void)
{
ble_gap_lesc_oob_data_t * p_lesc_oobd_own = NULL;
if (m_lesc_oobd_own_generated)
{
p_lesc_oobd_own = &m_ble_lesc_oobd_own;
}
else
{
NRF_LOG_ERROR("Trying to access LESC OOB data that have not been generated yet.");
}
return p_lesc_oobd_own;
}
void nrf_ble_lesc_peer_oob_data_handler_set(nrf_ble_lesc_peer_oob_data_handler handler)
{
m_lesc_oobd_peer_handler = handler;
}
/**@brief Function for calculating a DH key and responding to the DH key request on a given
* connection handle.
*
* @param[in] p_peer_public_key ECC peer public key, used to compute shared secret.
* @param[in] conn_handle Connection handle.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval Other Other error codes might be returned by the @ref nrf_crypto_ecdh_compute, @ref
* nrf_crypto_ecc_byte_order_invert, and @ref sd_ble_gap_lesc_dhkey_reply functions.
*/
static ret_code_t compute_and_give_dhkey(nrf_ble_lesc_peer_pub_key_t * p_peer_public_key,
uint16_t conn_handle)
{
ret_code_t err_code = NRF_ERROR_INTERNAL;
size_t shared_secret_size = BLE_GAP_LESC_DHKEY_LEN;
uint8_t * p_shared_secret = m_lesc_dh_key.key;
// Check if there is a valid generated and set a local ECDH public key.
if (!m_keypair_generated)
{
return NRF_ERROR_INTERNAL;
}
// Check if the public_key is valid
if (p_peer_public_key->is_valid)
{
err_code = nrf_crypto_ecdh_compute(&m_ecdh_context,
&m_private_key,
&p_peer_public_key->value,
p_shared_secret,
&shared_secret_size);
}
if (err_code == NRF_SUCCESS)
{
// Invert the shared secret for little endian format.
err_code = nrf_crypto_ecc_byte_order_invert(&g_nrf_crypto_ecc_secp256r1_curve_info,
p_shared_secret,
p_shared_secret,
BLE_GAP_LESC_DHKEY_LEN);
VERIFY_SUCCESS(err_code);
}
else
{
NRF_LOG_WARNING("Creating invalid shared secret to make LESC fail.");
err_code = nrf_crypto_rng_vector_generate(p_shared_secret, BLE_GAP_LESC_DHKEY_LEN);
VERIFY_SUCCESS(err_code);
}
NRF_LOG_INFO("Calling sd_ble_gap_lesc_dhkey_reply on conn_handle: %d", conn_handle);
err_code = sd_ble_gap_lesc_dhkey_reply(conn_handle, &m_lesc_dh_key);
return err_code;
}
ret_code_t nrf_ble_lesc_request_handler(void)
{
ret_code_t err_code = NRF_SUCCESS;
// If the LESC module is in an invalid state, a restart is required.
if (m_ble_lesc_internal_error)
{
return NRF_ERROR_INTERNAL;
}
for (uint16_t i = 0; i < NRF_BLE_LESC_LINK_COUNT; i++)
{
if (m_peer_keys[i].is_requested)
{
err_code = compute_and_give_dhkey(&m_peer_keys[i], i);
m_peer_keys[i].is_requested = false;
m_peer_keys[i].is_valid = false;
VERIFY_SUCCESS(err_code);
}
}
return err_code;
}
/**@brief Function for handling a DH key request event.
*
* @param[in] conn_handle Connection handle.
* @param[in] p_dhkey_request DH key request descriptor.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval Other Other error codes might be returned by the @ref nrf_crypto_ecc_byte_order_invert
* and @ref nrf_crypto_ecc_public_key_from_raw functions.
*/
static ret_code_t on_dhkey_request(uint16_t conn_handle,
ble_gap_evt_lesc_dhkey_request_t const * p_dhkey_request)
{
ret_code_t err_code = NRF_SUCCESS;
uint8_t public_raw[BLE_GAP_LESC_P256_PK_LEN];
uint8_t * p_public_raw;
size_t public_raw_len;
// Convert the received public key from big-endian to little endian.
p_public_raw = p_dhkey_request->p_pk_peer->pk;
public_raw_len = BLE_GAP_LESC_P256_PK_LEN;
err_code = nrf_crypto_ecc_byte_order_invert(&g_nrf_crypto_ecc_secp256r1_curve_info,
p_public_raw,
public_raw,
public_raw_len);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("nrf_crypto_ecc_byte_order_invert() returned error 0x%x.", err_code);
return err_code;
}
// Copy peer public key to the allocated context. The dhkey calculation will be performed in
// @ref nrf_ble_lesc_request_handler, so it does not block normal operation.
err_code = nrf_crypto_ecc_public_key_from_raw(&g_nrf_crypto_ecc_secp256r1_curve_info,
&m_peer_keys[conn_handle].value,
public_raw,
public_raw_len);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("nrf_crypto_ecc_public_key_from_raw() returned error 0x%x.", err_code);
m_peer_keys[conn_handle].is_valid = false;
}
else
{
m_peer_keys[conn_handle].is_valid = true;
}
m_peer_keys[conn_handle].is_requested = true;
return NRF_SUCCESS;
}
/**@brief Function for setting LESC OOB data.
*
* @param[in] conn_handle Connection handle.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval Other Other error codes might be returned by the @ref sd_ble_gap_lesc_oob_data_set.
*/
static ret_code_t lesc_oob_data_set(uint16_t conn_handle)
{
ret_code_t err_code;
ble_gap_lesc_oob_data_t * p_lesc_oobd_own;
ble_gap_lesc_oob_data_t * p_lesc_oobd_peer;
p_lesc_oobd_own = (m_lesc_oobd_own_generated) ? &m_ble_lesc_oobd_own : NULL;
p_lesc_oobd_peer = (m_lesc_oobd_peer_handler != NULL) ?
m_lesc_oobd_peer_handler(conn_handle) : NULL;
err_code = sd_ble_gap_lesc_oob_data_set(conn_handle,
p_lesc_oobd_own,
p_lesc_oobd_peer);
return err_code;
}
void nrf_ble_lesc_on_ble_evt(ble_evt_t const * p_ble_evt)
{
ret_code_t err_code = NRF_SUCCESS;
uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_DISCONNECTED:
m_peer_keys[conn_handle].is_valid = false;
m_peer_keys[conn_handle].is_requested = false;
break;
case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
NRF_LOG_DEBUG("BLE_GAP_EVT_LESC_DHKEY_REQUEST");
if (p_ble_evt->evt.gap_evt.params.lesc_dhkey_request.oobd_req)
{
err_code = lesc_oob_data_set(conn_handle);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_gap_lesc_oob_data_set() returned error 0x%x.", err_code);
m_ble_lesc_internal_error = true;
}
}
err_code = on_dhkey_request(conn_handle,
&p_ble_evt->evt.gap_evt.params.lesc_dhkey_request);
if (err_code != NRF_SUCCESS)
{
m_ble_lesc_internal_error = true;
}
break;
default:
break;
}
}
#endif // NRF_BLE_LESC_ENABLED