Newer
Older
mbed-os / connectivity / FEATURE_BLE / source / generic / SecurityManagerImpl.cpp
/* mbed Microcontroller Library
 * Copyright (c) 2006-2020 ARM Limited
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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.
 */

#if BLE_FEATURE_SECURITY

#include "ble/BLE.h"
#include "ble/common/BLERoles.h"

#include "source/pal/PalSecurityManager.h"

#include "source/generic/SecurityManagerImpl.h"
#include "source/generic/MemorySecurityDb.h"
#include "source/generic/FileSecurityDb.h"
#include "source/generic/KVStoreSecurityDb.h"

#include "mbed-trace/mbed_trace.h"
#include "common/ble_trace_helpers.h"

#define TRACE_GROUP "BLSM"


using ble::advertising_peer_address_type_t;
using ble::AuthenticationMask;
using ble::KeyDistribution;
using ble::connection_peer_address_type_t;

namespace ble {
namespace impl {

namespace {
constexpr auto SECURITY_MODE_ENCRYPTION_OPEN_LINK =
    ble::SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK;

constexpr auto SECURITY_MODE_ENCRYPTION_NO_MITM =
    ble::SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM;

constexpr auto SECURITY_MODE_ENCRYPTION_WITH_MITM =
    ble::SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM;

constexpr auto SECURITY_MODE_SIGNED_NO_MITM =
    ble::SecurityManager::SECURITY_MODE_SIGNED_NO_MITM;

constexpr auto SECURITY_MODE_SIGNED_WITH_MITM =
    ble::SecurityManager::SECURITY_MODE_SIGNED_WITH_MITM;

using SecurityCompletionStatus_t = ble::SecurityManager::SecurityCompletionStatus_t;

constexpr auto SEC_STATUS_TIMEOUT =
    ble::SecurityManager::SEC_STATUS_TIMEOUT;

constexpr auto SEC_STATUS_SUCCESS =
    ble::SecurityManager::SEC_STATUS_SUCCESS;

}

/* Implements PalSecurityManager */

////////////////////////////////////////////////////////////////////////////
// SM lifecycle management
//

ble_error_t SecurityManager::init(
    bool bondable,
    bool mitm,
    SecurityIOCapabilities_t iocaps,
    const uint8_t* passkey,
    bool signing,
    const char* db_path
)
{
    tr_info("SM init: bondable=%s, mitm=%s, iocaps=%s, passkey=%s, signing=%s, db_path=%p",
        to_string(bondable),
        to_string(mitm),
        to_string(iocaps),
        passkey_str(passkey),
        to_string(signing),
        db_path
    );
#if !(BLE_FEATURE_SIGNING)
    if (signing) {
        tr_error("Failed to init SM with signing option if signing support disabled");
        return BLE_ERROR_INVALID_PARAM;
    }
#endif // !(BLE_FEATURE_SIGNING)
    ble_error_t result = _pal.initialize();

    if (result != BLE_ERROR_NONE) {
        return result;
    }

    result = init_database(db_path);

    if (result != BLE_ERROR_NONE) {
        return result;
    }

    _pal.set_io_capability((io_capability_t::type) iocaps);

    if (passkey) {
        _pal.set_display_passkey(PasskeyAscii::to_num(passkey));
    } else {
        _pal.set_display_passkey(0);
    }

    _legacy_pairing_allowed = true;

    bool secure_connections;
    _pal.get_secure_connections_support(secure_connections);
    tr_debug("Secure connection support: %s", to_string(secure_connections));

    _default_authentication.set_bondable(bondable);
    _default_authentication.set_mitm(mitm);
    _default_authentication.set_secure_connections(secure_connections);
    _default_authentication.set_keypress_notification(true);

    // FIXME: depends on BR/EDR support
    _default_key_distribution.set_link(false);
#if BLE_FEATURE_SIGNING
    _default_key_distribution.set_signing(signing);
    if (signing) {
        init_signing();
    }
#else
    _default_key_distribution.set_signing(false);
#endif // BLE_FEATURE_SIGNING
#if BLE_FEATURE_CONNECTABLE
    _connection_monitor.set_connection_event_handler(this);
#endif
#if BLE_FEATURE_SIGNING
    _signing_monitor.set_signing_event_handler(this);
#endif
    _pal.set_event_handler(this);

#if BLE_FEATURE_PRIVACY
    result = init_resolving_list();
#endif

#if BLE_FEATURE_PRIVACY
    // set the local identity address and irk
    if (result == BLE_ERROR_NONE) {
    	result = init_identity();
    }
#endif // BLE_FEATURE_PRIVACY

    if (result != BLE_ERROR_NONE) {
        delete _db;
        _db = nullptr;
    }

    return result;
}


ble_error_t SecurityManager::setDatabaseFilepath(
    const char *db_path
)
{
    tr_info("Set DB path: %s", db_path);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }

    /* operation only allowed with no connections active */
    for (auto & _control_block : _control_blocks) {
        if (_control_block.connected) {
            return BLE_ERROR_OPERATION_NOT_PERMITTED;
        }
    }

