Newer
Older
mbed-os / connectivity / FEATURE_BLE / source / generic / SecurityManagerImpl.h
/* 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.
 */

#ifndef IMPL_SECURITY_MANAGER_H_
#define IMPL_SECURITY_MANAGER_H_

#include <cstdint>

#include "platform/Callback.h"

#include "ble/Gap.h"
#include "ble/SecurityManager.h"

#include "ble/common/CallChainOfFunctionPointersWithContext.h"
#include "ble/common/BLETypes.h"
#include "ble/common/blecommon.h"
#include "ble/common/BLETypes.h"

#include "source/pal/GapTypes.h"
#include "source/pal/PalConnectionMonitor.h"
#include "source/pal/PalSigningMonitor.h"
#include "source/pal/PalSecurityManager.h"

#include "source/generic/SecurityDb.h"


namespace ble {
class PalGenericAccessService;

namespace impl {

class SecurityManager :
    public ble::PalSecurityManagerEventHandler,
    public ble::PalConnectionMonitorEventHandler,
    public ble::PalSigningMonitorEventHandler {
    friend class ble::PalConnectionMonitorEventHandler;

    friend PalGenericAccessService;
    friend PalSecurityManager;

    using SecurityIOCapabilities_t = ble::SecurityManager::SecurityIOCapabilities_t;
    using SecurityMode_t = ble::SecurityManager::SecurityMode_t;
    using SecurityManagerShutdownCallback_t = ble::SecurityManager::SecurityManagerShutdownCallback_t;
    using SecurityManagerShutdownCallbackChain_t = ble::SecurityManager::SecurityManagerShutdownCallbackChain_t;
    using EventHandler = ble::SecurityManager::EventHandler;
    using Passkey_t  = ble::SecurityManager::Passkey_t ;

    static auto constexpr IO_CAPS_NONE = ble::SecurityManager::IO_CAPS_NONE;

public:
    ////////////////////////////////////////////////////////////////////////////
    // SM lifecycle management
    //

    ble_error_t init(
        bool enableBonding = true,
        bool requireMITM = true,
        SecurityIOCapabilities_t iocaps = IO_CAPS_NONE,
        const Passkey_t passkey = nullptr,
        bool signing = true,
        const char *dbFilepath = nullptr
    );

    ble_error_t setDatabaseFilepath(const char *dbFilepath = nullptr);

    ble_error_t reset(ble::SecurityManager* sm);

    ble_error_t preserveBondingStateOnReset(bool enable);

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

    ble_error_t purgeAllBondingState();

    ble_error_t generateWhitelistFromBondTable(::ble::whitelist_t *whitelist) const;

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

    ble_error_t requestPairing(ble::connection_handle_t connectionHandle);

    ble_error_t acceptPairingRequest(ble::connection_handle_t connectionHandle);

    ble_error_t cancelPairingRequest(ble::connection_handle_t connectionHandle);

    ble_error_t setPairingRequestAuthorisation(bool required = true);

    ble_error_t getPeerIdentity(ble::connection_handle_t connectionHandle);

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

    ble_error_t allowLegacyPairing(bool allow = true);

    ble_error_t getSecureConnectionsSupport(bool *enabled);

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

    ble_error_t setIoCapability(SecurityIOCapabilities_t iocaps);

    ble_error_t setDisplayPasskey(const Passkey_t passkey);

    ble_error_t setLinkSecurity(ble::connection_handle_t connectionHandle, SecurityMode_t securityMode);

    ble_error_t setKeypressNotification(bool enabled = true);

#if BLE_FEATURE_SIGNING

    ble_error_t enableSigning(ble::connection_handle_t connectionHandle, bool enabled = true);

#endif // BLE_FEATURE_SIGNING

    ble_error_t setHintFutureRoleReversal(bool enable = true);

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

    ble_error_t getLinkEncryption(ble::connection_handle_t connectionHandle, ble::link_encryption_t *encryption);

    ble_error_t setLinkEncryption(ble::connection_handle_t connectionHandle, ble::link_encryption_t encryption);

    ble_error_t setEncryptionKeyRequirements(uint8_t minimumByteSize, uint8_t maximumByteSize);

    ble_error_t getEncryptionKeySize(
        connection_handle_t connectionHandle,
        uint8_t *size
    );

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

    ble_error_t requestAuthentication(ble::connection_handle_t connectionHandle);

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

    ble_error_t generateOOB(const ble::address_t *address);

    ble_error_t setOOBDataUsage(ble::connection_handle_t connectionHandle, bool useOOB, bool OOBProvidesMITM = true);

    ble_error_t confirmationEntered(ble::connection_handle_t connectionHandle, bool confirmation);

    ble_error_t passkeyEntered(ble::connection_handle_t connectionHandle, Passkey_t passkey);

    ble_error_t sendKeypressNotification(ble::connection_handle_t connectionHandle, ble::Keypress_t keypress);

    ble_error_t legacyPairingOobReceived(const ble::address_t *address, const ble::oob_tk_t *tk);

    ble_error_t oobReceived(
        const ble::address_t *address,
        const ble::oob_lesc_value_t *random,
        const ble::oob_confirm_t *confirm
    );

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

    ble_error_t getSigningKey(ble::connection_handle_t connectionHandle, bool authenticated);

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

    ble_error_t setPrivateAddressTimeout(
        uint16_t timeout_in_seconds
    );

    /* Event callback handlers. */
public:

    void onShutdown(const SecurityManagerShutdownCallback_t &callback);

    template<typename T>
    void onShutdown(T *objPtr, void (T::*memberPtr)(const SecurityManager *));

    SecurityManagerShutdownCallbackChain_t &onShutdown();

    void setSecurityManagerEventHandler(EventHandler *handler);

    /* ===================================================================== */
    /*                    private implementation follows                     */

private:
    /**
     * Set the time after which an event will be generated unless we received a packet with
     * a valid MIC.
     *
     * @param[in] connectionHandle Handle to identify the connection.
     * @param[in] timeout_in_ms Timeout to set.
     *
     * @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
     */
    ble_error_t setAuthenticationTimeout(
        connection_handle_t connection,
        uint32_t timeout_in_ms
    );

    /**
     * Get the time after which an event will be generated unless we received a packet with
     * a valid MIC.
     *
     * @param[in] connectionHandle Handle to identify the connection.
     * @param[in] timeout_in_ms Returns the timeout.
     *
     * @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
     */
    ble_error_t getAuthenticationTimeout(
        connection_handle_t connection,
        uint32_t *timeout_in_ms
    );

    /* implements PalSecurityManager::EventHandler */
private:
    ////////////////////////////////////////////////////////////////////////////
    // Pairing
    //

    /** @copydoc PalSecurityManager::on_pairing_request
     */
    void on_pairing_request(
        connection_handle_t connection,
        bool use_oob,
        AuthenticationMask authentication,
        KeyDistribution initiator_dist,
        KeyDistribution responder_dist
    ) override;

    /** @copydoc PalSecurityManager::on_pairing_error
     */
    void on_pairing_error(
        connection_handle_t connection,
        pairing_failure_t error
    ) override;

    /** @copydoc PalSecurityManager::on_pairing_timed_out
     */
    void on_pairing_timed_out(
        connection_handle_t connection
    ) override;

    /** @copydoc PalSecurityManager::on_pairing_completed
     */
    void on_pairing_completed(
        connection_handle_t connection
    ) override;

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

    /** @copydoc PalSecurityManager::on_valid_mic_timeout
     */
    void on_valid_mic_timeout(
        connection_handle_t connection
    ) override;

    /** @copydoc PalSecurityManager::on_signed_write_received
     */
    void on_signed_write_received(
        connection_handle_t connection,
        uint32_t sign_coutner
    ) override;

    /** @copydoc PalSecurityManager::on_signed_write_verification_failure
     */
    void on_signed_write_verification_failure(
        connection_handle_t connection
    ) override;

    /** @copydoc PalSecurityManager::on_signed_write
     */
    void on_signed_write() override;

    /** @copydoc PalSecurityManager::on_slave_security_request
     */
    void on_slave_security_request(
        connection_handle_t connection,
        AuthenticationMask authentication
    ) override;

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

    /** @copydoc PalSecurityManager::on_link_encryption_result
     */
    void on_link_encryption_result(
        connection_handle_t connection,
        link_encryption_t result
    ) override;

    /** @copydoc PalSecurityManager::on_link_encryption_request_timed_out
     */
    void on_link_encryption_request_timed_out(
        connection_handle_t connection
    ) override;

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

    /** @copydoc PalSecurityManager::on_passkey_display
     */
    void on_passkey_display(
        connection_handle_t connection,
        passkey_num_t passkey
    ) override;

    /** @copydoc PalSecurityManager::on_keypress_notification
     */
    void on_keypress_notification(
        connection_handle_t connection,
        ble::Keypress_t keypress
    ) override;

    /** @copydoc PalSecurityManager::on_passkey_request
     */
    void on_passkey_request(
        connection_handle_t connection
    ) override;

    /** @copydoc PalSecurityManager::on_confirmation_request
     */
    void on_confirmation_request(
        connection_handle_t connection
    ) override;

    /** @copydoc PalSecurityManager::on_secure_connections_oob_request
     */
    void on_secure_connections_oob_request(
        connection_handle_t connection
    ) override;

    /** @copydoc PalSecurityManager::on_legacy_pairing_oob_request
     */
    void on_legacy_pairing_oob_request(
        connection_handle_t connection
    ) override;

    /** @copydoc PalSecurityManager::on_secure_connections_oob_generated
     */
    void on_secure_connections_oob_generated(
        const oob_lesc_value_t &random,
        const oob_confirm_t &confirm
    ) override;

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

    /** @copydoc PalSecurityManager::on_secure_connections_ltk_generated
     */
    void on_secure_connections_ltk_generated(
        connection_handle_t connection,
        const ltk_t &ltk
    ) override;

    /** @copydoc PalSecurityManager::on_keys_distributed_ltk
     */
    void on_keys_distributed_ltk(
        connection_handle_t connection,
        const ltk_t &ltk
    ) override;

    /** @copydoc PalSecurityManager::on_keys_distributed_ediv_rand
     */
    void on_keys_distributed_ediv_rand(
        connection_handle_t connection,
        const ediv_t &ediv,
        const rand_t &rand
    ) override;

    /** @copydoc PalSecurityManager::on_keys_distributed_local_ltk
     */
    void on_keys_distributed_local_ltk(
        connection_handle_t connection,
        const ltk_t &ltk
    ) override;

    /** @copydoc PalSecurityManager::on_keys_distributed_local_ediv_rand
     */
    void on_keys_distributed_local_ediv_rand(
        connection_handle_t connection,
        const ediv_t &ediv,
        const rand_t &rand
    ) override;

    /** @copydoc PalSecurityManager::on_keys_distributed_irk
     */
    void on_keys_distributed_irk(
        connection_handle_t connection,
        const irk_t &irk
    ) override;

    /** @copydoc PalSecurityManager::on_keys_distributed_bdaddr
     */
    void on_keys_distributed_bdaddr(
        connection_handle_t connection,
        advertising_peer_address_type_t peer_address_type,
        const address_t &peer_identity_address
    ) override;

    /** @copydoc PalSecurityManager::on_keys_distributed_csrk
     */
    void on_keys_distributed_csrk(
        connection_handle_t connection,
        const csrk_t &csrk
    ) override;

    /** @copydoc PalSecurityManager::on_ltk_requeston_ltk_request
     */
    void on_ltk_request(
        connection_handle_t connection,
        const ediv_t &ediv,
        const rand_t &rand
    ) override;

    /** @copydoc PalSecurityManager::on_ltk_requeston_ltk_request
     */
    void on_ltk_request(
        connection_handle_t connection
    ) override;

    /* end implements PalSecurityManager::EventHandler */

public:
    SecurityManager(
        ble::PalSecurityManager &palImpl,
        ble::PalConnectionMonitor &connMonitorImpl,
        ble::PalSigningMonitor &signingMonitorImpl
    ) : eventHandler(nullptr),
        _pal(palImpl),
        _connection_monitor(connMonitorImpl),
        _signing_monitor(signingMonitorImpl),
        _db(nullptr),
        _default_authentication(0),
        _default_key_distribution(KeyDistribution::KEY_DISTRIBUTION_ALL),
        _pairing_authorisation_required(false),
        _legacy_pairing_allowed(true),
        _master_sends_keys(false)
    {
        eventHandler = &defaultEventHandler;
        _pal.set_event_handler(this);

        /* We create a fake value for oob to allow creation of the next oob which needs
         * the last process to finish first before restarting (this is to simplify checking).
         * This fake value will not be used as the oob address is currently invalid */
        _oob_local_random[0] = 1;
    }

    ~SecurityManager()
    {
        delete _db;
    }

private:
    /* Disallow copy and assignment. */
    SecurityManager(const SecurityManager &);

    SecurityManager &operator=(const SecurityManager &);

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

private:

    ble_error_t init_database(const char *db_path = nullptr);

    ble_error_t init_resolving_list();

    ble_error_t init_signing();

    ble_error_t init_identity();

    ble_error_t get_random_data(
        uint8_t *buffer,
        size_t size
    );

    ble_error_t slave_security_request(
        connection_handle_t connection
    );

    ble_error_t enable_encryption(
        connection_handle_t connection
    );

    void enable_encryption_cb(
        ble::SecurityDb::entry_handle_t entry,
        const SecurityEntryKeys_t *entryKeys
    );

    void set_ltk_cb(
        SecurityDb::entry_handle_t entry,
        const SecurityEntryKeys_t *entryKeys
    );

    void return_csrk_cb(
        SecurityDb::entry_handle_t connection,
        const SecurityEntrySigning_t *signing
    );

    void set_peer_csrk_cb(
        SecurityDb::entry_handle_t connection,
        const SecurityEntrySigning_t *signing
    );

    void update_oob_presence(
        connection_handle_t connection
    );

    void set_mitm_performed(
        connection_handle_t connection,
        bool enable = true
    );

    void 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
    ) override;

