<commit_before>/* mbed Microcontroller Library
 * Copyright (c) 2017-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 __GENERIC_SECURITY_MANAGER_H__
#define __GENERIC_SECURITY_MANAGER_H__

#include "SecurityManager.h"
#include "PalSecurityManager.h"
#include "Callback.h"
#include "ble/pal/GapTypes.h"
#include "ble/BLETypes.h"

namespace ble {
namespace generic {

using ble::pal::address_t;
using ble::pal::advertising_peer_address_type_t;
using ble::pal::irk_t;
using ble::pal::csrk_t;
using ble::pal::ltk_t;
using ble::pal::ediv_t;
using ble::pal::rand_t;
using ble::pal::AuthenticationMask;
using ble::pal::KeyDistribution;
using ble::pairing_failure_t;
using ble::pal::PasskeyAsci;
using ble::pal::passkey_num_t;
typedef SecurityManager::SecurityIOCapabilities_t SecurityIOCapabilities_t;

/* separate structs to allow db implementation to minimise memory usage */

struct SecurityEntry_t {
    connection_handle_t handle;
    address_t peer_identity_address;
    uint8_t encryption_key_size;
    uint8_t peer_address_public:1;
    uint8_t mitm_performed:1;
    uint8_t mitm:1; /**< does the key provide mitm */
    uint8_t connected:1;
    uint8_t authenticated:1; /**< have we authenticated during this connection */
    uint8_t sign_data:1;
    uint8_t encryption_requested:1;
    uint8_t encrypted:1;
    uint8_t oob:1;
    uint8_t oob_mitm_protection:1;
    uint8_t secure_connections:1;
    uint8_t signing_key:1;
    uint8_t encryption_key:1;
};

struct SecurityEntryKeys_t {
    ltk_t  ltk;
    ediv_t ediv;
    rand_t rand;
};

struct SecurityEntryIdentity_t {
    irk_t  irk;
    csrk_t csrk;
};

enum DbCbAction_t {
    DB_CB_ACTION_UPDATE,
    DB_CB_ACTION_NO_UPDATE_REQUIRED, /* does not guarantee discarding changes if you made any */
    DB_CB_ACTION_REMOVE
};

typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&)> SecurityEntryDbCb_t;
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&, SecurityEntryKeys_t&)> SecurityEntryKeysDbCb_t;
typedef mbed::Callback<DbCbAction_t(connection_handle_t, csrk_t)> SecurityEntryCsrkDbCb_t;
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&, SecurityEntryIdentity_t&)> SecurityEntryIdentityDbCb_t;
typedef mbed::Callback<DbCbAction_t(Gap::Whitelist_t&)> WhitelistDbCb_t;

class GenericSecurityManagerEventHandler;

/**
 * SecurityDB holds the state for active connections and bonded devices.
 * Keys can be stored in NVM and are returned via callbacks.
 * SecurityDB is responsible for serialising any requests and keeping
 * the store in a consistent state.
 * Active connections state must be returned immediately.
 */
class SecurityDb {
public:
    SecurityDb() { };
    virtual ~SecurityDb() { };

    /**
     * Return immediately security entry containing the state
     * information for active connection.
     * @param[in] handle valid connection handle
     * @return pointer to security entry, NULL if handle was invalid
     */
    virtual SecurityEntry_t* get_entry(connection_handle_t connection);

    /* local keys */

    /* get */
    virtual void get_entry_local_keys(
        SecurityEntryKeysDbCb_t cb,
        connection_handle_t connection
    );
    /* set */
    virtual void set_entry_local_ltk(
        connection_handle_t connection,
        const ltk_t ltk
    );
    virtual void set_entry_local_ediv_rand(
        connection_handle_t connection,
        const ediv_t ediv,
        const rand_t rand
    );

    /* peer's keys */