    ble_error_t result = init_database(db_path);
    if (result != BLE_ERROR_NONE) {
        return result;
    }

#if BLE_FEATURE_PRIVACY
    init_resolving_list();
#endif

    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::reset()
{
    delete _db;
    _db = nullptr;
    _pal.reset();

    /* Notify that the instance is about to shutdown */
    shutdownCallChain.call(&ble::BLE::Instance().securityManager());
    shutdownCallChain.clear();
    eventHandler = &defaultEventHandler;

    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::preserveBondingStateOnReset(bool enabled)
{
    tr_info("Preserve bonding state: %s", to_string(enabled));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    _db->set_restore(enabled);
    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::writeBondingStateToPersistentStorage()
{
    tr_info("Writing bonding to storage");
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    _db->sync();
    return BLE_ERROR_NONE;
}

////////////////////////////////////////////////////////////////////////////
// List management
//


ble_error_t SecurityManager::purgeAllBondingState(void)
{
    tr_info("Purging all bonds");
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    _db->clear_entries();

    ble_error_t ret = BLE_ERROR_NONE;

#if BLE_FEATURE_SIGNING
    // generate new csrk and irk
    ret = init_signing();
    if (ret) {
        return ret;
    }
#endif // BLE_FEATURE_SIGNING
#if BLE_FEATURE_PRIVACY
    ret = init_identity();
#endif // BLE_FEATURE_PRIVACY

    return ret;
}


ble_error_t SecurityManager::generateWhitelistFromBondTable(::ble::whitelist_t *whitelist) const {
    tr_info("Generate whitelist from bonds");
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    if (eventHandler) {
        if (!whitelist) {
            tr_error("Failure, whitelist not provided");
            return BLE_ERROR_INVALID_PARAM;
        }
        _db->generate_whitelist_from_bond_table(
            mbed::callback(eventHandler, &EventHandler::whitelistFromBondTable),
            whitelist
        );
        return BLE_ERROR_NONE;
    } else {
        tr_error("No event handler registered to receive whitelist generated");
        return BLE_ERROR_INVALID_STATE;

    }
}

////////////////////////////////////////////////////////////////////////////
// Pairing
//

#if BLE_ROLE_CENTRAL
ble_error_t SecurityManager::requestPairing(connection_handle_t connection)
{
    tr_info("Connection %d - Request pairing", connection);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    if (!_legacy_pairing_allowed && !_default_authentication.get_secure_connections()) {
        tr_error("Failure, legacy pairing not allowed while controller doesn't support secure connection");
        return BLE_ERROR_INVALID_STATE;
    }

    set_mitm_performed(connection, false);
    update_oob_presence(connection);

    AuthenticationMask link_authentication(_default_authentication);

    if (cb->mitm_requested) {
        link_authentication.set_mitm(true);
    }

    /* by default the initiator doesn't send any keys other then identity */
    KeyDistribution initiator_distribution(
        KeyDistribution::KEY_DISTRIBUTION_IDENTITY |
        _default_key_distribution.get_link()
    );

    initiator_distribution.set_signing(
        cb->signing_override_default ?
            cb->signing_requested :
            _default_key_distribution.get_signing()
    );

    /* if requested the initiator may send all the default keys for later
     * use when roles are changed */
    if (_master_sends_keys) {
        initiator_distribution = _default_key_distribution;
    }

    KeyDistribution responder_distribution(_default_key_distribution);

    if (cb->signing_override_default) {
        responder_distribution.set_signing(cb->signing_requested);
    }

    return _pal.send_pairing_request(
        connection,
        cb->oob_present,
        link_authentication,
        initiator_distribution,
        responder_distribution
    );
}
#endif // BLE_ROLE_CENTRAL

#if BLE_ROLE_PERIPHERAL
ble_error_t SecurityManager::acceptPairingRequest(connection_handle_t connection)
{
    tr_info("Connection %d - Accept pairing request", connection);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    update_oob_presence(connection);

    AuthenticationMask link_authentication(_default_authentication);
    if (cb->mitm_requested) {
        link_authentication.set_mitm(true);
    }

    KeyDistribution initiator_distribution = cb->get_initiator_key_distribution();

    bool master_signing = initiator_distribution.get_signing();

    if (_master_sends_keys) {
        initiator_distribution &= _default_key_distribution;
    } else {
        initiator_distribution &= KeyDistribution(
            KeyDistribution::KEY_DISTRIBUTION_IDENTITY |
            KeyDistribution::KEY_DISTRIBUTION_LINK
        );
    }

    /* signing has to be offered and enabled on the link */
    if (master_signing) {
        initiator_distribution.set_signing(
            cb->signing_override_default ?
                cb->signing_requested :
                _default_key_distribution.get_signing()
        );
    }

    KeyDistribution responder_distribution(cb->get_responder_key_distribution());

    responder_distribution &= _default_key_distribution;

    /* signing has to be requested and enabled on the link */
    if (responder_distribution.get_signing()) {
        responder_distribution.set_signing(
            cb->signing_override_default ?
                cb->signing_requested :
                _default_key_distribution.get_signing()
        );
    }

    return _pal.send_pairing_response(
        connection,
        cb->oob_present,
        link_authentication,
        initiator_distribution,
        responder_distribution
    );
}
#endif


ble_error_t SecurityManager::cancelPairingRequest(connection_handle_t connection)
{
    tr_info("Connection %d - Cancel pairing request", connection);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    return _pal.cancel_pairing(connection, pairing_failure_t::UNSPECIFIED_REASON);
}


ble_error_t SecurityManager::setPairingRequestAuthorisation(bool required)
{
    tr_info("Set manual handling of pairing request: %s", to_string(required));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    _pairing_authorisation_required = required;
    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::getPeerIdentity(connection_handle_t connection)
{
    tr_info("Connection %d - Request peer identity", connection);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }

    if (!eventHandler) {
        tr_error("Event handler must be registered to request peer identity");
        return BLE_ERROR_INVALID_STATE;
    }

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Control block not found", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    _db->get_entry_identity(
        [connection,this](SecurityDb::entry_handle_t handle, const SecurityEntryIdentity_t* identity) {
            if (identity) {
                tr_info("Connection %d - Peer identity address retrieved: %s, public: %s",
                    connection,
                    to_string(identity->identity_address),
                    to_string(identity->identity_address_is_public)
                );
            } else {
                tr_info("Connection %d - Failed to retrieve peer identity address", connection);
            }
            if (eventHandler) {
                eventHandler->peerIdentity(
                    connection,
                    identity ? &identity->identity_address : nullptr,
                    identity ? identity->identity_address_is_public : false
                );
            } else {
                tr_warning("Event handler removed while retrieving peer ID address");
            }
        },
        cb->db_entry
    );
    return BLE_ERROR_NONE;
}

////////////////////////////////////////////////////////////////////////////
// Feature support
//

#if BLE_FEATURE_SECURE_CONNECTIONS
ble_error_t SecurityManager::allowLegacyPairing(bool allow)
{
    tr_info("Allow legacy pairing: %s", to_string(allow));
    _legacy_pairing_allowed = allow;
    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::getSecureConnectionsSupport(bool *enabled)
{
    return _pal.get_secure_connections_support(*enabled);
}
#endif // BLE_FEATURE_SECURE_CONNECTIONS

////////////////////////////////////////////////////////////////////////////
// Security settings
//


ble_error_t SecurityManager::setIoCapability(SecurityIOCapabilities_t iocaps)
{
    tr_info("Set I/O capabilities: %s", to_string(iocaps));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    return _pal.set_io_capability((io_capability_t::type) iocaps);
}


ble_error_t SecurityManager::setDisplayPasskey(const uint8_t* passkey)
{
    tr_info("Set display passkey: %s", passkey_str(passkey));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    return _pal.set_display_passkey(PasskeyAscii::to_num(passkey));
}


ble_error_t SecurityManager::setAuthenticationTimeout(
    connection_handle_t connection,
    uint32_t timeout_in_ms
)
{
    tr_info("Connection %d - Set connection authentication timeout: %" PRIu32 " ms", connection, timeout_in_ms);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    return _pal.set_authentication_timeout(connection, timeout_in_ms / 10);
}


ble_error_t SecurityManager::getAuthenticationTimeout(
    connection_handle_t connection,
    uint32_t *timeout_in_ms
)
{
    if (!_db) return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    uint16_t timeout_in_10ms;
    ble_error_t status = _pal.get_authentication_timeout(connection, timeout_in_10ms);
    *timeout_in_ms = 10 * timeout_in_10ms;
    return status;
}


ble_error_t SecurityManager::setLinkSecurity(
    connection_handle_t connection,
    SecurityMode_t securityMode
)
{
    tr_info("Connection %d - Set link security: %s", connection, to_string(securityMode));

    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not found", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    if (cb->encryption_requested) {
        tr_error("Failure, encryption already requested");
        return BLE_ERROR_OPERATION_NOT_PERMITTED;
    }

    switch (securityMode) {
        case SECURITY_MODE_ENCRYPTION_OPEN_LINK:
            return setLinkEncryption(connection, link_encryption_t::NOT_ENCRYPTED);

        case SECURITY_MODE_ENCRYPTION_NO_MITM:
            return setLinkEncryption(connection, link_encryption_t::ENCRYPTED);

        case SECURITY_MODE_ENCRYPTION_WITH_MITM:
            return setLinkEncryption(connection, link_encryption_t::ENCRYPTED_WITH_MITM);

#if BLE_FEATURE_SIGNING
        case SECURITY_MODE_SIGNED_NO_MITM:
            return getSigningKey(connection, false);

        case SECURITY_MODE_SIGNED_WITH_MITM:
            return getSigningKey(connection, true);
#endif // BLE_FEATURE_SIGNING

        default:
            tr_error("Failure, Invalid security mode");
            return BLE_ERROR_INVALID_PARAM;
    }
}


ble_error_t SecurityManager::setKeypressNotification(bool enabled)
{
    tr_info("Enable user keypress notification: %s", to_string(enabled));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    _default_authentication.set_keypress_notification(enabled);
    return BLE_ERROR_NONE;
}

#if BLE_FEATURE_SIGNING
ble_error_t SecurityManager::enableSigning(
    connection_handle_t connection,
    bool enabled
)
{
    tr_info("Connection %d - Enable signing: %s", connection, to_string(enabled));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return BLE_ERROR_INVALID_PARAM;
    }

    cb->signing_override_default = true;

    if (enabled && !cb->signing_requested && !_default_key_distribution.get_signing()) {
        cb->signing_requested = true;
        if (flags->csrk_stored) {
            /* used the stored ones when available */
            _db->get_entry_peer_csrk(
                mbed::callback(this, &SecurityManager::set_peer_csrk_cb),
                cb->db_entry
            );
        } else {
            tr_info("Connection %d - CSRK not available, pair to acquire it", connection);
            /* create keys if needed and exchange them */
            init_signing();
            if (cb->is_master) {
#if BLE_ROLE_CENTRAL
                return requestPairing(connection);
#else
                tr_error("Device configured as master while central role disabled");
                return BLE_ERROR_NOT_IMPLEMENTED;
#endif
            } else {
#if BLE_ROLE_PERIPHERAL
                return slave_security_request(connection);
#else
                tr_error("Device configured as slave while peripheral role disabled");
                return BLE_ERROR_NOT_IMPLEMENTED;
#endif
            }
        }
    } else {
        cb->signing_requested = enabled;
    }

    return BLE_ERROR_NONE;
}
#endif

ble_error_t SecurityManager::setHintFutureRoleReversal(bool enable)
{
    tr_info("Set role reversal hint: %s", to_string(enable));
    _master_sends_keys = enable;
    return BLE_ERROR_NONE;
}

////////////////////////////////////////////////////////////////////////////
// Encryption
//


ble_error_t SecurityManager::getLinkEncryption(
    connection_handle_t connection,
    link_encryption_t *encryption
)
{
    if (!_db) {
        tr_error("Failed to retrieve link encryption, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failed to retrieve link encryption: no CB available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Failed to retrieve link encryption, empty distribution flags", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    if (cb->encrypted) {
        if (flags->ltk_mitm_protected  || cb->mitm_performed) {
            if (flags->secure_connections_paired) {
                *encryption = link_encryption_t::ENCRYPTED_WITH_SC_AND_MITM;
            } else {
                *encryption = link_encryption_t::ENCRYPTED_WITH_MITM;
            }
        } else {
            *encryption = link_encryption_t::ENCRYPTED;
        }
    } else if (cb->encryption_requested) {
        *encryption = link_encryption_t::ENCRYPTION_IN_PROGRESS;
    } else {
        *encryption = link_encryption_t::NOT_ENCRYPTED;
    }

    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::setLinkEncryption(
    connection_handle_t connection,
    link_encryption_t encryption
)
{
    tr_info("Connection %d - Set encryption: %s", connection, to_string(encryption));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return BLE_ERROR_INVALID_PARAM;
    }

    link_encryption_t current_encryption(link_encryption_t::NOT_ENCRYPTED);
    getLinkEncryption(connection, &current_encryption);

    if (current_encryption == link_encryption_t::ENCRYPTION_IN_PROGRESS) {
        tr_error("Connection %d - Failure, encryption in progress", connection);
        return BLE_ERROR_OPERATION_NOT_PERMITTED;
    }

    if (current_encryption == encryption) {
        tr_info("Connection %d - Link already encrypted to the right level", connection);
        /* ignore if the link is already at required state*/

    } else if (encryption == link_encryption_t::NOT_ENCRYPTED) {
        // Fail as it is not permitted to turn down encryption
        tr_error("Connection %d - Failure, forbidden to turn down encryption", connection);
        return BLE_ERROR_OPERATION_NOT_PERMITTED;
    } else if (encryption == link_encryption_t::ENCRYPTED) {

        /* only change if we're not already encrypted with mitm */
        if (current_encryption != link_encryption_t::ENCRYPTED_WITH_MITM ||
            current_encryption != link_encryption_t::ENCRYPTED_WITH_SC_AND_MITM
        ) {
            cb->encryption_requested = true;
            return enable_encryption(connection);
        } else {
            tr_info("Connection %d - Encryption level %s sufficient", connection, to_string(current_encryption));
        }

    } else if (encryption == link_encryption_t::ENCRYPTED_WITH_MITM) {

        if (flags->ltk_mitm_protected && !cb->encrypted) {
            cb->encryption_requested = true;
            return enable_encryption(connection);
        } else {
            cb->encryption_requested = true;
            return requestAuthentication(connection);
        }

    } else if (encryption == link_encryption_t::ENCRYPTED_WITH_SC_AND_MITM) {

        if (flags->ltk_mitm_protected &&
            flags->secure_connections_paired &&
            !cb->encrypted
        ) {
            cb->encryption_requested = true;
            return enable_encryption(connection);
        } else {
            cb->encryption_requested = true;
            return requestAuthentication(connection);
        }

    } else {
        tr_error("Connection %d - Failure invalid encryption level", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    if (eventHandler) {
        eventHandler->linkEncryptionResult(connection, current_encryption);
    }

    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::getEncryptionKeySize(
    connection_handle_t connectionHandle,
    uint8_t *size
)
{
    if (!_db) {
        tr_error("Connection %d - Failed to retrieve encryption key size: DB not initialized", connectionHandle);
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    ControlBlock_t *cb = get_control_block(connectionHandle);
    if (!cb) {
        tr_error("Connection %d - Failed to retrieve encryption key size: no CB", connectionHandle);
        return BLE_ERROR_INVALID_PARAM;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Failed to retrieve encryption key size: no flags retrieved", connectionHandle);
        return BLE_ERROR_INVALID_PARAM;
    }

    *size = flags->encryption_key_size;
    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::setEncryptionKeyRequirements(
    uint8_t minimumByteSize,
    uint8_t maximumByteSize
)
{
    tr_info("Set encryption key requirements: [%d:%d]", minimumByteSize, maximumByteSize);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    return _pal.set_encryption_key_requirements(minimumByteSize, maximumByteSize);
}

////////////////////////////////////////////////////////////////////////////
// Keys
//

#if BLE_FEATURE_SIGNING
ble_error_t SecurityManager::getSigningKey(connection_handle_t connection, bool authenticated)
{
    tr_info("Connection %d - retrieve signing key: authenticated = %s", connection, to_string(authenticated));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Failure, no distribution flags set", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    if (flags->csrk_stored && (flags->csrk_mitm_protected || !authenticated)) {
        /* we have a key that is either authenticated or we don't care if it is
         * so retrieve it from the DB now */
        _db->get_entry_peer_csrk(
            mbed::callback(this, &SecurityManager::return_csrk_cb),
            cb->db_entry
        );
        return BLE_ERROR_NONE;

    } else {
        /* we don't have the right key so we need to get it first
         * keys exchange will create the signingKey event */
        if (authenticated) {
            tr_info("Connection %d - Signing key not available, request authentication", connection);
            return requestAuthentication(connection);
        } else if (cb->is_master) {
#if BLE_ROLE_CENTRAL
            tr_info("Connection %d - Signing key not available, request pairing", connection);
            return requestPairing(connection);
#else
            tr_error("Failure, can't act as master if central role disabled");
            return BLE_ERROR_NOT_IMPLEMENTED;
#endif
        } else {
#if BLE_ROLE_PERIPHERAL
            tr_info("Connection %d - Signing key not available, issue slave security request to pair", connection);
            return slave_security_request(connection);
#else
            tr_error("Failure, can't act as slave if peripheral role disabled");
            return BLE_ERROR_NOT_IMPLEMENTED;
#endif
        }
    }
}
#endif // BLE_FEATURE_SIGNING

////////////////////////////////////////////////////////////////////////////
// Privacy
//

#if BLE_FEATURE_PRIVACY
ble_error_t SecurityManager::setPrivateAddressTimeout(uint16_t timeout_in_seconds)
{
    tr_info("Set private address timeout: %d s", timeout_in_seconds);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    _private_address_controller.set_timeout(
        resolvable_address_timeout_t(timeout_in_seconds)
    );
    return BLE_ERROR_NONE;
}
#endif // BLE_FEATURE_PRIVACY

////////////////////////////////////////////////////////////////////////////
// Authentication
//


ble_error_t SecurityManager::requestAuthentication(connection_handle_t connection)
{
    tr_info("Connection %d - Request authentication", connection);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return BLE_ERROR_INVALID_PARAM;
    }

    if (flags->ltk_mitm_protected) {
        if (cb->authenticated) {
            tr_info("Connection %d - Nothing to do, link already authenticated", connection);
            return BLE_ERROR_NONE;
        } else {
            tr_info("Connection %d - ltk mitm protected, requesting encryption", connection);
            cb->encryption_requested = true;
            return enable_encryption(connection);
        }
    } else {
        cb->mitm_requested = true;
        if (cb->is_master) {
            tr_info("Connection %d - ltk not mitm protected, request new pairing", connection);
#if BLE_ROLE_CENTRAL
            return requestPairing(connection);
#else
            tr_error("Invalid state cannot request pairing if central role disabled");
            return BLE_ERROR_NOT_IMPLEMENTED;
#endif
        } else {
            tr_info("Connection %d - ltk not mitm protected, issue slave security request", connection);
#if BLE_ROLE_PERIPHERAL
            return slave_security_request(connection);
#else
            tr_error("Invalid state, cannot send slave security request if peripheral role disabled");
            return BLE_ERROR_NOT_IMPLEMENTED;
#endif
        }
    }
}

////////////////////////////////////////////////////////////////////////////
// MITM
//


ble_error_t SecurityManager::generateOOB(
    const address_t *address
)
{
    MBED_ASSERT(address != nullptr);

    tr_info("Generate OOB for %s", to_string(*address));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    /* legacy pairing */
    ble_error_t status = get_random_data(_oob_temporary_key.data(), 16);

    if (status == BLE_ERROR_NONE) {
        tr_info("OOB temporary key generated: %s", to_string(_oob_temporary_key));
        _oob_temporary_key_creator_address = *address;

        if (eventHandler) {
            eventHandler->legacyPairingOobGenerated(
                &_oob_temporary_key_creator_address,
                &_oob_temporary_key
            );
        }
    } else {
        tr_error("Failed to generate random data");
        return status;
    }

#if BLE_FEATURE_SECURE_CONNECTIONS
    /* Secure connections. Avoid generating if we're already waiting for it.
     * If a local random is set to 0 it means we're already calculating. */
    if (!is_all_zeros(_oob_local_random)) {
        /* save the current values in case the call to
         * generate_secure_connections_oob fails */
        address_t orig_local_address = _oob_local_address;
        oob_lesc_value_t orig_local_random = _oob_local_random;

        _oob_local_address = *address;
        /* this will be updated when calculation completes,
         * a value of all zeros is an invalid random value */
        set_all_zeros(_oob_local_random);

        status = _pal.generate_secure_connections_oob();
        if (status != BLE_ERROR_NONE && status != BLE_ERROR_NOT_IMPLEMENTED) {
            _oob_local_address = orig_local_address;
            _oob_local_random = orig_local_random;
            return status;
        }
    } else {
        tr_error("Impossible to generate SC OOB data: computation already running");
        return BLE_STACK_BUSY;
    }
#endif // BLE_FEATURE_SECURE_CONNECTIONS

    return BLE_ERROR_NONE;
}


ble_error_t SecurityManager::setOOBDataUsage(
    connection_handle_t connection,
    bool useOOB,
    bool OOBProvidesMITM
)
{
    tr_info("Connection %d - Set OOB data usage: use OOB = %s, Provides MITM = %s", connection, to_string(useOOB), to_string(OOBProvidesMITM));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    cb->attempt_oob = useOOB;
    cb->oob_mitm_protection = OOBProvidesMITM;

    if (useOOB) {
        return generateOOB(&cb->local_address);
    } else {
        return BLE_ERROR_NONE;
    }
}

#if BLE_FEATURE_SECURE_CONNECTIONS
ble_error_t SecurityManager::confirmationEntered(
    connection_handle_t connection,
    bool confirmation
)
{
    tr_info("Connection %d - confirmation entered: %s", connection, to_string(confirmation));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    return _pal.confirmation_entered(connection, confirmation);
}
#endif // BLE_FEATURE_SECURE_CONNECTIONS

ble_error_t SecurityManager::passkeyEntered(
    connection_handle_t connection,
    Passkey_t passkey
)
{
    tr_info("Connection %d - Passkey entered: %s", connection, passkey_str(passkey));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    return _pal.passkey_request_reply(
        connection,
        PasskeyAscii::to_num(passkey)
    );
}

#if BLE_FEATURE_SECURE_CONNECTIONS
ble_error_t SecurityManager::sendKeypressNotification(
    connection_handle_t connection,
    ble::Keypress_t keypress
)
{
    tr_info("Connection %d - Send Keypress notification: %s", connection, to_string(keypress));
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    return _pal.send_keypress_notification(connection, keypress);
}
#endif // BLE_FEATURE_SECURE_CONNECTIONS


ble_error_t SecurityManager::legacyPairingOobReceived(
    const address_t *address,
    const oob_tk_t *tk
)
{
    if (!_db) {
        tr_error("Failed to set legacy pairing OOB, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    if (address && tk) {
        tr_info("Legacy pairing OOB received: address = %s, tk = %s", to_string(address), to_string(tk));
        ControlBlock_t *cb = get_control_block(*address);
        if (!cb) {
            tr_error("Failure, control block not available");
            return BLE_ERROR_INVALID_PARAM;
        }

        SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
        if (!flags) {
            tr_error("Failure, no distribution flags set");
            return BLE_ERROR_INVALID_PARAM;
        }

        _oob_temporary_key = *tk;
        _oob_temporary_key_creator_address = *address;

        if (flags->peer_address == _oob_temporary_key_creator_address) {
            cb->attempt_oob = true;
        }

        if (cb->legacy_pairing_oob_request_pending) {
            on_legacy_pairing_oob_request(cb->connection);
            /* legacy_pairing_oob_request_pending stops us from
             * going into a loop of asking the user for oob
             * so this reset needs to happen after the call above */
            cb->legacy_pairing_oob_request_pending = false;
        }
    } else {
        tr_warning("Trying to set invalid legacy OOB pairing data: address = %p, temporary key = %p", address, tk);
    }
    return BLE_ERROR_NONE;
}

#if BLE_FEATURE_SECURE_CONNECTIONS
ble_error_t SecurityManager::oobReceived(
    const address_t *address,
    const oob_lesc_value_t *random,
    const oob_confirm_t *confirm
)
{
    if (!_db) {
        tr_error("Failed to set OOB pairing data, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    if (address && random && confirm) {
        tr_info("Set OOB pairing data: address = %s, random = %s, confirm = %s", to_string(*address), to_string(*random), to_string(*confirm));
        _oob_peer_address = *address;
        _oob_peer_random = *random;
        _oob_peer_confirm = *confirm;
        return BLE_ERROR_NONE;
    } else {
        tr_warning("Trying to set invalid OOB pairing data: address = %p, random = %p, confirm = %p", address, random, confirm);
    }

    return BLE_ERROR_INVALID_PARAM;
}
#endif // BLE_FEATURE_SECURE_CONNECTIONS

////////////////////////////////////////////////////////////////////////////
// Helper functions
//


ble_error_t SecurityManager::init_database(
    const char *db_path
)
{
    tr_info("Initialize database. Path = %s", db_path);
    delete _db;

#if BLE_SECURITY_DATABASE_FILESYSTEM
    FILE* db_file = FileSecurityDb::open_db_file(db_path);

    if (db_file) {
        _db = new (std::nothrow) FileSecurityDb(db_file);
    } else
#endif
#if BLE_SECURITY_DATABASE_KVSTORE
    if (KVStoreSecurityDb::open_db()) {
        _db = new (std::nothrow) KVStoreSecurityDb();
    } else
#endif
    {
        _db = new (std::nothrow) MemorySecurityDb();
    }

    if (!_db) {
        tr_error("Not enough memory to create the database");
        return BLE_ERROR_NO_MEM;
    }

    _db->restore();

    return BLE_ERROR_NONE;
}

#if BLE_FEATURE_PRIVACY
ble_error_t SecurityManager::init_resolving_list()
{
    tr_info("Initialize resolving list");
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }

    /* match the resolving list to the currently stored set of IRKs */
    uint8_t resolving_list_capacity = _private_address_controller.read_resolving_list_capacity();
    auto* identity_list_p =
        new (std::nothrow) SecurityEntryIdentity_t[resolving_list_capacity];

    if (identity_list_p) {
        Span<SecurityEntryIdentity_t> identity_list(
            identity_list_p,
            resolving_list_capacity
        );

        _db->get_identity_list(
            mbed::callback(this, &SecurityManager::on_identity_list_retrieved),
            identity_list
        );
    } else {
        tr_error("Not enough memory to create resolving list");
        return BLE_ERROR_NO_MEM;
    }

    return BLE_ERROR_NONE;
}
#endif // BLE_FEATURE_PRIVACY

#if BLE_FEATURE_SIGNING
ble_error_t SecurityManager::init_signing()
{
    tr_info("Init signing");
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    const csrk_t *pcsrk = _db->get_local_csrk();
    sign_count_t local_sign_counter = _db->get_local_sign_counter();

    csrk_t csrk;
    if (!pcsrk || *pcsrk == csrk_t{}) {
        ble_error_t ret = get_random_data(csrk.data(), csrk.size());
        if (ret != BLE_ERROR_NONE) {
            tr_error("Failed to create local csrk");
            return ret;
        }

        pcsrk = &csrk;
        _db->set_local_csrk(csrk);
        _db->set_local_sign_counter(local_sign_counter);
    }

    return _pal.set_csrk(*pcsrk, local_sign_counter);
}
#endif // BLE_FEATURE_SIGNING

#if BLE_FEATURE_PRIVACY
ble_error_t SecurityManager::init_identity()
{
    tr_info("Init identity");
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }
    const irk_t *pirk = nullptr;

    ble::Gap& gap = BLE::Instance().gap();

    irk_t irk = _db->get_local_irk();
    address_t identity_address;
    bool public_identity_address = false;
    if (irk != irk_t()) {
        pirk = &irk;
        public_identity_address = _db->is_local_identity_address_public();
        identity_address = _db->get_local_identity_address();

        if (!_db->is_local_identity_address_public()) {
            // Some controllers doesn't store their random static address and
            // instead generates them at each reboot.
            // The code should replace the random static address with the identity
            // address if this is the case.
            if (_db->get_local_identity_address() != gap.getRandomStaticAddress()) {
                ble_error_t err = gap.setRandomStaticAddress(_db->get_local_identity_address());
                if (err) {
                    return err;
                }
            }
        }
    } else {
        ble_error_t ret = get_random_data(irk.data(), irk.size());
        if (ret != BLE_ERROR_NONE) {
            tr_error("Failed to create local irk");
            return ret;
        }

        pirk = &irk;
        public_identity_address = false;
        identity_address = gap.getRandomStaticAddress();
        _db->set_local_identity(irk, identity_address, public_identity_address);
    }

    auto err = _pal.set_irk(*pirk);
    if (!err) {
        _private_address_controller.set_local_irk(*pirk);
        _pal.set_identity_address(identity_address, public_identity_address);
    }
    return err;
}
#endif // BLE_FEATURE_PRIVACY

ble_error_t SecurityManager::get_random_data(uint8_t *buffer, size_t size)
{
    size_t remaining_size = size;
    uint8_t* it = buffer;
    byte_array_t<8> random_data;

    while (remaining_size) {
        /* fill out the buffer by reading the random data in chunks
         * and copying it until reaching the set size */
        size_t copy_size = std::min(remaining_size, random_data.size());
        ble_error_t ret = _pal.get_random_data(random_data);
        if (ret != BLE_ERROR_NONE) {
            tr_error("Pal failed at generating remaining %d bytes of random data, abort get_random_data.", size);
            return ret;
        }
        memcpy(it, random_data.data(), copy_size);
        remaining_size -= copy_size;
        it += copy_size;
    }

    tr_info("Random data generated: %s", tr_array(buffer, size));

    return BLE_ERROR_NONE;
}


#if BLE_ROLE_PERIPHERAL
ble_error_t SecurityManager::slave_security_request(connection_handle_t connection)
{
    tr_info("Connection %d - Initiate security request", connection);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }
    AuthenticationMask link_authentication(_default_authentication);
    link_authentication.set_mitm(cb->mitm_requested);
    return _pal.slave_security_request(connection, link_authentication);
}
#endif // BLE_ROLE_PERIPHERAL


ble_error_t SecurityManager::enable_encryption(connection_handle_t connection)
{
    tr_info("Connection %d - Enable encryption", connection);
    if (!_db) {
        tr_error("Failure, DB not initialized");
        return BLE_ERROR_INITIALIZATION_INCOMPLETE;
    }

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return BLE_ERROR_INVALID_PARAM;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return BLE_ERROR_INVALID_PARAM;
    }

    if (cb->is_master) {
#if BLE_ROLE_CENTRAL
        if (flags->ltk_stored) {
            _db->get_entry_peer_keys(
                mbed::callback(this, &SecurityManager::enable_encryption_cb),
                cb->db_entry
            );
            return BLE_ERROR_NONE;
        } else {
            tr_info("Connection %d - Ltk not available, initiate pairing", connection);
            return requestPairing(connection);
        }
#else
        tr_error("Invalid state, cannot act as a master if central role disabled");
        return BLE_ERROR_NOT_IMPLEMENTED;
#endif // BLE_ROLE_CENTRAL
    } else {
#if BLE_ROLE_PERIPHERAL
        tr_info("Connection %d - Ltk not available, send slave security request", connection);
        return slave_security_request(connection);
#else
        tr_error("Invalid state, cannot act as a master if peripheral role disabled");
        return BLE_ERROR_NOT_IMPLEMENTED;
#endif // BLE_ROLE_PERIPHERAL
    }
}

#if BLE_ROLE_CENTRAL
void SecurityManager::enable_encryption_cb(
    SecurityDb::entry_handle_t db_entry,
    const SecurityEntryKeys_t* entryKeys
)
{
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(db_entry);
    if (!cb) {
        tr_error("Failure, control block %p not available", db_entry);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Cannot enable encryption, no distribution flags found for entry %p", cb->connection, cb->db_entry);
        return;
    }

    if (entryKeys) {
#if BLE_FEATURE_SECURE_CONNECTIONS
        if (flags->secure_connections_paired) {
            _pal.enable_encryption(cb->connection, entryKeys->ltk, flags->ltk_mitm_protected);
        } else
#endif
        {
            _pal.enable_encryption(cb->connection, entryKeys->ltk, entryKeys->rand, entryKeys->ediv, flags->ltk_mitm_protected);
        }
    }
}
#endif

void SecurityManager::set_ltk_cb(
    SecurityDb::entry_handle_t db_entry,
    const SecurityEntryKeys_t* entryKeys
)
{
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(db_entry);
    if (!cb) {
        tr_error("Failure, control block %p not available", db_entry);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Invalid state, cannot set remote ltk, no distribution flags found for entry %p",
            cb->connection, cb->db_entry);
        return;
    }

    if (entryKeys) {
        _pal.set_ltk(
            cb->connection,
            entryKeys->ltk,
            flags->ltk_mitm_protected,
            flags->secure_connections_paired
        );
    } else {
        _pal.set_ltk_not_found(cb->connection);
    }
}

#if BLE_FEATURE_SIGNING
void SecurityManager::set_peer_csrk_cb(
    SecurityDb::entry_handle_t db_entry,
    const SecurityEntrySigning_t* signing
)
{
    ControlBlock_t *cb = get_control_block(db_entry);
    if (!cb) {
        tr_error("Failure, control block not available for DB entry %p", db_entry);
        return;
    }

    if (!signing) {
        tr_error("Connection %d - No peer csrk found for DB entry %p", cb->connection, db_entry);
        return;
    }


    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return;
    }

    _pal.set_peer_csrk(
        cb->connection,
        signing->csrk,
        flags->csrk_mitm_protected,
        signing->counter
    );
}


void SecurityManager::return_csrk_cb(
    SecurityDb::entry_handle_t db_entry,
    const SecurityEntrySigning_t *signing
)
{
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(db_entry);
    if (!cb) {
        tr_error("Cannot retrieve signing key for db_entry %p", db_entry);
        return;
    }

    if (!signing) {
        tr_error("Connection %d - No signing key retrieve for DB entry %p", cb->connection, db_entry);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Signing key invalid for DB entry %p, flags not available", cb->connection, db_entry);
        return;
    }

    tr_info("Connection %d - Signing key %s, authenticated: %s retrieved",
        cb->connection, to_string(signing->csrk), to_string(flags->csrk_mitm_protected));
    if (eventHandler) {
        eventHandler->signingKey(
            cb->connection,
            &signing->csrk,
            flags->csrk_mitm_protected
        );
    }
}
#endif // BLE_FEATURE_SIGNING

void SecurityManager::update_oob_presence(connection_handle_t connection)
{
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure while updating OOB presence, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Failure while updating OOB presence, no distribution flags set", connection);
        return;
    }

    /* if we support secure connection we only care about secure connections oob data */
    if (_default_authentication.get_secure_connections()) {
        cb->oob_present = (flags->peer_address == _oob_peer_address);
    } else {
        /* otherwise for legacy pairing we first set the oob based on set preference */
        cb->oob_present = cb->attempt_oob;

        /* and also turn it on if we have oob data for legacy pairing */
        if (flags->peer_address == _oob_temporary_key_creator_address
            || cb->local_address == _oob_temporary_key_creator_address) {
            cb->oob_present = true;
        }
    }
    tr_info("Connection %d - Update OOB presence: %s", connection, to_string(cb->oob_present));
}


void SecurityManager::set_mitm_performed(connection_handle_t connection, bool enable)
{
    ControlBlock_t *cb = get_control_block(connection);
    if (cb) {
        cb->mitm_performed = enable;
        /* whenever we reset mitm performed we also reset pending requests
         * as this happens whenever a new pairing attempt happens */
        if (!enable) {
            cb->legacy_pairing_oob_request_pending = false;
        }
        tr_info("Connection %d - MITM performed: %s", connection, to_string(enable));
    } else {
        tr_error("Connection %d - Failed to apply mitm performed, CB not available", connection);
    }
}


void SecurityManager::on_connected(
    connection_handle_t connection,
    connection_role_t role,
    peer_address_type_t peer_address_type,
    address_t peer_address,
    own_address_type_t local_address_type,
    address_t local_address
)
{
#if BLE_FEATURE_SECURITY
    MBED_ASSERT(_db);
    ControlBlock_t *cb = acquire_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failed to acquire a control block for the new connection", connection);
        return;
    }