    void on_disconnected(
        connection_handle_t connection,
        disconnection_reason_t reason
    ) override;

    void on_security_entry_retrieved(
        SecurityDb::entry_handle_t entry,
        const SecurityEntryIdentity_t *identity
    );

    void on_identity_list_retrieved(
        Span<SecurityEntryIdentity_t> &identity_list,
        size_t count
    );

private:
    struct ControlBlock_t {
        ControlBlock_t();

        KeyDistribution get_initiator_key_distribution()
        {
            return KeyDistribution(initiator_key_distribution);
        };

        KeyDistribution get_responder_key_distribution()
        {
            return KeyDistribution(responder_key_distribution);
        };

        void set_initiator_key_distribution(KeyDistribution mask)
        {
            initiator_key_distribution = mask.value();
        };

        void set_responder_key_distribution(KeyDistribution mask)
        {
            responder_key_distribution = mask.value();
        };

        connection_handle_t connection;
        SecurityDb::entry_handle_t db_entry;

        address_t local_address; /**< address used for connection, possibly different from identity */

    private:
        uint8_t initiator_key_distribution: 4;
        uint8_t responder_key_distribution: 4;
    public:
        uint8_t connected: 1;
        uint8_t authenticated: 1; /**< have we turned encryption on during this connection */
        uint8_t is_master: 1;