    /* get */
    virtual void get_entry_csrk(
        SecurityEntryCsrkDbCb_t cb,
        connection_handle_t connection
    );
    virtual void get_entry_peer_keys(
        SecurityEntryKeysDbCb_t cb,
        const ediv_t ediv,
        const rand_t rand
    );
    virtual void get_entry_peer_keys(
        SecurityEntryKeysDbCb_t cb,
        connection_handle_t connection
    );
    /* set */
    virtual void set_entry_peer(
        connection_handle_t connection,
        bool address_is_public,
        const address_t &peer_address,
        const ediv_t ediv,
        const rand_t rand,
        const ltk_t ltk,
        const irk_t irk,
        const csrk_t csrk
    );
    virtual void set_entry_peer_ltk(
        connection_handle_t connection,
        const ltk_t ltk
    );
    virtual void set_entry_peer_ediv_rand(
        connection_handle_t connection,
        const ediv_t ediv,
        const rand_t rand
    );
    virtual void set_entry_peer_irk(
        connection_handle_t connection,
        const irk_t irk
    );
    virtual void set_entry_peer_bdaddr(
        connection_handle_t connection,
        bool address_is_public,
        const address_t &peer_address
    );
    virtual void set_entry_peer_csrk(
        connection_handle_t connection,
        const csrk_t csrk
    );

    /* local csrk */

    virtual const csrk_t& get_local_csrk();
    virtual void set_local_csrk(
        const csrk_t csrk
    );

    /* list management */

    virtual void remove_entry( address_t peer_identity_address);
    virtual void clear_entries();

    virtual void get_whitelist(WhitelistDbCb_t cb);

    virtual void update_whitelist(Gap::Whitelist_t& whitelist);
    virtual void add_whitelist_entry(const address_t &address);

    virtual void remove_whitelist_entry(const address_t &address);
    virtual void clear_whitelist();

    /* saving and loading from nvm */

    virtual void restore();
    virtual void sync();
    virtual void set_restore(bool reload);
};