    // setup the control block
    cb->local_address = local_address;
    cb->is_master = (role == connection_role_t::CENTRAL);

    // get the associated DB handle and the distribution flags if any
    cb->db_entry = _db->open_entry(peer_address_type, peer_address);

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);

    flags->peer_address = peer_address;
    flags->peer_address_is_public =
        (peer_address_type == peer_address_type_t::PUBLIC) ||
        (peer_address_type == peer_address_type_t::PUBLIC_IDENTITY);

    tr_info("Connection %d - Open: ltk=%s, csrk=%s, irk=%s",
        connection, to_string(flags->ltk_stored), to_string(flags->csrk_stored), to_string(flags->irk_stored));

#if BLE_FEATURE_SIGNING
    const bool signing = cb->signing_override_default ?
	                         cb->signing_requested :
	                         _default_key_distribution.get_signing();

    if (signing && flags->csrk_stored) {
        _db->get_entry_peer_csrk(
            mbed::callback(this, &SecurityManager::set_peer_csrk_cb),
            cb->db_entry
        );
    }
#endif // BLE_FEATURE_SIGNING
#endif // BLE_FEATURE_SECURITY
}


void SecurityManager::on_disconnected(
    connection_handle_t connection,
    disconnection_reason_t reason
)
{
    tr_info("Connection %d - Process disconnection", connection);
#if BLE_FEATURE_SECURITY
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Error, control block not available", connection);
        return;
    }
