From f7f1272647ed5776eb77986bdad67b66e3d3f6e6 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Fri, 11 May 2018 11:42:42 +0100 Subject: [PATCH] Nordic: Backport security manager pal for NRF5X targets. --- .../TARGET_NRF5x/source/nRF5xCrypto.cpp | 188 +++ .../TARGET_NRF5x/source/nRF5xCrypto.h | 146 ++ .../source/nRF5xPalSecurityManager.cpp | 1225 +++++++++++++++++ .../source/nRF5xPalSecurityManager.h | 403 ++++++ 4 files changed, 1962 insertions(+) create mode 100644 features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xCrypto.cpp create mode 100644 features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xCrypto.h create mode 100644 features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xPalSecurityManager.cpp create mode 100644 features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xPalSecurityManager.h diff --git a/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xCrypto.cpp b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xCrypto.cpp new file mode 100644 index 0000000000..b555b0dc33 --- /dev/null +++ b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xCrypto.cpp @@ -0,0 +1,188 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * 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 + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif +#include +#include + +#if defined(MBEDTLS_ECDH_C) + +#include "mbedtls/platform.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/memory_buffer_alloc.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ecp.h" + +#include "platform/NonCopyable.h" +#include "platform/CriticalSectionLock.h" +#include "ble/BLETypes.h" +#include "cmsis.h" +#include "nRF5xCrypto.h" +#include "platform/mbed_assert.h" +#include "nrf_soc.h" + + +namespace ble { +namespace pal { +namespace vendor { +namespace nordic { + +CryptoToolbox::CryptoToolbox() : _initialized(false) { + mbedtls_entropy_init(&_entropy_context); + mbedtls_ecp_group_init(&_group); + int err = mbedtls_ecp_group_load( + &_group, + MBEDTLS_ECP_DP_SECP256R1 + ); + _initialized = err ? false : true; +} + +CryptoToolbox::~CryptoToolbox() { + mbedtls_ecp_group_free(&_group); + mbedtls_entropy_free(&_entropy_context); +} + +bool CryptoToolbox::generate_keys( + ArrayView X, + ArrayView Y, + ArrayView secret +) { + mbedtls_mpi secret_key; + mbedtls_ecp_point public_keys; + + mbedtls_mpi_init(&secret_key); + mbedtls_ecp_point_init(&public_keys); + + int err = mbedtls_ecp_gen_keypair( + &_group, + &secret_key, + &public_keys, + mbedtls_entropy_func, + &_entropy_context + ); + + if (!err) { + store_mpi(secret, secret_key); + store_mpi(X, public_keys.X); + store_mpi(Y, public_keys.Y); + } + + mbedtls_ecp_point_free(&public_keys); + mbedtls_mpi_free(&secret_key); + + return err ? false : true; +} + +bool CryptoToolbox::generate_shared_secret( + const ArrayView& peer_X, + const ArrayView& peer_Y, + const ArrayView& own_secret, + ArrayView shared_secret +) { + mbedtls_mpi result; + mbedtls_mpi secret_key; + mbedtls_ecp_point public_keys; + + mbedtls_mpi_init(&result); + mbedtls_mpi_init(&secret_key); + mbedtls_ecp_point_init(&public_keys); + + load_mpi(secret_key, own_secret); + load_mpi(public_keys.X, peer_X); + load_mpi(public_keys.Y, peer_Y); + mbedtls_mpi_lset( &public_keys.Z, 1 ); + + int err = mbedtls_ecdh_compute_shared( + &_group, + &result, + &public_keys, + &secret_key, + /* rng function; optional */ NULL, + /* rng param */ NULL + ); + + if (!err) { + store_mpi(shared_secret, result); + } + + mbedtls_ecp_point_free(&public_keys); + mbedtls_mpi_free(&secret_key); + mbedtls_mpi_free(&result); + + return err ? false : true; +} + +bool CryptoToolbox::ah( + const ArrayView& irk, + const ArrayView& prand, + ArrayView hash +) { + // Note copy then swap operation can be optimized. + + // Note: the encryption block works in big endian; go figure. + nrf_ecb_hal_data_t ecb_hal_data; + + memcpy(ecb_hal_data.key, irk.data(), irk.size()); + swap_endian(ecb_hal_data.key, sizeof(ecb_hal_data.key)); + + memcpy(ecb_hal_data.cleartext, prand.data(), prand.size()); + memset(ecb_hal_data.cleartext + prand.size(), 0, sizeof(ecb_hal_data.cleartext) - prand.size()); + swap_endian(ecb_hal_data.cleartext, sizeof(ecb_hal_data.cleartext)); + + uint32_t err = sd_ecb_block_encrypt(&ecb_hal_data); + + if (err) { + return false; + } + + swap_endian(ecb_hal_data.ciphertext, sizeof(ecb_hal_data.ciphertext)); + + memcpy(hash.data(), ecb_hal_data.ciphertext, hash.size()); + + return true; +} + + +void CryptoToolbox::load_mpi(mbedtls_mpi& dest, const ArrayView& src) { + ble::public_key_coord_t src_be = src.data(); + swap_endian(src_be.data(), src_be.size()); + mbedtls_mpi_read_binary(&dest, src_be.data(), src_be.size()); +} + +void CryptoToolbox::store_mpi(ArrayView& dest, const mbedtls_mpi& src) { + mbedtls_mpi_write_binary(&src, dest.data(), dest.size()); + swap_endian(dest.data(), dest.size()); +} + +void CryptoToolbox::swap_endian(uint8_t* buf, size_t len) { + for(size_t low = 0, high = (len - 1); high > low; --high, ++low) { + std::swap(buf[low], buf[high]); + } +} + +} // nordic +} // vendor +} // pal +} // ble + +#endif //defined(MBEDTLS_ECDH_C) + diff --git a/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xCrypto.h b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xCrypto.h new file mode 100644 index 0000000000..35c56a875e --- /dev/null +++ b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xCrypto.h @@ -0,0 +1,146 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * 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. + */ + +#ifndef NRF5X_CRYPTO_ +#define NRF5X_CRYPTO_ + +#include + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_ECDH_C) + +#include "mbedtls/platform.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ecp.h" + +#include "platform/NonCopyable.h" +#include "ble/BLETypes.h" + +namespace ble { +namespace pal { +namespace vendor { +namespace nordic { + +/** + * Toolbox of cryptographic functions used in BLE. + */ +class CryptoToolbox : mbed::NonCopyable { + +public: + /** + * Size of the Key used in lesc crypto operations. + */ + static const ptrdiff_t lesc_key_size_ = public_key_coord_t::size_; + + /** + * Size of an IRK. + */ + static const ptrdiff_t irk_size_ = irk_t::size_; + + /** + * Size of the hash generated by ah. + */ + static const ptrdiff_t hash_size_ = 3; + + /** + * Size of prand. + */ + static const ptrdiff_t prand_size_ = 3; + + /** + * Create a new CryptoToolbox. + */ + CryptoToolbox(); + + /** + * Destroy a CryptoTioolbox object. + */ + ~CryptoToolbox(); + + /** + * Generate lesc public and private keys. + * @param[out] X The component X of the public key. + * @param[out] Y The component Y of the public key. + * @param[out] secret The secret key. + * @return true if the shared secret has been successfully generated and + * false otherwise. + */ + bool generate_keys( + ArrayView X, + ArrayView Y, + ArrayView secret + ); + + /** + * Generate a shared secret from a peer public key and a local secret key. + * @param[in] peer_X The component X of the peer public key. + * @param[in] peer_Y The component Y of the peer public key. + * @param[in] own_secret The local secret key. + * @param[out] shared_secret The shared secret generated. + * @return true if the shared secret has been successfully generated and + * false otherwise. + */ + bool generate_shared_secret( + const ArrayView& peer_X, + const ArrayView& peer_Y, + const ArrayView& own_secret, + ArrayView shared_secret + ); + + /** + * Execute the function ah. This function can be used to generate private + * resolvable addresses and resolve them. + * + * @note all parameters passed and return by this fucntion are in little + * endian. + * + * @param[in] irk The key used to create hash. + * @param[in] prand The random part from which the hash will be generated. + * @param[out] hash The hash generated. + * + * @return true in case of success and false otherwise. + */ + bool ah( + const ArrayView& irk, + const ArrayView& prand, + ArrayView hash + ); + +private: + void load_mpi(mbedtls_mpi& dest, const ArrayView& src); + + void store_mpi(ArrayView& dest, const mbedtls_mpi& src); + + void swap_endian(uint8_t* buf, size_t len); + + bool _initialized; + mbedtls_entropy_context _entropy_context; + mbedtls_ecp_group _group; +}; + +} // nordic +} // vendor +} // pal +} // ble + +#endif // defined(MBEDTLS_ECDH_C) + +#endif // NRF5X_CRYPTO_ diff --git a/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xPalSecurityManager.cpp b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xPalSecurityManager.cpp new file mode 100644 index 0000000000..0be03868c6 --- /dev/null +++ b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xPalSecurityManager.cpp @@ -0,0 +1,1225 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * 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 +#include "nRF5xPalSecurityManager.h" +#include "nrf_ble.h" +#include "ble_gap.h" +#include "nrf_soc.h" +#include "nrf_error.h" + +namespace ble { +namespace pal { +namespace vendor { +namespace nordic { + +namespace { +static ble_error_t convert_sd_error(uint32_t err) { + switch (err) { + case NRF_SUCCESS: + return BLE_ERROR_NONE; + case NRF_ERROR_INVALID_ADDR: + case NRF_ERROR_INVALID_PARAM: + case BLE_ERROR_INVALID_CONN_HANDLE: + return BLE_ERROR_INVALID_PARAM; + case NRF_ERROR_INVALID_STATE: + case BLE_ERROR_INVALID_ROLE: + return BLE_ERROR_INVALID_STATE; + case NRF_ERROR_NOT_SUPPORTED: + return BLE_ERROR_NOT_IMPLEMENTED; + case NRF_ERROR_BUSY: + return BLE_STACK_BUSY; + case NRF_ERROR_TIMEOUT: + return BLE_ERROR_INVALID_STATE; + case NRF_ERROR_NO_MEM: + return BLE_ERROR_NO_MEM; + default: + return BLE_ERROR_UNSPECIFIED; + } +} +} + +enum pairing_role_t { + PAIRING_INITIATOR, + PAIRING_RESPONDER +}; + +struct nRF5xSecurityManager::pairing_control_block_t { + pairing_control_block_t* next; + connection_handle_t connection; + pairing_role_t role; + + // flags of the key present + KeyDistribution initiator_dist; + KeyDistribution responder_dist; + + // own keys + ble_gap_enc_key_t own_enc_key; + ble_gap_id_key_t own_id_key; + ble_gap_sign_info_t own_sign_key; + ble_gap_lesc_p256_pk_t own_pk; + + // peer keys + ble_gap_enc_key_t peer_enc_key; + ble_gap_id_key_t peer_id_key; + ble_gap_sign_info_t peer_sign_key; + ble_gap_lesc_p256_pk_t peer_pk; + + // flag required to help DHKey computation/process; should be removed with + // later versions of the softdevice + uint8_t own_oob:1; + uint8_t peer_oob:1; +}; + +nRF5xSecurityManager::nRF5xSecurityManager() + : ::ble::pal::SecurityManager(), + _sign_counter(), + _io_capability(io_capability_t::NO_INPUT_NO_OUTPUT), + _min_encryption_key_size(7), + _max_encryption_key_size(16), + _control_blocks(NULL), + resolving_list_entry_count(0) +{ + +} + +nRF5xSecurityManager::~nRF5xSecurityManager() +{ + terminate(); +} + +//////////////////////////////////////////////////////////////////////////// +// SM lifecycle management +// + +ble_error_t nRF5xSecurityManager::initialize() +{ +#if defined(MBEDTLS_ECDH_C) + if (_crypto.generate_keys( + make_ArrayView(X), + make_ArrayView(Y), + make_ArrayView(secret) + )) { + return BLE_ERROR_NONE; + } + + return BLE_ERROR_INTERNAL_STACK_FAILURE; +#endif + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xSecurityManager::terminate() +{ + release_all_pairing_cb(); + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xSecurityManager::reset() +{ + ble_error_t err = terminate(); + if (err) { + return err; + } + + return initialize(); +} + +//////////////////////////////////////////////////////////////////////////// +// Resolving list management +// + +// FIXME: on nordic, the irk is passed in sd_ble_gap_scan_start where whitelist +// and resolving list are all mixed up. + +uint8_t nRF5xSecurityManager::read_resolving_list_capacity() +{ + return MAX_RESOLVING_LIST_ENTRIES; +} + +ble_error_t nRF5xSecurityManager::add_device_to_resolving_list( + advertising_peer_address_type_t peer_identity_address_type, + const address_t &peer_identity_address, + const irk_t &peer_irk +) { + if (resolving_list_entry_count >= MAX_RESOLVING_LIST_ENTRIES) { + return BLE_ERROR_INVALID_STATE; + } + + resolving_list_entry_t& entry = resolving_list[resolving_list_entry_count]; + entry.peer_identity_address_type = peer_identity_address_type; + entry.peer_identity_address = peer_identity_address; + entry.peer_irk = peer_irk; + + ++resolving_list_entry_count; + + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xSecurityManager::remove_device_from_resolving_list( + advertising_peer_address_type_t peer_identity_address_type, + const address_t &peer_identity_address +) { + size_t entry_index; + + // first the index needs to be found + for (entry_index = 0; entry_index < resolving_list_entry_count; ++entry_index) { + resolving_list_entry_t& entry = resolving_list[entry_index]; + if (entry.peer_identity_address_type == peer_identity_address_type && + entry.peer_identity_address == peer_identity_address + ) { + break; + } + } + + if (entry_index == resolving_list_entry_count) { + return BLE_ERROR_INVALID_PARAM; + } + + // Elements after the entry can be moved in the list + for (size_t i = entry_index; i < (resolving_list_entry_count - 1); ++i) { + resolving_list[i] = resolving_list[i + 1]; + } + + --resolving_list_entry_count; + + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xSecurityManager::clear_resolving_list() +{ + resolving_list_entry_count = 0; + return BLE_ERROR_NONE; +} + +ArrayView +nRF5xSecurityManager::get_resolving_list() { + return ArrayView( + resolving_list, + resolving_list_entry_count + ); +} + +const nRF5xSecurityManager::resolving_list_entry_t* +nRF5xSecurityManager::resolve_address(const address_t& resolvable_address) { +#if defined(MBEDTLS_ECDH_C) + typedef byte_array_t hash_t; + + for (size_t i = 0; i < resolving_list_entry_count; ++i) { + resolving_list_entry_t& entry = resolving_list[i]; + hash_t hash_generated; + + // Compute the hash part from the random address part when the irk of + // the entry is used + _crypto.ah( + make_const_ArrayView(entry.peer_irk), + make_const_ArrayView( + resolvable_address.data() + CryptoToolbox::hash_size_ + ), + make_ArrayView(hash_generated) + ); + + // Compare hash generated with the hash present in the address passed as + // parameter. If they are equal then the IRK of the entry has been used + // to generate the resolvable address. + if (memcmp(hash_generated.data(), resolvable_address.data(), CryptoToolbox::hash_size_) == 0) { + return &entry; + } + } +#endif + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////////// +// Pairing +// + + +ble_error_t nRF5xSecurityManager::send_pairing_request( + connection_handle_t connection, + bool oob_data_flag, + AuthenticationMask authentication_requirements, + KeyDistribution initiator_dist, + KeyDistribution responder_dist +) { + // allocate the control block required for the procedure completion + pairing_control_block_t* pairing_cb = allocate_pairing_cb(connection); + if (!pairing_cb) { + return BLE_ERROR_NO_MEM; + } + pairing_cb->role = PAIRING_INITIATOR; + + // override signing parameter + initiator_dist.set_signing(false); + responder_dist.set_signing(false); + + // override link parameter + initiator_dist.set_link(false); + responder_dist.set_link(false); + + ble_gap_sec_params_t security_params = make_security_params( + oob_data_flag, + authentication_requirements, + initiator_dist, + responder_dist + ); + + uint32_t err = sd_ble_gap_authenticate( + connection, + &security_params + ); + + if (err) { + release_pairing_cb(pairing_cb); + } + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::send_pairing_response( + connection_handle_t connection, + bool oob_data_flag, + AuthenticationMask authentication_requirements, + KeyDistribution initiator_dist, + KeyDistribution responder_dist +) { + pairing_control_block_t* pairing_cb = allocate_pairing_cb(connection); + if (!pairing_cb) { + return BLE_ERROR_NO_MEM; + } + pairing_cb->role = PAIRING_RESPONDER; + + // override signing parameter + initiator_dist.set_signing(false); + responder_dist.set_signing(false); + + // override link parameter + initiator_dist.set_link(false); + responder_dist.set_link(false); + + ble_gap_sec_params_t security_params = make_security_params( + oob_data_flag, + authentication_requirements, + initiator_dist, + responder_dist + ); + + ble_gap_sec_keyset_t keyset = make_keyset( + *pairing_cb, + initiator_dist, + responder_dist + ); + + uint32_t err = sd_ble_gap_sec_params_reply( + connection, + /* status */ BLE_GAP_SEC_STATUS_SUCCESS, + /* params */ &security_params, + /* keys */ &keyset + ); + + if (err) { + release_pairing_cb(pairing_cb); + } + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::cancel_pairing( + connection_handle_t connection, pairing_failure_t reason +) { + // this is the default path except when a key is expected to be entered by + // the user. + uint32_t err = sd_ble_gap_sec_params_reply( + connection, + reason.value() | 0x80, + /* sec params */ NULL, + /* keyset */ NULL + ); + + if (!err) { + return BLE_ERROR_NONE; + } + + // Failed because we're in the wrong state; try to cancel pairing with + // sd_ble_gap_auth_key_reply + if (err == NRF_ERROR_INVALID_STATE) { + err = sd_ble_gap_auth_key_reply( + connection, + /* key type */ BLE_GAP_AUTH_KEY_TYPE_NONE, + /* key */ NULL + ); + } + + return convert_sd_error(err); +} + +//////////////////////////////////////////////////////////////////////////// +// Feature support +// + +ble_error_t nRF5xSecurityManager::get_secure_connections_support( + bool &enabled +) { + enabled = true; + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xSecurityManager::set_io_capability(io_capability_t io_capability) +{ + _io_capability = io_capability; + return BLE_ERROR_NONE; +} + +//////////////////////////////////////////////////////////////////////////// +// Security settings +// + +ble_error_t nRF5xSecurityManager::set_authentication_timeout( + connection_handle_t connection, uint16_t timeout_in_10ms +) { + ble_opt_t opt; + opt.gap_opt.auth_payload_timeout.conn_handle = connection; + opt.gap_opt.auth_payload_timeout.auth_payload_timeout = timeout_in_10ms; + uint32_t err = sd_ble_opt_set(BLE_GAP_OPT_AUTH_PAYLOAD_TIMEOUT, &opt); + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::get_authentication_timeout( + connection_handle_t connection, uint16_t &timeout_in_10ms +) { + ble_opt_t opt; + opt.gap_opt.auth_payload_timeout.conn_handle = connection; + + uint32_t err = sd_ble_opt_get(BLE_GAP_OPT_AUTH_PAYLOAD_TIMEOUT, &opt); + if (err) { + return convert_sd_error(err); + } + + timeout_in_10ms = opt.gap_opt.auth_payload_timeout.auth_payload_timeout; + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xSecurityManager::set_encryption_key_requirements( + uint8_t min_encryption_key_size, + uint8_t max_encryption_key_size +) { + if ((min_encryption_key_size < 7) || (min_encryption_key_size > 16) || + (min_encryption_key_size > max_encryption_key_size)) { + return BLE_ERROR_INVALID_PARAM; + } + + _min_encryption_key_size = min_encryption_key_size; + _max_encryption_key_size = max_encryption_key_size; + + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xSecurityManager::slave_security_request( + connection_handle_t connection, + AuthenticationMask authentication +) { + // In the peripheral role, only the bond, mitm, lesc and keypress fields of + // this structure are used. + ble_gap_sec_params_t security_params = { + /* bond */ authentication.get_bondable(), + /* mitm */ authentication.get_mitm(), + /* lesc */ authentication.get_secure_connections(), + /* keypress */ authentication.get_keypress_notification(), + /* remainder of the data structure is ignored */ 0 + }; + + uint32_t err = sd_ble_gap_authenticate( + connection, + &security_params + ); + + return convert_sd_error(err); +} + +//////////////////////////////////////////////////////////////////////////// +// Encryption +// + +ble_error_t nRF5xSecurityManager::enable_encryption( + connection_handle_t connection, + const ltk_t <k, + const rand_t &rand, + const ediv_t &ediv, + bool mitm +) { + ble_gap_master_id_t master_id; + memcpy(master_id.rand, rand.data(), rand.size()); + memcpy(&master_id.ediv, ediv.data(), ediv.size()); + + ble_gap_enc_info_t enc_info; + memcpy(enc_info.ltk, ltk.data(), ltk.size()); + enc_info.lesc = false; + enc_info.auth = mitm; + + // FIXME: how to pass the lenght of the LTK ??? + enc_info.ltk_len = ltk.size(); + + uint32_t err = sd_ble_gap_encrypt( + connection, + &master_id, + &enc_info + ); + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::enable_encryption( + connection_handle_t connection, + const ltk_t <k, + bool mitm +) { + ble_gap_master_id_t master_id = {0}; + + ble_gap_enc_info_t enc_info; + memcpy(enc_info.ltk, ltk.data(), ltk.size()); + enc_info.lesc = true; + enc_info.auth = mitm; + enc_info.ltk_len = ltk.size(); + + uint32_t err = sd_ble_gap_encrypt( + connection, + &master_id, + &enc_info + ); + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::encrypt_data( + const byte_array_t<16> &key, + encryption_block_t &data +) { + nrf_ecb_hal_data_t ecb; + memcpy(&ecb.key, key.data(), key.size()); + memcpy(&ecb.cleartext, data.data(), data.size()); + + uint32_t err = sd_ecb_block_encrypt(&ecb); + if (err) { + return convert_sd_error(err); + } + + memcpy(data.data(), &ecb.ciphertext, data.size()); + return BLE_ERROR_NONE; +} + +//////////////////////////////////////////////////////////////////////////// +// Privacy +// + +ble_error_t nRF5xSecurityManager::set_private_address_timeout( + uint16_t timeout_in_seconds +) { + ble_gap_privacy_params_t privacy_config; + + uint32_t err = sd_ble_gap_privacy_get(&privacy_config); + if (err) { + return convert_sd_error(err); + } + + privacy_config.private_addr_cycle_s = timeout_in_seconds; + err = sd_ble_gap_privacy_set(&privacy_config); + + return convert_sd_error(err); +} + +//////////////////////////////////////////////////////////////////////////// +// Keys +// + +ble_error_t nRF5xSecurityManager::set_ltk( + connection_handle_t connection, + const ltk_t& ltk, + bool mitm, + bool secure_connections +) { + ble_gap_enc_info_t enc_info; + memcpy(enc_info.ltk, ltk.data(), ltk.size()); + enc_info.lesc = secure_connections; + enc_info.auth = mitm; + enc_info.ltk_len = ltk.size(); + + // FIXME: provide peer irk and csrk ? + uint32_t err = sd_ble_gap_sec_info_reply( + connection, + &enc_info, + /* id info */ NULL, + /* sign info */ NULL // Not supported + ); + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::set_ltk_not_found( + connection_handle_t connection +) { + uint32_t err = sd_ble_gap_sec_info_reply( + connection, + NULL, + NULL, + NULL // Not supported + ); + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::set_irk(const irk_t& irk) +{ + + ble_gap_privacy_params_t privacy_config; + + // get the previous config + uint32_t err = sd_ble_gap_privacy_get(&privacy_config); + if (err) { + return convert_sd_error(err); + } + + // set the new irk + memcpy(privacy_config.p_device_irk, irk.data(), irk.size()); + err = sd_ble_gap_privacy_set(&privacy_config); + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::set_csrk( + const csrk_t& csrk, + sign_count_t sign_counter +) { + _csrk = csrk; + _sign_counter = sign_counter; + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xSecurityManager::set_peer_csrk( + connection_handle_t connection, + const csrk_t &csrk, + bool authenticated, + sign_count_t sign_counter +) { + return BLE_ERROR_NOT_IMPLEMENTED; +} + +//////////////////////////////////////////////////////////////////////////// +// Authentication +// + +ble_error_t nRF5xSecurityManager::get_random_data(byte_array_t<8> &random_data) +{ + uint32_t err = sd_rand_application_vector_get( + random_data.data(), random_data.size() + ); + return convert_sd_error(err); +} + +//////////////////////////////////////////////////////////////////////////// +// MITM +// + +ble_error_t nRF5xSecurityManager::set_display_passkey(passkey_num_t passkey) +{ + PasskeyAscii passkey_ascii(passkey); + ble_opt_t sd_passkey; + sd_passkey.gap_opt.passkey.p_passkey = passkey ? passkey_ascii.value() : NULL; + uint32_t err = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &sd_passkey); + return convert_sd_error(err); +} + + +ble_error_t nRF5xSecurityManager::passkey_request_reply( + connection_handle_t connection, const passkey_num_t passkey +) { + pairing_control_block_t* pairing_cb = get_pairing_cb(connection); + if (!pairing_cb) { + return BLE_ERROR_INVALID_STATE; + } + + PasskeyAscii pkasc(passkey); + uint32_t err = sd_ble_gap_auth_key_reply( + connection, + BLE_GAP_AUTH_KEY_TYPE_PASSKEY, + pkasc.value() + ); + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::secure_connections_oob_request_reply( + connection_handle_t connection, + const oob_lesc_value_t &local_random, + const oob_lesc_value_t &peer_random, + const oob_confirm_t &peer_confirm +) { + pairing_control_block_t* pairing_cb = get_pairing_cb(connection); + if (!pairing_cb) { + return BLE_ERROR_INVALID_STATE; + } + + ble_gap_lesc_oob_data_t oob_own; + ble_gap_lesc_oob_data_t oob_peer; + + // is own address important ? + memcpy(oob_own.r, local_random.data(), local_random.size()); + // FIXME: What to do with local confirm ??? + + // is peer address important ? + memcpy(oob_peer.r, peer_random.data(), peer_random.size()); + memcpy(oob_peer.c, peer_confirm.data(), peer_confirm.size()); + + uint32_t err = sd_ble_gap_lesc_oob_data_set( + connection, + pairing_cb->own_oob ? &oob_own : NULL, + pairing_cb->peer_oob ? &oob_peer : NULL + ); + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::legacy_pairing_oob_request_reply( + connection_handle_t connection, + const oob_tk_t& oob_data +) { + uint32_t err = sd_ble_gap_auth_key_reply( + connection, + BLE_GAP_AUTH_KEY_TYPE_OOB, + oob_data.data() + ); + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::confirmation_entered( + connection_handle_t connection, bool confirmation +) { + pairing_control_block_t* pairing_cb = get_pairing_cb(connection); + if (!pairing_cb) { + return BLE_ERROR_INVALID_STATE; + } + + uint32_t err = sd_ble_gap_auth_key_reply( + connection, + confirmation ? BLE_GAP_AUTH_KEY_TYPE_PASSKEY : BLE_GAP_AUTH_KEY_TYPE_NONE, + NULL + ); + + return convert_sd_error(err); +} + +ble_error_t nRF5xSecurityManager::send_keypress_notification( + connection_handle_t connection, Keypress_t keypress +) { + uint32_t err = sd_ble_gap_keypress_notify( + connection, + static_cast(keypress) + ); + return convert_sd_error(err); +} + + +ble_error_t nRF5xSecurityManager::generate_secure_connections_oob() +{ +#if defined(MBEDTLS_ECDH_C) + ble_gap_lesc_p256_pk_t own_secret; + ble_gap_lesc_oob_data_t oob_data; + + memcpy(own_secret.pk, secret.data(), secret.size()); + + uint32_t err = sd_ble_gap_lesc_oob_data_get( + BLE_CONN_HANDLE_INVALID, + &own_secret, + &oob_data + ); + + if (!err) { + get_event_handler()->on_secure_connections_oob_generated( + oob_data.r, + oob_data.c + ); + } + + return convert_sd_error(err); +#endif + return BLE_ERROR_NOT_IMPLEMENTED; +} + +nRF5xSecurityManager& nRF5xSecurityManager::get_security_manager() +{ + static nRF5xSecurityManager _security_manager; + return _security_manager; +} + +bool nRF5xSecurityManager::sm_handler(const ble_evt_t *evt) +{ + nRF5xSecurityManager& self = nRF5xSecurityManager::get_security_manager(); + SecurityManager::EventHandler* handler = self.get_event_handler(); + + if ((evt == NULL) || (handler == NULL)) { + return false; + } + + const ble_gap_evt_t& gap_evt = evt->evt.gap_evt; + uint16_t connection = gap_evt.conn_handle; + pairing_control_block_t* pairing_cb = self.get_pairing_cb(connection); + + switch (evt->header.evt_id) { + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { + const ble_gap_sec_params_t& params = + gap_evt.params.sec_params_request.peer_params; + + KeyDistribution initiator_dist( + params.kdist_peer.enc, + params.kdist_peer.id, + params.kdist_peer.sign, + params.kdist_peer.link + ); + + KeyDistribution responder_dist( + params.kdist_own.enc, + params.kdist_own.id, + params.kdist_own.sign, + params.kdist_own.link + ); + + if (pairing_cb && pairing_cb->role == PAIRING_INITIATOR) { + // when this event is received by an initiator, it should not be + // forwarded via the handler; this is not a behaviour expected + // by the bluetooth standard ... + ble_gap_sec_keyset_t keyset = make_keyset( + *pairing_cb, + initiator_dist, + responder_dist + ); + uint32_t err = sd_ble_gap_sec_params_reply( + connection, + /* status */ BLE_GAP_SEC_STATUS_SUCCESS, + /* params */ NULL, + /* keys ... */ &keyset + ); + + // in case of error; release the pairing control block and signal + // it to the event handler + if (err) { + release_pairing_cb(pairing_cb); + handler->on_pairing_error( + connection, + pairing_failure_t::UNSPECIFIED_REASON + ); + } + } else { + handler->on_pairing_request( + connection, + params.oob, + AuthenticationMask( + params.bond, + params.mitm, + params.lesc, + params.keypress + ), + initiator_dist, + responder_dist + ); + } + return true; + } + + case BLE_GAP_EVT_SEC_INFO_REQUEST: { + const ble_gap_evt_sec_info_request_t& req = + gap_evt.params.sec_info_request; + + handler->on_ltk_request( + connection, + ediv_t((uint8_t*)(&req.master_id.ediv)), + rand_t(req.master_id.rand) + ); + + return true; + } + + case BLE_GAP_EVT_PASSKEY_DISPLAY: { + const ble_gap_evt_passkey_display_t& req = + gap_evt.params.passkey_display; + + if (req.match_request == 0) { + handler->on_passkey_display( + connection, + PasskeyAscii::to_num(req.passkey) + ); + } else { + handler->on_passkey_display( + connection, + PasskeyAscii::to_num(req.passkey) + ); + handler->on_confirmation_request(connection); + } + + return true; + } + + case BLE_GAP_EVT_KEY_PRESSED: { + handler->on_keypress_notification( + connection, + (Keypress_t)gap_evt.params.key_pressed.kp_not + ); + return true; + } + + case BLE_GAP_EVT_AUTH_KEY_REQUEST: { + uint8_t key_type = gap_evt.params.auth_key_request.key_type; + + switch (key_type) { + case BLE_GAP_AUTH_KEY_TYPE_NONE: // Illegal + break; + + case BLE_GAP_AUTH_KEY_TYPE_PASSKEY: + handler->on_passkey_request(connection); + break; + + case BLE_GAP_AUTH_KEY_TYPE_OOB: + handler->on_legacy_pairing_oob_request(connection); + break; + } + + return true; + } + + case BLE_GAP_EVT_LESC_DHKEY_REQUEST: { +#if defined(MBEDTLS_ECDH_C) + const ble_gap_evt_lesc_dhkey_request_t& dhkey_request = + gap_evt.params.lesc_dhkey_request; + + static const size_t key_size = public_key_coord_t::size_; + ble_gap_lesc_dhkey_t shared_secret; + + _crypto.generate_shared_secret( + make_const_ArrayView(dhkey_request.p_pk_peer->pk), + make_const_ArrayView(dhkey_request.p_pk_peer->pk + key_size), + make_const_ArrayView(secret), + shared_secret.key + ); + + sd_ble_gap_lesc_dhkey_reply(connection, &shared_secret); + + if (dhkey_request.oobd_req) { + handler->on_secure_connections_oob_request(connection); + } +#endif + return true; + } + + case BLE_GAP_EVT_AUTH_STATUS: { + const ble_gap_evt_auth_status_t& status = gap_evt.params.auth_status; + + switch (status.auth_status) { + // NOTE: pairing_cb must be valid if this event has been + // received as it is being allocated earlier and release + // in this block + // The memory is released before the last call to the event handler + // to free the heap a bit before subsequent allocation with user + // code. + case BLE_GAP_SEC_STATUS_SUCCESS: { + KeyDistribution own_dist; + KeyDistribution peer_dist; + + if (pairing_cb->role == PAIRING_INITIATOR) { + own_dist = pairing_cb->initiator_dist; + peer_dist = pairing_cb->responder_dist; + } else { + own_dist = pairing_cb->responder_dist; + peer_dist = pairing_cb->initiator_dist; + } + + if (own_dist.get_encryption()) { + handler->on_keys_distributed_local_ltk( + connection, + ltk_t(pairing_cb->own_enc_key.enc_info.ltk) + ); + + handler->on_keys_distributed_local_ediv_rand( + connection, + ediv_t(reinterpret_cast( + &pairing_cb->own_enc_key.master_id.ediv + )), + pairing_cb->own_enc_key.master_id.rand + ); + } + + if (peer_dist.get_encryption()) { + handler->on_keys_distributed_ltk( + connection, + ltk_t(pairing_cb->peer_enc_key.enc_info.ltk) + ); + + handler->on_keys_distributed_ediv_rand( + connection, + ediv_t(reinterpret_cast( + &pairing_cb->peer_enc_key.master_id.ediv + )), + pairing_cb->peer_enc_key.master_id.rand + ); + } + + if (peer_dist.get_identity()) { + handler->on_keys_distributed_irk( + connection, + irk_t(pairing_cb->peer_id_key.id_info.irk) + ); + + advertising_peer_address_type_t + address_type(advertising_peer_address_type_t::PUBLIC_ADDRESS); + + if (pairing_cb->peer_id_key.id_addr_info.addr_type) { + address_type = advertising_peer_address_type_t::RANDOM_ADDRESS; + } + + handler->on_keys_distributed_bdaddr( + connection, + address_type, + ble::address_t(pairing_cb->peer_id_key.id_addr_info.addr) + ); + } + + if (peer_dist.get_signing()) { + handler->on_keys_distributed_csrk( + connection, + pairing_cb->peer_sign_key.csrk + ); + } + + self.release_pairing_cb(pairing_cb); + handler->on_pairing_completed(connection); + break; + } + + case BLE_GAP_SEC_STATUS_TIMEOUT: + if (!pairing_cb) { + // Note: if pairing_cb does not exist then the timeout; + // is caused by a security request as the paiting_cb is + // created when the module receive the pairing request. + handler->on_link_encryption_request_timed_out(connection); + } else { + self.release_pairing_cb(pairing_cb); + handler->on_pairing_timed_out(connection); + } + break; + + case BLE_GAP_SEC_STATUS_PASSKEY_ENTRY_FAILED: + case BLE_GAP_SEC_STATUS_OOB_NOT_AVAILABLE: + case BLE_GAP_SEC_STATUS_AUTH_REQ: + case BLE_GAP_SEC_STATUS_CONFIRM_VALUE: + case BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP: + case BLE_GAP_SEC_STATUS_ENC_KEY_SIZE: + case BLE_GAP_SEC_STATUS_SMP_CMD_UNSUPPORTED: + case BLE_GAP_SEC_STATUS_UNSPECIFIED: + case BLE_GAP_SEC_STATUS_REPEATED_ATTEMPTS: + case BLE_GAP_SEC_STATUS_INVALID_PARAMS: + case BLE_GAP_SEC_STATUS_DHKEY_FAILURE: + case BLE_GAP_SEC_STATUS_NUM_COMP_FAILURE: + case BLE_GAP_SEC_STATUS_BR_EDR_IN_PROG: + case BLE_GAP_SEC_STATUS_X_TRANS_KEY_DISALLOWED: + self.release_pairing_cb(pairing_cb); + handler->on_pairing_error( + connection, + (pairing_failure_t::type) (status.auth_status & 0xF) + ); + break; + + default: + self.release_pairing_cb(pairing_cb); + break; + } + + return true; + } + + case BLE_GAP_EVT_CONN_SEC_UPDATE: { + const ble_gap_evt_conn_sec_update_t& req = + gap_evt.params.conn_sec_update; + + if((req.conn_sec.sec_mode.sm == 1) && (req.conn_sec.sec_mode.lv >= 2)) { + handler->on_link_encryption_result( + connection, link_encryption_t::ENCRYPTED + ); + } else { + handler->on_link_encryption_result( + connection, link_encryption_t::NOT_ENCRYPTED + ); + } + return true; + } + + case BLE_GAP_EVT_TIMEOUT: { + switch (gap_evt.params.timeout.src) { + case BLE_GAP_TIMEOUT_SRC_AUTH_PAYLOAD: + handler->on_valid_mic_timeout(connection); + return true; + + default: + return false; + } + return false; + } + + case BLE_GAP_EVT_SEC_REQUEST: { + const ble_gap_evt_sec_request_t& req = gap_evt.params.sec_request; + handler->on_slave_security_request( + connection, + AuthenticationMask(req.bond, req.mitm, req.lesc, req.keypress) + ); + return true; + } + + default: + return false; + } +} + +ble_gap_sec_params_t nRF5xSecurityManager::make_security_params( + bool oob_data_flag, + AuthenticationMask authentication_requirements, + KeyDistribution initiator_dist, + KeyDistribution responder_dist +) { + ble_gap_sec_params_t security_params = { + /* bond */ authentication_requirements.get_bondable(), + /* mitm */ authentication_requirements.get_mitm(), + /* lesc */ authentication_requirements.get_secure_connections(), + /* keypress */ authentication_requirements.get_keypress_notification(), + /* io_caps */ _io_capability.value(), + /* oob */ oob_data_flag, + /* min_key_size */ _min_encryption_key_size, + /* max_key_size */ _max_encryption_key_size, + /* kdist_periph */ { + /* enc */ responder_dist.get_encryption(), + /* id */ responder_dist.get_identity(), + /* sign */ responder_dist.get_signing(), + /* link */ responder_dist.get_link() + }, + /* kdist_central */ { + /* enc */ initiator_dist.get_encryption(), + /* id */ initiator_dist.get_identity(), + /* sign */ initiator_dist.get_signing(), + /* link */ initiator_dist.get_link() + } + }; + return security_params; +} + +ble_gap_sec_keyset_t nRF5xSecurityManager::make_keyset( + pairing_control_block_t& pairing_cb, + KeyDistribution initiator_dist, + KeyDistribution responder_dist +) { + pairing_cb.initiator_dist = initiator_dist; + pairing_cb.responder_dist = responder_dist; + + KeyDistribution* own_dist = NULL; + KeyDistribution* peer_dist = NULL; + + if (pairing_cb.role == PAIRING_INITIATOR) { + own_dist = &initiator_dist; + peer_dist = &responder_dist; + } else { + own_dist = &responder_dist; + peer_dist = &initiator_dist; + } + + ble_gap_sec_keyset_t keyset = { + /* keys_own */ { + own_dist->get_encryption() ? &pairing_cb.own_enc_key : NULL, + own_dist->get_identity() ? &pairing_cb.own_id_key : NULL, + own_dist->get_signing() ? &pairing_cb.own_sign_key : NULL, + &pairing_cb.own_pk + }, + /* keys_peer */ { + peer_dist->get_encryption() ? &pairing_cb.peer_enc_key : NULL, + peer_dist->get_identity() ? &pairing_cb.peer_id_key : NULL, + peer_dist->get_signing() ? &pairing_cb.peer_sign_key : NULL, + &pairing_cb.peer_pk + } + }; + + // copy csrk if necessary + if (keyset.keys_own.p_sign_key) { + memcpy(keyset.keys_own.p_sign_key->csrk, _csrk.data(), _csrk.size()); + } + + // copy public keys used +#if defined(MBEDTLS_ECDH_C) + memcpy(pairing_cb.own_pk.pk, X.data(), X.size()); + memcpy(pairing_cb.own_pk.pk + X.size(), Y.data(), Y.size()); +#endif + return keyset; +} + +nRF5xSecurityManager::pairing_control_block_t* +nRF5xSecurityManager::allocate_pairing_cb(connection_handle_t connection) +{ + pairing_control_block_t* pairing_cb = + new (std::nothrow) pairing_control_block_t(); + if (pairing_cb) { + pairing_cb->next = _control_blocks; + _control_blocks = pairing_cb; + } + return pairing_cb; +} + +void nRF5xSecurityManager::release_pairing_cb(pairing_control_block_t* pairing_cb) +{ + if (pairing_cb == _control_blocks) { + _control_blocks = _control_blocks->next; + delete pairing_cb; + } else { + pairing_control_block_t* it = _control_blocks; + while (it->next) { + if (it->next == pairing_cb) { + it->next = pairing_cb->next; + delete pairing_cb; + return; + } + it = it->next; + } + } +} + +nRF5xSecurityManager::pairing_control_block_t* +nRF5xSecurityManager::get_pairing_cb(connection_handle_t connection) +{ + pairing_control_block_t* pcb = _control_blocks; + while (pcb) { + if (pcb->connection == connection) { + return pcb; + } + pcb = pcb->next; + } + + return NULL; +} + +void nRF5xSecurityManager::release_all_pairing_cb() +{ + while(_control_blocks) { + release_pairing_cb(_control_blocks); + } +} + +} // nordic +} // vendor +} // pal +} // ble + diff --git a/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xPalSecurityManager.h b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xPalSecurityManager.h new file mode 100644 index 0000000000..e88e9075f9 --- /dev/null +++ b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NRF5x/source/nRF5xPalSecurityManager.h @@ -0,0 +1,403 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * 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. + */ + +#ifndef NRF5X_PAL_SECURITY_MANAGER_ +#define NRF5X_PAL_SECURITY_MANAGER_ + +#include "ble/BLETypes.h" +#include "ble/pal/PalSecurityManager.h" +#include "nrf_ble.h" +#include "nRF5xCrypto.h" + + +namespace ble { +namespace pal { +namespace vendor { +namespace nordic { + +class nRF5xSecurityManager : public ::ble::pal::SecurityManager { +public: + nRF5xSecurityManager(); + + virtual ~nRF5xSecurityManager(); + + //////////////////////////////////////////////////////////////////////////// + // SM lifecycle management + // + + /** + * @see ::ble::pal::SecurityManager::initialize + */ + virtual ble_error_t initialize(); + + /** + * @see ::ble::pal::SecurityManager::terminate + */ + virtual ble_error_t terminate(); + + /** + * @see ::ble::pal::SecurityManager::reset + */ + virtual ble_error_t reset() ; + + //////////////////////////////////////////////////////////////////////////// + // Resolving list management + // + + /** + * @see ::ble::pal::SecurityManager::read_resolving_list_capacity + */ + virtual uint8_t read_resolving_list_capacity(); + + /** + * @see ::ble::pal::SecurityManager::add_device_to_resolving_list + */ + virtual ble_error_t add_device_to_resolving_list( + advertising_peer_address_type_t peer_identity_address_type, + const address_t &peer_identity_address, + const irk_t &peer_irk + ); + + /** + * @see ::ble::pal::SecurityManager::remove_device_from_resolving_list + */ + virtual ble_error_t remove_device_from_resolving_list( + advertising_peer_address_type_t peer_identity_address_type, + const address_t &peer_identity_address + ); + + /** + * @see ::ble::pal::SecurityManager::clear_resolving_list + */ + virtual ble_error_t clear_resolving_list(); + + /** + * An entry of the resolving list stored in the SecurityManager. + */ + struct resolving_list_entry_t { + resolving_list_entry_t() : + peer_identity_address_type( + advertising_peer_address_type_t::PUBLIC_ADDRESS + ) + { } + + irk_t peer_irk; + address_t peer_identity_address; + advertising_peer_address_type_t peer_identity_address_type; + }; + + /** + * Return the IRKs present in the resolving list + * @param count The number of entries present in the resolving list. + * @param pointer to the first entry of the resolving list. + */ + ArrayView get_resolving_list(); + + /** + * Try to resolve a private resolvable address. + * + * @param resolvable_address The address to resolve. + * + * @return Pointer to the entry found if any. + */ + const resolving_list_entry_t* resolve_address( + const address_t& resolvable_address + ); + + + //////////////////////////////////////////////////////////////////////////// + // Pairing + // + + /** + * @see ::ble::pal::SecurityManager::send_pairing_request + */ + virtual ble_error_t send_pairing_request( + connection_handle_t connection, + bool oob_data_flag, + AuthenticationMask authentication_requirements, + KeyDistribution initiator_dist, + KeyDistribution responder_dist + ); + + /** + * @see ::ble::pal::SecurityManager::send_pairing_response + */ + virtual ble_error_t send_pairing_response( + connection_handle_t connection, + bool oob_data_flag, + AuthenticationMask authentication_requirements, + KeyDistribution initiator_dist, + KeyDistribution responder_dist + ); + + /** + * @see ::ble::pal::SecurityManager::cancel_pairing + */ + virtual ble_error_t cancel_pairing( + connection_handle_t connection, pairing_failure_t reason + ); + + + //////////////////////////////////////////////////////////////////////////// + // Feature support + // + + /** + * @see ::ble::pal::SecurityManager::get_secure_connections_support + */ + virtual ble_error_t get_secure_connections_support( + bool &enabled + ); + + /** + * @see ::ble::pal::SecurityManager::set_io_capability + */ + virtual ble_error_t set_io_capability(io_capability_t io_capability); + + //////////////////////////////////////////////////////////////////////////// + // Security settings + // + + /** + * @see ::ble::pal::SecurityManager::set_authentication_timeout + */ + virtual ble_error_t set_authentication_timeout( + connection_handle_t, uint16_t timeout_in_10ms + ); + + /** + * @see ::ble::pal::SecurityManager::get_authentication_timeout + */ + virtual ble_error_t get_authentication_timeout( + connection_handle_t, uint16_t &timeout_in_10ms + ); + + /** + * @see ::ble::pal::SecurityManager::set_encryption_key_requirements + */ + virtual ble_error_t set_encryption_key_requirements( + uint8_t min_encryption_key_size, + uint8_t max_encryption_key_size + ); + + /** + * @see ::ble::pal::SecurityManager::slave_security_request + */ + virtual ble_error_t slave_security_request( + connection_handle_t connection, + AuthenticationMask authentication + ); + + //////////////////////////////////////////////////////////////////////////// + // Encryption + // + + /** + * @see ::ble::pal::SecurityManager::enable_encryption + */ + virtual ble_error_t enable_encryption( + connection_handle_t connection, + const ltk_t <k, + const rand_t &rand, + const ediv_t &ediv, + bool mitm + ); + + /** + * @see ::ble::pal::SecurityManager::enable_encryption + */ + virtual ble_error_t enable_encryption( + connection_handle_t connection, + const ltk_t <k, + bool mitm + ) ; + + /** + * @see ::ble::pal::SecurityManager::encrypt_data + */ + virtual ble_error_t encrypt_data( + const byte_array_t<16> &key, + encryption_block_t &data + ); + + //////////////////////////////////////////////////////////////////////////// + // Privacy + // + + /** + * @see ::ble::pal::SecurityManager::set_private_address_timeout + */ + virtual ble_error_t set_private_address_timeout(uint16_t timeout_in_seconds); + + //////////////////////////////////////////////////////////////////////////// + // Keys + // + + /** + * @see ::ble::pal::SecurityManager::set_ltk + */ + virtual ble_error_t set_ltk( + connection_handle_t connection, + const ltk_t <k, + bool mitm, + bool secure_connections + ); + + /** + * @see ::ble::pal::SecurityManager::set_ltk_not_found + */ + virtual ble_error_t set_ltk_not_found( + connection_handle_t connection + ); + + /** + * @see ::ble::pal::SecurityManager::set_irk + */ + virtual ble_error_t set_irk(const irk_t &irk); + + /** + * @see ::ble::pal::SecurityManager::set_csrk + */ + virtual ble_error_t set_csrk(const csrk_t &csrk, sign_count_t sign_counter); + + /** + * @see ::ble::pal::SecurityManager::set_peer_csrk + */ + virtual ble_error_t set_peer_csrk( + connection_handle_t connection, + const csrk_t &csrk, + bool authenticated, + sign_count_t sign_counter + ); + + + //////////////////////////////////////////////////////////////////////////// + // Authentication + // + + /** + * @see ::ble::pal::SecurityManager::get_random_data + */ + virtual ble_error_t get_random_data(byte_array_t<8> &random_data); + + //////////////////////////////////////////////////////////////////////////// + // MITM + // + + /** + * @see ::ble::pal::SecurityManager::set_display_passkey + */ + virtual ble_error_t set_display_passkey(passkey_num_t passkey); + + /** + * @see ::ble::pal::SecurityManager::passkey_request_reply + */ + virtual ble_error_t passkey_request_reply( + connection_handle_t connection, + passkey_num_t passkey + ); + + /** + * @see ::ble::pal::SecurityManager::secure_connections_oob_request_reply + */ + virtual ble_error_t secure_connections_oob_request_reply( + connection_handle_t connection, + const oob_lesc_value_t &local_random, + const oob_lesc_value_t &peer_random, + const oob_confirm_t &peer_confirm + ); + + /** + * @see ::ble::pal::SecurityManager::legacy_pairing_oob_request_reply + */ + virtual ble_error_t legacy_pairing_oob_request_reply( + connection_handle_t connection, + const oob_tk_t &oob_data + ); + + /** + * @see ::ble::pal::SecurityManager::confirmation_entered + */ + virtual ble_error_t confirmation_entered( + connection_handle_t connection, bool confirmation + ); + + /** + * @see ::ble::pal::SecurityManager::send_keypress_notification + */ + virtual ble_error_t send_keypress_notification( + connection_handle_t connection, Keypress_t keypress + ); + + /** + * @see ::ble::pal::SecurityManager::generate_secure_connections_oob + */ + virtual ble_error_t generate_secure_connections_oob(); + + // singleton of nordic Security Manager + static nRF5xSecurityManager& get_security_manager(); + + // Event handler + bool sm_handler(const ble_evt_t *evt); + +private: + csrk_t _csrk; + sign_count_t _sign_counter; + io_capability_t _io_capability; + uint8_t _min_encryption_key_size; + uint8_t _max_encryption_key_size; + + struct pairing_control_block_t; + + ble_gap_sec_params_t make_security_params( + bool oob_data_flag, + AuthenticationMask authentication_requirements, + KeyDistribution initiator_dist, + KeyDistribution responder_dist + ); + + ble_gap_sec_keyset_t make_keyset( + pairing_control_block_t& pairing_cb, + KeyDistribution initiator_dist, + KeyDistribution responder_dist + ); + + pairing_control_block_t* allocate_pairing_cb(connection_handle_t connection); + void release_pairing_cb(pairing_control_block_t* pairing_cb); + pairing_control_block_t* get_pairing_cb(connection_handle_t connection); + void release_all_pairing_cb(); + + pairing_control_block_t* _control_blocks; +#if defined(MBEDTLS_ECDH_C) + CryptoToolbox _crypto; + ble::public_key_coord_t X; + ble::public_key_coord_t Y; + ble::public_key_coord_t secret; +#endif + + static const size_t MAX_RESOLVING_LIST_ENTRIES = BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT; + + size_t resolving_list_entry_count; + resolving_list_entry_t resolving_list[MAX_RESOLVING_LIST_ENTRIES]; +}; + +} // nordic +} // vendor +} // pal +} // ble + +#endif /* NRF5X_PAL_SECURITY_MANAGER_ */