class GenericSecurityManager : public SecurityManager,
                               public ble::pal::SecurityManagerEventHandler {
public:
    ////////////////////////////////////////////////////////////////////////////
    // SM lifecycle management
    //
    virtual ble_error_t init(
        bool bondable = true,
        bool mitm     = true,
        SecurityIOCapabilities_t iocaps = IO_CAPS_NONE,
        const Passkey_t passkey  = NULL,
        bool signing = true
    ) {
        db.restore();
        pal.set_io_capability((io_capability_t::type) iocaps);
        pal.set_display_passkey(PasskeyAsci::to_num(passkey));
        legacy_pairing_allowed = true;

        bool secure_connections;
        pal.get_secure_connections_support(secure_connections);

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

        key_distribution.set_signing(signing);

        return BLE_ERROR_NONE;
    }

    virtual ble_error_t reset(void) {
        db.sync();
        SecurityManager::reset();

        return BLE_ERROR_NONE;
    }

    virtual ble_error_t preserveBondingStateOnReset(bool enabled) {
        db.set_restore(enabled);
        return BLE_ERROR_NONE;
    }

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

    virtual ble_error_t purgeAllBondingState(void) {
        db.clear_entries();
        return BLE_ERROR_NONE;
    }

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

    virtual ble_error_t requestPairing(connection_handle_t connection) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (entry) {
            return pal.send_pairing_request(
                connection,
                entry->oob,
                authentication,
                key_distribution,
                key_distribution
            );
        } else {
            return BLE_ERROR_INVALID_PARAM;
        }
    }

    virtual ble_error_t acceptPairingRequest(connection_handle_t connection) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (entry) {
            return pal.send_pairing_response(
                connection,
                entry->oob,
                authentication,
                key_distribution,
                key_distribution
            );
        } else {
            return BLE_ERROR_INVALID_PARAM;
        }
    }

    virtual ble_error_t canceltPairingRequest(connection_handle_t connection) {
        return pal.cancel_pairing(connection, pairing_failure_t::UNSPECIFIED_REASON);
    }

    virtual ble_error_t setPairingRequestAuthorisation(bool required = true) {
        pairing_authorisation_required = required;
        return BLE_ERROR_NONE;
    }

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

    virtual ble_error_t allowLegacyPairing(bool allow = true) {
        legacy_pairing_allowed = allow;
        return BLE_ERROR_NONE;
    }

    virtual ble_error_t getSecureConnectionsSupport(bool *enabled) {
        return pal.get_secure_connections_support(*enabled);
    }

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

    virtual ble_error_t setIoCapability(SecurityIOCapabilities_t iocaps) {
        return pal.set_io_capability((io_capability_t::type) iocaps);
    }

    virtual ble_error_t setDisplayPasskey(const Passkey_t passkey) {
        return pal.set_display_passkey(PasskeyAsci::to_num(passkey));
    }

    virtual ble_error_t setAuthenticationTimeout(
        connection_handle_t connection,
        uint32_t timeout_in_ms
    ) {
        return pal.set_authentication_timeout(connection, timeout_in_ms / 10);
    }

    virtual ble_error_t getAuthenticationTimeout(
        connection_handle_t connection,
        uint32_t *timeout_in_ms
    ) {
        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;
    }

    virtual ble_error_t setLinkSecurity(
        connection_handle_t connection,
        SecurityMode_t securityMode
    ) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (!entry) {
            return BLE_ERROR_INVALID_PARAM;
        }
        if (entry->encryption_requested) {
            return BLE_ERROR_OPERATION_NOT_PERMITTED;
        }

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

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

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

            case SECURITY_MODE_SIGNED_NO_MITM:
                return getSigningKey(connection, false);
                break;

            case SECURITY_MODE_SIGNED_WITH_MITM:
                return getSigningKey(connection, true);
                break;

            default:
                return BLE_ERROR_INVALID_PARAM;
                break;
        }
    }

    virtual ble_error_t setKeypressNotification(bool enabled = true) {
        authentication.set_keypress_notification(enabled);
        return BLE_ERROR_NONE;
    }

    virtual ble_error_t enableSigning(
        connection_handle_t connection,
        bool enabled = true
    ) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (!entry) {
            return BLE_ERROR_INVALID_PARAM;
        }
        if (!entry->signing_key && enabled) {
            KeyDistribution distribution = key_distribution;
            distribution.set_signing(enabled);
            return pal.send_pairing_request(
                connection,
                entry->oob,
                authentication,
                distribution,
                distribution
            );
        }
        return BLE_ERROR_NONE;
    }

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

    /**
     * Get the security status of a connection.
     *
     * @param[in]  connection   Handle to identify the connection.
     * @param[out] securityStatusP    Security status.
     *
     * @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
     */
    virtual ble_error_t getLinkEncryption(
        connection_handle_t connection,
        link_encryption_t *encryption
    ) {

        SecurityEntry_t *entry = db.get_entry(connection);
        if (!entry) {
            return BLE_ERROR_INVALID_PARAM;
        }

        if (entry->encrypted) {
            if (entry->mitm) {
                *encryption = link_encryption_t::ENCRYPTED_WITH_MITM;
            } else {
                *encryption = link_encryption_t::ENCRYPTED;
            }
        } else if (entry->encryption_requested) {
            *encryption = link_encryption_t::ENCRYPTION_IN_PROGRESS;
        } else {
            *encryption = link_encryption_t::NOT_ENCRYPTED;
        }

        return BLE_ERROR_NONE;
    }

    virtual ble_error_t setLinkEncryption(
        connection_handle_t connection,
        link_encryption_t encryption
    ) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (!entry) {
            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) {
            return BLE_ERROR_OPERATION_NOT_PERMITTED;
        }

        /* ignore if the link is already at required state*/
        if (current_encryption == encryption) {
            return BLE_ERROR_NONE;
        }

        switch(encryption.value()) {
            case link_encryption_t::NOT_ENCRYPTED:
                if (entry->encrypted) {
                    return pal.disable_encryption(connection);
                }
                break;

            case link_encryption_t::ENCRYPTED:
                /* if already better than encrypted don't bother */
                if (current_encryption == link_encryption_t::ENCRYPTED_WITH_MITM) {
                    return BLE_ERROR_NONE;
                }
                entry->encryption_requested = true;
                return enable_encryption(connection);
                break;

            case link_encryption_t::ENCRYPTED_WITH_MITM:
                if (entry->mitm && !entry->encrypted) {
                    entry->encryption_requested = true;
                    return enable_encryption(connection);
                } else {
                    entry->encryption_requested = true;
                    return requestAuthentication(connection);
                }
                break;

            default:
                return BLE_ERROR_INVALID_PARAM;
        }<commit_msg>restructure cases to avoid warnings<commit_after>/* mbed Microcontroller Library
 * Copyright (c) 2017-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 __GENERIC_SECURITY_MANAGER_H__
#define __GENERIC_SECURITY_MANAGER_H__

#include "SecurityManager.h"
#include "PalSecurityManager.h"
#include "Callback.h"
#include "ble/pal/GapTypes.h"
#include "ble/BLETypes.h"

namespace ble {
namespace generic {

using ble::pal::address_t;
using ble::pal::advertising_peer_address_type_t;
using ble::pal::irk_t;
using ble::pal::csrk_t;
using ble::pal::ltk_t;
using ble::pal::ediv_t;
using ble::pal::rand_t;
using ble::pal::AuthenticationMask;
using ble::pal::KeyDistribution;
using ble::pairing_failure_t;
using ble::pal::PasskeyAsci;
using ble::pal::passkey_num_t;
typedef SecurityManager::SecurityIOCapabilities_t SecurityIOCapabilities_t;

/* separate structs to allow db implementation to minimise memory usage */