#if BLE_FEATURE_SIGNING
    _pal.remove_peer_csrk(connection);
#endif

    _db->close_entry(cb->db_entry);
    release_control_block(cb);
#endif // BLE_FEATURE_SECURITY
}


void SecurityManager::on_security_entry_retrieved(
    SecurityDb::entry_handle_t entry,
    const SecurityEntryIdentity_t* identity
)
{
    if (!identity) {
        tr_info("No identity retrieve for entry %p", entry);
        return;
    }

    tr_info("Security identity retrieved for entry %p: address: %s, public: %s, irk: %s",
        entry, to_string(identity->identity_address),
        to_string(identity->identity_address_is_public), to_string(identity->irk));

    typedef advertising_peer_address_type_t address_type_t;
#if BLE_FEATURE_PRIVACY
    _private_address_controller.add_device_to_resolving_list(
        identity->identity_address_is_public ?
            address_type_t::PUBLIC :
            address_type_t::RANDOM,
        identity->identity_address,
        identity->irk
    );
#endif // BLE_FEATURE_PRIVACY
}


void SecurityManager::on_identity_list_retrieved(
    Span<SecurityEntryIdentity_t>& identity_list,
    size_t count
)
{
    tr_info("Identity list retrieved: %d entries to add to the resolving list", count);
    typedef advertising_peer_address_type_t address_type_t;

#if BLE_FEATURE_PRIVACY
    _private_address_controller.clear_resolving_list();
    for (size_t i = 0; i < count; ++i) {
        _private_address_controller.add_device_to_resolving_list(
            identity_list[i].identity_address_is_public ?
                address_type_t::PUBLIC :
                address_type_t::RANDOM,
            identity_list[i].identity_address,
            identity_list[i].irk
        );
    }
#endif // BLE_FEATURE_PRIVACY

    delete [] identity_list.data();
}