        uint8_t encryption_requested: 1;
        uint8_t encryption_failed: 1;
        uint8_t encrypted: 1;
        uint8_t signing_requested: 1;
        uint8_t signing_override_default: 1;

        uint8_t mitm_requested: 1;
        uint8_t mitm_performed: 1; /**< keys exchange will have MITM protection */

        uint8_t attempt_oob: 1;
        uint8_t oob_mitm_protection: 1;
        uint8_t oob_present: 1;
        uint8_t legacy_pairing_oob_request_pending: 1;

        uint8_t csrk_failures: 2;
    };

    /* list management */

    ControlBlock_t *acquire_control_block(connection_handle_t connection);

    ControlBlock_t *get_control_block(connection_handle_t connection);

    ControlBlock_t *get_control_block(const address_t &peer_address);

    ControlBlock_t *get_control_block(SecurityDb::entry_handle_t db_entry);

    void release_control_block(ControlBlock_t *entry);

private:
    SecurityManagerShutdownCallbackChain_t shutdownCallChain;
    EventHandler *eventHandler;
    EventHandler defaultEventHandler;

    PalSecurityManager &_pal;
    PalConnectionMonitor &_connection_monitor;
    PalSigningMonitor &_signing_monitor;

    SecurityDb *_db;

    /* OOB data */
    address_t _oob_local_address;
    address_t _oob_peer_address;
    oob_lesc_value_t _oob_peer_random;
    oob_confirm_t _oob_peer_confirm;
    oob_lesc_value_t _oob_local_random;
    address_t _oob_temporary_key_creator_address; /**< device which generated and sent the TK */
    oob_tk_t _oob_temporary_key; /**< used for legacy pairing */

    AuthenticationMask _default_authentication;
    KeyDistribution _default_key_distribution;

    bool _pairing_authorisation_required;
    bool _legacy_pairing_allowed;
    bool _master_sends_keys;

    static const size_t MAX_CONTROL_BLOCKS = 5;
    ControlBlock_t _control_blocks[MAX_CONTROL_BLOCKS];
};

} // namespace impl
} // ble

#endif /*IMPL_SECURITY_MANAGER_H_*/