struct SecurityEntry_t {
    connection_handle_t handle;
    address_t peer_identity_address;
    uint8_t encryption_key_size;
    uint8_t peer_address_public:1;
    uint8_t mitm_performed:1;
    uint8_t mitm:1; /**< does the key provide mitm */
    uint8_t connected:1;
    uint8_t authenticated:1; /**< have we authenticated during this connection */
    uint8_t sign_data:1;
    uint8_t encryption_requested:1;
    uint8_t encrypted:1;
    uint8_t oob:1;
    uint8_t oob_mitm_protection:1;
    uint8_t secure_connections:1;
    uint8_t signing_key:1;
    uint8_t encryption_key:1;
};

struct SecurityEntryKeys_t {
    ltk_t  ltk;
    ediv_t ediv;
    rand_t rand;
};

struct SecurityEntryIdentity_t {
    irk_t  irk;
    csrk_t csrk;
};

enum DbCbAction_t {
    DB_CB_ACTION_UPDATE,
    DB_CB_ACTION_NO_UPDATE_REQUIRED, /* does not guarantee discarding changes if you made any */
    DB_CB_ACTION_REMOVE
};

typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&)> SecurityEntryDbCb_t;
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&, SecurityEntryKeys_t&)> SecurityEntryKeysDbCb_t;
typedef mbed::Callback<DbCbAction_t(connection_handle_t, csrk_t)> SecurityEntryCsrkDbCb_t;
typedef mbed::Callback<DbCbAction_t(SecurityEntry_t&, SecurityEntryIdentity_t&)> SecurityEntryIdentityDbCb_t;
typedef mbed::Callback<DbCbAction_t(Gap::Whitelist_t&)> WhitelistDbCb_t;

class GenericSecurityManagerEventHandler;

/**
 * SecurityDB holds the state for active connections and bonded devices.
 * Keys can be stored in NVM and are returned via callbacks.
 * SecurityDB is responsible for serialising any requests and keeping
 * the store in a consistent state.
 * Active connections state must be returned immediately.
 */
class SecurityDb {
public:
    SecurityDb() { };
    virtual ~SecurityDb() { };

    /**
     * Return immediately security entry containing the state
     * information for active connection.
     * @param[in] handle valid connection handle
     * @return pointer to security entry, NULL if handle was invalid
     */
    virtual SecurityEntry_t* get_entry(connection_handle_t connection);

    /* local keys */

    /* get */
    virtual void get_entry_local_keys(
        SecurityEntryKeysDbCb_t cb,
        connection_handle_t connection
    );
    /* set */
    virtual void set_entry_local_ltk(
        connection_handle_t connection,
        const ltk_t ltk
    );
    virtual void set_entry_local_ediv_rand(
        connection_handle_t connection,
        const ediv_t ediv,
        const rand_t rand
    );

    /* peer's keys */