/* Implements ble::PalSecurityManagerEventHandler */

////////////////////////////////////////////////////////////////////////////
// Pairing
//

#if BLE_ROLE_PERIPHERAL
void SecurityManager::on_pairing_request(
    connection_handle_t connection,
    bool use_oob,
    AuthenticationMask authentication,
    KeyDistribution initiator_dist,
    KeyDistribution responder_dist
)
{
    tr_info("Connection %d - pairing request received: use_oob = %s, authentication = %02X, initiator_dist = %02X, responder_dist = %02X",
        connection, to_string(use_oob), authentication.value(), initiator_dist.value(), responder_dist.value()
    );

    /* cancel pairing if secure connection paring is not possible */
    if (!_legacy_pairing_allowed && !authentication.get_secure_connections()) {
        tr_info("Connection %d - Cancel pairing request, peer does not meet SC requirements", connection);
        cancelPairingRequest(connection);
        return;
    }

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    cb->set_initiator_key_distribution(initiator_dist);
    cb->set_responder_key_distribution(responder_dist);

    cb->mitm_performed = false;

    if (_pairing_authorisation_required) {
        if (eventHandler) {
            tr_info("Connection %d - Forwarding pairing request to user handler", connection);
            eventHandler->pairingRequest(connection);
        } else {
            tr_error("Connection %d - Cannot process pairing request: Require user validation but no handler has been registered.", connection);
        }
    } else {
        acceptPairingRequest(connection);
    }
}
#endif // BLE_ROLE_PERIPHERAL

void SecurityManager::on_pairing_error(
    connection_handle_t connection,
    pairing_failure_t error
)
{
    tr_info("Connection %d - Pairing error %s received", connection, to_string(error.value()));
    set_mitm_performed(connection, false);

    if (eventHandler) {
        eventHandler->pairingResult(
            connection,
            (SecurityCompletionStatus_t)(error.value() | 0x80)
        );
    }

    /* if this pairing was triggered by a failed encryption attempt
     * inform the application of the encryption failure */
    ControlBlock_t *cb = get_control_block(connection);
    if (cb && cb->encryption_requested && cb->encryption_failed && eventHandler) {
        tr_info("Connection %d - Report encryption failure to app handler", connection);
        eventHandler->linkEncryptionResult(
            connection,
            link_encryption_t::NOT_ENCRYPTED
        );
    }
}


void SecurityManager::on_pairing_timed_out(connection_handle_t connection)
{
    tr_info("Connection %d - Pairing timed out", connection);
    set_mitm_performed(connection, false);

    eventHandler->pairingResult(
        connection,
        SEC_STATUS_TIMEOUT
    );
}


void SecurityManager::on_pairing_completed(connection_handle_t connection)
{
    tr_info("Connection %d - Pairing successfully completed", connection);
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (cb) {
        tr_info("Connection %d - Retrieve identity address to register it into resolving list", connection);
        _db->get_entry_identity(
            mbed::callback(this, &SecurityManager::on_security_entry_retrieved),
            cb->db_entry
        );
    }

    eventHandler->pairingResult(
        connection,
        SEC_STATUS_SUCCESS
    );
}