    /* get */
    virtual void get_entry_csrk(
        SecurityEntryCsrkDbCb_t cb,
        connection_handle_t connection
    );
    virtual void get_entry_peer_keys(
        SecurityEntryKeysDbCb_t cb,
        const ediv_t ediv,
        const rand_t rand
    );
    virtual void get_entry_peer_keys(
        SecurityEntryKeysDbCb_t cb,
        connection_handle_t connection
    );
    /* set */
    virtual void set_entry_peer(
        connection_handle_t connection,
        bool address_is_public,
        const address_t &peer_address,
        const ediv_t ediv,
        const rand_t rand,
        const ltk_t ltk,
        const irk_t irk,
        const csrk_t csrk
    );
    virtual void set_entry_peer_ltk(
        connection_handle_t connection,
        const ltk_t ltk
    );
    virtual void set_entry_peer_ediv_rand(
        connection_handle_t connection,
        const ediv_t ediv,
        const rand_t rand
    );
    virtual void set_entry_peer_irk(
        connection_handle_t connection,
        const irk_t irk
    );
    virtual void set_entry_peer_bdaddr(
        connection_handle_t connection,
        bool address_is_public,
        const address_t &peer_address
    );
    virtual void set_entry_peer_csrk(
        connection_handle_t connection,
        const csrk_t csrk
    );

    /* local csrk */

    virtual const csrk_t& get_local_csrk();
    virtual void set_local_csrk(
        const csrk_t csrk
    );

    /* list management */

    virtual void remove_entry( address_t peer_identity_address);
    virtual void clear_entries();

    virtual void get_whitelist(WhitelistDbCb_t cb);

    virtual void update_whitelist(Gap::Whitelist_t& whitelist);
    virtual void add_whitelist_entry(const address_t &address);

    virtual void remove_whitelist_entry(const address_t &address);
    virtual void clear_whitelist();

    /* saving and loading from nvm */

    virtual void restore();
    virtual void sync();
    virtual void set_restore(bool reload);
};

class GenericSecurityManager : public SecurityManager,
                               public ble::pal::SecurityManagerEventHandler {
public:
    ////////////////////////////////////////////////////////////////////////////
    // SM lifecycle management
    //
    virtual ble_error_t init(
        bool bondable = true,
        bool mitm     = true,
        SecurityIOCapabilities_t iocaps = IO_CAPS_NONE,
        const Passkey_t passkey  = NULL,
        bool signing = true
    ) {
        db.restore();
        pal.set_io_capability((io_capability_t::type) iocaps);
        pal.set_display_passkey(PasskeyAsci::to_num(passkey));
        legacy_pairing_allowed = true;

        bool secure_connections;
        pal.get_secure_connections_support(secure_connections);

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

        key_distribution.set_signing(signing);

        return BLE_ERROR_NONE;
    }

    virtual ble_error_t reset(void) {
        db.sync();
        SecurityManager::reset();

        return BLE_ERROR_NONE;
    }

    virtual ble_error_t preserveBondingStateOnReset(bool enabled) {
        db.set_restore(enabled);
        return BLE_ERROR_NONE;
    }

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

    virtual ble_error_t purgeAllBondingState(void) {
        db.clear_entries();
        return BLE_ERROR_NONE;
    }

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

    virtual ble_error_t requestPairing(connection_handle_t connection) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (entry) {
            return pal.send_pairing_request(
                connection,
                entry->oob,
                authentication,
                key_distribution,
                key_distribution
            );
        } else {
            return BLE_ERROR_INVALID_PARAM;
        }
    }

    virtual ble_error_t acceptPairingRequest(connection_handle_t connection) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (entry) {
            return pal.send_pairing_response(
                connection,
                entry->oob,
                authentication,
                key_distribution,
                key_distribution
            );
        } else {
            return BLE_ERROR_INVALID_PARAM;
        }
    }

    virtual ble_error_t canceltPairingRequest(connection_handle_t connection) {
        return pal.cancel_pairing(connection, pairing_failure_t::UNSPECIFIED_REASON);
    }

    virtual ble_error_t setPairingRequestAuthorisation(bool required = true) {
        pairing_authorisation_required = required;
        return BLE_ERROR_NONE;
    }

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

    virtual ble_error_t allowLegacyPairing(bool allow = true) {
        legacy_pairing_allowed = allow;
        return BLE_ERROR_NONE;
    }

    virtual ble_error_t getSecureConnectionsSupport(bool *enabled) {
        return pal.get_secure_connections_support(*enabled);
    }

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

    virtual ble_error_t setIoCapability(SecurityIOCapabilities_t iocaps) {
        return pal.set_io_capability((io_capability_t::type) iocaps);
    }

    virtual ble_error_t setDisplayPasskey(const Passkey_t passkey) {
        return pal.set_display_passkey(PasskeyAsci::to_num(passkey));
    }

    virtual ble_error_t setAuthenticationTimeout(
        connection_handle_t connection,
        uint32_t timeout_in_ms
    ) {
        return pal.set_authentication_timeout(connection, timeout_in_ms / 10);
    }

    virtual ble_error_t getAuthenticationTimeout(
        connection_handle_t connection,
        uint32_t *timeout_in_ms
    ) {
        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;
    }

    virtual ble_error_t setLinkSecurity(
        connection_handle_t connection,
        SecurityMode_t securityMode
    ) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (!entry) {
            return BLE_ERROR_INVALID_PARAM;
        }
        if (entry->encryption_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);

            case SECURITY_MODE_SIGNED_NO_MITM:
                return getSigningKey(connection, false);

            case SECURITY_MODE_SIGNED_WITH_MITM:
                return getSigningKey(connection, true);

            default:
                return BLE_ERROR_INVALID_PARAM;
        }
    }

    virtual ble_error_t setKeypressNotification(bool enabled = true) {
        authentication.set_keypress_notification(enabled);
        return BLE_ERROR_NONE;
    }

    virtual ble_error_t enableSigning(
        connection_handle_t connection,
        bool enabled = true
    ) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (!entry) {
            return BLE_ERROR_INVALID_PARAM;
        }
        if (!entry->signing_key && enabled) {
            KeyDistribution distribution = key_distribution;
            distribution.set_signing(enabled);
            return pal.send_pairing_request(
                connection,
                entry->oob,
                authentication,
                distribution,
                distribution
            );
        }
        return BLE_ERROR_NONE;
    }

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

    /**
     * Get the security status of a connection.
     *
     * @param[in]  connection   Handle to identify the connection.
     * @param[out] securityStatusP    Security status.
     *
     * @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
     */
    virtual ble_error_t getLinkEncryption(
        connection_handle_t connection,
        link_encryption_t *encryption
    ) {

        SecurityEntry_t *entry = db.get_entry(connection);
        if (!entry) {
            return BLE_ERROR_INVALID_PARAM;
        }

        if (entry->encrypted) {
            if (entry->mitm) {
                *encryption = link_encryption_t::ENCRYPTED_WITH_MITM;
            } else {
                *encryption = link_encryption_t::ENCRYPTED;
            }
        } else if (entry->encryption_requested) {
            *encryption = link_encryption_t::ENCRYPTION_IN_PROGRESS;
        } else {
            *encryption = link_encryption_t::NOT_ENCRYPTED;
        }

        return BLE_ERROR_NONE;
    }

    virtual ble_error_t setLinkEncryption(
        connection_handle_t connection,
        link_encryption_t encryption
    ) {
        SecurityEntry_t *entry = db.get_entry(connection);
        if (!entry) {
            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) {
            return BLE_ERROR_OPERATION_NOT_PERMITTED;
        }

        /* ignore if the link is already at required state*/
        if (current_encryption == encryption) {
            return BLE_ERROR_NONE;
        }

        if (encryption == link_encryption_t::NOT_ENCRYPTED) {

            if (entry->encrypted) {
                return pal.disable_encryption(connection);
            }

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

            /* if already better than encrypted don't bother */
            if (current_encryption == link_encryption_t::ENCRYPTED_WITH_MITM) {
                return BLE_ERROR_NONE;
            }
            entry->encryption_requested = true;
            return enable_encryption(connection);

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

            if (entry->mitm && !entry->encrypted) {
                entry->encryption_requested = true;
                return enable_encryption(connection);
            } else {
                entry->encryption_requested = true;
                return requestAuthentication(connection);
            }

        } else {
            return BLE_ERROR_INVALID_PARAM;
        }
{'fraction_non_alphanumeric': 0.09847434119278779, 'fraction_numerical': 0.0036325209695528696, 'mean_word_length': 2.2927041426552135, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1563388', 'n_tokens_mistral': 8913, 'n_tokens_neox': 8522, 'n_words': 1843}