////////////////////////////////////////////////////////////////////////////
// Security
//


void SecurityManager::on_valid_mic_timeout(connection_handle_t connection)
{
    tr_info("Connection %d - Received authentication timeout", connection);
    (void)connection;
}

#if BLE_FEATURE_SIGNING
void SecurityManager::on_signed_write_received(
    connection_handle_t connection,
    sign_count_t sign_counter
)
{
    MBED_ASSERT(_db);
    tr_info("Connection %d - Signed write received: write counter = %" PRIu32, connection, sign_counter);

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        return;
    }
    _db->set_entry_peer_sign_counter(cb->db_entry, sign_counter);
}


void SecurityManager::on_signed_write_verification_failure(
    connection_handle_t connection
)
{
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    tr_info("Connection %d - Received signed write verification failure", connection);

    const bool signing = cb->signing_override_default ?
	                         cb->signing_requested :
	                         _default_key_distribution.get_signing();

    if (signing) {
        cb->csrk_failures++;
        if (cb->csrk_failures == 3) {
            cb->csrk_failures = 0;
            if (cb->is_master) {
#if BLE_ROLE_CENTRAL
                tr_info("Connection %d - Signed write verification failure threshold reached, request pairing", connection);
                requestPairing(connection);
#endif
            } else {
#if BLE_ROLE_PERIPHERAL
                tr_info("Connection %d - Signed write verification failure threshold reached, send slave security request", connection);
                slave_security_request(connection);
#endif
            }
        }
    }
}


void SecurityManager::on_signed_write()
{
    MBED_ASSERT(_db);
    _db->set_local_sign_counter(_db->get_local_sign_counter() + 1);
    tr_info("Signed write sent, local sign counter: %" PRIu32, _db->get_local_sign_counter());
}
#endif // BLE_FEATURE_SIGNING

#if BLE_ROLE_CENTRAL
void SecurityManager::on_slave_security_request(
    connection_handle_t connection,
    AuthenticationMask authentication
)
{
    tr_info("Connection %d - Slave security request received: authentication = %02X",
        connection, authentication.value());
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return;
    }

    bool pairing_required = false;

    if (authentication.get_secure_connections() && !flags->secure_connections_paired
        && _default_authentication.get_secure_connections()) {
        pairing_required = true;
    }

    if (authentication.get_mitm() && !flags->ltk_mitm_protected) {
        pairing_required = true;
        cb->mitm_requested = true;
    }

    if (pairing_required) {
        tr_info("Connection %d - Initiate pairing to satisfy slave security request, mitm: %s", connection, to_string(cb->mitm_requested));
        requestPairing(connection);
    } else if (!cb->encryption_requested) {
        /* this will refresh keys if encryption is already present */
        tr_info("Connection %d - Enable encryption to satisfy slave security request", connection);
        enable_encryption(connection);
    } else {
        tr_info("Connection %d - Encryption pending, nothing to do to satisfy slave security request", connection);
    }
}
#endif // BLE_ROLE_CENTRAL

////////////////////////////////////////////////////////////////////////////
// Encryption
//


void SecurityManager::on_link_encryption_result(
    connection_handle_t connection,
    link_encryption_t result
)
{
    tr_info("Connection %d - Link encryption: updated = %s", connection, to_string(result));

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    if (result == link_encryption_t::ENCRYPTED) {

        cb->encryption_requested = false;
        cb->encryption_failed = false;
        cb->encrypted = true;

    } else if (
        result == link_encryption_t::ENCRYPTED_WITH_MITM ||
        result == link_encryption_t::ENCRYPTED_WITH_SC_AND_MITM
    ) {

        cb->encryption_requested = false;
        cb->encryption_failed = false;
        cb->authenticated = true;
        cb->encrypted = true;

    }
#if BLE_ROLE_CENTRAL
    else if (result == link_encryption_t::NOT_ENCRYPTED
               && cb->encryption_requested
               && !cb->encryption_failed) {

        /* if we failed encryption for the first time
         * retry repairing in case slave lost LTK */
        tr_info("Connection %d - Encryption failed, attempt to pair again, the slave may have lost the shared LTK", connection);
        requestPairing(cb->connection);
        cb->encryption_failed = true;
        /* don't return an event yet since we are retrying */
        return;
    }
#endif // BLE_ROLE_CENTRAL

    eventHandler->linkEncryptionResult(connection, result);
}


void SecurityManager::on_link_encryption_request_timed_out(
    connection_handle_t connection
)
{
    tr_info("Connection %d - Link encryption timed out", connection);
    eventHandler->linkEncryptionResult(
        connection,
        link_encryption_t::NOT_ENCRYPTED
    );
}

////////////////////////////////////////////////////////////////////////////
// MITM
//


void SecurityManager::on_passkey_display(
    connection_handle_t connection,
    passkey_num_t passkey
)
{
    set_mitm_performed(connection);
    PasskeyAscii ascii_passkey(passkey);
    tr_info("Connection %d - Received passkey display %.*s",
        connection, ascii_passkey.PASSKEY_LEN, ascii_passkey.value());
    if (eventHandler) {
        eventHandler->passkeyDisplay(connection, ascii_passkey.value());
    } else {
        tr_warning("Connection %d - No app handler to display pass key", connection);
    }

}

void SecurityManager::on_passkey_request(connection_handle_t connection)
{
    tr_info("Connection %d - Received passkey request event", connection);
    set_mitm_performed(connection);
    if (eventHandler) {
        eventHandler->passkeyRequest(connection);
    } else {
        tr_warning("Connection %d - No app handler to forward pass key request", connection);
    }

}

void SecurityManager::on_legacy_pairing_oob_request(connection_handle_t connection)
{
    tr_info("Connection %d - Received OOB request for legacy pairing", connection);
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return;
    }

    if (flags->peer_address == _oob_temporary_key_creator_address
        || cb->local_address == _oob_temporary_key_creator_address) {
        tr_info("Connection %d - Use OOB temporary key stored locally", connection);
        set_mitm_performed(connection);
        _pal.legacy_pairing_oob_request_reply(connection, _oob_temporary_key);

        /* do not re-use peer OOB */
        if (flags->peer_address == _oob_temporary_key_creator_address) {
            set_all_zeros(_oob_temporary_key_creator_address);
        }

    } else if (!cb->legacy_pairing_oob_request_pending) {

        cb->legacy_pairing_oob_request_pending = true;
        if (eventHandler) {
            tr_info("Connection %d - Send OOB request to application", connection);
            eventHandler->legacyPairingOobRequest(connection);
        } else {
            tr_error("Connection %d - OOB requested but no application event handler available", connection);
        }
    }
}

#if BLE_FEATURE_SECURE_CONNECTIONS
void SecurityManager::on_keypress_notification(
    connection_handle_t connection,
    ble::Keypress_t keypress
)
{
    tr_info("Connection %d - Keypress notification %s received", connection, to_string(keypress));
    set_mitm_performed(connection);
    if (eventHandler) {
        eventHandler->keypressNotification(connection, keypress);
    } else {
        tr_error("Impossible to forward Keypress notification to application, no event handler registered");
    }
}

void SecurityManager::on_confirmation_request(connection_handle_t connection)
{
    tr_info("Connection %d - Confirmation request received", connection);
    set_mitm_performed(connection);
    if (eventHandler) {
        eventHandler->confirmationRequest(connection);
    } else {
        tr_error("Impossible to forward confirmation request to application, no event handler registered");
    }
}

void SecurityManager::on_secure_connections_oob_request(connection_handle_t connection)
{
    tr_info("Connection %d - Received SC OOB request", connection);
    set_mitm_performed(connection);

    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return;
    }

    if (flags->peer_address == _oob_peer_address) {
        _pal.secure_connections_oob_request_reply(connection, _oob_local_random, _oob_peer_random, _oob_peer_confirm);
        /* do not re-use peer OOB */
        set_all_zeros(_oob_peer_address);
    } else {
        _pal.cancel_pairing(connection, pairing_failure_t::OOB_NOT_AVAILABLE);
    }
}

void SecurityManager::on_secure_connections_oob_generated(
    const oob_lesc_value_t &random,
    const oob_confirm_t &confirm
)
{
    tr_info("SC OOB generated, random = %s, confirm = %s", to_string(random), to_string(confirm));
    eventHandler->oobGenerated(&_oob_local_address, &random, &confirm);
    _oob_local_random = random;
}
#endif // BLE_FEATURE_SECURE_CONNECTIONS

////////////////////////////////////////////////////////////////////////////
// Keys
//

#if BLE_FEATURE_SECURE_CONNECTIONS
void SecurityManager::on_secure_connections_ltk_generated(
    connection_handle_t connection,
    const ltk_t &ltk
)
{
    tr_info("Connection %d - SC LTK generated: %s", connection, to_string(ltk));
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return;
    }

    flags->ltk_mitm_protected = cb->mitm_performed;
    flags->secure_connections_paired = true;

    _db->set_entry_peer_ltk(cb->db_entry, ltk);
    _db->set_entry_local_ltk(cb->db_entry, ltk);
}
#endif // BLE_FEATURE_SECURE_CONNECTIONS


void SecurityManager::on_keys_distributed_ltk(
    connection_handle_t connection,
    const ltk_t &ltk
)
{
    tr_info("Connection %d - LTK distributed: %s", connection, to_string(ltk));
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return;
    }

    flags->ltk_mitm_protected = cb->mitm_performed;

    _db->set_entry_peer_ltk(cb->db_entry, ltk);
}


void SecurityManager::on_keys_distributed_ediv_rand(
    connection_handle_t connection,
    const ediv_t &ediv,
    const rand_t &rand
)
{
    tr_info("Connection %d - EDIV and RAND distributed: ediv = %s, rand = %s", connection, to_string(ediv), to_string(rand));
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    _db->set_entry_peer_ediv_rand(cb->db_entry, ediv, rand);
}


void SecurityManager::on_keys_distributed_local_ltk(
    connection_handle_t connection,
    const ltk_t &ltk
)
{
    tr_info("Connection %d - Local LTK distributed: %s", connection, to_string(ltk));
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Failure, no distribution flags set");
        return;
    }

    _db->set_entry_local_ltk(cb->db_entry, ltk);
}


void SecurityManager::on_keys_distributed_local_ediv_rand(
    connection_handle_t connection,
    const ediv_t &ediv,
    const rand_t &rand
)
{
    tr_info("Connection %d - Local EDIV/RAND distributed: EDIV = %s, RAND = %s", connection, to_string(ediv), to_string(rand));
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    _db->set_entry_local_ediv_rand(cb->db_entry, ediv, rand);
}


void SecurityManager::on_keys_distributed_irk(
    connection_handle_t connection,
    const irk_t &irk
)
{
    tr_info("Connection %d - IRK distributed: %s", connection, to_string(irk));
    
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Failure, no distribution flags set", connection);
        return;
    }

    _db->set_entry_peer_irk(cb->db_entry, irk);
}


void SecurityManager::on_keys_distributed_bdaddr(
    connection_handle_t connection,
    advertising_peer_address_type_t peer_address_type,
    const address_t &peer_identity_address
)
{
    tr_info("Connection %d - Peer identity distributed: address = %s, public = %s",
        connection, to_string(peer_identity_address), to_string(peer_address_type == advertising_peer_address_type_t::PUBLIC));

    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    _db->set_entry_peer_bdaddr(
        cb->db_entry,
        (peer_address_type == advertising_peer_address_type_t::PUBLIC),
        peer_identity_address
    );
}

#if BLE_FEATURE_SIGNING
void SecurityManager::on_keys_distributed_csrk(
    connection_handle_t connection,
    const csrk_t &csrk
)
{
    tr_info("Connection %d - CSRK distributed: %s", connection, to_string(csrk));
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Failure, no distribution flags set", connection);
        return;
    }

    flags->csrk_mitm_protected = cb->mitm_performed;
    _db->set_entry_peer_csrk(cb->db_entry, csrk);

    eventHandler->signingKey(
        connection,
        &csrk,
        flags->csrk_mitm_protected
    );
}
#endif // BLE_FEATURE_SIGNING


void SecurityManager::on_ltk_request(
    connection_handle_t connection,
    const ediv_t &ediv,
    const rand_t &rand
)
{
    tr_info("Connection %d - LTK requested: EDIV = %s, RAND = %s", connection, to_string(ediv), to_string(rand));
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
    if (!flags) {
        tr_error("Connection %d - Failure, no distribution flags set", connection);
        return;
    }

    _db->get_entry_local_keys(
        mbed::callback(this, &SecurityManager::set_ltk_cb),
        &cb->db_entry,
        ediv,
        rand
    );
}

/* control blocks list management */


SecurityManager::ControlBlock_t::ControlBlock_t() :
    connection(0),
    db_entry(nullptr),
    local_address(),
    connected(false),
    authenticated(false),
    is_master(false),
    encryption_requested(false),
    encryption_failed(false),
    encrypted(false),
    signing_requested(false),
    signing_override_default(false),
    mitm_requested(false),
    mitm_performed(false),
    attempt_oob(false),
    oob_mitm_protection(false),
    oob_present(false),
    legacy_pairing_oob_request_pending(false),
    csrk_failures(0) { }


void SecurityManager::on_ltk_request(connection_handle_t connection)
{
    tr_info("Connection %d - SC LTK requested", connection);
    MBED_ASSERT(_db);
    ControlBlock_t *cb = get_control_block(connection);
    if (!cb) {
        tr_error("Connection %d - Failure, control block not available", connection);
        return;
    }

    _db->get_entry_local_keys(
        mbed::callback(this, &SecurityManager::set_ltk_cb),
        cb->db_entry
    );
}


typename SecurityManager::ControlBlock_t*
SecurityManager::acquire_control_block(connection_handle_t connection)
{
    /* grab the first disconnected slot*/
    for (auto & control_block : _control_blocks) {
        if (!control_block.connected) {
            ControlBlock_t* cb = &control_block;
            cb->connected = true;
            cb->connection = connection;
            return cb;
        }
    }

    return nullptr;
}


typename SecurityManager::ControlBlock_t*
SecurityManager::get_control_block(
    connection_handle_t connection
)
{
    for (auto & cb : _control_blocks) {
        if (!cb.connected) {
            continue;
        } else if (connection == cb.connection) {
            return &cb;
        }
    }
    return nullptr;
}


typename SecurityManager::ControlBlock_t*
SecurityManager::get_control_block(
    const address_t &peer_address
)
{
    MBED_ASSERT(_db);
    for (auto & control_block : _control_blocks) {
        ControlBlock_t *cb = &control_block;
        if (cb->connected) {
            SecurityDistributionFlags_t* flags = _db->get_distribution_flags(cb->db_entry);
            if (flags && (flags->peer_address == peer_address)) {
                return cb;
            }
        }
    }
    return nullptr;
}


typename SecurityManager::ControlBlock_t*
SecurityManager::get_control_block(
    SecurityDb::entry_handle_t db_entry
)
{
    for (auto & cb : _control_blocks) {
        if (!cb.connected) {
            continue;
        } else if (db_entry == cb.db_entry) {
            return &cb;
        }
    }
    return nullptr;
}


void SecurityManager::release_control_block(ControlBlock_t* cb)
{
    *cb = ControlBlock_t();
}

void SecurityManager::onShutdown(const SecurityManagerShutdownCallback_t& callback)
{
    shutdownCallChain.add(callback);
}

template <typename T>
void SecurityManager::onShutdown(T *objPtr, void (T::*memberPtr)(const SecurityManager *))
{
    shutdownCallChain.add(objPtr, memberPtr);
}

SecurityManager::SecurityManagerShutdownCallbackChain_t& SecurityManager::onShutdown()
{
    return shutdownCallChain;
}

void SecurityManager::setSecurityManagerEventHandler(EventHandler* handler)
{
    if (handler) {
        eventHandler = handler;
    } else {
        eventHandler = &defaultEventHandler;
    }
}

} /* namespace impl */
} /* namespace ble */

#endif // BLE_FEATURE_SECURITY