mirror of https://github.com/ARMmbed/mbed-os.git
commit
88d9342b71
|
@ -0,0 +1 @@
|
|||
tests/*
|
|
@ -28,7 +28,7 @@
|
|||
#ifdef YOTTA_CFG_MBED_OS
|
||||
#include "mbed-drivers/mbed_error.h"
|
||||
#else
|
||||
#include "mbed_error.h"
|
||||
#include "platform/mbed_error.h"
|
||||
#endif
|
||||
|
||||
#include "platform/mbed_toolchain.h"
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "ble/SafeEnum.h"
|
||||
|
||||
/**
|
||||
* @addtogroup ble
|
||||
|
@ -54,7 +56,7 @@ typedef uint16_t attribute_handle_t;
|
|||
*/
|
||||
struct attribute_handle_range_t {
|
||||
/**
|
||||
* Begining of the range.
|
||||
* Beginning of the range.
|
||||
*/
|
||||
attribute_handle_t begin;
|
||||
|
||||
|
@ -96,7 +98,7 @@ struct attribute_handle_range_t {
|
|||
/**
|
||||
* Construct an attribute_handle_range_t from its first and last attribute handle.
|
||||
*
|
||||
* @param begin Handle at the begining of the range.
|
||||
* @param begin Handle at the beginning of the range.
|
||||
* @param end Handle at the end of the range.
|
||||
*
|
||||
* @return An instance of attribute_handle_range_t where
|
||||
|
@ -117,6 +119,266 @@ static inline attribute_handle_range_t attribute_handle_range(
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type that describes link's encryption state..
|
||||
*/
|
||||
struct link_encryption_t : SafeEnum<link_encryption_t, uint8_t> {
|
||||
enum type {
|
||||
NOT_ENCRYPTED, /**< The link is not secured. */
|
||||
ENCRYPTION_IN_PROGRESS, /**< Link security is being established. */
|
||||
ENCRYPTED, /**< The link is secure. */
|
||||
ENCRYPTED_WITH_MITM /**< The link is secure and authenticated. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a new instance of link_encryption_t.
|
||||
*/
|
||||
link_encryption_t(type value) : SafeEnum<link_encryption_t, uint8_t>(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* Type that describe a pairing failure.
|
||||
*/
|
||||
struct pairing_failure_t : SafeEnum<pairing_failure_t, uint8_t> {
|
||||
enum type {
|
||||
PASSKEY_ENTRY_FAILED = 0x01,
|
||||
OOB_NOT_AVAILABLE = 0x02,
|
||||
AUTHENTICATION_REQUIREMENTS = 0x03,
|
||||
CONFIRM_VALUE_FAILED = 0x04,
|
||||
PAIRING_NOT_SUPPORTED = 0x05,
|
||||
ENCRYPTION_KEY_SIZE = 0x06,
|
||||
COMMAND_NOT_SUPPORTED = 0x07,
|
||||
UNSPECIFIED_REASON = 0x08,
|
||||
REPEATED_ATTEMPTS = 0x09,
|
||||
INVALID_PARAMETERS = 0x0A,
|
||||
DHKEY_CHECK_FAILED = 0x0B,
|
||||
NUMERIC_COMPARISON_FAILED = 0x0c,
|
||||
BR_EDR_PAIRING_IN_PROGRESS = 0x0D,
|
||||
CROSS_TRANSPORT_KEY_DERIVATION_OR_GENERATION_NOT_ALLOWED = 0x0E
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a new instance of pairing_failure_t.
|
||||
*/
|
||||
pairing_failure_t(type value) : SafeEnum<pairing_failure_t, uint8_t>(value) { }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Type that describe the IO capability of a device; it is used during Pairing
|
||||
* Feature exchange.
|
||||
*/
|
||||
struct io_capability_t : SafeEnum<io_capability_t, uint8_t> {
|
||||
enum type {
|
||||
DISPLAY_ONLY = 0x00,
|
||||
DISPLAY_YES_NO = 0x01,
|
||||
KEYBOARD_ONLY = 0x02,
|
||||
NO_INPUT_NO_OUTPUT = 0x03,
|
||||
KEYBOARD_DISPLAY = 0x04
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a new instance of io_capability_t.
|
||||
*/
|
||||
io_capability_t(type value) : SafeEnum<io_capability_t, uint8_t>(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* Passkey stored as a number.
|
||||
*/
|
||||
typedef uint32_t passkey_num_t;
|
||||
|
||||
/**
|
||||
* Passkey stored as a string of digits.
|
||||
*/
|
||||
class PasskeyAscii {
|
||||
public:
|
||||
static const uint8_t PASSKEY_LEN = 6;
|
||||
static const uint8_t NUMBER_OFFSET = '0';
|
||||
|
||||
/**
|
||||
* Default to all zeroes
|
||||
*/
|
||||
PasskeyAscii() {
|
||||
memset(ascii, NUMBER_OFFSET, PASSKEY_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a data from a string.
|
||||
*
|
||||
* @param input_value value of the data.
|
||||
*/
|
||||
PasskeyAscii(const uint8_t* passkey) {
|
||||
if (passkey) {
|
||||
memcpy(ascii, passkey, PASSKEY_LEN);
|
||||
} else {
|
||||
memset(ascii, NUMBER_OFFSET, PASSKEY_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a data from a number.
|
||||
*
|
||||
* @param input_value value of the data.
|
||||
*/
|
||||
PasskeyAscii(passkey_num_t passkey) {
|
||||
for (int i = 5, m = 100000; i >= 0; --i, m /= 10) {
|
||||
uint32_t result = passkey / m;
|
||||
ascii[i] = NUMBER_OFFSET + result;
|
||||
passkey -= result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast to number.
|
||||
*/
|
||||
operator passkey_num_t() {
|
||||
return to_num(ascii);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert ASCII string of digits into a number.
|
||||
* @param ASCII string of 6 digits stored as ASCII characters
|
||||
* @return Passkey as a number.
|
||||
*/
|
||||
static uint32_t to_num(const uint8_t *ascii) {
|
||||
uint32_t passkey = 0;
|
||||
for (size_t i = 0, m = 1; i < PASSKEY_LEN; ++i, m *= 10) {
|
||||
passkey += (ascii[i] - NUMBER_OFFSET) * m;
|
||||
}
|
||||
return passkey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pointer to the buffer holding the string.
|
||||
*/
|
||||
uint8_t* value() {
|
||||
return ascii;
|
||||
}
|
||||
private:
|
||||
uint8_t ascii[PASSKEY_LEN];
|
||||
};
|
||||
|
||||
template <size_t octet_size>
|
||||
struct octet_type_t {
|
||||
/**
|
||||
* Default to all zeroes
|
||||
*/
|
||||
octet_type_t() {
|
||||
memset(_value, 0x00, sizeof(_value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a data from an array of bytes.
|
||||
*
|
||||
* @param input_value value of the data.
|
||||
*/
|
||||
octet_type_t(const uint8_t *input_value) {
|
||||
memcpy(_value, input_value, sizeof(_value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a data from an buffer of bytes.
|
||||
*
|
||||
* @param input_value pointer to buffer.
|
||||
* @param size buffer size
|
||||
*/
|
||||
octet_type_t(const uint8_t* input_value, size_t size) {
|
||||
memcpy(_value, input_value, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equal operator between two octet types.
|
||||
*/
|
||||
friend bool operator==(const octet_type_t& lhs, const octet_type_t& rhs) {
|
||||
return memcmp(lhs._value, rhs._value, sizeof(lhs._value)) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Non equal operator between two octet types.
|
||||
*/
|
||||
friend bool operator!=(const octet_type_t& lhs, const octet_type_t& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscript operator to access data content
|
||||
*/
|
||||
uint8_t& operator[](uint8_t i) {
|
||||
return _value[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pointer to the buffer holding data.
|
||||
*/
|
||||
const uint8_t* data() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pointer to the buffer holding data.
|
||||
*/
|
||||
uint8_t* buffer() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size in byte of a data.
|
||||
*/
|
||||
static size_t size() {
|
||||
return octet_size;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t _value[octet_size];
|
||||
};
|
||||
|
||||
/** 128 bit keys used by paired devices */
|
||||
typedef octet_type_t<16> key_t;
|
||||
typedef octet_type_t<16> irk_t;
|
||||
typedef octet_type_t<16> csrk_t;
|
||||
typedef octet_type_t<16> ltk_t;
|
||||
|
||||
/** Used to identify LTK for legacy pairing connections */
|
||||
typedef octet_type_t<2> ediv_t;
|
||||
typedef octet_type_t<8> rand_t;
|
||||
|
||||
/** Used to store the random data generated by the chip */
|
||||
typedef octet_type_t<8> random_data_t;
|
||||
|
||||
/** Out of band data exchanged during pairing */
|
||||
typedef octet_type_t<16> oob_tk_t; /**< legacy pairing TK */
|
||||
typedef octet_type_t<16> oob_rand_t; /**< secure connections oob random 128 value */
|
||||
typedef octet_type_t<16> oob_confirm_t; /**< secure connections oob confirmation value */
|
||||
|
||||
/** data to be encrypted */
|
||||
typedef octet_type_t<16> encryption_block_t;
|
||||
|
||||
typedef octet_type_t<32> public_key_t;
|
||||
typedef octet_type_t<32> private_key_t;
|
||||
typedef octet_type_t<32> dhkey_t;
|
||||
|
||||
/**
|
||||
* MAC address data type.
|
||||
*/
|
||||
struct address_t : public octet_type_t<6> {
|
||||
/**
|
||||
* Create an invalid mac address, equal to FF:FF:FF:FF:FF:FF
|
||||
*/
|
||||
address_t() {
|
||||
memset(_value, 0xFF, sizeof(_value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a data from an array of bytes.
|
||||
*
|
||||
* @param input_value value of the data.
|
||||
*/
|
||||
address_t(const uint8_t *input_value) {
|
||||
memcpy(_value, input_value, sizeof(_value));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ble
|
||||
|
||||
/**
|
||||
|
|
|
@ -237,7 +237,7 @@ class GapAdvertisingData;
|
|||
* // Initiate the connection procedure
|
||||
* gap.connect(
|
||||
* packet->peerAddr,
|
||||
* BLEProtocol::RANDOM_STATIC,
|
||||
* packet->addressType,
|
||||
* &connection_parameters,
|
||||
* &scanning_params
|
||||
* );
|
||||
|
@ -608,6 +608,11 @@ public:
|
|||
* Pointer to the advertisement packet's data.
|
||||
*/
|
||||
const uint8_t *advertisingData;
|
||||
|
||||
/**
|
||||
* Type of the address received
|
||||
*/
|
||||
AddressType_t addressType;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2281,7 +2286,7 @@ public:
|
|||
* @param[in] ownAddr Address this device uses for this connection.
|
||||
* @param[in] connectionParams Parameters of the connection.
|
||||
*/
|
||||
void processConnectionEvent(
|
||||
virtual void processConnectionEvent(
|
||||
Handle_t handle,
|
||||
Role_t role,
|
||||
BLEProtocol::AddressType_t peerAddrType,
|
||||
|
@ -2304,6 +2309,7 @@ public:
|
|||
ownAddr,
|
||||
connectionParams
|
||||
);
|
||||
|
||||
connectionCallChain.call(&callbackParams);
|
||||
}
|
||||
|
||||
|
@ -2316,7 +2322,7 @@ public:
|
|||
* @param[in] handle Handle of the terminated connection.
|
||||
* @param[in] reason Reason of the disconnection.
|
||||
*/
|
||||
void processDisconnectionEvent(Handle_t handle, DisconnectionReason_t reason)
|
||||
virtual void processDisconnectionEvent(Handle_t handle, DisconnectionReason_t reason)
|
||||
{
|
||||
/* Update Gap state */
|
||||
--connectionCount;
|
||||
|
@ -2349,7 +2355,8 @@ public:
|
|||
bool isScanResponse,
|
||||
GapAdvertisingParams::AdvertisingType_t type,
|
||||
uint8_t advertisingDataLen,
|
||||
const uint8_t *advertisingData
|
||||
const uint8_t *advertisingData,
|
||||
BLEProtocol::AddressType_t addressType
|
||||
) {
|
||||
AdvertisementCallbackParams_t params;
|
||||
memcpy(params.peerAddr, peerAddr, ADDR_LEN);
|
||||
|
@ -2358,6 +2365,7 @@ public:
|
|||
params.type = type;
|
||||
params.advertisingDataLen = advertisingDataLen;
|
||||
params.advertisingData = advertisingData;
|
||||
params.addressType = addressType;
|
||||
onAdvertisementReport.call(¶ms);
|
||||
}
|
||||
|
||||
|
@ -2447,6 +2455,13 @@ protected:
|
|||
*/
|
||||
DisconnectionEventCallbackChain_t disconnectionCallChain;
|
||||
|
||||
/**
|
||||
* Register a callback handling connection events to be used internally and serviced first.
|
||||
*
|
||||
* @param[in] callback Event handler being registered.
|
||||
*/
|
||||
virtual void onConnectionPrivate(ConnectionEventCallback_t callback) { }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Callchain containing all registered callback handlers for shutdown
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,14 +25,15 @@
|
|||
#include "ble/pal/PalGap.h"
|
||||
#include "ble/pal/GapEvents.h"
|
||||
#include "ble/pal/GapTypes.h"
|
||||
#include "ble/BLETypes.h"
|
||||
#include "ble/pal/GenericAccessService.h"
|
||||
#include "ble/pal/EventQueue.h"
|
||||
#include "ble/pal/ConnectionEventMonitor.h"
|
||||
|
||||
#include "drivers/Timeout.h"
|
||||
|
||||
namespace ble {
|
||||
namespace generic {
|
||||
|
||||
/**
|
||||
* Generic implementation of the Gap class.
|
||||
* It requires a pal::Gap and a pal::GenericAccessService injected at
|
||||
|
@ -40,7 +41,8 @@ namespace generic {
|
|||
*
|
||||
* @attention: Not part of the public interface of BLE API.
|
||||
*/
|
||||
class GenericGap : public ::Gap {
|
||||
class GenericGap : public ::Gap,
|
||||
public pal::ConnectionEventMonitor {
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -249,6 +251,32 @@ public:
|
|||
*/
|
||||
virtual ble_error_t reset(void);
|
||||
|
||||
/**
|
||||
* @copydoc ::Gap::processConnectionEvent
|
||||
*/
|
||||
virtual void processConnectionEvent(
|
||||
Handle_t handle,
|
||||
Role_t role,
|
||||
BLEProtocol::AddressType_t peerAddrType,
|
||||
const BLEProtocol::AddressBytes_t peerAddr,
|
||||
BLEProtocol::AddressType_t ownAddrType,
|
||||
const BLEProtocol::AddressBytes_t ownAddr,
|
||||
const ConnectionParams_t *connectionParams
|
||||
);
|
||||
|
||||
/**
|
||||
* @copydoc ::Gap::processDisconnectionEvent
|
||||
*/
|
||||
virtual void processDisconnectionEvent(
|
||||
Handle_t handle,
|
||||
DisconnectionReason_t reason
|
||||
);
|
||||
|
||||
/** @note Implements ConnectionEventMonitor.
|
||||
* @copydoc ConnectionEventMonitor::set_connection_event_handler
|
||||
*/
|
||||
void set_connection_event_handler(pal::ConnectionEventHandler *_connection_event_handler);
|
||||
|
||||
private:
|
||||
void on_scan_timeout();
|
||||
|
||||
|
@ -282,12 +310,14 @@ private:
|
|||
pal::Gap &_pal_gap;
|
||||
pal::GenericAccessService &_gap_service;
|
||||
BLEProtocol::AddressType_t _address_type;
|
||||
ble::address_t _address;
|
||||
pal::initiator_policy_t _initiator_policy_mode;
|
||||
pal::scanning_filter_policy_t _scanning_filter_policy;
|
||||
pal::advertising_filter_policy_t _advertising_filter_policy;
|
||||
mutable Whitelist_t _whitelist;
|
||||
mbed::Timeout _advertising_timeout;
|
||||
mbed::Timeout _scan_timeout;
|
||||
pal::ConnectionEventHandler *_connection_event_handler;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,652 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 "ble/pal/GapTypes.h"
|
||||
#include "ble/BLETypes.h"
|
||||
#include "ble/pal/SecurityDb.h"
|
||||
#include "platform/Callback.h"
|
||||
#include "ble/pal/ConnectionEventMonitor.h"
|
||||
#include "ble/generic/GenericGap.h"
|
||||
#include "ble/pal/PalSecurityManager.h"
|
||||
|
||||
namespace ble {
|
||||
namespace generic {
|
||||
|
||||
using pal::advertising_peer_address_type_t;
|
||||
using pal::AuthenticationMask;
|
||||
using pal::KeyDistribution;
|
||||
using pal::connection_peer_address_type_t;
|
||||
|
||||
typedef SecurityManager::SecurityIOCapabilities_t SecurityIOCapabilities_t;
|
||||
|
||||
class GenericSecurityManagerEventHandler;
|
||||
|
||||
class GenericSecurityManager : public SecurityManager,
|
||||
public pal::SecurityManagerEventHandler,
|
||||
public pal::ConnectionEventHandler {
|
||||
public:
|
||||
typedef ble::pal::SecurityEntry_t SecurityEntry_t;
|
||||
typedef ble::pal::SecurityEntryKeys_t SecurityEntryKeys_t;
|
||||
|
||||
/* implements SecurityManager */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
);
|
||||
|
||||
virtual ble_error_t reset();
|
||||
|
||||
virtual ble_error_t preserveBondingStateOnReset(
|
||||
bool enabled
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// List management
|
||||
//
|
||||
|
||||
virtual ble_error_t purgeAllBondingState();
|
||||
|
||||
virtual ble_error_t generateWhitelistFromBondTable(
|
||||
Gap::Whitelist_t *whitelist
|
||||
) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Pairing
|
||||
//
|
||||
|
||||
virtual ble_error_t requestPairing(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
virtual ble_error_t acceptPairingRequest(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
virtual ble_error_t canceltPairingRequest(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
virtual ble_error_t setPairingRequestAuthorisation(
|
||||
bool required = true
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Feature support
|
||||
//
|
||||
|
||||
virtual ble_error_t allowLegacyPairing(
|
||||
bool allow = true
|
||||
);
|
||||
|
||||
virtual ble_error_t getSecureConnectionsSupport(
|
||||
bool *enabled
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Security settings
|
||||
//
|
||||
|
||||
virtual ble_error_t setIoCapability(
|
||||
SecurityIOCapabilities_t iocaps
|
||||
);
|
||||
|
||||
virtual ble_error_t setDisplayPasskey(
|
||||
const Passkey_t passkey
|
||||
);
|
||||
|
||||
virtual ble_error_t setAuthenticationTimeout(
|
||||
connection_handle_t connection,
|
||||
uint32_t timeout_in_ms
|
||||
);
|
||||
|
||||
virtual ble_error_t getAuthenticationTimeout(
|
||||
connection_handle_t connection,
|
||||
uint32_t *timeout_in_ms
|
||||
);
|
||||
|
||||
virtual ble_error_t setLinkSecurity(
|
||||
connection_handle_t connection,
|
||||
SecurityMode_t securityMode
|
||||
);
|
||||
|
||||
virtual ble_error_t setKeypressNotification(
|
||||
bool enabled = true
|
||||
);
|
||||
|
||||
virtual ble_error_t enableSigning(
|
||||
connection_handle_t connection,
|
||||
bool enabled = true
|
||||
);
|
||||
|
||||
virtual ble_error_t setHintFutureRoleReversal(
|
||||
bool enable = true
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Encryption
|
||||
//
|
||||
|
||||
virtual ble_error_t getLinkEncryption(
|
||||
connection_handle_t connection,
|
||||
link_encryption_t *encryption
|
||||
);
|
||||
|
||||
virtual ble_error_t setLinkEncryption(
|
||||
connection_handle_t connection,
|
||||
link_encryption_t encryption
|
||||
);
|
||||
|
||||
virtual ble_error_t getEncryptionKeySize(
|
||||
connection_handle_t connection,
|
||||
uint8_t *size
|
||||
);
|
||||
|
||||
virtual ble_error_t setEncryptionKeyRequirements(
|
||||
uint8_t minimumByteSize,
|
||||
uint8_t maximumByteSize
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Privacy
|
||||
//
|
||||
|
||||
virtual ble_error_t setPrivateAddressTimeout(
|
||||
uint16_t timeout_in_seconds
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
//
|
||||
|
||||
virtual ble_error_t getSigningKey(
|
||||
connection_handle_t connection,
|
||||
bool authenticated
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Authentication
|
||||
//
|
||||
|
||||
virtual ble_error_t requestAuthentication(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// MITM
|
||||
//
|
||||
|
||||
virtual ble_error_t setOOBDataUsage(
|
||||
connection_handle_t connection,
|
||||
bool useOOB,
|
||||
bool OOBProvidesMITM = true
|
||||
);
|
||||
|
||||
virtual ble_error_t confirmationEntered(
|
||||
connection_handle_t connection,
|
||||
bool confirmation
|
||||
);
|
||||
|
||||
virtual ble_error_t passkeyEntered(
|
||||
connection_handle_t connection,
|
||||
Passkey_t passkey
|
||||
);
|
||||
|
||||
virtual ble_error_t sendKeypressNotification(
|
||||
connection_handle_t connection,
|
||||
Keypress_t keypress
|
||||
);
|
||||
|
||||
virtual ble_error_t legacyPairingOobReceived(
|
||||
const address_t *address,
|
||||
const oob_tk_t *tk
|
||||
);
|
||||
|
||||
virtual ble_error_t oobReceived(
|
||||
const address_t *address,
|
||||
const oob_rand_t *random,
|
||||
const oob_confirm_t *confirm
|
||||
);
|
||||
|
||||
/* ends implements SecurityManager */
|
||||
|
||||
public:
|
||||
GenericSecurityManager(
|
||||
pal::SecurityManager &palImpl,
|
||||
pal::SecurityDb &dbImpl,
|
||||
pal::ConnectionEventMonitor &connMonitorImpl
|
||||
) : _pal(palImpl),
|
||||
_db(dbImpl),
|
||||
_connection_monitor(connMonitorImpl),
|
||||
_default_authentication(0),
|
||||
_default_key_distribution(KeyDistribution::KEY_DISTRIBUTION_ALL),
|
||||
_pairing_authorisation_required(false),
|
||||
_legacy_pairing_allowed(true),
|
||||
_master_sends_keys(false),
|
||||
_public_keys_generated(false) {
|
||||
_pal.set_event_handler(this);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
private:
|
||||
/**
|
||||
* Generate the CSRK if needed.
|
||||
*
|
||||
* @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
|
||||
*/
|
||||
ble_error_t init_signing();
|
||||
|
||||
/**
|
||||
* Fills the buffer with the specified number of bytes of random data
|
||||
* produced by the link controller
|
||||
*
|
||||
* @param[out] buffer buffer to be filled with random data
|
||||
* @param[in] size number of bytes to fill with random data
|
||||
* @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
|
||||
*/
|
||||
ble_error_t get_random_data(
|
||||
uint8_t *buffer,
|
||||
size_t size
|
||||
);
|
||||
|
||||
/**
|
||||
* Send slave security request based on current link settings.
|
||||
*
|
||||
* @param[in] connectionHandle Handle to identify the connection.
|
||||
* @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
|
||||
*/
|
||||
ble_error_t slave_security_request(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/**
|
||||
* Enable encryption on the link, depending on whether device is master or slave.
|
||||
*
|
||||
* @param[in] connectionHandle Handle to identify the connection.
|
||||
* @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
|
||||
*/
|
||||
ble_error_t enable_encryption(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the requested LTK to the PAL. Called by the security db.
|
||||
*
|
||||
* @param[in] entry security entry returned by the database.
|
||||
* @param[in] entryKeys security entry containing keys.
|
||||
*/
|
||||
void enable_encryption_cb(
|
||||
const SecurityEntry_t* entry,
|
||||
const SecurityEntryKeys_t* entryKeys
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the requested LTK to the PAL. Called by the security db.
|
||||
*
|
||||
* @param[in] entry security entry returned by the database.
|
||||
* @param[in] entryKeys security entry containing keys.
|
||||
*/
|
||||
void set_ltk_cb(
|
||||
const SecurityEntry_t* entry,
|
||||
const SecurityEntryKeys_t* entryKeys
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the CSRK for the connection. Called by the security db.
|
||||
*
|
||||
* @param[in] connectionHandle Handle to identify the connection.
|
||||
* @param[in] entryKeys security entry containing keys.
|
||||
*/
|
||||
void return_csrk_cb(
|
||||
connection_handle_t connection,
|
||||
const csrk_t *csrk
|
||||
);
|
||||
|
||||
#if defined(MBEDTLS_CMAC_C)
|
||||
/**
|
||||
* Generate local OOB data to be sent to the application which sends it to the peer.
|
||||
*
|
||||
* @param[in] connectionHandle Handle to identify the connection.
|
||||
*/
|
||||
void generate_secure_connections_oob(
|
||||
connection_handle_t connection
|
||||
);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Updates the entry for the connection with OOB data presence.
|
||||
*
|
||||
* @param[in] connectionHandle Handle to identify the connection.
|
||||
*/
|
||||
void update_oob_presence(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
#if defined(MBEDTLS_CMAC_C)
|
||||
/**
|
||||
* Calculate the confirmation value for secure connections OOB data based
|
||||
* on local public key and a random number.
|
||||
* @see BLUETOOTH SPECIFICATION Version 5.0 | Vol 3, Part H - 2.2.6
|
||||
|
||||
* @param[in] U public key x component
|
||||
* @param[in] V public key y component
|
||||
* @param[in] X random number
|
||||
* @param[out] confirm confirmation value
|
||||
* @return true if cryptography functioned worked
|
||||
*/
|
||||
static bool crypto_toolbox_f4(
|
||||
const public_key_t &U,
|
||||
const public_key_t &V,
|
||||
const oob_rand_t &X,
|
||||
oob_confirm_t &confirm
|
||||
);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Set the MITM protection setting on the database entry
|
||||
*
|
||||
* @param[in] connectionHandle Handle to identify the connection.
|
||||
* @param[in] enable if true set the MITM protection to on.
|
||||
*/
|
||||
virtual void set_mitm_performed(
|
||||
connection_handle_t connection,
|
||||
bool enable = true
|
||||
);
|
||||
|
||||
/**
|
||||
* Inform the Security manager of a new connection. This will create
|
||||
* or retrieve an existing security manager entry for the connected device.
|
||||
* Called by GAP.
|
||||
*
|
||||
* @param[in] connectionHandle Handle to identify the connection.
|
||||
* @param[in] is_master True if device is the master.
|
||||
* @param[in] peer_address_type type of address.
|
||||
* @param[in] peer_address Address of the connected device.
|
||||
* @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
|
||||
*/
|
||||
virtual void on_connected(
|
||||
connection_handle_t connection,
|
||||
Gap::Role_t role,
|
||||
BLEProtocol::AddressType_t peer_address_type,
|
||||
const BLEProtocol::AddressBytes_t peer_address,
|
||||
BLEProtocol::AddressType_t local_address_type,
|
||||
const BLEProtocol::AddressBytes_t local_address,
|
||||
const Gap::ConnectionParams_t *connection_params
|
||||
);
|
||||
|
||||
/**
|
||||
* Inform the security manager that a device has been disconnected and its
|
||||
* entry can be put in NVM storage. Called by GAP.
|
||||
*
|
||||
* @param[in] connectionHandle Handle to identify the connection.
|
||||
* @return BLE_ERROR_NONE or appropriate error code indicating the failure reason.
|
||||
*/
|
||||
virtual void on_disconnected(
|
||||
connection_handle_t connection,
|
||||
Gap::DisconnectionReason_t reason
|
||||
);
|
||||
|
||||
/**
|
||||
* Inform the security manager of a new connection.
|
||||
*
|
||||
* @param[in] params information about the new connection.
|
||||
*/
|
||||
void connection_callback(
|
||||
const Gap::ConnectionCallbackParams_t* params
|
||||
);
|
||||
|
||||
/**
|
||||
* Inform the security manager that a connection ended.
|
||||
*
|
||||
* @param[in] params handle and reason of the disconnection.
|
||||
*/
|
||||
void disconnection_callback(
|
||||
const Gap::DisconnectionCallbackParams_t* params
|
||||
);
|
||||
|
||||
private:
|
||||
pal::SecurityManager &_pal;
|
||||
pal::SecurityDb &_db;
|
||||
pal::ConnectionEventMonitor &_connection_monitor;
|
||||
|
||||
AuthenticationMask _default_authentication;
|
||||
KeyDistribution _default_key_distribution;
|
||||
|
||||
bool _pairing_authorisation_required;
|
||||
bool _legacy_pairing_allowed;
|
||||
bool _master_sends_keys;
|
||||
bool _public_keys_generated;
|
||||
|
||||
|
||||
/* implements ble::pal::SecurityManagerEventHandler */
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Pairing
|
||||
//
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_pairing_request
|
||||
*/
|
||||
virtual void on_pairing_request(
|
||||
connection_handle_t connection,
|
||||
bool use_oob,
|
||||
AuthenticationMask authentication,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_pairing_error
|
||||
*/
|
||||
virtual void on_pairing_error(
|
||||
connection_handle_t connection,
|
||||
pairing_failure_t error
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_pairing_timed_out
|
||||
*/
|
||||
virtual void on_pairing_timed_out(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_pairing_completed
|
||||
*/
|
||||
virtual void on_pairing_completed(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Security
|
||||
//
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_valid_mic_timeout
|
||||
*/
|
||||
virtual void on_valid_mic_timeout(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_slave_security_request
|
||||
*/
|
||||
virtual void on_slave_security_request(
|
||||
connection_handle_t connection,
|
||||
AuthenticationMask authentication
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Encryption
|
||||
//
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_link_encryption_result
|
||||
*/
|
||||
virtual void on_link_encryption_result(
|
||||
connection_handle_t connection,
|
||||
link_encryption_t result
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_link_encryption_request_timed_out
|
||||
*/
|
||||
virtual void on_link_encryption_request_timed_out(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// MITM
|
||||
//
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_passkey_display
|
||||
*/
|
||||
virtual void on_passkey_display(
|
||||
connection_handle_t connection,
|
||||
passkey_num_t passkey
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_keypress_notification
|
||||
*/
|
||||
virtual void on_keypress_notification(
|
||||
connection_handle_t connection,
|
||||
SecurityManager::Keypress_t keypress
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_passkey_request
|
||||
*/
|
||||
virtual void on_passkey_request(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_confirmation_request
|
||||
*/
|
||||
virtual void on_confirmation_request(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_legacy_pairing_oob_request
|
||||
*/
|
||||
virtual void on_legacy_pairing_oob_request(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_oob_data_verification_request
|
||||
*/
|
||||
virtual void on_oob_data_verification_request(
|
||||
connection_handle_t connection,
|
||||
const public_key_t &peer_public_key_x,
|
||||
const public_key_t &peer_public_key_y
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
//
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_public_key_generated
|
||||
*/
|
||||
virtual void on_public_key_generated(
|
||||
const public_key_t &public_key_x,
|
||||
const public_key_t &public_key_y
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_secure_connections_ltk_generated
|
||||
*/
|
||||
virtual void on_secure_connections_ltk_generated(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_keys_distributed_ltk
|
||||
*/
|
||||
virtual void on_keys_distributed_ltk(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_keys_distributed_ediv_rand
|
||||
*/
|
||||
virtual void on_keys_distributed_ediv_rand(
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_keys_distributed_local_ltk
|
||||
*/
|
||||
virtual void on_keys_distributed_local_ltk(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_keys_distributed_local_ediv_rand
|
||||
*/
|
||||
virtual void on_keys_distributed_local_ediv_rand(
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_keys_distributed_irk
|
||||
*/
|
||||
virtual void on_keys_distributed_irk(
|
||||
connection_handle_t connection,
|
||||
const irk_t &irk
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_keys_distributed_bdaddr
|
||||
*/
|
||||
virtual void on_keys_distributed_bdaddr(
|
||||
connection_handle_t connection,
|
||||
advertising_peer_address_type_t peer_address_type,
|
||||
const address_t &peer_identity_address
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_keys_distributed_csrk
|
||||
*/
|
||||
virtual void on_keys_distributed_csrk(
|
||||
connection_handle_t connection,
|
||||
const csrk_t &csrk
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_ltk_requeston_ltk_request
|
||||
*/
|
||||
virtual void on_ltk_request(
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
);
|
||||
|
||||
/** @copydoc SecurityManagerEventHandler::on_ltk_requeston_ltk_request
|
||||
*/
|
||||
virtual void on_ltk_request(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/* end implements ble::pal::SecurityManagerEventHandler */
|
||||
};
|
||||
|
||||
|
||||
} /* namespace generic */
|
||||
} /* namespace ble */
|
||||
|
||||
|
||||
#endif /*__GENERIC_SECURITY_MANAGER_H__*/
|
|
@ -0,0 +1,66 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 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 MBED_BLE_CONNECTION_EVENT_MONITOR
|
||||
#define MBED_BLE_CONNECTION_EVENT_MONITOR
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "ble/BLE.h"
|
||||
#include "ble/BLEProtocol.h"
|
||||
#include "ble/Gap.h"
|
||||
#include "ble/pal/PalGap.h"
|
||||
#include "ble/pal/GapEvents.h"
|
||||
#include "ble/pal/GapTypes.h"
|
||||
#include "ble/BLETypes.h"
|
||||
#include "ble/pal/GenericAccessService.h"
|
||||
#include "ble/pal/EventQueue.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
|
||||
class ConnectionEventHandler {
|
||||
public:
|
||||
virtual void on_connected(
|
||||
connection_handle_t connection,
|
||||
::Gap::Role_t role,
|
||||
BLEProtocol::AddressType_t peer_address_type,
|
||||
const BLEProtocol::AddressBytes_t peer_address,
|
||||
BLEProtocol::AddressType_t local_address_type,
|
||||
const BLEProtocol::AddressBytes_t local_address,
|
||||
const ::Gap::ConnectionParams_t *connection_params
|
||||
) = 0;
|
||||
|
||||
virtual void on_disconnected(
|
||||
connection_handle_t connection,
|
||||
::Gap::DisconnectionReason_t reason
|
||||
) = 0;
|
||||
};
|
||||
|
||||
class ConnectionEventMonitor {
|
||||
public:
|
||||
/**
|
||||
* Register a handler for connection events to be used internally and serviced first.
|
||||
*
|
||||
* @param[in] connection_event_handler Event handler being registered.
|
||||
*/
|
||||
virtual void set_connection_event_handler(ConnectionEventHandler *connection_event_handler) = 0;
|
||||
};
|
||||
|
||||
} // namespace pal
|
||||
} // namespace ble
|
||||
|
||||
#endif /* MBED_BLE_CONNECTION_EVENT_MONITOR */
|
|
@ -410,78 +410,6 @@ struct initiator_policy_t : SafeEnum<initiator_policy_t, uint8_t> {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* MAC address data type.
|
||||
*/
|
||||
struct address_t {
|
||||
/**
|
||||
* Create an invalid mac address, equal to FF:FF:FF:FF:FF:FF
|
||||
*/
|
||||
address_t() {
|
||||
memset(value, 0xFF, sizeof(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a mac address from an array of bytes.
|
||||
*
|
||||
* @param input_value value of the MAC address.
|
||||
*/
|
||||
address_t(const uint8_t (&input_value)[6]) {
|
||||
memcpy(value, input_value, sizeof(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a mac address from a pointer to a buffer.
|
||||
*
|
||||
* @param input_value Buffer containing the mac address. It shall be at
|
||||
* least 6 long.
|
||||
*
|
||||
* @param tag Tag used to select this constructor. The value does not matter.
|
||||
*/
|
||||
address_t(const uint8_t* input_value, bool tag) {
|
||||
memcpy(value, input_value, sizeof(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Equal operator between two addresses.
|
||||
*/
|
||||
friend bool operator==(const address_t& lhs, const address_t& rhs) {
|
||||
return memcmp(lhs.value, rhs.value, sizeof(lhs.value)) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Non equal operator between two addresses.
|
||||
*/
|
||||
friend bool operator!=(const address_t& lhs, const address_t& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscript operator to access mac address content
|
||||
*/
|
||||
uint8_t operator[](uint8_t i) const {
|
||||
return value[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pointer to the buffer holding mac address.
|
||||
*/
|
||||
const uint8_t* data() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size in byte of a mac address.
|
||||
*/
|
||||
uint8_t size() const {
|
||||
return sizeof(value);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t value[6];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hold advertising data.
|
||||
*/
|
||||
|
@ -551,7 +479,6 @@ private:
|
|||
uint8_t value[31];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Type of advertising the LE subsystem can use when it advertise.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,415 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 PAL_MEMORY_SECURITY_MANAGER_DB_H__
|
||||
#define PAL_MEMORY_SECURITY_MANAGER_DB_H__
|
||||
|
||||
#include "SecurityDB.h"
|
||||
#include "Gap.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
|
||||
/* naive memory implementation for verification
|
||||
* TODO: make thread safe */
|
||||
class MemorySecurityDb : public SecurityDb {
|
||||
private:
|
||||
struct db_store_t {
|
||||
db_store_t() { };
|
||||
SecurityEntry_t entry;
|
||||
SecurityEntryKeys_t peer_keys;
|
||||
SecurityEntryKeys_t local_keys;
|
||||
csrk_t csrk;
|
||||
};
|
||||
static const size_t MAX_ENTRIES = 5;
|
||||
|
||||
public:
|
||||
MemorySecurityDb() { };
|
||||
virtual ~MemorySecurityDb() { };
|
||||
|
||||
virtual SecurityEntry_t* get_entry(
|
||||
connection_handle_t connection
|
||||
) {
|
||||
SecurityEntry_t *entry = NULL;
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
entry = &store->entry;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
virtual SecurityEntry_t* get_entry(
|
||||
const address_t &peer_address
|
||||
) {
|
||||
SecurityEntry_t *entry = NULL;
|
||||
for (size_t i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (!_db[i].entry.connected) {
|
||||
continue;
|
||||
} else if (peer_address == _db[i].entry.peer_address) {
|
||||
entry = &_db[i].entry;
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* local keys */
|
||||
|
||||
/* get */
|
||||
virtual void get_entry_local_keys(
|
||||
SecurityEntryKeysDbCb_t cb,
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
) {
|
||||
SecurityEntry_t *entry = NULL;
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
entry = &store->entry;
|
||||
}
|
||||
|
||||
/* validate we have the correct key */
|
||||
if (ediv == store->local_keys.ediv
|
||||
&& rand == store->local_keys.rand) {
|
||||
cb(entry, &store->local_keys);
|
||||
} else {
|
||||
cb(entry, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void get_entry_local_keys(
|
||||
SecurityEntryKeysDbCb_t cb,
|
||||
connection_handle_t connection
|
||||
) {
|
||||
SecurityEntry_t *entry = NULL;
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
entry = &store->entry;
|
||||
}
|
||||
|
||||
/* validate we have the correct key */
|
||||
if (entry->secure_connections_paired) {
|
||||
cb(entry, &store->local_keys);
|
||||
} else {
|
||||
cb(entry, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* set */
|
||||
virtual void set_entry_local_ltk(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->local_keys.ltk = ltk;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_entry_local_ediv_rand(
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->local_keys.ediv = ediv;
|
||||
store->local_keys.rand = rand;
|
||||
}
|
||||
}
|
||||
|
||||
/* peer's keys */
|
||||
|
||||
/* get */
|
||||
virtual void get_entry_peer_csrk(
|
||||
SecurityEntryCsrkDbCb_t cb,
|
||||
connection_handle_t connection
|
||||
) {
|
||||
SecurityEntry_t *entry = NULL;
|
||||
csrk_t csrk;
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
entry = &store->entry;
|
||||
csrk = store->csrk;
|
||||
}
|
||||
cb(entry->handle, &csrk);
|
||||
}
|
||||
|
||||
virtual void get_entry_peer_keys(
|
||||
SecurityEntryKeysDbCb_t cb,
|
||||
connection_handle_t connection
|
||||
) {
|
||||
SecurityEntry_t *entry = NULL;
|
||||
SecurityEntryKeys_t *key = NULL;
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
entry = &store->entry;
|
||||
key = &store->peer_keys;
|
||||
}
|
||||
cb(entry, key);
|
||||
}
|
||||
|
||||
/* set */
|
||||
|
||||
virtual void set_entry_peer_ltk(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->peer_keys.ltk = ltk;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_entry_peer_ediv_rand(
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->peer_keys.ediv = ediv;
|
||||
store->peer_keys.rand = rand;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_entry_peer_irk(
|
||||
connection_handle_t connection,
|
||||
const irk_t &irk
|
||||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
size_t index = store - _db;
|
||||
_identities[index].irk = irk;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_entry_peer_bdaddr(
|
||||
connection_handle_t connection,
|
||||
bool address_is_public,
|
||||
const address_t &peer_address
|
||||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
size_t index = store - _db;
|
||||
_identities[index].identity_address = peer_address;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_entry_peer_csrk(
|
||||
connection_handle_t connection,
|
||||
const csrk_t &csrk
|
||||
) {
|
||||
db_store_t *store = get_store(connection);
|
||||
if (store) {
|
||||
store->csrk = csrk;
|
||||
}
|
||||
}
|
||||
|
||||
/* local csrk */
|
||||
|
||||
virtual const csrk_t* get_local_csrk() {
|
||||
return &_local_csrk;
|
||||
}
|
||||
|
||||
virtual void set_local_csrk(const csrk_t &csrk) {
|
||||
_local_csrk = csrk;
|
||||
}
|
||||
|
||||
/* public key */
|
||||
|
||||
virtual const public_key_t& get_public_key_x() {
|
||||
return _public_key_x;
|
||||
}
|
||||
|
||||
virtual const public_key_t& get_public_key_y() {
|
||||
return _public_key_y;
|
||||
}
|
||||
|
||||
virtual void set_public_key(
|
||||
const public_key_t &public_key_x,
|
||||
const public_key_t &public_key_y
|
||||
) {
|
||||
_public_key_x = public_key_x;
|
||||
_public_key_y = public_key_y;
|
||||
}
|
||||
|
||||
/* oob data */
|
||||
|
||||
/** There is always only one OOB data set stored at a time */
|
||||
|
||||
virtual const address_t& get_peer_sc_oob_address() {
|
||||
return _peer_sc_oob_address;
|
||||
}
|
||||
|
||||
virtual const oob_rand_t& get_peer_sc_oob_random() {
|
||||
return _peer_sc_oob_random;
|
||||
}
|
||||
|
||||
virtual const oob_confirm_t& get_peer_sc_oob_confirm() {
|
||||
return _peer_sc_oob_confirm;
|
||||
}
|
||||
|
||||
virtual void get_sc_oob_data(
|
||||
address_t &peer_address,
|
||||
oob_rand_t &peer_random,
|
||||
oob_confirm_t &peer_confirm,
|
||||
oob_rand_t &local_random
|
||||
) {
|
||||
peer_address = _peer_sc_oob_address;
|
||||
peer_random = _peer_sc_oob_random;
|
||||
peer_confirm = _peer_sc_oob_confirm;
|
||||
local_random = _local_sc_oob_random;
|
||||
}
|
||||
|
||||
virtual const oob_rand_t& get_local_sc_oob_random() {
|
||||
return _local_sc_oob_random;
|
||||
}
|
||||
|
||||
virtual void set_peer_sc_oob_data(
|
||||
const address_t &address,
|
||||
const oob_rand_t &random,
|
||||
const oob_confirm_t &confirm
|
||||
) {
|
||||
_peer_sc_oob_address = address;
|
||||
_peer_sc_oob_random = random;
|
||||
_peer_sc_oob_confirm = confirm;
|
||||
}
|
||||
|
||||
virtual void set_local_sc_oob_random(
|
||||
const oob_rand_t &random
|
||||
) {
|
||||
_local_sc_oob_random = random;
|
||||
}
|
||||
|
||||
/* list management */
|
||||
|
||||
virtual SecurityEntry_t* connect_entry(
|
||||
connection_handle_t connection,
|
||||
BLEProtocol::AddressType_t peer_address_type,
|
||||
const address_t &peer_address,
|
||||
const address_t &local_address
|
||||
) {
|
||||
const bool peer_address_public =
|
||||
(peer_address_type == BLEProtocol::AddressType::PUBLIC);
|
||||
|
||||
for (size_t i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (_db[i].entry.connected) {
|
||||
continue;
|
||||
} else if (peer_address == _identities[i].identity_address
|
||||
&& _db[i].entry.peer_address_is_public == peer_address_public) {
|
||||
return &_db[i].entry;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we din't find one grab the first disconnected slot*/
|
||||
for (size_t i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (!_db[i].entry.connected) {
|
||||
_db[i] = db_store_t();
|
||||
_identities[i] = SecurityEntryIdentity_t();
|
||||
_db[i].entry.peer_address = peer_address;
|
||||
_db[i].entry.local_address = local_address;
|
||||
_db[i].entry.peer_address_is_public = peer_address_public;
|
||||
return &_db[i].entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual void disconnect_entry(connection_handle_t connection) { }
|
||||
|
||||
virtual void remove_entry(address_t peer_identity_address) { }
|
||||
|
||||
virtual void clear_entries() {
|
||||
for (size_t i = 0; i < MAX_ENTRIES; i++) {
|
||||
_db[i] = db_store_t();
|
||||
}
|
||||
_local_identity = SecurityEntryIdentity_t();
|
||||
_local_csrk = csrk_t();
|
||||
}
|
||||
|
||||
virtual void get_whitelist(WhitelistDbCb_t cb, ::Gap::Whitelist_t *whitelist) {
|
||||
/*TODO: fill whitelist*/
|
||||
cb(whitelist);
|
||||
}
|
||||
|
||||
virtual void generate_whitelist_from_bond_table(WhitelistDbCb_t cb, ::Gap::Whitelist_t *whitelist) {
|
||||
for (size_t i = 0; i < MAX_ENTRIES && i < whitelist->capacity; i++) {
|
||||
if (_db[i].entry.peer_address_is_public) {
|
||||
whitelist->addresses[i].type = BLEProtocol::AddressType::PUBLIC;
|
||||
} else {
|
||||
whitelist->addresses[i].type = BLEProtocol::AddressType::RANDOM_STATIC;
|
||||
}
|
||||
|
||||
memcpy(
|
||||
whitelist->addresses[i].address,
|
||||
_identities[i].identity_address.data(),
|
||||
sizeof(BLEProtocol::AddressBytes_t)
|
||||
);
|
||||
}
|
||||
|
||||
cb(whitelist);
|
||||
}
|
||||
|
||||
virtual void update_whitelist(::Gap::Whitelist_t &whitelist) { }
|
||||
|
||||
virtual void set_whitelist(const ::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) { }
|
||||
|
||||
private:
|
||||
|
||||
virtual db_store_t* get_store(connection_handle_t connection) {
|
||||
for (size_t i = 0; i < MAX_ENTRIES; i++) {
|
||||
if (!_db[i].entry.connected) {
|
||||
continue;
|
||||
} else if (connection == _db[i].entry.handle) {
|
||||
return &_db[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db_store_t _db[MAX_ENTRIES];
|
||||
SecurityEntryIdentity_t _identities[MAX_ENTRIES];
|
||||
SecurityEntryIdentity_t _local_identity;
|
||||
csrk_t _local_csrk;
|
||||
public_key_t _public_key_x;
|
||||
public_key_t _public_key_y;
|
||||
|
||||
address_t _peer_sc_oob_address;
|
||||
oob_rand_t _peer_sc_oob_random;
|
||||
oob_confirm_t _peer_sc_oob_confirm;
|
||||
oob_rand_t _local_sc_oob_random;
|
||||
};
|
||||
|
||||
} /* namespace pal */
|
||||
} /* namespace ble */
|
||||
|
||||
#endif /*PAL_MEMORY_SECURITY_MANAGER_DB_H__*/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,548 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 PAL_SECURITY_MANAGER_DB_H__
|
||||
#define PAL_SECURITY_MANAGER_DB_H__
|
||||
|
||||
#include "platform/Callback.h"
|
||||
#include "ble/pal/GapTypes.h"
|
||||
#include "ble/BLETypes.h"
|
||||
#include "ble/Gap.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
|
||||
/* separate structs for keys to allow db implementation
|
||||
* to minimise memory usage, only holding live connection
|
||||
* state in memory */
|
||||
|
||||
struct SecurityEntry_t {
|
||||
SecurityEntry_t()
|
||||
: handle(0),
|
||||
encryption_key_size(0),
|
||||
peer_address_is_public(false),
|
||||
local_address_is_public(false),
|
||||
csrk_stored(false),
|
||||
csrk_mitm_protected(false),
|
||||
ltk_stored(false),
|
||||
ltk_mitm_protected(false),
|
||||
secure_connections_paired(false),
|
||||
connected(false),
|
||||
authenticated(false),
|
||||
is_master(false),
|
||||
encryption_requested(false),
|
||||
encryption_failed(false),
|
||||
encrypted(false),
|
||||
signing_requested(false),
|
||||
mitm_requested(false),
|
||||
mitm_performed(false),
|
||||
attempt_oob(false),
|
||||
oob_mitm_protection(false),
|
||||
oob_present(false) { }
|
||||
|
||||
/**
|
||||
* Reset state of the connection when disconnected.
|
||||
*/
|
||||
void reset() {
|
||||
local_address = address_t();
|
||||
|
||||
connected = false;
|
||||
authenticated = false;
|
||||
is_master = false;
|
||||
|
||||
encryption_requested = false;
|
||||
encryption_failed = false;
|
||||
encrypted = false;
|
||||
signing_requested = false;
|
||||
|
||||
mitm_requested = false;
|
||||
mitm_performed = false;
|
||||
|
||||
attempt_oob = false;
|
||||
oob_mitm_protection = false;
|
||||
oob_present = false;
|
||||
}
|
||||
|
||||
connection_handle_t handle;
|
||||
address_t peer_address;
|
||||
|
||||
uint8_t encryption_key_size;
|
||||
uint8_t peer_address_is_public:1;
|
||||
uint8_t local_address_is_public:1;
|
||||
|
||||
uint8_t csrk_stored:1;
|
||||
uint8_t csrk_mitm_protected:1;
|
||||
uint8_t ltk_stored:1;
|
||||
uint8_t ltk_mitm_protected:1;
|
||||
uint8_t secure_connections_paired:1;
|
||||
|
||||
/* do not store in NVM */
|
||||
|
||||
address_t local_address; /**< address used for connection, possibly different from identity */
|
||||
|
||||
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 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;
|
||||
};
|
||||
|
||||
struct SecurityEntryKeys_t {
|
||||
ltk_t ltk;
|
||||
ediv_t ediv;
|
||||
rand_t rand;
|
||||
};
|
||||
|
||||
struct SecurityEntryIdentity_t {
|
||||
address_t identity_address;
|
||||
irk_t irk;
|
||||
};
|
||||
|
||||
/* callbacks for asynchronous data retrieval from the security db */
|
||||
|
||||
typedef mbed::Callback<void(const SecurityEntry_t*, const SecurityEntryKeys_t*)> SecurityEntryKeysDbCb_t;
|
||||
typedef mbed::Callback<void(connection_handle_t, const csrk_t*)> SecurityEntryCsrkDbCb_t;
|
||||
typedef mbed::Callback<void(::Gap::Whitelist_t*)> WhitelistDbCb_t;
|
||||
|
||||
/**
|
||||
* 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
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Return immediately security entry containing the state
|
||||
* information for active connection.
|
||||
*
|
||||
* @param[in] peer_address peer address in the entry being requested
|
||||
* @return pointer to security entry, NULL if no entry was fined
|
||||
*/
|
||||
virtual SecurityEntry_t* get_entry(
|
||||
const address_t &peer_address
|
||||
) = 0;
|
||||
|
||||
/* local keys */
|
||||
|
||||
/**
|
||||
* Retrieve stored LTK based on passed in EDIV and RAND values.
|
||||
*
|
||||
* @param[in] cb callback that will receive the LTK struct
|
||||
* @param[in] connection handle for the connection requesting the key
|
||||
* @param[in] ediv one of the values used to identify the LTK
|
||||
* @param[in] rand one of the values used to identify the LTK
|
||||
*/
|
||||
virtual void get_entry_local_keys(
|
||||
SecurityEntryKeysDbCb_t cb,
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Retrieve stored LTK generated during secure connections pairing.
|
||||
*
|
||||
* @param[in] cb callback that will receive the LTK struct
|
||||
* @param[in] connection handle for the connection requesting the key
|
||||
*/
|
||||
virtual void get_entry_local_keys(
|
||||
SecurityEntryKeysDbCb_t cb,
|
||||
connection_handle_t connection
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Save new local LTK for a connection.
|
||||
*
|
||||
* @param[in] connection handle for which the LTK is being updated
|
||||
* @param[in] ltk the new LTK, if the device is slave, this is the LTK that will
|
||||
* be used when link is encrypted
|
||||
*/
|
||||
virtual void set_entry_local_ltk(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Update EDIV and RAND used to identify the LTK.
|
||||
*
|
||||
* @param[in] connection handle for the connection which values are being updated
|
||||
* @param[in] ediv new EDIV value
|
||||
* @param[in] rand new RAND value
|
||||
*/
|
||||
virtual void set_entry_local_ediv_rand(
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
) = 0;
|
||||
|
||||
/* peer's keys */
|
||||
|
||||
/**
|
||||
* Return asynchronously the peer signing key through a callback
|
||||
* so that signed packets can be verified.
|
||||
*
|
||||
* @param[in] cb callback which will receive the key
|
||||
* @param[in] connection handle of the connection queried
|
||||
*/
|
||||
virtual void get_entry_peer_csrk(
|
||||
SecurityEntryCsrkDbCb_t cb,
|
||||
connection_handle_t connection
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Return asynchronously the peer encryption key through a callback
|
||||
* so that encryption can be enabled.
|
||||
*
|
||||
* @param[in] cb callback which will receive the key
|
||||
* @param[in] connection handle of the connection queried
|
||||
*/
|
||||
virtual void get_entry_peer_keys(
|
||||
SecurityEntryKeysDbCb_t cb,
|
||||
connection_handle_t connection
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Save new LTK received from the peer.
|
||||
*
|
||||
* @param[in] connection handle for which the LTK is being updated
|
||||
* @param[in] ltk the new LTK, if the peer device is slave, this is the LTK that will
|
||||
* be used when link is encrypted
|
||||
*/
|
||||
virtual void set_entry_peer_ltk(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Update EDIV and RAND used to identify the LTK sent by the peer.
|
||||
*
|
||||
* @param[in] connection handle for the connection which values are being updated
|
||||
* @param[in] ediv new EDIV value
|
||||
* @param[in] rand new RAND value
|
||||
*/
|
||||
virtual void set_entry_peer_ediv_rand(
|
||||
connection_handle_t connection,
|
||||
const ediv_t &ediv,
|
||||
const rand_t &rand
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Update IRK for this connection.
|
||||
*
|
||||
* @param[in] connection handle of the connection being updated
|
||||
* @param[in] irk new IRK value
|
||||
*/
|
||||
virtual void set_entry_peer_irk(
|
||||
connection_handle_t connection,
|
||||
const irk_t &irk
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Update the identity address of the peer.
|
||||
*
|
||||
* @param[in] connection connection for the peer address being updated
|
||||
* @param[in] address_is_public is the identity address public or private
|
||||
* @param[in] peer_address the new address
|
||||
*/
|
||||
virtual void set_entry_peer_bdaddr(
|
||||
connection_handle_t connection,
|
||||
bool address_is_public,
|
||||
const address_t &peer_address
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Update peer signing key.
|
||||
*
|
||||
* @param[in] connection handle of the connection being updated
|
||||
* @param[in] csrk new CSRK value
|
||||
*/
|
||||
virtual void set_entry_peer_csrk(
|
||||
connection_handle_t connection,
|
||||
const csrk_t &csrk
|
||||
) = 0;
|
||||
|
||||
/* local csrk */
|
||||
|
||||
/**
|
||||
* Return local signing key used for signing packets.
|
||||
*
|
||||
* @return pointer to local CSRK
|
||||
*/
|
||||
virtual const csrk_t* get_local_csrk() = 0;
|
||||
|
||||
/**
|
||||
* Update local signing key.
|
||||
*
|
||||
* @param[in] csrk new CSRK value
|
||||
*/
|
||||
virtual void set_local_csrk(
|
||||
const csrk_t &csrk
|
||||
) = 0;
|
||||
|
||||
/* public keys */
|
||||
|
||||
/**
|
||||
* Return local public key.
|
||||
*
|
||||
* @return ref to x component of public key
|
||||
*/
|
||||
virtual const public_key_t& get_public_key_x() = 0;
|
||||
|
||||
/**
|
||||
* Return local public key.
|
||||
*
|
||||
* @return ref to y component of public key
|
||||
*/
|
||||
virtual const public_key_t& get_public_key_y() = 0;
|
||||
|
||||
/**
|
||||
* Set local public key.
|
||||
*
|
||||
* @param[in] public_key_x new public key value of the x coordinate
|
||||
* @param[in] public_key_y new public key value of the y coordinate
|
||||
*/
|
||||
virtual void set_public_key(
|
||||
const public_key_t &public_key_x,
|
||||
const public_key_t &public_key_y
|
||||
) = 0;
|
||||
|
||||
/* oob data */
|
||||
|
||||
/** There is always only one OOB data set stored at a time */
|
||||
|
||||
/**
|
||||
* Return peer address the OOB data belongs to.
|
||||
*
|
||||
* @return peer address
|
||||
*/
|
||||
virtual const address_t& get_peer_sc_oob_address() = 0;
|
||||
|
||||
/**
|
||||
* Return random number from the peer received in OOB data.
|
||||
*
|
||||
* @return random number the peer chose
|
||||
*/
|
||||
virtual const oob_rand_t& get_peer_sc_oob_random() = 0;
|
||||
|
||||
/**
|
||||
* Return confirm number from the peer received in OOB data.
|
||||
*
|
||||
* @return confirm value calculated by peer based
|
||||
* on the random number, its public key and address
|
||||
*/
|
||||
virtual const oob_confirm_t& get_peer_sc_oob_confirm() = 0;
|
||||
|
||||
/**
|
||||
* Return OOB data in a single transaction.
|
||||
*
|
||||
* @param[out] peer_address peer address OOB data belongs to
|
||||
* @param[out] peer_random random number the peer chose
|
||||
* @param[out] peer_confirm confirm value calculated by peer based
|
||||
* on the random number, its public key and address
|
||||
* @param[out] local_random random number chosen by the local device
|
||||
*/
|
||||
virtual void get_sc_oob_data(
|
||||
address_t &peer_address,
|
||||
oob_rand_t &peer_random,
|
||||
oob_confirm_t &peer_confirm,
|
||||
oob_rand_t &local_random
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Return random number used by the local device to calculate
|
||||
* the confirm value sent the peer in OOB data.
|
||||
*
|
||||
* @return random number chosen by local device
|
||||
*/
|
||||
virtual const oob_rand_t& get_local_sc_oob_random() = 0;
|
||||
|
||||
/**
|
||||
* Store the OOB data received from the peer.
|
||||
*
|
||||
* @param address peer address OOB data belongs to
|
||||
* @param random random number the peer chose
|
||||
* @param confirm confirm value calculated by peer based
|
||||
* on the random number, its public key and address
|
||||
*/
|
||||
virtual void set_peer_sc_oob_data(
|
||||
const address_t &address,
|
||||
const oob_rand_t &random,
|
||||
const oob_confirm_t &confirm
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Set random number used for OOB data calculation on the local device.
|
||||
*
|
||||
* @param random random number chosen by the local device
|
||||
*/
|
||||
virtual void set_local_sc_oob_random(
|
||||
const oob_rand_t &random
|
||||
) = 0;
|
||||
|
||||
/* list management */
|
||||
|
||||
/**
|
||||
* Create a new entry or retrieve existing stored entry
|
||||
* and put it in the live connections store to be retrieved
|
||||
* synchronously through connection handle.
|
||||
*
|
||||
* @param[in] connection this will be the index for live entries.
|
||||
* @param[in] peer_address_type type of address
|
||||
* @param[in] peer_address this address will be used to locate existing entry.
|
||||
*
|
||||
* @return pointer to entry newly created or located existing entry.
|
||||
*/
|
||||
virtual SecurityEntry_t* connect_entry(
|
||||
connection_handle_t connection,
|
||||
BLEProtocol::AddressType_t peer_address_type,
|
||||
const address_t &peer_address,
|
||||
const address_t &local_address
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Create a new entry or retrieve existing stored entry
|
||||
* and put it in the live connections store to be retrieved
|
||||
* synchronously through connection handle.
|
||||
*
|
||||
* @param[in] connection this handle will be freed up from the security db
|
||||
*/
|
||||
virtual void disconnect_entry(
|
||||
connection_handle_t connection
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Remove entry for this peer from NVM.
|
||||
*
|
||||
* @param[in] peer_identity_address peer address that no longer needs NVM storage.
|
||||
*/
|
||||
virtual void remove_entry(
|
||||
const address_t peer_identity_address
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Remove all entries from the security DB.
|
||||
*/
|
||||
virtual void clear_entries() = 0;
|
||||
|
||||
/**
|
||||
* Asynchronously return the whitelist stored in NVM through a callback. Function
|
||||
* takes ownership of the memory. The whitelist and the ownership will be returned
|
||||
* in the callback.
|
||||
*
|
||||
* @param[in] cb callback that will receive the whitelist
|
||||
* @param[in] whitelist preallocated whitelist that will be filled in
|
||||
*/
|
||||
virtual void get_whitelist(
|
||||
WhitelistDbCb_t cb,
|
||||
::Gap::Whitelist_t *whitelist
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Asynchronously return a whitelist through a callback, generated from the bond table.
|
||||
* Function takes ownership of the memory. The whitelist and the ownership will be
|
||||
* returned in the callback.
|
||||
*
|
||||
* @param[in] cb callback that will receive the whitelist
|
||||
* @param[in] whitelist preallocated whitelist that will be filled in
|
||||
*/
|
||||
virtual void generate_whitelist_from_bond_table(
|
||||
WhitelistDbCb_t cb,
|
||||
::Gap::Whitelist_t *whitelist
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Update the whitelist stored in NVM by replacing it with new one.
|
||||
*
|
||||
* @param[in] whitelist
|
||||
*/
|
||||
virtual void set_whitelist(
|
||||
const ::Gap::Whitelist_t &whitelist
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Add a new entry to the whitelist in the NVM.
|
||||
*
|
||||
* @param[in] address new whitelist entry
|
||||
*/
|
||||
virtual void add_whitelist_entry(
|
||||
const address_t &address
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Remove whitelist entry from NVM.
|
||||
*
|
||||
* @param[in] address entry to be removed
|
||||
*/
|
||||
virtual void remove_whitelist_entry(
|
||||
const address_t &address
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
*Remove all whitelist entries stored in the NVM.
|
||||
*/
|
||||
virtual void clear_whitelist() = 0;
|
||||
|
||||
/* saving and loading from nvm */
|
||||
|
||||
/**
|
||||
* Read values from storage.
|
||||
*/
|
||||
virtual void restore() = 0;
|
||||
|
||||
/**
|
||||
* Flush all values which might be stored in memory into NVM.
|
||||
*/
|
||||
virtual void sync() = 0;
|
||||
|
||||
/**
|
||||
* Toggle whether values should be preserved across resets.
|
||||
*
|
||||
* @param[in] reload if true values will be preserved across resets.
|
||||
*/
|
||||
virtual void set_restore(bool reload) = 0;
|
||||
};
|
||||
|
||||
} /* namespace pal */
|
||||
} /* namespace ble */
|
||||
|
||||
#endif /*PAL_SECURITY_MANAGER_DB_H__*/
|
|
@ -44,6 +44,14 @@ static const uint16_t advertising_interval_max = 0x4000;
|
|||
static const uint16_t supervision_timeout_min = 0x000A;
|
||||
static const uint16_t supervision_timeout_max = 0x0C80;
|
||||
|
||||
static const Gap::ConnectionParams_t default_connection_params = {
|
||||
/* min conn interval */ 50,
|
||||
/* max conn interval */ 100,
|
||||
/* slave latency */ 0,
|
||||
/* supervision timeout */ 600
|
||||
};
|
||||
|
||||
static const GapScanningParams default_scan_params;
|
||||
|
||||
/*
|
||||
* Return true if value is included in the range [lower_bound : higher_bound]
|
||||
|
@ -381,7 +389,8 @@ GenericGap::GenericGap(
|
|||
_advertising_filter_policy(pal::advertising_filter_policy_t::NO_FILTER),
|
||||
_whitelist(),
|
||||
_advertising_timeout(),
|
||||
_scan_timeout()
|
||||
_scan_timeout(),
|
||||
_connection_event_handler(NULL)
|
||||
{
|
||||
_pal_gap.when_gap_event_received(
|
||||
mbed::callback(this, &GenericGap::on_gap_event_received)
|
||||
|
@ -408,13 +417,14 @@ ble_error_t GenericGap::setAddress(
|
|||
}
|
||||
|
||||
ble_error_t err = _pal_gap.set_random_address(
|
||||
pal::address_t(address, true)
|
||||
ble::address_t(address)
|
||||
);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
_address_type = type;
|
||||
_address = ble::address_t(address);
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
@ -437,7 +447,7 @@ ble_error_t GenericGap::getAddress(
|
|||
BLEProtocol::AddressBytes_t address
|
||||
) {
|
||||
*type = _address_type;
|
||||
pal::address_t address_value;
|
||||
ble::address_t address_value;
|
||||
if (_address_type == BLEProtocol::AddressType::PUBLIC) {
|
||||
address_value = _pal_gap.get_device_address();
|
||||
} else {
|
||||
|
@ -491,6 +501,14 @@ ble_error_t GenericGap::connect(
|
|||
const ConnectionParams_t* connectionParams,
|
||||
const GapScanningParams* scanParams
|
||||
) {
|
||||
if (connectionParams == NULL) {
|
||||
connectionParams = &default_connection_params;
|
||||
}
|
||||
|
||||
if (scanParams == NULL) {
|
||||
scanParams = &default_scan_params;
|
||||
}
|
||||
|
||||
if (is_scan_params_valid(scanParams) == false) {
|
||||
return BLE_ERROR_PARAM_OUT_OF_RANGE;
|
||||
}
|
||||
|
@ -501,12 +519,15 @@ ble_error_t GenericGap::connect(
|
|||
|
||||
// TODO fix upper layer API, address type factorization is incorrect.
|
||||
|
||||
// Force scan stop before initiating the scan used for connection
|
||||
stopScan();
|
||||
|
||||
return _pal_gap.create_connection(
|
||||
scanParams->getInterval(),
|
||||
scanParams->getWindow(),
|
||||
_initiator_policy_mode,
|
||||
(pal::connection_peer_address_type_t::type) peerAddrType,
|
||||
pal::address_t(peerAddr, true),
|
||||
ble::address_t(peerAddr),
|
||||
(pal::own_address_type_t::type) _address_type,
|
||||
connectionParams->minConnectionInterval,
|
||||
connectionParams->maxConnectionInterval,
|
||||
|
@ -864,7 +885,7 @@ ble_error_t GenericGap::startAdvertising(const GapAdvertisingParams& params)
|
|||
(pal::advertising_type_t::type) params.getAdvertisingType(),
|
||||
get_own_address_type(),
|
||||
pal::advertising_peer_address_type_t::PUBLIC_ADDRESS,
|
||||
pal::address_t(),
|
||||
ble::address_t(),
|
||||
pal::advertising_channel_map_t::ALL_ADVERTISING_CHANNELS,
|
||||
_advertising_filter_policy
|
||||
);
|
||||
|
@ -901,6 +922,55 @@ ble_error_t GenericGap::reset(void)
|
|||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
void GenericGap::processConnectionEvent(
|
||||
Handle_t handle,
|
||||
Role_t role,
|
||||
BLEProtocol::AddressType_t peerAddrType,
|
||||
const BLEProtocol::AddressBytes_t peerAddr,
|
||||
BLEProtocol::AddressType_t ownAddrType,
|
||||
const BLEProtocol::AddressBytes_t ownAddr,
|
||||
const ConnectionParams_t *connectionParams
|
||||
) {
|
||||
if (_connection_event_handler) {
|
||||
_connection_event_handler->on_connected(
|
||||
handle,
|
||||
role,
|
||||
peerAddrType,
|
||||
peerAddr,
|
||||
ownAddrType,
|
||||
ownAddr,
|
||||
connectionParams
|
||||
);
|
||||
}
|
||||
|
||||
::Gap::processConnectionEvent(
|
||||
handle,
|
||||
role,
|
||||
peerAddrType,
|
||||
peerAddr,
|
||||
ownAddrType,
|
||||
ownAddr,
|
||||
connectionParams
|
||||
);
|
||||
}
|
||||
|
||||
void GenericGap::processDisconnectionEvent(
|
||||
Handle_t handle,
|
||||
DisconnectionReason_t reason
|
||||
) {
|
||||
if (_connection_event_handler) {
|
||||
_connection_event_handler->on_disconnected(
|
||||
handle,
|
||||
reason
|
||||
);
|
||||
}
|
||||
|
||||
::Gap::processDisconnectionEvent(
|
||||
handle,
|
||||
reason
|
||||
);
|
||||
}
|
||||
|
||||
void GenericGap::on_scan_timeout()
|
||||
{
|
||||
_event_queue.post(mbed::callback(this, &GenericGap::process_scan_timeout));
|
||||
|
@ -972,7 +1042,8 @@ void GenericGap::on_advertising_report(const pal::GapAdvertisingReportEvent& e)
|
|||
advertising.type == pal::received_advertising_type_t::SCAN_RESPONSE,
|
||||
(GapAdvertisingParams::AdvertisingType_t) advertising.type.value(),
|
||||
advertising.data.size(),
|
||||
advertising.data.data()
|
||||
advertising.data.data(),
|
||||
(BLEProtocol::AddressType_t) advertising.address_type.value()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -995,7 +1066,7 @@ void GenericGap::on_connection_complete(const pal::GapConnectionCompleteEvent& e
|
|||
e.connection_latency,
|
||||
e.supervision_timeout
|
||||
};
|
||||
pal::address_t address;
|
||||
ble::address_t address;
|
||||
if (_address_type == BLEProtocol::AddressType::PUBLIC) {
|
||||
address = _pal_gap.get_device_address();
|
||||
} else {
|
||||
|
@ -1098,5 +1169,10 @@ bool GenericGap::initialize_whitelist() const
|
|||
return true;
|
||||
}
|
||||
|
||||
void GenericGap::set_connection_event_handler(pal::ConnectionEventHandler *connection_event_handler)
|
||||
{
|
||||
_connection_event_handler = connection_event_handler;
|
||||
}
|
||||
|
||||
} // namespace generic
|
||||
} // namespace ble
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -332,7 +332,8 @@ static void armHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
|||
(scan->scanReport.eventType == DM_ADV_SCAN_RESPONSE) ? true : false,
|
||||
(GapAdvertisingParams::AdvertisingType_t)scan->scanReport.eventType,
|
||||
scan->scanReport.len,
|
||||
scan->scanReport.pData);
|
||||
scan->scanReport.pData,
|
||||
(BLEProtocol::AddressType_t) scan->scanReport.addrType);
|
||||
}
|
||||
break;
|
||||
case DM_CONN_OPEN_IND:
|
||||
|
|
|
@ -23,13 +23,14 @@
|
|||
|
||||
#include "CordioHCIDriver.h"
|
||||
#include "CordioGattServer.h"
|
||||
#include "CordioSecurityManager.h"
|
||||
#include "CordioPalAttClient.h"
|
||||
#include "ble/pal/AttClientToGattClientAdapter.h"
|
||||
#include "ble/generic/GenericGattClient.h"
|
||||
#include "CordioPalGap.h"
|
||||
#include "CordioPalGenericAccessService.h"
|
||||
#include "ble/generic/GenericGap.h"
|
||||
#include "ble/generic/GenericSecurityManager.h"
|
||||
#include "ble/pal/MemorySecurityDB.h"
|
||||
#include "ble/pal/SimpleEventQueue.h"
|
||||
|
||||
namespace ble {
|
||||
|
@ -127,6 +128,12 @@ public:
|
|||
virtual void processEvents();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Return singleton.
|
||||
* @return GenericGap instance.
|
||||
*/
|
||||
const generic::GenericGap& getGenericGap() const;
|
||||
|
||||
static void stack_handler(wsfEventMask_t event, wsfMsgHdr_t* msg);
|
||||
static void device_manager_cb(dmEvt_t* dm_event);
|
||||
static void connection_handler(dmEvt_t* dm_event);
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
}
|
||||
|
||||
virtual address_t get_device_address() {
|
||||
return address_t(HciGetBdAddr(), true);
|
||||
return address_t(HciGetBdAddr());
|
||||
}
|
||||
|
||||
virtual address_t get_random_address() {
|
||||
|
|
|
@ -0,0 +1,339 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018-2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CORDIO_PAL_SECURITY_MANAGER_
|
||||
#define CORDIO_PAL_SECURITY_MANAGER_
|
||||
|
||||
#include "ble/pal/PalSecurityManager.h"
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_os.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace cordio {
|
||||
|
||||
class CordioSecurityManager : public ::ble::pal::SecurityManager {
|
||||
public:
|
||||
CordioSecurityManager();
|
||||
|
||||
virtual ~CordioSecurityManager();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// SM lifecycle management
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::initialize
|
||||
*/
|
||||
virtual ble_error_t initialize();
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::terminate
|
||||
*/
|
||||
virtual ble_error_t terminate();
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::reset
|
||||
*/
|
||||
virtual ble_error_t reset() ;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Resolving list management
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::read_resolving_list_capacity
|
||||
*/
|
||||
virtual uint8_t read_resolving_list_capacity();
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::add_device_to_resolving_list
|
||||
*/
|
||||
virtual ble_error_t add_device_to_resolving_list(
|
||||
advertising_peer_address_type_t peer_identity_address_type,
|
||||
const address_t &peer_identity_address,
|
||||
const irk_t &peer_irk
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::remove_device_from_resolving_list
|
||||
*/
|
||||
virtual ble_error_t remove_device_from_resolving_list(
|
||||
advertising_peer_address_type_t peer_identity_address_type,
|
||||
const address_t &peer_identity_address
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::clear_resolving_list
|
||||
*/
|
||||
virtual ble_error_t clear_resolving_list();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Feature support
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_secure_connections_support
|
||||
*/
|
||||
virtual ble_error_t set_secure_connections_support(
|
||||
bool enabled, bool secure_connections_only = false
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_secure_connections_support
|
||||
*/
|
||||
virtual ble_error_t get_secure_connections_support(
|
||||
bool &enabled
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Security settings
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_authentication_timeout
|
||||
*/
|
||||
virtual ble_error_t set_authentication_timeout(
|
||||
connection_handle_t, uint16_t timeout_in_10ms
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_authentication_timeout
|
||||
*/
|
||||
virtual ble_error_t get_authentication_timeout(
|
||||
connection_handle_t, uint16_t &timeout_in_10ms
|
||||
);
|
||||
|
||||
virtual ble_error_t slave_security_request(
|
||||
connection_handle_t connection,
|
||||
AuthenticationMask authentication
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Encryption
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::enable_encryption
|
||||
*/
|
||||
virtual ble_error_t enable_encryption(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k,
|
||||
const rand_t &rand,
|
||||
const ediv_t &ediv
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::enable_encryption
|
||||
*/
|
||||
virtual ble_error_t enable_encryption(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::disable_encryption
|
||||
*/
|
||||
virtual ble_error_t disable_encryption(connection_handle_t connection);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_encryption_status
|
||||
*/
|
||||
virtual ble_error_t get_encryption_status(
|
||||
connection_handle_t connection, LinkSecurityStatus_t &status
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_encryption_key_size
|
||||
*/
|
||||
virtual ble_error_t get_encryption_key_size(
|
||||
connection_handle_t, uint8_t &bitsize
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::encrypt_data
|
||||
*/
|
||||
virtual ble_error_t encrypt_data(
|
||||
const key_t &key,
|
||||
encryption_block_t &data
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Privacy
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_private_address_timeout
|
||||
*/
|
||||
virtual ble_error_t set_private_address_timeout(uint16_t timeout_in_seconds);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_ltk
|
||||
*/
|
||||
virtual ble_error_t set_ltk(connection_handle_t connection, const ltk_t <k);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_ltk_not_found
|
||||
*/
|
||||
virtual ble_error_t set_ltk_not_found(
|
||||
connection_handle_t connection
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_irk
|
||||
*/
|
||||
virtual ble_error_t set_irk(const irk_t &irk);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_csrk
|
||||
*/
|
||||
virtual ble_error_t set_csrk(const csrk_t &csrk);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::generate_public_key
|
||||
*/
|
||||
virtual ble_error_t generate_public_key();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Global parameters
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_display_passkey
|
||||
*/
|
||||
virtual ble_error_t set_display_passkey(passkey_num_t passkey);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_io_capability
|
||||
*/
|
||||
virtual ble_error_t set_io_capability(io_capability_t io_capability);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_encryption_key_requirements
|
||||
*/
|
||||
virtual ble_error_t set_encryption_key_requirements(
|
||||
uint8_t min_encryption_key_size,
|
||||
uint8_t max_encryption_key_size
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Authentication
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::send_pairing_request
|
||||
*/
|
||||
virtual ble_error_t send_pairing_request(
|
||||
connection_handle_t connection,
|
||||
bool oob_data_flag,
|
||||
AuthenticationMask authentication_requirements,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::send_pairing_response
|
||||
*/
|
||||
virtual ble_error_t send_pairing_response(
|
||||
connection_handle_t connection,
|
||||
bool oob_data_flag,
|
||||
AuthenticationMask authentication_requirements,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::cancel_pairing
|
||||
*/
|
||||
virtual ble_error_t cancel_pairing(
|
||||
connection_handle_t connection, pairing_failure_t reason
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::request_authentication
|
||||
*/
|
||||
virtual ble_error_t request_authentication(connection_handle_t connection);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_random_data
|
||||
*/
|
||||
virtual ble_error_t get_random_data(random_data_t &random_data);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// MITM
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::passkey_request_reply
|
||||
*/
|
||||
virtual ble_error_t passkey_request_reply(
|
||||
connection_handle_t connection,
|
||||
passkey_num_t passkey
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::legacy_pairing_oob_data_request_reply
|
||||
*/
|
||||
virtual ble_error_t legacy_pairing_oob_data_request_reply(
|
||||
connection_handle_t connection,
|
||||
const oob_tk_t &oob_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::confirmation_entered
|
||||
*/
|
||||
virtual ble_error_t confirmation_entered(
|
||||
connection_handle_t connection, bool confirmation
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::send_keypress_notification
|
||||
*/
|
||||
virtual ble_error_t send_keypress_notification(
|
||||
connection_handle_t connection, Keypress_t keypress
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::oob_data_verified
|
||||
*/
|
||||
virtual ble_error_t oob_data_verified(
|
||||
connection_handle_t connection,
|
||||
const oob_rand_t &local_random,
|
||||
const oob_rand_t &peer_random
|
||||
);
|
||||
|
||||
// singleton of the ARM Cordio Security Manager
|
||||
static CordioSecurityManager &get_security_manager();
|
||||
|
||||
// Event handler
|
||||
static bool sm_handler(const wsfMsgHdr_t* msg);
|
||||
|
||||
private:
|
||||
bool _use_default_passkey;
|
||||
passkey_num_t _default_passkey;
|
||||
};
|
||||
|
||||
} // cordio
|
||||
} // vendor
|
||||
} // pal
|
||||
} // ble
|
||||
|
||||
#endif /* CORDIO_PAL_SECURITY_MANAGER_ */
|
|
@ -35,6 +35,7 @@
|
|||
#include "mbed_assert.h"
|
||||
|
||||
#include "CordioPalAttClient.h"
|
||||
#include "CordioPalSecurityManager.h"
|
||||
|
||||
/*! WSF handler ID */
|
||||
wsfHandlerId_t stack_handler_id;
|
||||
|
@ -171,6 +172,11 @@ const char* BLE::getVersion()
|
|||
}
|
||||
|
||||
const ::Gap& BLE::getGap() const
|
||||
{
|
||||
return getGenericGap();
|
||||
};
|
||||
|
||||
const generic::GenericGap& BLE::getGenericGap() const
|
||||
{
|
||||
static pal::vendor::cordio::Gap& cordio_pal_gap =
|
||||
pal::vendor::cordio::Gap::get_gap();
|
||||
|
@ -181,7 +187,7 @@ const ::Gap& BLE::getGap() const
|
|||
cordio_gap_service
|
||||
);
|
||||
return gap;
|
||||
};
|
||||
}
|
||||
|
||||
GattServer& BLE::getGattServer()
|
||||
{
|
||||
|
@ -205,12 +211,21 @@ const GattServer& BLE::getGattServer() const
|
|||
|
||||
SecurityManager& BLE::getSecurityManager()
|
||||
{
|
||||
return cordio::SecurityManager::getInstance();
|
||||
const BLE* self = this;
|
||||
return const_cast<SecurityManager&>(self->getSecurityManager());
|
||||
}
|
||||
|
||||
const SecurityManager& BLE::getSecurityManager() const
|
||||
{
|
||||
return cordio::SecurityManager::getInstance();
|
||||
static pal::MemorySecurityDb m_db;
|
||||
pal::vendor::cordio::CordioSecurityManager &m_pal = pal::vendor::cordio::CordioSecurityManager::get_security_manager();
|
||||
static generic::GenericSecurityManager m_instance(
|
||||
m_pal,
|
||||
m_db,
|
||||
const_cast<generic::GenericGap&>(getGenericGap())
|
||||
);
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void BLE::waitForEvent()
|
||||
|
@ -241,6 +256,10 @@ void BLE::processEvents()
|
|||
return;
|
||||
}
|
||||
|
||||
if (ble::pal::vendor::cordio::CordioSecurityManager::get_security_manager().sm_handler(msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(msg->event) {
|
||||
case DM_RESET_CMPL_IND: {
|
||||
::BLE::InitializationCompleteCallbackContext context = {
|
||||
|
@ -311,11 +330,10 @@ void BLE::stack_setup()
|
|||
SecInit();
|
||||
|
||||
// Note: enable once security is supported
|
||||
#if 0
|
||||
SecRandInit();
|
||||
SecAesInit();
|
||||
SecCmacInit();
|
||||
SecEccInit();
|
||||
#endif
|
||||
|
||||
handlerId = WsfOsSetNextHandler(HciHandler);
|
||||
HciHandlerInit(handlerId);
|
||||
|
@ -329,10 +347,8 @@ void BLE::stack_setup()
|
|||
DmSecInit();
|
||||
|
||||
// Note: enable once security is supported
|
||||
#if 0
|
||||
DmSecLescInit();
|
||||
DmPrivInit();
|
||||
#endif
|
||||
DmHandlerInit(handlerId);
|
||||
|
||||
handlerId = WsfOsSetNextHandler(L2cSlaveHandler);
|
||||
|
@ -349,11 +365,15 @@ void BLE::stack_setup()
|
|||
|
||||
handlerId = WsfOsSetNextHandler(SmpHandler);
|
||||
SmpHandlerInit(handlerId);
|
||||
SmprInit();
|
||||
SmprScInit();
|
||||
SmpiInit();
|
||||
SmpiScInit();
|
||||
|
||||
stack_handler_id = WsfOsSetNextHandler(&BLE::stack_handler);
|
||||
|
||||
HciSetMaxRxAclLen(100);
|
||||
|
||||
DmRegister(BLE::device_manager_cb);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, BLE::device_manager_cb);
|
||||
AttConnRegister(BLE::connection_handler);
|
||||
|
|
|
@ -0,0 +1,601 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018-2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "CordioPalSecurityManager.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "wsf_os.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace cordio {
|
||||
|
||||
CordioSecurityManager::CordioSecurityManager() :
|
||||
::ble::pal::SecurityManager(),
|
||||
_use_default_passkey(false),
|
||||
_default_passkey(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CordioSecurityManager::~CordioSecurityManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// SM lifecycle management
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::initialize()
|
||||
{
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::terminate()
|
||||
{
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::reset()
|
||||
{
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Resolving list management
|
||||
//
|
||||
|
||||
uint8_t CordioSecurityManager::read_resolving_list_capacity()
|
||||
{
|
||||
// FIXME: implement
|
||||
return 0;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::add_device_to_resolving_list(
|
||||
advertising_peer_address_type_t peer_identity_address_type,
|
||||
const address_t &peer_identity_address,
|
||||
const irk_t &peer_irk
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::remove_device_from_resolving_list(
|
||||
advertising_peer_address_type_t peer_identity_address_type,
|
||||
const address_t& peer_identity_address
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::clear_resolving_list()
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Feature support
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::set_secure_connections_support(
|
||||
bool enabled, bool secure_connections_only
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::get_secure_connections_support(
|
||||
bool &enabled
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Security settings
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::set_authentication_timeout(
|
||||
connection_handle_t, uint16_t timeout_in_10ms
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::get_authentication_timeout(
|
||||
connection_handle_t, uint16_t &timeout_in_10ms
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::slave_security_request(
|
||||
connection_handle_t connection,
|
||||
AuthenticationMask authentication
|
||||
) {
|
||||
DmSecSlaveReq(connection, authentication.value());
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Encryption
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::enable_encryption(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k,
|
||||
const rand_t &rand,
|
||||
const ediv_t &ediv
|
||||
) {
|
||||
dmSecLtk_t sec_ltk;
|
||||
memcpy(sec_ltk.key, ltk.data(), ltk.size());
|
||||
memcpy(sec_ltk.rand, rand.data(), rand.size());
|
||||
memcpy(&sec_ltk.ediv, ediv.data(), ediv.size());
|
||||
|
||||
DmSecEncryptReq(
|
||||
connection,
|
||||
/* FIXME: Security Level */ DM_SEC_LEVEL_ENC_AUTH,
|
||||
&sec_ltk
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::enable_encryption(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
) {
|
||||
dmSecLtk_t sec_ltk = { 0 };
|
||||
memcpy(sec_ltk.key, ltk.data(), ltk.size());
|
||||
|
||||
DmSecEncryptReq(
|
||||
connection,
|
||||
DM_SEC_LEVEL_ENC_LESC,
|
||||
&sec_ltk
|
||||
);
|
||||
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::disable_encryption(connection_handle_t connection)
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::get_encryption_status(
|
||||
connection_handle_t connection, LinkSecurityStatus_t &status
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::get_encryption_key_size(
|
||||
connection_handle_t, uint8_t &bitsize
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::encrypt_data(
|
||||
const key_t &key,
|
||||
encryption_block_t &data
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Privacy
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::set_private_address_timeout(
|
||||
uint16_t timeout_in_seconds
|
||||
) {
|
||||
DmPrivSetResolvablePrivateAddrTimeout(timeout_in_seconds);
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::set_ltk(
|
||||
connection_handle_t connection, const ltk_t& ltk
|
||||
) {
|
||||
// FIXME: get access to the security level of a key
|
||||
DmSecLtkRsp(
|
||||
connection,
|
||||
/* key found */ true,
|
||||
/* sec level ??? */ DM_SEC_LEVEL_ENC_AUTH,
|
||||
const_cast<uint8_t*>(ltk.data())
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::set_ltk_not_found(
|
||||
connection_handle_t connection
|
||||
) {
|
||||
DmSecLtkRsp(
|
||||
connection,
|
||||
/* key found */ false,
|
||||
/* sec level ??? */ DM_SEC_LEVEL_NONE,
|
||||
NULL
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::set_irk(const irk_t& irk)
|
||||
{
|
||||
DmSecSetLocalIrk(const_cast<uint8_t*>(irk.data()));
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::set_csrk(const csrk_t& csrk)
|
||||
{
|
||||
DmSecSetLocalCsrk(const_cast<uint8_t*>(csrk.data()));
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::generate_public_key()
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Global parameters
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::set_display_passkey(passkey_num_t passkey)
|
||||
{
|
||||
if (passkey) {
|
||||
_use_default_passkey = true;
|
||||
_default_passkey = passkey;
|
||||
} else {
|
||||
_use_default_passkey = false;
|
||||
}
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::set_io_capability(io_capability_t io_capability)
|
||||
{
|
||||
pSmpCfg->auth = io_capability.value();
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::set_encryption_key_requirements(
|
||||
uint8_t min_encryption_key_size,
|
||||
uint8_t max_encryption_key_size
|
||||
) {
|
||||
if ((min_encryption_key_size < 7) || (min_encryption_key_size > 16) ||
|
||||
(min_encryption_key_size > max_encryption_key_size)) {
|
||||
return BLE_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
pSmpCfg->minKeyLen = min_encryption_key_size;
|
||||
pSmpCfg->maxKeyLen = max_encryption_key_size;
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Authentication
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::send_pairing_request(
|
||||
connection_handle_t connection,
|
||||
bool oob_data_flag,
|
||||
AuthenticationMask authentication_requirements,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
) {
|
||||
//FIXME: understand if this is required
|
||||
pSmpCfg->auth = authentication_requirements.value();
|
||||
|
||||
DmSecPairReq(
|
||||
connection,
|
||||
oob_data_flag,
|
||||
authentication_requirements.value(),
|
||||
initiator_dist.value(),
|
||||
responder_dist.value()
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::send_pairing_response(
|
||||
connection_handle_t connection,
|
||||
bool oob_data_flag,
|
||||
AuthenticationMask authentication_requirements,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
) {
|
||||
DmSecPairRsp(
|
||||
connection,
|
||||
oob_data_flag,
|
||||
authentication_requirements.value(),
|
||||
initiator_dist.value(),
|
||||
responder_dist.value()
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::cancel_pairing(
|
||||
connection_handle_t connection, pairing_failure_t reason
|
||||
) {
|
||||
DmSecCancelReq(connection, reason.value());
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::request_authentication(connection_handle_t connection)
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::get_random_data(random_data_t &random_data)
|
||||
{
|
||||
SecRand(random_data.buffer(), random_data.size());
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// MITM
|
||||
//
|
||||
|
||||
ble_error_t CordioSecurityManager::passkey_request_reply(
|
||||
connection_handle_t connection, passkey_num_t passkey
|
||||
) {
|
||||
DmSecAuthRsp(
|
||||
connection,
|
||||
3,
|
||||
reinterpret_cast<uint8_t*>(&passkey)
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::legacy_pairing_oob_data_request_reply(
|
||||
connection_handle_t connection,
|
||||
const oob_tk_t &oob_data
|
||||
) {
|
||||
DmSecAuthRsp(
|
||||
connection,
|
||||
16,
|
||||
const_cast<uint8_t*>(oob_data.data())
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::confirmation_entered(
|
||||
connection_handle_t connection, bool confirmation
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::send_keypress_notification(
|
||||
connection_handle_t connection, Keypress_t keypress
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t CordioSecurityManager::oob_data_verified(
|
||||
connection_handle_t connection,
|
||||
const oob_rand_t &local_random,
|
||||
const oob_rand_t &peer_random
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
CordioSecurityManager& CordioSecurityManager::get_security_manager()
|
||||
{
|
||||
static CordioSecurityManager _security_manager;
|
||||
return _security_manager;
|
||||
}
|
||||
|
||||
bool CordioSecurityManager::sm_handler(const wsfMsgHdr_t* msg) {
|
||||
SecurityManagerEventHandler* handler =
|
||||
get_security_manager().get_event_handler();
|
||||
|
||||
if ((msg == NULL) || (handler == NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (msg->event) {
|
||||
case DM_SEC_PAIR_CMPL_IND: {
|
||||
dmSecPairCmplIndEvt_t* evt = (dmSecPairCmplIndEvt_t*) msg;
|
||||
// Note: authentication and bonding flags present in the auth field
|
||||
handler->on_pairing_completed(evt->hdr.param);
|
||||
return true;
|
||||
}
|
||||
|
||||
case DM_SEC_PAIR_FAIL_IND: {
|
||||
connection_handle_t connection = msg->param;
|
||||
uint8_t status = msg->status;
|
||||
|
||||
if (status >= pairing_failure_t::PASSKEY_ENTRY_FAILED &&
|
||||
status <= pairing_failure_t::CROSS_TRANSPORT_KEY_DERIVATION_OR_GENERATION_NOT_ALLOWED) {
|
||||
handler->on_pairing_error(
|
||||
connection,
|
||||
(pairing_failure_t::type) status
|
||||
);
|
||||
} else if (status == SMP_ERR_MEMORY) {
|
||||
// note: report the error as an unspecified error
|
||||
handler->on_pairing_error(
|
||||
connection,
|
||||
pairing_failure_t::UNSPECIFIED_REASON
|
||||
);
|
||||
} else if (msg->status == SMP_ERR_TIMEOUT) {
|
||||
handler->on_pairing_timed_out(connection);
|
||||
} else {
|
||||
// note: report the error as an unspecified error
|
||||
handler->on_pairing_error(
|
||||
connection,
|
||||
pairing_failure_t::UNSPECIFIED_REASON
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case DM_SEC_ENCRYPT_IND: {
|
||||
dmSecEncryptIndEvt_t* evt = (dmSecEncryptIndEvt_t*) msg;
|
||||
// note: the field usingLtk of the message indicates if an LTK was
|
||||
// used to encrypt the link
|
||||
handler->on_link_encryption_result(evt->hdr.param, link_encryption_t::ENCRYPTED);
|
||||
return true;
|
||||
}
|
||||
|
||||
case DM_SEC_ENCRYPT_FAIL_IND: {
|
||||
// note: msg->status contains the encryption failure status
|
||||
handler->on_link_encryption_result(msg->param, link_encryption_t::NOT_ENCRYPTED);
|
||||
return true;
|
||||
}
|
||||
|
||||
case DM_SEC_AUTH_REQ_IND: {
|
||||
dmSecAuthReqIndEvt_t* evt = (dmSecAuthReqIndEvt_t*) msg;
|
||||
connection_handle_t connection = evt->hdr.param;
|
||||
|
||||
if (evt->oob) {
|
||||
handler->on_legacy_pairing_oob_request(connection);
|
||||
} else if (evt->display) {
|
||||
if (get_security_manager()._use_default_passkey) {
|
||||
handler->on_passkey_display(
|
||||
connection,
|
||||
get_security_manager()._default_passkey
|
||||
);
|
||||
DmSecAuthRsp(
|
||||
connection,
|
||||
3,
|
||||
reinterpret_cast<uint8_t*>(&(get_security_manager()._default_passkey))
|
||||
);
|
||||
} else {
|
||||
// FIXME: generate a random passkey
|
||||
passkey_num_t passkey = 0x00654321;
|
||||
handler->on_passkey_display(connection, passkey);
|
||||
DmSecAuthRsp(
|
||||
connection, 3, reinterpret_cast<uint8_t*>(&passkey)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
handler->on_passkey_request(connection);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case DM_SEC_KEY_IND: {
|
||||
dmSecKeyIndEvt_t* evt = (dmSecKeyIndEvt_t*) msg;
|
||||
connection_handle_t connection = evt->hdr.param;
|
||||
|
||||
switch(evt->type) {
|
||||
case DM_KEY_LOCAL_LTK:
|
||||
// FIXME: forward local ltk
|
||||
#if 0
|
||||
printf("local ltk: ");
|
||||
for (size_t i = 0; i < sizeof(ltk_t); ++i) {
|
||||
printf("%02X ", evt->keyData.ltk.key[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
printf("local ediv: %04X\r\n", evt->keyData.ltk.ediv);
|
||||
printf("local rand: ");
|
||||
for (size_t i = 0; i < sizeof(rand_t); ++i) {
|
||||
printf("%02X ", evt->keyData.ltk.rand[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
memcpy(local_ltk, evt->keyData.ltk.key, sizeof(local_ltk));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case DM_KEY_PEER_LTK:
|
||||
handler->on_keys_distributed_ltk(
|
||||
connection,
|
||||
ltk_t(reinterpret_cast<uint8_t*>(evt->keyData.ltk.key))
|
||||
);
|
||||
handler->on_keys_distributed_ediv_rand(
|
||||
connection,
|
||||
ediv_t(reinterpret_cast<uint8_t*>(&(evt->keyData.ltk.ediv))),
|
||||
evt->keyData.ltk.rand
|
||||
);
|
||||
break;
|
||||
|
||||
case DM_KEY_IRK:
|
||||
handler->on_keys_distributed_bdaddr(
|
||||
connection,
|
||||
(advertising_peer_address_type_t::type) evt->keyData.irk.addrType,
|
||||
evt->keyData.irk.bdAddr
|
||||
);
|
||||
handler->on_keys_distributed_irk(
|
||||
connection,
|
||||
irk_t(reinterpret_cast<uint8_t*>(evt->keyData.irk.key))
|
||||
);
|
||||
break;
|
||||
|
||||
case DM_KEY_CSRK:
|
||||
handler->on_keys_distributed_csrk(
|
||||
connection, evt->keyData.csrk.key
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: what to do with the security level and encryption key len ???
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case DM_SEC_LTK_REQ_IND: {
|
||||
hciLeLtkReqEvt_t* evt = (hciLeLtkReqEvt_t*) msg;
|
||||
handler->on_ltk_request(
|
||||
evt->hdr.param,
|
||||
reinterpret_cast<uint8_t*>(&evt->encDiversifier),
|
||||
evt->randNum
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
case DM_SEC_PAIR_IND: {
|
||||
dmSecPairIndEvt_t* evt = (dmSecPairIndEvt_t*) msg;
|
||||
handler->on_pairing_request(
|
||||
/* connection */ evt->hdr.param,
|
||||
evt->oob,
|
||||
evt->auth,
|
||||
evt->iKeyDist,
|
||||
evt->rKeyDist
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
case DM_SEC_SLAVE_REQ_IND:
|
||||
return true;
|
||||
|
||||
case DM_SEC_CALC_OOB_IND:
|
||||
return true;
|
||||
|
||||
case DM_SEC_ECC_KEY_IND:
|
||||
return true;
|
||||
|
||||
case DM_SEC_COMPARE_IND:
|
||||
return true;
|
||||
|
||||
case DM_SEC_KEYPRESS_IND:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // cordio
|
||||
} // vendor
|
||||
} // pal
|
||||
} // ble
|
||||
|
|
@ -168,7 +168,8 @@ static void maximHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
|||
(scanReport->eventType == DM_ADV_SCAN_RESPONSE) ? true : false,
|
||||
(GapAdvertisingParams::AdvertisingType_t)scanReport->eventType,
|
||||
scanReport->len,
|
||||
scanReport->pData);
|
||||
scanReport->pData,
|
||||
(BLEProtocol::AddressType_t) scan->scanReport.addrType);
|
||||
}
|
||||
break;
|
||||
case DM_CONN_OPEN_IND:
|
||||
|
@ -281,7 +282,7 @@ ble_error_t MaximBLE::init(BLE::InstanceID_t instanceID, FunctionPointerWithCont
|
|||
DmRegister(DmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, DmCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
|
||||
|
||||
/* Reset the device */
|
||||
reset_complete = 0;
|
||||
DmDevReset();
|
||||
|
|
|
@ -220,7 +220,8 @@ static void btle_handler(ble_evt_t *p_ble_evt)
|
|||
advReport->scan_rsp,
|
||||
static_cast<GapAdvertisingParams::AdvertisingType_t>(advReport->type),
|
||||
advReport->dlen,
|
||||
advReport->data);
|
||||
advReport->data,
|
||||
static_cast<BLEProtocol::AddressType_t>(advReport->peer_addr.addr_type));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -287,7 +287,8 @@ static void btle_handler(ble_evt_t *p_ble_evt)
|
|||
advReport->scan_rsp,
|
||||
static_cast<GapAdvertisingParams::AdvertisingType_t>(advReport->type),
|
||||
advReport->dlen,
|
||||
advReport->data);
|
||||
advReport->data,
|
||||
static_cast<BLEProtocol::AddressType_t>(advReport->peer_addr.addr_type));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,614 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018-2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nRF5xPalSecurityManager.h"
|
||||
#include "nrf_ble.h"
|
||||
#include "nrf_ble_gap.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace nordic {
|
||||
|
||||
namespace {
|
||||
static ble_error_t convert_sd_error(uint32_t err) {
|
||||
// TODO: implementation
|
||||
return err ? BLE_ERROR_UNSPECIFIED : BLE_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nRF5xSecurityManager::nRF5xSecurityManager()
|
||||
: ::ble::pal::SecurityManager(),
|
||||
_io_capability(io_capability_t::NO_INPUT_NO_OUTPUT),
|
||||
_max_encryption_key_size(16)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
nRF5xSecurityManager::~nRF5xSecurityManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// SM lifecycle management
|
||||
//
|
||||
|
||||
ble_error_t nRF5xSecurityManager::initialize()
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::terminate()
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::reset()
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Resolving list management
|
||||
//
|
||||
|
||||
// FIXME: on nordic, the irk is passed in sd_ble_gap_scan_start where whitelist
|
||||
// and resolving list are all mixed up.
|
||||
|
||||
uint8_t nRF5xSecurityManager::read_resolving_list_capacity()
|
||||
{
|
||||
// FIXME: implement
|
||||
return 0;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::add_device_to_resolving_list(
|
||||
advertising_peer_address_type_t peer_identity_address_type,
|
||||
const address_t &peer_identity_address,
|
||||
const irk_t &peer_irk
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::remove_device_from_resolving_list(
|
||||
advertising_peer_address_type_t peer_identity_address_type,
|
||||
const address_t &peer_identity_address
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::clear_resolving_list()
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Feature support
|
||||
//
|
||||
|
||||
ble_error_t nRF5xSecurityManager::get_secure_connections_support(
|
||||
bool &enabled
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_io_capability(
|
||||
io_capability_t io_capability
|
||||
) {
|
||||
_io_capability = io_capability;
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Security settings
|
||||
//
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_authentication_timeout(
|
||||
connection_handle_t connection, uint16_t timeout_in_10ms
|
||||
) {
|
||||
// NOTE: Nothing in the Nordic API to manipulate the authentication timeout
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::get_authentication_timeout(
|
||||
connection_handle_t connection, uint16_t &timeout_in_10ms
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_encryption_key_requirements(
|
||||
uint8_t min_encryption_key_size,
|
||||
uint8_t max_encryption_key_size
|
||||
) {
|
||||
_max_encryption_key_size = max_encryption_key_size;
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::slave_security_request(
|
||||
connection_handle_t connection,
|
||||
AuthenticationMask authentication
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Encryption
|
||||
//
|
||||
|
||||
ble_error_t nRF5xSecurityManager::enable_encryption(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
) {
|
||||
// use sd_ble_gap_encrypt it requires:
|
||||
// - ediv
|
||||
// - rand
|
||||
// - ltk
|
||||
// - flag indicating if the key was generated with lesc
|
||||
// - flag indicated if the key is an authenticated key
|
||||
// - flag indicating the length of the ltk ???
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::disable_encryption(connection_handle_t connection)
|
||||
{
|
||||
// NO FUNCTION to disable encryption
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::get_encryption_status(
|
||||
connection_handle_t connection, LinkSecurityStatus_t &status
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::get_encryption_key_size(
|
||||
connection_handle_t connection, uint8_t &bitsize
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::enable_encryption(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k,
|
||||
const rand_t &rand,
|
||||
const ediv_t &ediv
|
||||
) {
|
||||
// NO FUNCTION to disable encryption
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::encrypt_data(
|
||||
const key_t &key,
|
||||
encryption_block_t &data
|
||||
) {
|
||||
// NO FUNCTION to disable encryption
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Privacy
|
||||
//
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_private_address_timeout(
|
||||
uint16_t timeout_in_seconds
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
//
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_ltk_not_found(
|
||||
connection_handle_t connection
|
||||
) {
|
||||
// usefulness ?
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_ltk(
|
||||
connection_handle_t connection, const ltk_t <k
|
||||
) {
|
||||
// usefulness ?
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_irk(const irk_t& irk)
|
||||
{
|
||||
memcpy(_irk.buffer(), irk.data(), _csrk.size());
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_csrk(const csrk_t& csrk)
|
||||
{
|
||||
memcpy(_csrk.buffer(), csrk.data(), _csrk.size());
|
||||
return BLE_ERROR_NONE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Authentication
|
||||
//
|
||||
|
||||
ble_error_t nRF5xSecurityManager::send_pairing_request(
|
||||
connection_handle_t connection,
|
||||
bool oob_data_flag,
|
||||
AuthenticationMask authentication_requirements,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
) {
|
||||
ble_gap_sec_params_t security_params = {
|
||||
/* bond */ authentication_requirements.get_bondable(),
|
||||
/* mitm */ authentication_requirements.get_mitm(),
|
||||
/* lesc */ authentication_requirements.get_secure_connections(),
|
||||
/* keypress */ authentication_requirements.get_keypress_notification(),
|
||||
/* io_caps */ _io_capability.value(),
|
||||
/* oob */ oob_data_flag,
|
||||
/* min_key_size */ 7, // FIXME!
|
||||
/* max_key_size */ _max_encryption_key_size,
|
||||
/* kdist_periph */ {
|
||||
/* enc */ responder_dist.get_encryption(),
|
||||
/* id */ responder_dist.get_identity(),
|
||||
/* sign */ responder_dist.get_signing(),
|
||||
/* link */ responder_dist.get_link()
|
||||
},
|
||||
/* kdist_central */ {
|
||||
/* enc */ initiator_dist.get_encryption(),
|
||||
/* id */ initiator_dist.get_identity(),
|
||||
/* sign */ initiator_dist.get_signing(),
|
||||
/* link */ initiator_dist.get_link()
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t err = sd_ble_gap_authenticate(
|
||||
connection,
|
||||
&security_params
|
||||
);
|
||||
|
||||
return convert_sd_error(err);
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::send_pairing_response(
|
||||
connection_handle_t connection,
|
||||
bool oob_data_flag,
|
||||
AuthenticationMask authentication_requirements,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
) {
|
||||
ble_gap_sec_params_t security_params = {
|
||||
/* bond */ authentication_requirements.get_bondable(),
|
||||
/* mitm */ authentication_requirements.get_mitm(),
|
||||
/* lesc */ authentication_requirements.get_secure_connections(),
|
||||
/* keypress */ authentication_requirements.get_keypress_notification(),
|
||||
/* io_caps */ _io_capability.value(),
|
||||
/* oob */ oob_data_flag,
|
||||
/* min_key_size */ 7, // FIXME!
|
||||
/* max_key_size */ _max_encryption_key_size,
|
||||
/* kdist_periph */ {
|
||||
/* enc */ responder_dist.get_encryption(),
|
||||
/* id */ responder_dist.get_identity(),
|
||||
/* sign */ responder_dist.get_signing(),
|
||||
/* link */ responder_dist.get_link()
|
||||
},
|
||||
/* kdist_central */ {
|
||||
/* enc */ initiator_dist.get_encryption(),
|
||||
/* id */ initiator_dist.get_identity(),
|
||||
/* sign */ initiator_dist.get_signing(),
|
||||
/* link */ initiator_dist.get_link()
|
||||
}
|
||||
};
|
||||
|
||||
ble_gap_sec_keyset_t keyset = {
|
||||
/* keys_own */ {
|
||||
/* encryption key */ NULL,
|
||||
/* id key */ NULL, /* FIXME: pass in the IRK mixed up with address ?!?*/
|
||||
/* signing key */ NULL, /* FIXME: put _csrk in the type of data structure used by nordic */
|
||||
/* P-256 Public Key */ NULL
|
||||
},
|
||||
/* keys_peer */ {
|
||||
// FIXME: The softdevice requires the application to maintain this memory chunk ...
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t err = sd_ble_gap_sec_params_reply(
|
||||
connection,
|
||||
/* status */ BLE_GAP_SEC_STATUS_SUCCESS,
|
||||
/* params */ &security_params,
|
||||
/* keys ... */ &keyset
|
||||
);
|
||||
|
||||
return convert_sd_error(err);
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::cancel_pairing(
|
||||
connection_handle_t connection, pairing_failure_t reason
|
||||
) {
|
||||
// FIXME: there is no fixed function that can be used to cancel pairing all
|
||||
// the time. However sd_ble_gap_sec_params_reply should be used to cancel a
|
||||
// pairing after a pairing request.
|
||||
|
||||
// sd_ble_gap_auth_key_reply should be used to cancel pairing when a key
|
||||
// entered by the user is required.
|
||||
|
||||
uint32_t err = sd_ble_gap_sec_params_reply(
|
||||
connection,
|
||||
/* status */ reason.value() | 0x80,
|
||||
/* params */ NULL,
|
||||
/* keys ... */ NULL
|
||||
);
|
||||
|
||||
return convert_sd_error(err);
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::request_authentication(connection_handle_t connection)
|
||||
{
|
||||
uint8_t authentication_requirements;
|
||||
// FIXME: need authentication requirements from the caller
|
||||
ble_gap_sec_params_t security_params = {
|
||||
/* bond */ static_cast<uint8_t>((authentication_requirements >> 0) & 3),
|
||||
/* mitm */static_cast<uint8_t>((authentication_requirements >> 2) & 1),
|
||||
/* lesc */ static_cast<uint8_t>((authentication_requirements >> 3) & 1),
|
||||
/* keypress */ static_cast<uint8_t>((authentication_requirements >> 4) & 1)
|
||||
/* other parameters ignored ??? TODO: check*/
|
||||
};
|
||||
|
||||
uint32_t err = sd_ble_gap_authenticate(
|
||||
connection,
|
||||
&security_params
|
||||
);
|
||||
|
||||
return convert_sd_error(err);
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::get_random_data(random_data_t &random_data)
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::generate_public_key()
|
||||
{
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// MITM
|
||||
//
|
||||
|
||||
ble_error_t nRF5xSecurityManager::set_display_passkey(
|
||||
passkey_num_t passkey
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::passkey_request_reply(
|
||||
connection_handle_t connection, const passkey_num_t passkey
|
||||
) {
|
||||
uint32_t err = sd_ble_gap_auth_key_reply(
|
||||
connection,
|
||||
BLE_GAP_AUTH_KEY_TYPE_PASSKEY,
|
||||
/* FIXME: convert passkey_num into and asci key */ NULL
|
||||
);
|
||||
|
||||
return convert_sd_error(err);
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::legacy_pairing_oob_data_request_reply(
|
||||
connection_handle_t connection, const oob_tk_t& oob_data
|
||||
) {
|
||||
uint32_t err = sd_ble_gap_auth_key_reply(
|
||||
connection,
|
||||
BLE_GAP_AUTH_KEY_TYPE_OOB,
|
||||
oob_data.data()
|
||||
);
|
||||
|
||||
return convert_sd_error(err);
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::confirmation_entered(
|
||||
connection_handle_t connection, bool confirmation
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::send_keypress_notification(
|
||||
connection_handle_t connection, Keypress_t keypress
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ble_error_t nRF5xSecurityManager::oob_data_verified(
|
||||
connection_handle_t connection,
|
||||
const oob_rand_t &local_random,
|
||||
const oob_rand_t &peer_random
|
||||
) {
|
||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nRF5xSecurityManager& nRF5xSecurityManager::get_security_manager()
|
||||
{
|
||||
static nRF5xSecurityManager _security_manager;
|
||||
return _security_manager;
|
||||
}
|
||||
|
||||
bool nRF5xSecurityManager::sm_handler(const ble_evt_t *evt)
|
||||
{
|
||||
SecurityManagerEventHandler* handler =
|
||||
get_security_manager().get_event_handler();
|
||||
|
||||
if ((evt == NULL) || (handler == NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ble_gap_evt_t& gap_evt = evt->evt.gap_evt;
|
||||
uint16_t connection = gap_evt.conn_handle;
|
||||
|
||||
switch (evt->header.evt_id) {
|
||||
case BLE_GAP_EVT_SEC_PARAMS_REQUEST: {
|
||||
const ble_gap_sec_params_t& params =
|
||||
gap_evt.params.sec_params_request.peer_params;
|
||||
|
||||
AuthenticationMask authentication_requirements(
|
||||
params.bond,
|
||||
params.mitm,
|
||||
params.lesc,
|
||||
params.keypress
|
||||
);
|
||||
|
||||
KeyDistribution initiator_dist(
|
||||
params.kdist_peer.enc,
|
||||
params.kdist_peer.id,
|
||||
params.kdist_peer.sign,
|
||||
params.kdist_peer.link
|
||||
);
|
||||
|
||||
KeyDistribution responder_dist(
|
||||
params.kdist_own.enc,
|
||||
params.kdist_own.id,
|
||||
params.kdist_own.sign,
|
||||
params.kdist_own.link
|
||||
);
|
||||
|
||||
// FIXME: pass min key size
|
||||
handler->on_pairing_request(
|
||||
connection,
|
||||
params.oob,
|
||||
authentication_requirements,
|
||||
initiator_dist,
|
||||
responder_dist
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVT_SEC_INFO_REQUEST: {
|
||||
const ble_gap_evt_sec_info_request_t& req =
|
||||
gap_evt.params.sec_info_request;
|
||||
|
||||
handler->on_ltk_request(
|
||||
connection,
|
||||
ediv_t((uint8_t*)(&req.master_id.ediv)),
|
||||
rand_t(req.master_id.rand)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVT_PASSKEY_DISPLAY: {
|
||||
const ble_gap_evt_passkey_display_t& req =
|
||||
gap_evt.params.passkey_display;
|
||||
|
||||
if (req.match_request == 0) {
|
||||
handler->on_passkey_display(
|
||||
connection,
|
||||
0 /* TODO: conversion of req.passkey into a numerical passkey */
|
||||
);
|
||||
} else {
|
||||
// handle this case for secure pairing
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVT_KEY_PRESSED:
|
||||
// TODO: add with LESC support
|
||||
return true;
|
||||
|
||||
case BLE_GAP_EVT_AUTH_KEY_REQUEST: {
|
||||
uint8_t key_type = gap_evt.params.auth_key_request.key_type;
|
||||
|
||||
switch (key_type) {
|
||||
case BLE_GAP_AUTH_KEY_TYPE_NONE:
|
||||
break;
|
||||
|
||||
case BLE_GAP_AUTH_KEY_TYPE_PASSKEY:
|
||||
handler->on_passkey_request(connection);
|
||||
break;
|
||||
|
||||
case BLE_GAP_AUTH_KEY_TYPE_OOB:
|
||||
handler->on_legacy_pairing_oob_request(connection);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
|
||||
// TODO: Add with LESC support
|
||||
return true;
|
||||
|
||||
case BLE_GAP_EVT_AUTH_STATUS: {
|
||||
const ble_gap_evt_auth_status_t& status =
|
||||
gap_evt.params.auth_status;
|
||||
|
||||
switch (status.auth_status) {
|
||||
case BLE_GAP_SEC_STATUS_SUCCESS:
|
||||
// FIXME: needs success handler
|
||||
break;
|
||||
case BLE_GAP_SEC_STATUS_TIMEOUT:
|
||||
// FIXME: needs timeout handler
|
||||
break;
|
||||
|
||||
case BLE_GAP_SEC_STATUS_PASSKEY_ENTRY_FAILED:
|
||||
case BLE_GAP_SEC_STATUS_OOB_NOT_AVAILABLE:
|
||||
case BLE_GAP_SEC_STATUS_AUTH_REQ:
|
||||
case BLE_GAP_SEC_STATUS_CONFIRM_VALUE:
|
||||
case BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP:
|
||||
case BLE_GAP_SEC_STATUS_ENC_KEY_SIZE:
|
||||
case BLE_GAP_SEC_STATUS_SMP_CMD_UNSUPPORTED:
|
||||
case BLE_GAP_SEC_STATUS_UNSPECIFIED:
|
||||
case BLE_GAP_SEC_STATUS_REPEATED_ATTEMPTS:
|
||||
case BLE_GAP_SEC_STATUS_INVALID_PARAMS:
|
||||
case BLE_GAP_SEC_STATUS_DHKEY_FAILURE:
|
||||
case BLE_GAP_SEC_STATUS_NUM_COMP_FAILURE:
|
||||
case BLE_GAP_SEC_STATUS_BR_EDR_IN_PROG:
|
||||
case BLE_GAP_SEC_STATUS_X_TRANS_KEY_DISALLOWED:
|
||||
handler->on_pairing_error(
|
||||
connection,
|
||||
(pairing_failure_t::type) (status.auth_status & 0xF)
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVT_CONN_SEC_UPDATE:
|
||||
// FIXME: Invoke handler indicating the link encryption
|
||||
return true;
|
||||
|
||||
case BLE_GAP_EVT_TIMEOUT:
|
||||
// FIXME: forward event when available
|
||||
return true;
|
||||
|
||||
case BLE_GAP_EVT_SEC_REQUEST:
|
||||
// FIXME: forward event when available
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // nordic
|
||||
} // vendor
|
||||
} // pal
|
||||
} // ble
|
||||
|
|
@ -0,0 +1,333 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018-2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NRF5X_PAL_SECURITY_MANAGER_
|
||||
#define NRF5X_PAL_SECURITY_MANAGER_
|
||||
|
||||
#include "ble/pal/PalSecurityManager.h"
|
||||
#include "nrf_ble.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace nordic {
|
||||
|
||||
class nRF5xSecurityManager : public ::ble::pal::SecurityManager {
|
||||
public:
|
||||
nRF5xSecurityManager();
|
||||
|
||||
virtual ~nRF5xSecurityManager();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// SM lifecycle management
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::initialize
|
||||
*/
|
||||
virtual ble_error_t initialize();
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::terminate
|
||||
*/
|
||||
virtual ble_error_t terminate();
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::reset
|
||||
*/
|
||||
virtual ble_error_t reset() ;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Resolving list management
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::read_resolving_list_capacity
|
||||
*/
|
||||
virtual uint8_t read_resolving_list_capacity();
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::add_device_to_resolving_list
|
||||
*/
|
||||
virtual ble_error_t add_device_to_resolving_list(
|
||||
advertising_peer_address_type_t peer_identity_address_type,
|
||||
const address_t &peer_identity_address,
|
||||
const irk_t &peer_irk
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::remove_device_from_resolving_list
|
||||
*/
|
||||
virtual ble_error_t remove_device_from_resolving_list(
|
||||
advertising_peer_address_type_t peer_identity_address_type,
|
||||
const address_t &peer_identity_address
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::clear_resolving_list
|
||||
*/
|
||||
virtual ble_error_t clear_resolving_list();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Feature support
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_secure_connections_support
|
||||
*/
|
||||
virtual ble_error_t get_secure_connections_support(
|
||||
bool &enabled
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_io_capability
|
||||
*/
|
||||
virtual ble_error_t set_io_capability(
|
||||
io_capability_t io_capability
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Security settings
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_authentication_timeout
|
||||
*/
|
||||
virtual ble_error_t set_authentication_timeout(
|
||||
connection_handle_t, uint16_t timeout_in_10ms
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_authentication_timeout
|
||||
*/
|
||||
virtual ble_error_t get_authentication_timeout(
|
||||
connection_handle_t, uint16_t &timeout_in_10ms
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_encryption_key_requirements
|
||||
*/
|
||||
virtual ble_error_t set_encryption_key_requirements(
|
||||
uint8_t min_encryption_key_size,
|
||||
uint8_t max_encryption_key_size
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::slave_security_request
|
||||
*/
|
||||
virtual ble_error_t slave_security_request(
|
||||
connection_handle_t connection,
|
||||
AuthenticationMask authentication
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Encryption
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::enable_encryption
|
||||
*/
|
||||
virtual ble_error_t enable_encryption(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::disable_encryption
|
||||
*/
|
||||
virtual ble_error_t disable_encryption(connection_handle_t connection);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_encryption_status
|
||||
*/
|
||||
virtual ble_error_t get_encryption_status(
|
||||
connection_handle_t connection, LinkSecurityStatus_t &status
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_encryption_key_size
|
||||
*/
|
||||
virtual ble_error_t get_encryption_key_size(
|
||||
connection_handle_t, uint8_t &bitsize
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::enable_encryption
|
||||
*/
|
||||
virtual ble_error_t enable_encryption(
|
||||
connection_handle_t connection,
|
||||
const ltk_t <k,
|
||||
const rand_t &rand,
|
||||
const ediv_t &ediv
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::encrypt_data
|
||||
*/
|
||||
virtual ble_error_t encrypt_data(
|
||||
const key_t &key,
|
||||
encryption_block_t &data
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Privacy
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_private_address_timeout
|
||||
*/
|
||||
virtual ble_error_t set_private_address_timeout(uint16_t timeout_in_seconds);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Keys
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_ltk
|
||||
*/
|
||||
virtual ble_error_t set_ltk(connection_handle_t connection, const ltk_t <k);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_ltk
|
||||
*/
|
||||
virtual ble_error_t set_ltk_not_found(connection_handle_t connection);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_irk
|
||||
*/
|
||||
virtual ble_error_t set_irk(const irk_t& irk);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_csrk
|
||||
*/
|
||||
virtual ble_error_t set_csrk(const csrk_t& csrk);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Authentication
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::send_pairing_request
|
||||
*/
|
||||
virtual ble_error_t send_pairing_request(
|
||||
connection_handle_t connection,
|
||||
bool oob_data_flag,
|
||||
AuthenticationMask authentication_requirements,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::send_pairing_response
|
||||
*/
|
||||
virtual ble_error_t send_pairing_response(
|
||||
connection_handle_t connection,
|
||||
bool oob_data_flag,
|
||||
AuthenticationMask authentication_requirements,
|
||||
KeyDistribution initiator_dist,
|
||||
KeyDistribution responder_dist
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::cancel_pairing
|
||||
*/
|
||||
virtual ble_error_t cancel_pairing(
|
||||
connection_handle_t connection, pairing_failure_t reason
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::request_authentication
|
||||
*/
|
||||
virtual ble_error_t request_authentication(connection_handle_t connection);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::get_random_data
|
||||
*/
|
||||
virtual ble_error_t get_random_data(random_data_t &random_data);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::generate_public_key
|
||||
*/
|
||||
virtual ble_error_t generate_public_key();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// MITM
|
||||
//
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::set_display_passkey
|
||||
*/
|
||||
virtual ble_error_t set_display_passkey(
|
||||
passkey_num_t passkey
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::passkey_request_reply
|
||||
*/
|
||||
virtual ble_error_t passkey_request_reply(
|
||||
connection_handle_t connection, const passkey_num_t passkey
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::oob_data_request_reply
|
||||
*/
|
||||
virtual ble_error_t legacy_pairing_oob_data_request_reply(
|
||||
connection_handle_t connection, const oob_tk_t& oob_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::confirmation_entered
|
||||
*/
|
||||
virtual ble_error_t confirmation_entered(
|
||||
connection_handle_t connection, bool confirmation
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::send_keypress_notification
|
||||
*/
|
||||
virtual ble_error_t send_keypress_notification(
|
||||
connection_handle_t connection, Keypress_t keypress
|
||||
);
|
||||
|
||||
/**
|
||||
* @see ::ble::pal::SecurityManager::oob_data_verified
|
||||
*/
|
||||
virtual ble_error_t oob_data_verified(
|
||||
connection_handle_t connection,
|
||||
const oob_rand_t &local_random,
|
||||
const oob_rand_t &peer_random
|
||||
);
|
||||
|
||||
// singleton of nordic Security Manager
|
||||
static nRF5xSecurityManager& get_security_manager();
|
||||
|
||||
// Event handler
|
||||
// FIXME: set proper event handling type
|
||||
static bool sm_handler(const ble_evt_t *evt);
|
||||
|
||||
private:
|
||||
irk_t _irk;
|
||||
csrk_t _csrk;
|
||||
io_capability_t _io_capability;
|
||||
uint8_t _max_encryption_key_size;
|
||||
};
|
||||
|
||||
} // nordic
|
||||
} // vendor
|
||||
} // pal
|
||||
} // ble
|
||||
|
||||
#endif /* NRF5X_PAL_SECURITY_MANAGER_ */
|
|
@ -0,0 +1,112 @@
|
|||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
# Make PROJECT_SOURCE_DIR, PROJECT_BINARY_DIR, and PROJECT_NAME available.
|
||||
set(PROJECT_NAME ble-tests)
|
||||
project(${PROJECT_NAME})
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
################################
|
||||
# GTEST
|
||||
################################
|
||||
|
||||
# Download and unpack googletest at configure time
|
||||
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
|
||||
if(result)
|
||||
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
|
||||
endif()
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
|
||||
if(result)
|
||||
message(FATAL_ERROR "Build step for googletest failed: ${result}")
|
||||
endif()
|
||||
|
||||
# Prevent overriding the parent project's compiler/linker
|
||||
# settings on Windows
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
|
||||
# Add googletest directly to our build. This defines
|
||||
# the gtest and gtest_main targets.
|
||||
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
|
||||
${CMAKE_BINARY_DIR}/googletest-build
|
||||
EXCLUDE_FROM_ALL)
|
||||
|
||||
# The gtest/gtest_main targets carry header search path
|
||||
# dependencies automatically when using CMake 2.8.11 or
|
||||
# later. Otherwise we have to add them here ourselves.
|
||||
if (CMAKE_VERSION VERSION_LESS 2.8.11)
|
||||
include_directories(BEFORE SYSTEM
|
||||
"${gtest_SOURCE_DIR}/include" "${gmock_SOURCE_DIR}/include")
|
||||
else()
|
||||
target_include_directories(gmock_main SYSTEM BEFORE INTERFACE
|
||||
"${gtest_SOURCE_DIR}/include" "${gmock_SOURCE_DIR}/include")
|
||||
endif()
|
||||
|
||||
|
||||
################################
|
||||
# Testing
|
||||
################################
|
||||
|
||||
enable_testing()
|
||||
|
||||
###############################
|
||||
# GattClient test
|
||||
###############################
|
||||
|
||||
add_executable(gatt-client-tests
|
||||
mbed_os_stub/mbed_assert.c
|
||||
generic/GattClient/mock/MockCallbacks.cpp
|
||||
generic/GattClient/mock/MockPalGattClient.cpp
|
||||
generic/GattClient/util/Equality.cpp
|
||||
generic/GattClient/TestCharacteristicDesctiptorDiscovery.cpp
|
||||
generic/GattClient/TestDiscoverAllServices.cpp
|
||||
generic/GattClient/TestNoCb.cpp
|
||||
generic/GattClient/TestRead.cpp
|
||||
generic/GattClient/TestServerEvent.cpp
|
||||
generic/GattClient/TestWrite.cpp
|
||||
${PROJECT_SOURCE_DIR}/../source/generic/GenericGattClient.cpp
|
||||
)
|
||||
|
||||
target_include_directories(gatt-client-tests PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/.."
|
||||
"${PROJECT_SOURCE_DIR}/../../.."
|
||||
"${PROJECT_SOURCE_DIR}/generic/GattClient"
|
||||
)
|
||||
|
||||
# Standard linking to gtest stuff.
|
||||
target_link_libraries(gatt-client-tests gmock_main)
|
||||
|
||||
# This is so you can do 'make gatt-client-tests' to see all your tests run, instead of
|
||||
# manually running the executable runUnitTests to see those specific tests.
|
||||
add_test(NAME GattClientTests COMMAND gatt-client-tests)
|
||||
|
||||
###############################
|
||||
# SecurityManager test
|
||||
###############################
|
||||
|
||||
add_executable(security-manager-tests
|
||||
mbed_os_stub/mbed_assert.c
|
||||
generic/SecurityManager/mock/MockPalSecurityManager.cpp
|
||||
generic/SecurityManager/mock/MockPalSecurityDb.cpp
|
||||
${PROJECT_SOURCE_DIR}/../source/generic/GenericSecurityManager.cpp
|
||||
)
|
||||
|
||||
target_include_directories(security-manager-tests PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/.."
|
||||
"${PROJECT_SOURCE_DIR}/../../.."
|
||||
"${PROJECT_SOURCE_DIR}/mbed_os_stub"
|
||||
"${PROJECT_SOURCE_DIR}/generic/SecurityManager"
|
||||
)
|
||||
|
||||
# Standard linking to gtest stuff.
|
||||
target_link_libraries(security-manager-tests gmock_main)
|
||||
|
||||
# This is so you can do 'make security-manager-tests' to see all your tests run, instead of
|
||||
# manually running the executable runUnitTests to see those specific tests.
|
||||
add_test(NAME SecurityManagerTests COMMAND security-manager-tests)
|
|
@ -0,0 +1,15 @@
|
|||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
project(googletest-download NONE)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG master
|
||||
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
|
||||
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
|
@ -0,0 +1,684 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ble/generic/GenericGattClient.h"
|
||||
#include "ble/pal/AttServerMessage.h"
|
||||
#include "ble/pal/SimpleAttServerMessage.h"
|
||||
#include "ble/DiscoveredService.h"
|
||||
|
||||
#include "mock/MockPalGattClient.h"
|
||||
#include "mock/MockCallbacks.h"
|
||||
|
||||
#include "util/PrettyPrinter.h"
|
||||
#include "util/Equality.h"
|
||||
#include "util/Log.h"
|
||||
|
||||
// using declarations
|
||||
using ble::pal::AttErrorResponse;
|
||||
using ble::pal::AttributeOpcode;
|
||||
using ble::pal::AttFindInformationResponse;
|
||||
using ble::generic::GenericGattClient;
|
||||
using ble::pal::vendor::mock::MockPalGattClient;
|
||||
|
||||
using ble::connection_handle_t;
|
||||
using ble::attribute_handle_range;
|
||||
using ble::attribute_handle_range_t;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::AllOf;
|
||||
using ::testing::ResultOf;
|
||||
using ::testing::InSequence;
|
||||
using ::testing::Pointee;
|
||||
|
||||
using std::vector;
|
||||
using std::tuple;
|
||||
using std::pair;
|
||||
|
||||
struct ConstructibleDiscoveredCharacteristic : public DiscoveredCharacteristic {
|
||||
ConstructibleDiscoveredCharacteristic(
|
||||
GattClient* client,
|
||||
Gap::Handle_t connection_handle,
|
||||
const UUID& uuid,
|
||||
uint16_t declaration_handle,
|
||||
uint16_t value_handle,
|
||||
uint16_t last_handle
|
||||
) : DiscoveredCharacteristic() {
|
||||
this->gattc = client;
|
||||
this->uuid = uuid;
|
||||
declHandle = declaration_handle;
|
||||
valueHandle = value_handle;
|
||||
lastHandle = last_handle;
|
||||
connHandle = connection_handle;
|
||||
}
|
||||
|
||||
void set_last_handle(uint16_t last_handle) {
|
||||
lastHandle = last_handle;
|
||||
}
|
||||
|
||||
void set_value_handle(uint16_t value_handle) {
|
||||
valueHandle = value_handle;
|
||||
}
|
||||
};
|
||||
|
||||
class TestGattClientDescriptorDiscovery : public ::testing::Test {
|
||||
protected:
|
||||
TestGattClientDescriptorDiscovery() :
|
||||
_mock_client(),
|
||||
_gatt_client(&_mock_client),
|
||||
_connection_handle(0xDEAD),
|
||||
_attribute_handle(0x5645),
|
||||
_descriptor_cb(),
|
||||
_termination_cb(),
|
||||
_mtu_size(23),
|
||||
_characteristic(
|
||||
&_gatt_client,
|
||||
_connection_handle,
|
||||
UUID(0xDEAF),
|
||||
_attribute_handle,
|
||||
_attribute_handle + 1,
|
||||
_attribute_handle + 1
|
||||
) {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
ON_CALL(
|
||||
_mock_client, get_mtu_size(_connection_handle, _)
|
||||
).WillByDefault(Invoke([&](auto, auto& size){
|
||||
size = this->_mtu_size;
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
}
|
||||
|
||||
void set_mtu_size_stub() {
|
||||
EXPECT_CALL(
|
||||
_mock_client, get_mtu_size(_connection_handle, _)
|
||||
).WillRepeatedly(::testing::DoDefault());
|
||||
}
|
||||
|
||||
MockPalGattClient _mock_client;
|
||||
GenericGattClient _gatt_client;
|
||||
const Gap::Handle_t _connection_handle;
|
||||
uint16_t _attribute_handle;
|
||||
descriptor_callback_t _descriptor_cb;
|
||||
descriptor_termination_callback_t _termination_cb;
|
||||
uint16_t _mtu_size;
|
||||
ConstructibleDiscoveredCharacteristic _characteristic;
|
||||
};
|
||||
|
||||
// errors: Invalid handle if:
|
||||
// starting handle > ending handle
|
||||
// stating handle == 00
|
||||
// ending handle > last handle on the server
|
||||
|
||||
// if no handle will be return => ATTRIBUTE NOT FOUND
|
||||
|
||||
// Complete when ATTRIBUTE NOT FOUND is returned or an attribute handle
|
||||
// in the response is equal to the ending handle in the request.
|
||||
|
||||
|
||||
// Find information response:
|
||||
// format & [(handle, UUID)]
|
||||
// format == 1 => 16 bit UUID
|
||||
// format == 2 => 128 bit UUID
|
||||
|
||||
/*
|
||||
* Given a discovered characteristic with the value handle equal to the last
|
||||
* handle of the characteristic.
|
||||
* When the client discover the descriptor of the characteristic
|
||||
* Then:
|
||||
* - no request is issued to the PAL.
|
||||
* - the function doesn't return any error.
|
||||
* - the termination callback is called immediately
|
||||
*/
|
||||
TEST_F(TestGattClientDescriptorDiscovery, descriptor_discovery_on_characteristics_without_descriptor_shall_return_immediatelly) {
|
||||
_characteristic.set_last_handle(_characteristic.getValueHandle());
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, discover_characteristics_descriptors(_, _)
|
||||
).Times(0);
|
||||
|
||||
EXPECT_CALL(
|
||||
_termination_cb, call(Pointee(CharacteristicDescriptorDiscovery::TerminationCallbackParams_t {
|
||||
_characteristic,
|
||||
BLE_ERROR_NONE,
|
||||
0x00
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ble_error_t err = _gatt_client.discoverCharacteristicDescriptors(
|
||||
_characteristic,
|
||||
makeFunctionPointer(&_descriptor_cb, &descriptor_callback_t::call),
|
||||
makeFunctionPointer(&_termination_cb, &descriptor_termination_callback_t::call)
|
||||
);
|
||||
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
}
|
||||
|
||||
typedef tuple<uint16_t, uint16_t, vector<vector<pair<uint16_t, UUID>>>> test_param_t;
|
||||
|
||||
class TestGattClientDescriptorDiscoveryP :
|
||||
public TestGattClientDescriptorDiscovery,
|
||||
public ::testing::WithParamInterface<test_param_t> {
|
||||
|
||||
protected:
|
||||
TestGattClientDescriptorDiscoveryP() :
|
||||
TestGattClientDescriptorDiscovery(),
|
||||
_value_handle(0),
|
||||
_last_handle(0),
|
||||
_descriptors() {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
TestGattClientDescriptorDiscovery::SetUp();
|
||||
std::tie(_value_handle, _last_handle, _descriptors) = GetParam();
|
||||
_characteristic.set_value_handle(_value_handle);
|
||||
_characteristic.set_last_handle(_last_handle);
|
||||
}
|
||||
|
||||
uint16_t _value_handle;
|
||||
uint16_t _last_handle;
|
||||
vector<vector<pair<uint16_t, UUID>>> _descriptors;
|
||||
};
|
||||
|
||||
struct MockFindInformationResponse : public AttFindInformationResponse {
|
||||
MockFindInformationResponse(vector<pair<uint16_t, UUID>> response) :
|
||||
_response(response) { }
|
||||
|
||||
virtual size_t size() const {
|
||||
return _response.size();
|
||||
}
|
||||
|
||||
virtual information_data_t operator[](size_t index) const {
|
||||
auto& element = _response[index];
|
||||
return {
|
||||
element.first,
|
||||
element.second
|
||||
};
|
||||
}
|
||||
|
||||
vector<pair<uint16_t, UUID>> _response;
|
||||
};
|
||||
|
||||
|
||||
static const DiscoveredCharacteristic& get_characteristic(const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t* p) {
|
||||
return p->characteristic;
|
||||
}
|
||||
|
||||
static const DiscoveredCharacteristicDescriptor& get_descriptor(const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t* p) {
|
||||
return p->descriptor;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given a discovered characteristic with the value handle not equal to the
|
||||
* last handle of the characteristic.
|
||||
* when the client launch the discovery of the descriptor of the characteristic.
|
||||
* Then:
|
||||
* - the client invoke the pal function discover_characteristics_descriptors
|
||||
* with an handle range starting at characteristic value handle + 1 and ending
|
||||
* at the last characteristic handle.
|
||||
* - The pal will reply with a FindInformationResponse containing the
|
||||
* descriptors discovered.
|
||||
* The operation is repeated until the response contains an attribute with an
|
||||
* handle equal to the last handle of the range or the pal reply with an error
|
||||
* (attribute not found). The termination callback is then call with a status
|
||||
* containing BLE_ERROR_NONE.
|
||||
*/
|
||||
TEST_P(TestGattClientDescriptorDiscoveryP, descriptor_discovery) {
|
||||
uint16_t current_handle = _characteristic.getValueHandle() + 1;
|
||||
auto descriptors = _descriptors;
|
||||
|
||||
InSequence seq;
|
||||
|
||||
while (true) {
|
||||
//Log::info() << "expect discover_characteristics_descriptors(" << current_handle << ", " << _last_handle << ")" << std::endl;
|
||||
EXPECT_CALL(
|
||||
_mock_client, discover_characteristics_descriptors(
|
||||
_connection_handle,
|
||||
attribute_handle_range(current_handle, _last_handle)
|
||||
)
|
||||
).WillOnce(Invoke([&, descriptors, current_handle](auto connection, auto range) {
|
||||
if (descriptors.empty()) {
|
||||
// send an attribute not found response
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::FIND_INFORMATION_REQUEST,
|
||||
current_handle,
|
||||
AttErrorResponse::ATTRIBUTE_NOT_FOUND
|
||||
)
|
||||
);
|
||||
} else {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
MockFindInformationResponse(descriptors.front())
|
||||
);
|
||||
}
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
if (descriptors.empty() == false) {
|
||||
for (const auto& descriptor : descriptors.front()) {
|
||||
DiscoveredCharacteristicDescriptor discovered_descriptor(
|
||||
&_gatt_client,
|
||||
_connection_handle,
|
||||
descriptor.first,
|
||||
descriptor.second
|
||||
);
|
||||
|
||||
//Log::info() << "expect _descriptor_cb(" << _connection_handle << ", " << descriptor.first << ", " << descriptor.second << ")" << std::endl;
|
||||
|
||||
EXPECT_CALL(
|
||||
_descriptor_cb, call(AllOf(
|
||||
ResultOf(get_characteristic, _characteristic),
|
||||
ResultOf(get_descriptor, discovered_descriptor)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptors.empty() ||
|
||||
std::any_of(
|
||||
begin(descriptors.front()),
|
||||
end(descriptors.front()),
|
||||
[&](auto& val) { return val.first == _last_handle; }
|
||||
)
|
||||
) {
|
||||
//Log::info() << "expect termination" << std::endl;
|
||||
EXPECT_CALL(
|
||||
_termination_cb, call(Pointee(CharacteristicDescriptorDiscovery::TerminationCallbackParams_t {
|
||||
_characteristic,
|
||||
BLE_ERROR_NONE,
|
||||
0x00
|
||||
}))
|
||||
);
|
||||
break;
|
||||
} else {
|
||||
current_handle = descriptors.front().back().first + 1;
|
||||
descriptors.erase(begin(descriptors));
|
||||
}
|
||||
}
|
||||
|
||||
ble_error_t err = _gatt_client.discoverCharacteristicDescriptors(
|
||||
_characteristic,
|
||||
makeFunctionPointer(&_descriptor_cb, &descriptor_callback_t::call),
|
||||
makeFunctionPointer(&_termination_cb, &descriptor_termination_callback_t::call)
|
||||
);
|
||||
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
TestGattClientDescriptorDiscoveryP_combination,
|
||||
TestGattClientDescriptorDiscoveryP,
|
||||
::testing::Values(
|
||||
// single transaction, single 16 bit value in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// single transaction, single 16 bit value in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1001,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// single transaction, single 128 bit value in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID("1881e01d-02af-41e9-abe9-bc940c09ca65") }
|
||||
}
|
||||
}
|
||||
},
|
||||
// single transaction, single 128 bit value in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1001,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID("8a239fd3-61a3-44a4-8b7c-2db9a9baa3c8") }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// single transaction, multiple 16 bit value in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) },
|
||||
{ 0x1020, UUID(0xFFAB) },
|
||||
{ 0x1030, UUID(0xFFAC) },
|
||||
{ 0x1040, UUID(0xFFAD) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// single transaction, multiple 16 bit value in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1040,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) },
|
||||
{ 0x1020, UUID(0xFFAB) },
|
||||
{ 0x1030, UUID(0xFFAC) },
|
||||
{ 0x1040, UUID(0xFFAD) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// single transaction, multiple 128 bit value in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID("0b20ef0c-578b-4b8a-8c59-065d4aff5b2e") },
|
||||
{ 0x1020, UUID("66ef3556-8889-4741-adfc-c89d7ff22710") },
|
||||
{ 0x1030, UUID("96a16d34-46d6-4081-82c3-25420ba01e5b") },
|
||||
{ 0x1040, UUID("86c7c947-c079-46e4-8bc4-7bfc011b7ffe") }
|
||||
}
|
||||
}
|
||||
},
|
||||
// single transaction, multiple 128 bit value in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1040,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID("0b20ef0c-578b-4b8a-8c59-065d4aff5b2e") },
|
||||
{ 0x1020, UUID("66ef3556-8889-4741-adfc-c89d7ff22710") },
|
||||
{ 0x1030, UUID("96a16d34-46d6-4081-82c3-25420ba01e5b") },
|
||||
{ 0x1040, UUID("86c7c947-c079-46e4-8bc4-7bfc011b7ffe") }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// multiple transaction, single 16 bit value in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) }
|
||||
},
|
||||
{
|
||||
{ 0x1020, UUID(0xFFAB) }
|
||||
},
|
||||
{
|
||||
{ 0x1030, UUID(0xFFAC) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// single transaction, single 16 bit value in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1030,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) }
|
||||
},
|
||||
{
|
||||
{ 0x1020, UUID(0xFFAB) }
|
||||
},
|
||||
{
|
||||
{ 0x1030, UUID(0xFFAC) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// multiple transaction, single 128 bit value in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID("1881e01d-02af-41e9-abe9-bc940c09ca65") }
|
||||
},
|
||||
{
|
||||
{ 0x1015, UUID("0eb7f338-cd2a-4220-9f41-b61d95485b8d") }
|
||||
},
|
||||
{
|
||||
{ 0x1050, UUID("ac77d105-73eb-4cfd-959e-c191feb592b8") }
|
||||
}
|
||||
}
|
||||
},
|
||||
// multiple transaction, single 128 bit value in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1050,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID("1881e01d-02af-41e9-abe9-bc940c09ca65") }
|
||||
},
|
||||
{
|
||||
{ 0x1015, UUID("0eb7f338-cd2a-4220-9f41-b61d95485b8d") }
|
||||
},
|
||||
{
|
||||
{ 0x1050, UUID("ac77d105-73eb-4cfd-959e-c191feb592b8") }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// multiple transaction, multiple 16 bit value in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) },
|
||||
{ 0x1020, UUID(0xFFAB) }
|
||||
},
|
||||
{
|
||||
{ 0x1030, UUID(0xFFAC) },
|
||||
{ 0x1040, UUID(0xFFAD) }
|
||||
},
|
||||
{
|
||||
{ 0x1055, UUID(0xFFAE) },
|
||||
{ 0x1075, UUID(0xFFAF) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// multiple transaction, multiple 16 bit value in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1075,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) },
|
||||
{ 0x1020, UUID(0xFFAB) }
|
||||
},
|
||||
{
|
||||
{ 0x1030, UUID(0xFFAC) },
|
||||
{ 0x1040, UUID(0xFFAD) }
|
||||
},
|
||||
{
|
||||
{ 0x1055, UUID(0xFFAE) },
|
||||
{ 0x1075, UUID(0xFFAF) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// multiple transaction, multiple 128 bit value in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID("0b20ef0c-578b-4b8a-8c59-065d4aff5b2e") }
|
||||
},
|
||||
{
|
||||
{ 0x1020, UUID("66ef3556-8889-4741-adfc-c89d7ff22710") },
|
||||
{ 0x1030, UUID("96a16d34-46d6-4081-82c3-25420ba01e5b") },
|
||||
},
|
||||
{
|
||||
{ 0x1040, UUID("86c7c947-c079-46e4-8bc4-7bfc011b7ffe") }
|
||||
}
|
||||
}
|
||||
},
|
||||
// multiple transaction, multiple 128 bit value in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1040,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID("0b20ef0c-578b-4b8a-8c59-065d4aff5b2e") }
|
||||
},
|
||||
{
|
||||
{ 0x1020, UUID("66ef3556-8889-4741-adfc-c89d7ff22710") },
|
||||
{ 0x1030, UUID("96a16d34-46d6-4081-82c3-25420ba01e5b") },
|
||||
},
|
||||
{
|
||||
{ 0x1040, UUID("86c7c947-c079-46e4-8bc4-7bfc011b7ffe") }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// multiple transaction, mixed UUID in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) }
|
||||
},
|
||||
{
|
||||
{ 0x1020, UUID("0b20ef0c-578b-4b8a-8c59-065d4aff5b2e") }
|
||||
},
|
||||
{
|
||||
{ 0x1025, UUID("66ef3556-8889-4741-adfc-c89d7ff22710") }
|
||||
},
|
||||
{
|
||||
{ 0x1030, UUID(0xFFAC) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// multiple transaction, mixed UUID in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1030,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) }
|
||||
},
|
||||
{
|
||||
{ 0x1020, UUID("0b20ef0c-578b-4b8a-8c59-065d4aff5b2e") }
|
||||
},
|
||||
{
|
||||
{ 0x1025, UUID("66ef3556-8889-4741-adfc-c89d7ff22710") }
|
||||
},
|
||||
{
|
||||
{ 0x1030, UUID(0xFFAC) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// multiple transaction, mixed multiple UUIDs in the transaction, ends with error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1100,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) },
|
||||
{ 0x1020, UUID(0xFFAB) }
|
||||
},
|
||||
{
|
||||
{ 0x1025, UUID("66ef3556-8889-4741-adfc-c89d7ff22710") },
|
||||
{ 0x1030, UUID("96a16d34-46d6-4081-82c3-25420ba01e5b") }
|
||||
},
|
||||
{
|
||||
{ 0x1055, UUID(0xFFAE) },
|
||||
{ 0x1075, UUID(0xFFAF) }
|
||||
}
|
||||
}
|
||||
},
|
||||
// multiple transaction, mixed multiple UUIDs in the transaction, ends without error
|
||||
test_param_t {
|
||||
0x1000,
|
||||
0x1075,
|
||||
{
|
||||
{
|
||||
{ 0x1001, UUID(0xFFAA) },
|
||||
{ 0x1020, UUID(0xFFAB) }
|
||||
},
|
||||
{
|
||||
{ 0x1025, UUID("66ef3556-8889-4741-adfc-c89d7ff22710") },
|
||||
{ 0x1030, UUID("96a16d34-46d6-4081-82c3-25420ba01e5b") }
|
||||
},
|
||||
{
|
||||
{ 0x1055, UUID(0xFFAE) },
|
||||
{ 0x1075, UUID(0xFFAF) }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// errors: Invalid handle if:
|
||||
// starting handle > ending handle
|
||||
// stating handle == 00
|
||||
// ending handle > last handle on the server
|
||||
|
||||
// if no handle will be return => ATTRIBUTE NOT FOUND
|
||||
|
||||
// Complete when ATTRIBUTE NOT FOUND is returned or an attribute handle
|
||||
// in the response is equal to the ending handle in the request.
|
||||
|
||||
|
||||
// Find information response:
|
||||
// format & [(handle, UUID)]
|
||||
// format == 1 => 16 bit UUID
|
||||
// format == 2 => 128 bit UUID
|
||||
|
||||
|
||||
/*
|
||||
* Given a discovered characteristic with the value handle not equal to the
|
||||
* last handle of the characteristic.
|
||||
* when the client launch the discovery of the descriptor of the characteristic.
|
||||
* Then:
|
||||
* - the client invoke the pal function discover_characteristics_descriptors
|
||||
* with an handle range starting at characteristic value handle + 1 and ending
|
||||
* at the last characteristic handle.
|
||||
* - The pal will reply with a FindInformationResponse containing the
|
||||
* descriptors discovered.
|
||||
* When the client call terminateCharacteristicDescriptorDiscovery the termination
|
||||
* callback is called immediately.
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,655 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ble/generic/GenericGattClient.h"
|
||||
#include "ble/pal/AttServerMessage.h"
|
||||
#include "ble/pal/SimpleAttServerMessage.h"
|
||||
#include "ble/DiscoveredService.h"
|
||||
|
||||
#include "mock/MockPalGattClient.h"
|
||||
#include "mock/MockCallbacks.h"
|
||||
#include "util/ServerDescription.h"
|
||||
#include "util/PrettyPrinter.h"
|
||||
#include "util/Equality.h"
|
||||
#include "util/Log.h"
|
||||
|
||||
// imports
|
||||
using ble::pal::SimpleAttFindByTypeValueResponse;
|
||||
using ble::pal::SimpleAttReadByGroupTypeResponse;
|
||||
using ble::pal::SimpleAttReadByTypeResponse;
|
||||
using ble::pal::AttErrorResponse;
|
||||
using ble::pal::AttributeOpcode;
|
||||
using ble::attribute_handle_range_t;
|
||||
using ble::generic::GenericGattClient;
|
||||
using ble::pal::vendor::mock::MockPalGattClient;
|
||||
|
||||
using ble::connection_handle_t;
|
||||
using ble::attribute_handle_t;
|
||||
using ble::make_const_ArrayView;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::InvokeWithoutArgs;
|
||||
using ::testing::AllOf;
|
||||
using ::testing::Property;
|
||||
using ::testing::InSequence;
|
||||
|
||||
class LaunchDiscoveryNoServiceFilter : public ::testing::TestWithParam<
|
||||
std::tuple<std::tuple<bool, bool>, UUID, UUID, server_description_t>
|
||||
> {
|
||||
protected:
|
||||
LaunchDiscoveryNoServiceFilter() :
|
||||
mock_client(),
|
||||
gatt_client(&mock_client),
|
||||
connection_handle(0xDEAD),
|
||||
termination_callback(),
|
||||
has_service_cb(),
|
||||
service_callback(),
|
||||
has_characteristic_cb(),
|
||||
characteristic_callback(),
|
||||
service_filter(),
|
||||
characteristic_filter(),
|
||||
server_stub() {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
gatt_client.onServiceDiscoveryTermination(
|
||||
makeFunctionPointer(
|
||||
&termination_callback,
|
||||
&termination_callback_t::call
|
||||
)
|
||||
);
|
||||
|
||||
std::tuple<bool, bool> cb_combination;
|
||||
std::tie(
|
||||
cb_combination,
|
||||
service_filter,
|
||||
characteristic_filter,
|
||||
server_stub
|
||||
) = GetParam();
|
||||
|
||||
std::tie(has_service_cb, has_characteristic_cb) = cb_combination;
|
||||
}
|
||||
|
||||
auto reply_read_by_group_type(const std::vector<service_description_t>& service_transactions) {
|
||||
return Invoke([service_transactions, this](connection_handle_t connection, attribute_handle_t handle) -> ble_error_t {
|
||||
//Log::info() << "discover primary service (" << connection << "," << handle << ")" << std::endl;
|
||||
uint8_t uuid_size = service_transactions.front().uuid.getLen();
|
||||
uint8_t element_size = uuid_size + 4;
|
||||
size_t data_size = element_size * service_transactions.size();
|
||||
std::vector<uint8_t> result(data_size);
|
||||
uint8_t* data = result.data();
|
||||
|
||||
for (const auto& service : service_transactions) {
|
||||
memcpy(data, &service.start, 2);
|
||||
data += 2;
|
||||
memcpy(data, &service.end, 2);
|
||||
data += 2;
|
||||
memcpy(data, service.uuid.getBaseUUID(), uuid_size);
|
||||
data += uuid_size;
|
||||
}
|
||||
|
||||
mock_client.on_server_event(
|
||||
connection,
|
||||
SimpleAttReadByGroupTypeResponse(
|
||||
element_size,
|
||||
make_const_ArrayView(result.data(), result.size())
|
||||
)
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
});
|
||||
}
|
||||
|
||||
auto reply_find_by_type_value(const std::vector<service_description_t>& service_transactions) {
|
||||
return Invoke([service_transactions, this](connection_handle_t connection, attribute_handle_t handle, const UUID& uuid) -> ble_error_t {
|
||||
//Log::info() << "discover primary service by uuid(" << connection << "," << handle << "," << uuid << ")" << std::endl;
|
||||
std::vector<uint8_t> result(service_transactions.size() * 4);
|
||||
uint8_t* data = result.data();
|
||||
for (const auto& service : service_transactions) {
|
||||
memcpy(data, &service.start, 2);
|
||||
data += 2;
|
||||
memcpy(data, &service.end, 2);
|
||||
data += 2;
|
||||
}
|
||||
|
||||
mock_client.on_server_event(
|
||||
connection,
|
||||
SimpleAttFindByTypeValueResponse(
|
||||
make_const_ArrayView(result.data(), result.size())
|
||||
)
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
});
|
||||
}
|
||||
|
||||
uint8_t properties_to_byte(DiscoveredCharacteristic::Properties_t p) {
|
||||
return (
|
||||
p.broadcast() << 0 |
|
||||
p.read() << 1 |
|
||||
p.writeWoResp() << 2 |
|
||||
p.write() << 3 |
|
||||
p.notify() << 4 |
|
||||
p.indicate() << 5 |
|
||||
p.authSignedWrite() << 6
|
||||
/* extented properties ignored for now */
|
||||
);
|
||||
}
|
||||
|
||||
auto reply_read_by_type(const std::vector<characteristic_description_t>& transaction) {
|
||||
return Invoke([transaction, this](connection_handle_t connection, attribute_handle_range_t range) -> ble_error_t {
|
||||
//Log::info() << "discover characteristic(" << connection << "," << range.begin << "," << range.end << ")" << std::endl;
|
||||
size_t uuid_size = transaction.front().uuid.getLen();
|
||||
uint8_t element_size = uuid_size + 5;
|
||||
std::vector<uint8_t> result(element_size * transaction.size());
|
||||
uint8_t* data = result.data();
|
||||
for (const auto& characteristic : transaction) {
|
||||
memcpy(data, &characteristic.handle, 2);
|
||||
data += 2;
|
||||
*data++ = properties_to_byte(characteristic.properties);
|
||||
memcpy(data, &characteristic.value_handle, 2);
|
||||
data += 2;
|
||||
memcpy(data, characteristic.uuid.getBaseUUID(), uuid_size);
|
||||
data += uuid_size;
|
||||
}
|
||||
|
||||
mock_client.on_server_event(
|
||||
connection,
|
||||
SimpleAttReadByTypeResponse(
|
||||
element_size,
|
||||
make_const_ArrayView(result.data(), result.size())
|
||||
)
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
});
|
||||
}
|
||||
|
||||
auto reply_error(AttributeOpcode opcode, AttErrorResponse::AttributeErrorCode error_code) {
|
||||
return InvokeWithoutArgs([this, opcode, error_code]() -> ble_error_t {
|
||||
//Log::info() << "reply error: opcode = " << (uint8_t) opcode << ", error_code = " << error_code << std::endl;
|
||||
mock_client.on_server_event(
|
||||
connection_handle,
|
||||
AttErrorResponse(opcode, error_code)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
});
|
||||
}
|
||||
|
||||
// helper
|
||||
// note sequence insured by caller
|
||||
void set_discover_all_services_expectations() {
|
||||
auto services_transactions = get_services_transactions();
|
||||
|
||||
uint16_t next_attribute_handle = 0x0001;
|
||||
for (const auto& service_transactions : services_transactions) {
|
||||
//Log::info() << "expect discover_primary_service(" << connection_handle << ", " << next_attribute_handle << ")" <<std::endl;
|
||||
// set expectation on the discovery function
|
||||
EXPECT_CALL(
|
||||
mock_client, discover_primary_service(connection_handle, next_attribute_handle)
|
||||
).WillOnce(
|
||||
reply_read_by_group_type(service_transactions)
|
||||
);
|
||||
if (!has_characteristic_cb) {
|
||||
set_services_callback_expectations(service_transactions);
|
||||
}
|
||||
|
||||
next_attribute_handle = service_transactions.back().end + 1;
|
||||
}
|
||||
|
||||
// if counter didn't wrap then send an error
|
||||
if (next_attribute_handle != 0) {
|
||||
// set expectation on the discovery function
|
||||
//Log::info() << "expect discover_primary_service(" << connection_handle << ", " << next_attribute_handle << ") "
|
||||
//<< "to fail with opcode = " << AttributeOpcode::READ_BY_GROUP_TYPE_REQUEST << " and error = " << AttErrorResponse::ATTRIBUTE_NOT_FOUND << std::endl;
|
||||
EXPECT_CALL(
|
||||
mock_client, discover_primary_service(connection_handle, next_attribute_handle)
|
||||
).WillOnce(
|
||||
reply_error(AttributeOpcode::READ_BY_GROUP_TYPE_REQUEST, AttErrorResponse::ATTRIBUTE_NOT_FOUND)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void set_discover_services_by_uuid_expectations() {
|
||||
std::vector<std::vector<uint8_t>> services_response;
|
||||
auto services_transactions = get_services_transactions();
|
||||
|
||||
uint16_t next_attribute_handle = 0x0001;
|
||||
for (const auto& transaction : services_transactions) {
|
||||
|
||||
//Log::info() << "expect discover_primary_service_by_service_uuid(" << connection_handle << ", " << next_attribute_handle << "," << service_filter << ")" <<std::endl;
|
||||
// set expectation on the discovery function
|
||||
EXPECT_CALL(
|
||||
mock_client, discover_primary_service_by_service_uuid(
|
||||
connection_handle,
|
||||
next_attribute_handle,
|
||||
service_filter
|
||||
)
|
||||
).WillOnce(
|
||||
reply_find_by_type_value(transaction)
|
||||
);
|
||||
|
||||
if (!has_characteristic_cb) {
|
||||
set_services_callback_expectations(transaction);
|
||||
}
|
||||
next_attribute_handle = transaction.back().end + 1;
|
||||
}
|
||||
|
||||
// note if last service end handle was 0xFFFF do nothing
|
||||
// otherwise set a discovery expectation and reply with an error
|
||||
// if counter didn't wrap then send an error
|
||||
if (next_attribute_handle != 0) {
|
||||
// set expectation on the discovery function
|
||||
//Log::info() << "expect discover_primary_service_by_service_uuid(" << connection_handle << ", " << next_attribute_handle << "," << service_filter << ") "
|
||||
//<< "to fail with opcode = " << AttributeOpcode::FIND_BY_TYPE_VALUE_REQUEST << " and error = " << AttErrorResponse::ATTRIBUTE_NOT_FOUND << std::endl;
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_client, discover_primary_service_by_service_uuid(
|
||||
connection_handle, next_attribute_handle, service_filter
|
||||
)
|
||||
).WillOnce(
|
||||
reply_error(AttributeOpcode::FIND_BY_TYPE_VALUE_REQUEST, AttErrorResponse::ATTRIBUTE_NOT_FOUND)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void set_services_callback_expectations(const std::vector<service_description_t>& services) {
|
||||
if (!has_service_cb) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& service : services) {
|
||||
set_service_callback_expectation(service);
|
||||
}
|
||||
}
|
||||
|
||||
void set_service_callback_expectation(const service_description_t& service) {
|
||||
if (!has_service_cb) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Log::info() << "expect service_callback(" << service.start << ", " << service.end << "," << service.uuid << ")" <<std::endl;
|
||||
|
||||
EXPECT_CALL(
|
||||
service_callback, call(AllOf(
|
||||
Property(
|
||||
&DiscoveredService::getStartHandle,
|
||||
service.start
|
||||
),
|
||||
Property(
|
||||
&DiscoveredService::getEndHandle,
|
||||
service.end
|
||||
),
|
||||
Property(
|
||||
&DiscoveredService::getUUID,
|
||||
service.uuid
|
||||
)
|
||||
))
|
||||
).WillOnce(Invoke([](const DiscoveredService* service) -> void {
|
||||
//Log::info() << "service_callback(" << service->getStartHandle() << ", " << service->getEndHandle() << "," << service->getUUID() << ")" <<std::endl;
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
void set_discover_characteristics_expectations() {
|
||||
auto services = get_services_discovered();
|
||||
|
||||
for (const auto& service : services) {
|
||||
set_service_callback_expectation(service);
|
||||
auto characteristic_transactions = get_characteristics_transactions(service);
|
||||
|
||||
uint16_t next_attribute_handle = service.start;
|
||||
characteristic_description_t previous_characteristic { };
|
||||
for (const auto& characteristic_transaction : characteristic_transactions) {
|
||||
//Log::info() << "expect discover_characteristics_of_a_service(" << connection_handle << ", " << next_attribute_handle << "," << service.end << ")" <<std::endl;
|
||||
EXPECT_CALL(
|
||||
mock_client, discover_characteristics_of_a_service(
|
||||
connection_handle,
|
||||
attribute_handle_range_t { next_attribute_handle, service.end }
|
||||
)
|
||||
).WillOnce(
|
||||
reply_read_by_type(characteristic_transaction)
|
||||
);
|
||||
|
||||
set_characteristics_callbacks_expectations(
|
||||
previous_characteristic,
|
||||
characteristic_transaction
|
||||
);
|
||||
previous_characteristic = characteristic_transaction.back();
|
||||
next_attribute_handle = characteristic_transaction.back().value_handle + 1;
|
||||
}
|
||||
|
||||
if(next_attribute_handle < service.end) {
|
||||
//Log::info() << "expect discover_characteristics_of_a_service(" << connection_handle << ", " << next_attribute_handle << "," << service.end << ") "
|
||||
// << "to fail with opcode = " << AttributeOpcode::READ_BY_TYPE_REQUEST << " and error = " << AttErrorResponse::ATTRIBUTE_NOT_FOUND << std::endl;
|
||||
EXPECT_CALL(
|
||||
mock_client, discover_characteristics_of_a_service(
|
||||
connection_handle,
|
||||
attribute_handle_range_t { next_attribute_handle, service.end }
|
||||
)
|
||||
).WillOnce(
|
||||
reply_error(AttributeOpcode::READ_BY_TYPE_REQUEST, AttErrorResponse::ATTRIBUTE_NOT_FOUND)
|
||||
);
|
||||
|
||||
set_characteristic_callback_expectation(previous_characteristic);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void set_characteristics_callbacks_expectations(characteristic_description_t previous_characteristic, std::vector<characteristic_description_t> transaction) {
|
||||
if (transaction.empty() == false) {
|
||||
//Log::info() << "pop last characteristic in transaction" <<std::endl;
|
||||
transaction.pop_back();
|
||||
}
|
||||
|
||||
if (previous_characteristic != characteristic_description_t()) {
|
||||
//Log::info() << "add previous characteristic" <<std::endl;
|
||||
transaction.insert(transaction.begin(), previous_characteristic);
|
||||
}
|
||||
|
||||
for (const auto& characteristic : transaction) {
|
||||
set_characteristic_callback_expectation(characteristic);
|
||||
}
|
||||
}
|
||||
|
||||
void set_characteristic_callback_expectation(characteristic_description_t characteristic) {
|
||||
if (!has_characteristic_cb) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (characteristic_filter != UUID() && characteristic_filter != characteristic.uuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Log::info() << "expect characteristic_callback(" << characteristic.uuid << ", " << characteristic.handle << "," << characteristic.value_handle << ")" <<std::endl;
|
||||
EXPECT_CALL(
|
||||
characteristic_callback,
|
||||
call(AllOf(
|
||||
Property(
|
||||
&DiscoveredCharacteristic::getUUID,
|
||||
characteristic.uuid
|
||||
),
|
||||
Property(
|
||||
&DiscoveredCharacteristic::getDeclHandle,
|
||||
characteristic.handle
|
||||
),
|
||||
Property(
|
||||
&DiscoveredCharacteristic::getValueHandle,
|
||||
characteristic.value_handle
|
||||
),
|
||||
Property(
|
||||
&DiscoveredCharacteristic::getConnectionHandle,
|
||||
connection_handle
|
||||
)
|
||||
))
|
||||
).WillOnce(Invoke([](const DiscoveredCharacteristic* characteristic) -> void {
|
||||
//Log::info() << "characteristic_callback(" << characteristic->getDeclHandle() << ", " << characteristic->getValueHandle() << "," << characteristic->getUUID() << ")" <<std::endl;
|
||||
}));
|
||||
}
|
||||
|
||||
std::vector<std::vector<service_description_t>> get_services_transactions() {
|
||||
std::vector<service_description_t> working_set = get_services_discovered();
|
||||
|
||||
std::vector<std::vector<service_description_t>> result;
|
||||
for (const auto& service : working_set) {
|
||||
if(result.empty()) {
|
||||
result.push_back(std::vector<service_description_t>());
|
||||
}
|
||||
|
||||
auto& last_group = result.back();
|
||||
if (last_group.empty()) {
|
||||
last_group.push_back(service);
|
||||
} else {
|
||||
auto last_group_uuid = last_group.front().uuid.shortOrLong();
|
||||
auto last_group_size = last_group.size();
|
||||
if (last_group_uuid != service.uuid.shortOrLong()) {
|
||||
result.push_back(std::vector<service_description_t> { service });
|
||||
} else {
|
||||
if (last_group_uuid == UUID::UUID_TYPE_SHORT && last_group_size < 4) {
|
||||
last_group.push_back(service);
|
||||
} else {
|
||||
result.push_back(std::vector<service_description_t> { service });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<service_description_t> get_services_discovered() {
|
||||
std::vector<service_description_t> working_set;
|
||||
|
||||
if (service_filter != UUID()) {
|
||||
for (const auto& service : server_stub.services) {
|
||||
if (service.uuid == service_filter) {
|
||||
working_set.push_back(service);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
working_set = server_stub.services;
|
||||
}
|
||||
|
||||
return working_set;
|
||||
}
|
||||
|
||||
std::vector<std::vector<characteristic_description_t>> get_characteristics_transactions(const service_description_t& service) {
|
||||
std::vector<std::vector<characteristic_description_t>> transactions;
|
||||
for (const auto& characteristic : service.characteristics) {
|
||||
if(transactions.empty()) {
|
||||
transactions.push_back(
|
||||
std::vector<characteristic_description_t> { characteristic }
|
||||
);
|
||||
} else {
|
||||
auto& last_group = transactions.back();
|
||||
auto last_group_uuid = last_group.front().uuid.shortOrLong();
|
||||
auto last_group_size = last_group.size();
|
||||
if (last_group_uuid != service.uuid.shortOrLong()) {
|
||||
transactions.push_back(std::vector<characteristic_description_t> { characteristic });
|
||||
} else {
|
||||
if (last_group_uuid == UUID::UUID_TYPE_SHORT && last_group_size < 3) {
|
||||
last_group.push_back(characteristic);
|
||||
} else {
|
||||
transactions.push_back(std::vector<characteristic_description_t> { characteristic });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return transactions;
|
||||
}
|
||||
|
||||
MockPalGattClient mock_client;
|
||||
GenericGattClient gatt_client;
|
||||
const Gap::Handle_t connection_handle;
|
||||
termination_callback_t termination_callback;
|
||||
bool has_service_cb;
|
||||
service_callback_t service_callback;
|
||||
bool has_characteristic_cb;
|
||||
characteristic_callback_t characteristic_callback;
|
||||
UUID service_filter;
|
||||
UUID characteristic_filter;
|
||||
server_description_t server_stub;
|
||||
};
|
||||
|
||||
TEST_P(LaunchDiscoveryNoServiceFilter, regular) {
|
||||
|
||||
ON_CALL(
|
||||
mock_client, discover_primary_service(_, _)
|
||||
).WillByDefault(Invoke([](connection_handle_t connection, attribute_handle_t handle) -> ble_error_t {
|
||||
//Log::info() << "default discover primary service (" << connection << "," << handle << ")" << std::endl;
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
ON_CALL(
|
||||
mock_client, discover_primary_service_by_service_uuid(_, _, _)
|
||||
).WillByDefault(Invoke([](connection_handle_t connection, attribute_handle_t handle, const UUID& uuid) -> ble_error_t {
|
||||
//Log::info() << "default discover primary service by service uuid(" << connection << "," << handle << ", " << uuid << ")" << std::endl;
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
ON_CALL(
|
||||
mock_client, discover_characteristics_of_a_service(_, _)
|
||||
).WillByDefault(Invoke([](connection_handle_t connection, attribute_handle_range_t handle) -> ble_error_t {
|
||||
//Log::info() << "default discover characteristic of a service(" << connection << "," << handle << ")" << std::endl;
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
if (has_service_cb) {
|
||||
ON_CALL(
|
||||
service_callback, call(_)
|
||||
).WillByDefault(Invoke([](const DiscoveredService* service) -> void {
|
||||
//Log::info() << "default service_callback(" << service->getEndHandle() << ", " << service->getEndHandle() << ", " << service->getUUID() << ")" << std::endl;
|
||||
}));
|
||||
}
|
||||
|
||||
if (has_characteristic_cb) {
|
||||
ON_CALL(
|
||||
characteristic_callback, call(_)
|
||||
).WillByDefault(Invoke([](const DiscoveredCharacteristic* characteristic) -> void {
|
||||
//Log::info() << "default characteristic_callback(" << characteristic->getDeclHandle() << ", " << characteristic->getValueHandle() << ", " << characteristic->getUUID() << ")" << std::endl;
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
InSequence seq;
|
||||
if (service_filter == UUID()) {
|
||||
set_discover_all_services_expectations();
|
||||
} else {
|
||||
set_discover_services_by_uuid_expectations();
|
||||
}
|
||||
|
||||
if (has_characteristic_cb) {
|
||||
set_discover_characteristics_expectations();
|
||||
}
|
||||
|
||||
EXPECT_CALL(termination_callback, call(connection_handle));
|
||||
}
|
||||
|
||||
ble_error_t err = gatt_client.launchServiceDiscovery(
|
||||
connection_handle,
|
||||
has_service_cb ?
|
||||
makeFunctionPointer(
|
||||
&service_callback, &decltype(service_callback)::call
|
||||
) : NULL,
|
||||
has_characteristic_cb ?
|
||||
makeFunctionPointer(
|
||||
&characteristic_callback, &decltype(characteristic_callback)::call
|
||||
) : NULL,
|
||||
service_filter,
|
||||
characteristic_filter
|
||||
);
|
||||
|
||||
EXPECT_EQ(BLE_ERROR_NONE, err);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
GattClient_launch_discovery_no_service_filter,
|
||||
LaunchDiscoveryNoServiceFilter,
|
||||
::testing::Combine(
|
||||
::testing::Values(
|
||||
std::tuple<bool, bool>(true, false),
|
||||
std::tuple<bool, bool>(false, true),
|
||||
std::tuple<bool, bool>(true, true)
|
||||
),
|
||||
::testing::Values(UUID(), UUID(0x1452), UUID("a3d1495f-dba7-4441-99f2-d0a20f663422")),
|
||||
::testing::Values(UUID(), UUID(0xBEEF), UUID("1f551ee3-aef4-4719-8c52-8b419fc4ac01")),
|
||||
::testing::Values(
|
||||
server_description_t { },
|
||||
server_description_t {
|
||||
{
|
||||
0x0001,
|
||||
0x0030,
|
||||
UUID(0xAAAA),
|
||||
{
|
||||
{ 0x0002, { 0 }, UUID(0xBBBA) },
|
||||
{ 0x0006, { 1, 0 }, UUID(0xBBBB) },
|
||||
{ 0x0012, { 1, 0 }, UUID(0xBBBC) },
|
||||
{ 0x0015, { 1, 1, 1, 0 }, UUID(0xBBBD) }
|
||||
},
|
||||
},
|
||||
{
|
||||
0x0031,
|
||||
0x0060,
|
||||
UUID(0xAAAB),
|
||||
{
|
||||
{ 0x0032, { 0 }, UUID(0xCBBA) },
|
||||
{ 0x0036, { 1, 0 }, UUID(0xCBBB) },
|
||||
{ 0x0042, { 1, 0 }, UUID(0xCBBC) },
|
||||
{ 0x0050, { 1, 1, 1, 0 }, UUID(0xCBBD) }
|
||||
},
|
||||
},
|
||||
{
|
||||
0x0061,
|
||||
0x0090,
|
||||
UUID(0xAAAB),
|
||||
{
|
||||
{ 0x0062, { 0 }, UUID(0xDBBA) },
|
||||
{ 0x0066, { 1, 0 }, UUID(0xDBBB) },
|
||||
{ 0x0072, { 1, 0 }, UUID(0xDBBC) },
|
||||
{ 0x0082, { 1, 1, 1, 0 }, UUID(0xDBBD) }
|
||||
}
|
||||
}
|
||||
},
|
||||
server_description_t {
|
||||
{
|
||||
0x0001,
|
||||
0x0030,
|
||||
UUID(0xAAAA),
|
||||
{
|
||||
{ 0x0002, { 0 }, UUID(0xBBBA) },
|
||||
{ 0x0006, { 1, 0 }, UUID(0xBBBB) },
|
||||
{ 0x0012, { 1, 0 }, UUID(0xBBBC) },
|
||||
{ 0x0024, { 1, 1, 1, 0 }, UUID(0xBBBC) }
|
||||
},
|
||||
},
|
||||
{
|
||||
0x0031,
|
||||
0x0060,
|
||||
UUID(0xAAAB),
|
||||
{
|
||||
{ 0x0032, { 0 }, UUID(0xCBBA) },
|
||||
{ 0x0036, { 1, 0 }, UUID(0xCBBB) },
|
||||
{ 0x0042, { 1, 0 }, UUID(0xCBBC) },
|
||||
{ 0x0045, { 1, 1, 1, 0 }, UUID(0xCBBC) }
|
||||
},
|
||||
},
|
||||
{
|
||||
0x0061,
|
||||
0xFFFF,
|
||||
UUID(0xAAAB),
|
||||
{
|
||||
{ 0x0062, { 0 }, UUID(0xDBBA) },
|
||||
{ 0x0066, { 1, 0 }, UUID(0xDBBB) },
|
||||
{ 0x0072, { 1, 0 }, UUID(0xDBBC) },
|
||||
{ 0x0082, { 1, 1, 1, 0 }, UUID(0xDBBC) }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <cstddef>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ble/generic/GenericGattClient.h"
|
||||
#include "ble/pal/AttServerMessage.h"
|
||||
#include "ble/pal/SimpleAttServerMessage.h"
|
||||
#include "ble/DiscoveredService.h"
|
||||
|
||||
#include "mock/MockCallbacks.h"
|
||||
#include "mock/MockPalGattClient.h"
|
||||
|
||||
using std::nullptr_t;
|
||||
|
||||
using ble::generic::GenericGattClient;
|
||||
using ble::pal::vendor::mock::MockPalGattClient;
|
||||
|
||||
using ::testing::_;
|
||||
|
||||
|
||||
class LaunchDiscoveryNoCb :
|
||||
public ::testing::TestWithParam<std::tuple<UUID, UUID>> {
|
||||
protected:
|
||||
typedef std::tuple<UUID, UUID> parameters_t;
|
||||
|
||||
LaunchDiscoveryNoCb() :
|
||||
_mock_client(),
|
||||
_gatt_client(&_mock_client),
|
||||
_connection_handle(0xDEAD),
|
||||
_termination_callback(),
|
||||
_service_cb(),
|
||||
_characteristic_cb(),
|
||||
_service_filter(),
|
||||
_characteristic_filter() {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
_gatt_client.onServiceDiscoveryTermination(
|
||||
makeFunctionPointer(
|
||||
&_termination_callback,
|
||||
&termination_callback_t::call
|
||||
)
|
||||
);
|
||||
|
||||
std::tie(_service_filter, _characteristic_filter) = GetParam();
|
||||
}
|
||||
|
||||
MockPalGattClient _mock_client;
|
||||
GenericGattClient _gatt_client;
|
||||
|
||||
const Gap::Handle_t _connection_handle;
|
||||
termination_callback_t _termination_callback;
|
||||
nullptr_t _service_cb;
|
||||
nullptr_t _characteristic_cb;
|
||||
UUID _service_filter;
|
||||
UUID _characteristic_filter;
|
||||
};
|
||||
|
||||
|
||||
/* Given initialised GattClient
|
||||
* When the service discovery is launched with service and characteristic
|
||||
* callbacks set to NULL.
|
||||
* Then
|
||||
* - The function shall return BLE_ERROR_NONE
|
||||
* - No call to any discovery function shall have been made
|
||||
* - The termination callback shall have been called once with the connection
|
||||
* handle used to launch the discovery.
|
||||
*/
|
||||
|
||||
// no pending transaction
|
||||
TEST_P(LaunchDiscoveryNoCb, shall_never_launch_any_discovery) {
|
||||
EXPECT_FALSE(_gatt_client.isServiceDiscoveryActive());
|
||||
|
||||
// no call on _mock_client expected
|
||||
EXPECT_CALL(_mock_client, discover_primary_service(_, _)).Times(0);
|
||||
EXPECT_CALL(_mock_client, discover_primary_service_by_service_uuid(_, _, _)).Times(0);
|
||||
EXPECT_CALL(_mock_client, find_included_service(_, _)).Times(0);
|
||||
EXPECT_CALL(_mock_client, discover_characteristics_of_a_service(_, _)).Times(0);
|
||||
EXPECT_CALL(_mock_client, discover_characteristics_descriptors(_, _)).Times(0);
|
||||
|
||||
// termination callback shall be call once with connection handle as parameter
|
||||
EXPECT_CALL(_termination_callback, call(_)).Times(0);
|
||||
EXPECT_CALL(_termination_callback, call(_connection_handle)).Times(1);
|
||||
|
||||
ble_error_t err = _gatt_client.launchServiceDiscovery(
|
||||
_connection_handle,
|
||||
_service_cb,
|
||||
_characteristic_cb,
|
||||
_service_filter,
|
||||
_characteristic_filter
|
||||
);
|
||||
|
||||
EXPECT_EQ(BLE_ERROR_NONE, err);
|
||||
EXPECT_FALSE(_gatt_client.isServiceDiscoveryActive());
|
||||
}
|
||||
|
||||
// no CB
|
||||
// pending transaction
|
||||
TEST_P(LaunchDiscoveryNoCb, shall_not_change_discovery_status) {
|
||||
EXPECT_FALSE(_gatt_client.isServiceDiscoveryActive());
|
||||
|
||||
// launch a discovery on another connection
|
||||
const Gap::Handle_t dummy_connection_handle = _connection_handle + 1;
|
||||
void (*dummy_service_cb)(const DiscoveredService*) = [](const DiscoveredService *) { };
|
||||
|
||||
EXPECT_CALL(_mock_client, discover_primary_service(dummy_connection_handle, _))
|
||||
.WillOnce(::testing::Return(BLE_ERROR_NONE));
|
||||
|
||||
_gatt_client.launchServiceDiscovery(
|
||||
dummy_connection_handle,
|
||||
dummy_service_cb,
|
||||
NULL,
|
||||
UUID((uint16_t) 0),
|
||||
UUID((uint16_t) 0)
|
||||
);
|
||||
|
||||
EXPECT_TRUE(_gatt_client.isServiceDiscoveryActive());
|
||||
|
||||
// no call on _mock_client expected
|
||||
EXPECT_CALL(_mock_client, discover_primary_service(_, _)).Times(0);
|
||||
EXPECT_CALL(_mock_client, discover_primary_service_by_service_uuid(_, _, _)).Times(0);
|
||||
EXPECT_CALL(_mock_client, find_included_service(_, _)).Times(0);
|
||||
EXPECT_CALL(_mock_client, discover_characteristics_of_a_service(_, _)).Times(0);
|
||||
EXPECT_CALL(_mock_client, discover_characteristics_descriptors(_, _)).Times(0);
|
||||
|
||||
// termination callback shall be call once with connection handle as parameter
|
||||
EXPECT_CALL(_termination_callback, call(_)).Times(0);
|
||||
EXPECT_CALL(_termination_callback, call(_connection_handle)).Times(1);
|
||||
|
||||
ble_error_t err = _gatt_client.launchServiceDiscovery(
|
||||
_connection_handle,
|
||||
_service_cb,
|
||||
_characteristic_cb,
|
||||
_service_filter,
|
||||
_characteristic_filter
|
||||
);
|
||||
|
||||
EXPECT_EQ(BLE_ERROR_NONE, err);
|
||||
EXPECT_TRUE(_gatt_client.isServiceDiscoveryActive());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
GattClient_launch_discovery_no_cb,
|
||||
LaunchDiscoveryNoCb,
|
||||
::testing::Combine(
|
||||
::testing::Values(UUID(), UUID(0x1452), UUID("a3d1495f-dba7-4441-99f2-d0a20f663422")),
|
||||
::testing::Values(UUID(), UUID(0xBEEF), UUID("1f551ee3-aef4-4719-8c52-8b419fc4ac01"))
|
||||
)
|
||||
);
|
|
@ -0,0 +1,560 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ble/generic/GenericGattClient.h"
|
||||
#include "ble/pal/AttServerMessage.h"
|
||||
#include "ble/pal/SimpleAttServerMessage.h"
|
||||
#include "ble/DiscoveredService.h"
|
||||
|
||||
#include "mock/MockPalGattClient.h"
|
||||
#include "mock/MockCallbacks.h"
|
||||
|
||||
#include "util/PrettyPrinter.h"
|
||||
#include "util/Equality.h"
|
||||
|
||||
using ble::pal::AttErrorResponse;
|
||||
using ble::pal::AttributeOpcode;
|
||||
using ble::pal::AttReadResponse;
|
||||
using ble::pal::AttReadBlobResponse;
|
||||
using ble::generic::GenericGattClient;
|
||||
using ble::pal::vendor::mock::MockPalGattClient;
|
||||
|
||||
using ble::connection_handle_t;
|
||||
using ble::attribute_handle_t;
|
||||
using ble::make_const_ArrayView;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::InSequence;
|
||||
using ::testing::Mock;
|
||||
using ::testing::Pointee;
|
||||
|
||||
using std::vector;
|
||||
using std::tuple;
|
||||
|
||||
static vector<uint8_t> make_char_value(uint16_t length) {
|
||||
vector<uint8_t> characteristic_value(length);
|
||||
for (auto& byte : characteristic_value) {
|
||||
byte = std::rand();
|
||||
}
|
||||
return characteristic_value;
|
||||
}
|
||||
|
||||
class TestGattClientRead : public ::testing::Test {
|
||||
protected:
|
||||
TestGattClientRead() :
|
||||
_mock_client(),
|
||||
_gatt_client(&_mock_client),
|
||||
_connection_handle(0xDEAD),
|
||||
_attribute_handle(0x5645),
|
||||
_read_cb(),
|
||||
_mtu_size(23) {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
_gatt_client.onDataRead(
|
||||
makeFunctionPointer(&_read_cb, &read_callback_t::call)
|
||||
);
|
||||
|
||||
ON_CALL(_mock_client, get_mtu_size(_connection_handle, _)).
|
||||
WillByDefault(Invoke([&](auto, auto& size){
|
||||
size = this->_mtu_size;
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
}
|
||||
|
||||
void set_mtu_size_stub() {
|
||||
EXPECT_CALL(_mock_client, get_mtu_size(_connection_handle, _))
|
||||
.WillRepeatedly(::testing::DoDefault());
|
||||
}
|
||||
|
||||
MockPalGattClient _mock_client;
|
||||
GenericGattClient _gatt_client;
|
||||
const Gap::Handle_t _connection_handle;
|
||||
uint16_t _attribute_handle;
|
||||
read_callback_t _read_cb;
|
||||
uint16_t _mtu_size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Given an attribute on a server with a value with a length less than ATT_MTU - 1.
|
||||
* When the client issue tries to read is with GattClient::read with an offset
|
||||
* of 0.
|
||||
* Then:
|
||||
* - The function read_attribute_value from the pal is called with
|
||||
* connection handle and attribute handle used in GattClient::read.
|
||||
* - The pal::GattClient fire an AttReadResponse containing the value of the
|
||||
* attribute.
|
||||
* - The generic GattClient invoke the read handler with a content equal to
|
||||
* the value received and an error _status set to BLE_ERROR_NONE.
|
||||
*/
|
||||
TEST_F(TestGattClientRead, read_short_characteristic_without_offset_shall_succeed) {
|
||||
uint16_t offset = 0; // force offset to 0 for this test
|
||||
|
||||
auto char_value = make_char_value(1000);
|
||||
|
||||
// iterate over possible MTU size
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 5) {
|
||||
// iterate over possible characteristic value size
|
||||
for (uint16_t char_len = 0; char_len < (_mtu_size - 1); ++char_len) {
|
||||
set_mtu_size_stub();
|
||||
|
||||
InSequence seq;
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, read_attribute_value(_connection_handle, _attribute_handle)
|
||||
).WillOnce(Invoke([&](auto connection, auto attribute) {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttReadResponse(
|
||||
make_const_ArrayView(char_value.data(), char_len)
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(_read_cb, call(Pointee(GattReadCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
offset,
|
||||
(uint16_t) char_len,
|
||||
char_value.data(),
|
||||
BLE_ERROR_NONE
|
||||
})));
|
||||
|
||||
ble_error_t err = _gatt_client.read(_connection_handle, _attribute_handle, offset);
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue = Mock::VerifyAndClearExpectations(&_read_cb) && Mock::VerifyAndClearExpectations(&_mock_client);
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an attribute on a server with a value with a length less than ATT_MTU - 1.
|
||||
* When the client issue tries to reads it with GattClient::read with an offset
|
||||
* not equal to 0 and in the range of the attribute length.
|
||||
* Then:
|
||||
* - The function read_attribute_blob from the pal is called with
|
||||
* connection handle, the attribute handle and the offset used in
|
||||
* GattClient::read.
|
||||
* - The pal::GattClient fire an AttErrorresponse with:
|
||||
* * opcode equal to READ_BLOB_REQUEST
|
||||
* * _error_code equal to ATTRIBUTE_NOT_LONG
|
||||
* - The generic GattClient invoke the read handler with the _status set to
|
||||
* BLE_ERROR_PARAM_OUT_OF_RANGE and an _error_code set to ATTRIBUTE_NOT_LONG.
|
||||
*/
|
||||
TEST_F(TestGattClientRead, read_short_characteristic_with_offset_shall_fail) {
|
||||
// iterate over possible MTU size
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size+=10) {
|
||||
// iterate over possible characteristic value size
|
||||
for (uint16_t char_len = 1; char_len < (_mtu_size - 1); char_len += 10) {
|
||||
// iterate over possible offsets
|
||||
for (uint16_t offset = 1; offset <= char_len; offset += 10) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, read_attribute_blob(_connection_handle, _attribute_handle, offset)
|
||||
).WillOnce(Invoke([&](auto connection, auto attribute, auto off) {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::READ_BLOB_REQUEST,
|
||||
attribute,
|
||||
AttErrorResponse::ATTRIBUTE_NOT_LONG
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(_read_cb, call(Pointee(GattReadCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
offset,
|
||||
AttErrorResponse::ATTRIBUTE_NOT_LONG,
|
||||
NULL,
|
||||
BLE_ERROR_PARAM_OUT_OF_RANGE
|
||||
})));
|
||||
|
||||
ble_error_t err = _gatt_client.read(_connection_handle, _attribute_handle, offset);
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue = Mock::VerifyAndClearExpectations(&_read_cb) && Mock::VerifyAndClearExpectations(&_mock_client);
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an attribute on a server with a value with a length less than ATT_MTU - 1.
|
||||
* When the client tries to reads it with GattClient::read with an offset
|
||||
* out of the range of the attribute length.
|
||||
* Then:
|
||||
* - The function read_attribute_blob from the pal is called with
|
||||
* connection handle, the attribute handle and the offset used in
|
||||
* GattClient::read.
|
||||
* - The pal::GattClient fire an AttErrorresponse with:
|
||||
* * opcode equal to READ_BLOB_REQUEST
|
||||
* * _error_code equal to INVALID_OFFSET
|
||||
* - The generic GattClient invoke the read handler with the _status set to
|
||||
* BLE_ERROR_PARAM_OUT_OF_RANGE and an _error_code set to INVALID_OFFSET.
|
||||
*/
|
||||
TEST_F(TestGattClientRead, read_with_out_of_range_offset_shall_fail) {
|
||||
// iterate over possible MTU size
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 10) {
|
||||
// iterate over possible characteristic value size
|
||||
for (uint16_t char_len = 1; char_len < (_mtu_size - 1); char_len += 10) {
|
||||
// iterate over possible offsets
|
||||
for (uint16_t offset = char_len + 1; offset < 518; offset += 10) {
|
||||
InSequence seq;
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, read_attribute_blob(_connection_handle, _attribute_handle, offset)
|
||||
).WillOnce(Invoke([&](auto connection, auto attribute, auto off) {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::READ_BLOB_REQUEST,
|
||||
attribute,
|
||||
AttErrorResponse::INVALID_OFFSET
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(_read_cb, call(Pointee(GattReadCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
offset,
|
||||
AttErrorResponse::INVALID_OFFSET,
|
||||
NULL,
|
||||
BLE_ERROR_PARAM_OUT_OF_RANGE,
|
||||
})));
|
||||
|
||||
ble_error_t err = _gatt_client.read(_connection_handle, _attribute_handle, offset);
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue = Mock::VerifyAndClearExpectations(&_read_cb) && Mock::VerifyAndClearExpectations(&_mock_client);
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestGattClientReadAttributeError :
|
||||
public TestGattClientRead,
|
||||
public ::testing::WithParamInterface<tuple<AttErrorResponse::AttributeErrorCode, ble_error_t>> {
|
||||
protected:
|
||||
TestGattClientReadAttributeError() :
|
||||
TestGattClientRead(),
|
||||
_error_code((AttErrorResponse::AttributeErrorCode) 0x00),
|
||||
_status(BLE_ERROR_NONE) {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
TestGattClientRead::SetUp();
|
||||
std::tie(_error_code, _status) = GetParam();
|
||||
}
|
||||
|
||||
AttErrorResponse::AttributeErrorCode _error_code;
|
||||
ble_error_t _status;
|
||||
};
|
||||
|
||||
/*
|
||||
* Given an attribute which cannot be read.
|
||||
* When the client tries to reads it with GattClient::read with an offset
|
||||
* of 0
|
||||
* Then:
|
||||
* - The function read_attribute_value from the pal is called with
|
||||
* connection handle, the attribute handle in GattClient::read.
|
||||
* - The pal::GattClient fire an AttErrorresponse with:
|
||||
* * opcode equal to READ_REQUEST
|
||||
* * _error_code equal to the expected error
|
||||
* - The generic GattClient invoke the read handler with the _status set to
|
||||
* the expected _status and an _error_code set to error code in the
|
||||
* AttErrorResponse.
|
||||
*/
|
||||
TEST_P(TestGattClientReadAttributeError, read_no_offset) {
|
||||
uint16_t offset = 0;
|
||||
|
||||
InSequence seq;
|
||||
EXPECT_CALL(
|
||||
_mock_client, read_attribute_value(_connection_handle, _attribute_handle)
|
||||
).WillOnce(Invoke([&](auto connection, auto attribute) {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::READ_REQUEST,
|
||||
attribute,
|
||||
_error_code
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(_read_cb, call(Pointee(GattReadCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
offset,
|
||||
_error_code,
|
||||
/* data */ NULL,
|
||||
_status
|
||||
})));
|
||||
|
||||
ble_error_t err = _gatt_client.read(_connection_handle, _attribute_handle, offset);
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an attribute which cannot be read.
|
||||
* When the client tries to reads it with GattClient::read with an offset
|
||||
* of 0
|
||||
* Then:
|
||||
* - The function read_attribute_blob from the pal is called with
|
||||
* connection handle, the attribute handle and the offset passed to
|
||||
* GattClient::read.
|
||||
* - The pal::GattClient fire an AttErrorresponse with:
|
||||
* * opcode equal to READ_BLOB_REQUEST
|
||||
* * _error_code equal to the expected error
|
||||
* - The generic GattClient invoke the read handler with the _status set to
|
||||
* the expected _status and an _error_code set to error code in the
|
||||
* AttErrorResponse.
|
||||
*/
|
||||
TEST_P(TestGattClientReadAttributeError, read_with_offset) {
|
||||
uint16_t offset = 10;
|
||||
|
||||
InSequence seq;
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, read_attribute_blob(_connection_handle, _attribute_handle, offset)
|
||||
).WillOnce(Invoke([&](auto connection, auto attribute, auto off) {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::READ_BLOB_REQUEST,
|
||||
attribute,
|
||||
_error_code
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(_read_cb, call(Pointee(GattReadCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
offset,
|
||||
_error_code,
|
||||
/* data */ NULL,
|
||||
_status
|
||||
})));
|
||||
|
||||
ble_error_t err = _gatt_client.read(_connection_handle, _attribute_handle, offset);
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
TestGattClientReadAttributeError_combination,
|
||||
TestGattClientReadAttributeError,
|
||||
::testing::Values(
|
||||
std::make_tuple(AttErrorResponse::INVALID_HANDLE, BLE_ERROR_INVALID_PARAM),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_AUTHORIZATION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_AUTHENTICATION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_ENCRYPTION_KEY_SIZE, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_ENCRYPTION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::READ_NOT_PERMITTED, BLE_ERROR_OPERATION_NOT_PERMITTED)
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* Given a readable attribute with a value longer than ATT_MTU - 2.
|
||||
* When the GattClient::read is called without offset
|
||||
* Then:
|
||||
* - The client call the pal function read_attribute_value with the connection
|
||||
* handle and the the attribute handle used in GattClient::read.
|
||||
* When the pal::GattClient fire an AttReadResponse containing the first
|
||||
* (ATT_MTU - 1) bytes of the attribute value.
|
||||
* Then:
|
||||
* - The client call the function read_attribute_blob with the connection
|
||||
* handle and the attribute handle used in GattClient::read. The offset is
|
||||
* equal of the number of bytes read in the characteristic.
|
||||
* When the pal::Client fire an AttReadBlobResponse containing the partial value
|
||||
* of the characteristic starting at the offset requested. If the length of
|
||||
* the remaining bytes is greater than ATT_MTU - 1 then the length of the
|
||||
* partial value in the response shall be ATT_MTU - 1. Otherwise it is the
|
||||
* length of the remaining bytes.
|
||||
* Then:
|
||||
* - If the value received by the client has a len greater than ATT_MTU - 1.
|
||||
* Then the client issue a new read_attribute_blob with an updated index.
|
||||
* - Else the read callback is called with the value of the attribute read.
|
||||
*/
|
||||
TEST_F(TestGattClientRead, read_long_characteristic_without_offset_shall_succeed) {
|
||||
const uint16_t offset = 0; // force offset to 0 for this test
|
||||
|
||||
auto char_value = make_char_value(517 * 6);
|
||||
|
||||
// iterate over possible MTU size
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 5) {
|
||||
// iterate over possible characteristic value size
|
||||
for (uint16_t char_len = (_mtu_size - 1); char_len < (_mtu_size * 6); char_len += 10) {
|
||||
set_mtu_size_stub();
|
||||
|
||||
InSequence seq;
|
||||
EXPECT_CALL(
|
||||
_mock_client, read_attribute_value(_connection_handle, _attribute_handle)
|
||||
).WillOnce(Invoke([&](auto connection, auto attribute) {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttReadResponse(
|
||||
make_const_ArrayView(char_value.data(), _mtu_size - 1)
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
for(uint16_t blob_offset = (_mtu_size - 1); blob_offset <= char_len; blob_offset += (_mtu_size - 1)) {
|
||||
EXPECT_CALL(
|
||||
_mock_client, read_attribute_blob(_connection_handle, _attribute_handle, blob_offset)
|
||||
).WillOnce(Invoke([&](auto connection, auto attribute, uint16_t offset) {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttReadBlobResponse(
|
||||
make_const_ArrayView(
|
||||
char_value.data() + offset,
|
||||
(char_len - offset) > (_mtu_size - 1) ? (_mtu_size - 1) : (char_len - offset)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
}
|
||||
|
||||
EXPECT_CALL(_read_cb, call(Pointee(GattReadCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
offset,
|
||||
(uint16_t) char_len,
|
||||
char_value.data(),
|
||||
BLE_ERROR_NONE
|
||||
})));
|
||||
|
||||
ble_error_t err = _gatt_client.read(_connection_handle, _attribute_handle, offset);
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue = Mock::VerifyAndClearExpectations(&_mock_client) && Mock::VerifyAndClearExpectations(&_read_cb);
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a readable attribute with a value longer than ATT_MTU - 2.
|
||||
* When the GattClient::read is called without offset
|
||||
* Then:
|
||||
* - The client call the pal function read_attribute_blob with the connection
|
||||
* handle, the attribute handle and the offset used in GattClient::read.
|
||||
* When the pal::GattClient fire an AttReadResponse containing the first bytes
|
||||
* of the attribute value.
|
||||
* Then:
|
||||
* - If the response length is less than (ATT_MTU - 1), the read callback is
|
||||
* called with the value of the attribute read.
|
||||
* - The client call the function read_attribute_blob with the connection
|
||||
* handle and the attribute handle used in GattClient::read. The offset is
|
||||
* equal of the number of bytes read in the characteristic plus the
|
||||
* first offset.
|
||||
* When the pal::Client fire an AttReadBlobResponse containing the partial value
|
||||
* of the characteristic starting at the offset requested. If the length of
|
||||
* the remaining bytes is greater than ATT_MTU - 1 then the length of the
|
||||
* partial value in the response shall be ATT_MTU - 1. Otherwise it is the
|
||||
* length of the remaining bytes.
|
||||
* Then:
|
||||
* - If the value received by the client has a len greater than ATT_MTU - 1.
|
||||
* Then the client issue a new read_attribute_blob with an updated index.
|
||||
* - Else the read callback is called with the value of the attribute read.
|
||||
*/
|
||||
TEST_F(TestGattClientRead, read_long_characteristic_with_offset_shall_succeed) {
|
||||
auto char_value = make_char_value(517 * 6);
|
||||
|
||||
// iterate over possible MTU size
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 35) {
|
||||
// iterate over possible characteristic value size
|
||||
for (uint16_t char_len = (_mtu_size - 1); char_len < (_mtu_size * 4); char_len += 30) {
|
||||
for(uint16_t offset = 1; offset < char_len; offset += 20) {
|
||||
set_mtu_size_stub();
|
||||
|
||||
InSequence seq;
|
||||
for(uint16_t blob_offset = offset; blob_offset <= char_len; blob_offset += (_mtu_size - 1)) {
|
||||
EXPECT_CALL(
|
||||
_mock_client, read_attribute_blob(_connection_handle, _attribute_handle, blob_offset)
|
||||
).WillOnce(Invoke([&](auto connection, auto attribute, auto offset) {
|
||||
_mock_client.on_server_event(
|
||||
connection,
|
||||
AttReadBlobResponse(
|
||||
make_const_ArrayView(
|
||||
char_value.data() + offset,
|
||||
(char_len - offset) > (_mtu_size - 1) ? (_mtu_size - 1) : (char_len - offset)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
}
|
||||
|
||||
EXPECT_CALL(_read_cb, call(Pointee(GattReadCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
offset,
|
||||
(uint16_t) (char_len - offset),
|
||||
char_value.data() + offset,
|
||||
BLE_ERROR_NONE
|
||||
})));
|
||||
|
||||
ble_error_t err = _gatt_client.read(_connection_handle, _attribute_handle, offset);
|
||||
EXPECT_EQ(err, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue = Mock::VerifyAndClearExpectations(&_mock_client) && Mock::VerifyAndClearExpectations(&_read_cb);
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Add test for failure in the middle of the transaction
|
|
@ -0,0 +1,147 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ble/generic/GenericGattClient.h"
|
||||
#include "ble/pal/AttServerMessage.h"
|
||||
#include "ble/pal/SimpleAttServerMessage.h"
|
||||
#include "ble/DiscoveredService.h"
|
||||
|
||||
#include "mock/MockCallbacks.h"
|
||||
#include "mock/MockPalGattClient.h"
|
||||
|
||||
#include "util/PrettyPrinter.h"
|
||||
#include "util/Equality.h"
|
||||
|
||||
using ble::pal::AttHandleValueIndication;
|
||||
using ble::pal::AttHandleValueNotification;
|
||||
using ble::generic::GenericGattClient;
|
||||
using ble::pal::vendor::mock::MockPalGattClient;
|
||||
|
||||
using ble::connection_handle_t;
|
||||
using ble::make_const_ArrayView;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::Pointee;
|
||||
|
||||
using std::vector;
|
||||
|
||||
static vector<uint8_t> make_char_value(uint16_t length) {
|
||||
vector<uint8_t> characteristic_value(length);
|
||||
for (auto& byte : characteristic_value) {
|
||||
byte = std::rand();
|
||||
}
|
||||
return characteristic_value;
|
||||
}
|
||||
|
||||
class TestGattServerEvent : public ::testing::Test {
|
||||
protected:
|
||||
TestGattServerEvent() :
|
||||
_mock_client(),
|
||||
_gatt_client(&_mock_client),
|
||||
_connection_handle(0xDEAD),
|
||||
_attribute_handle(0x5645),
|
||||
_mtu_size(23) {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
_gatt_client.onHVX(
|
||||
makeFunctionPointer(&_event_cb, &server_event_callback_t::call)
|
||||
);
|
||||
|
||||
ON_CALL(_mock_client, get_mtu_size(_connection_handle, _)).
|
||||
WillByDefault(Invoke([&](auto, auto& mtu_size ){
|
||||
mtu_size = this->_mtu_size;
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
}
|
||||
|
||||
void set_mtu_size_stub() {
|
||||
EXPECT_CALL(_mock_client, get_mtu_size(_connection_handle, _))
|
||||
.WillRepeatedly(::testing::DoDefault());
|
||||
}
|
||||
|
||||
MockPalGattClient _mock_client;
|
||||
GenericGattClient _gatt_client;
|
||||
const Gap::Handle_t _connection_handle;
|
||||
uint16_t _attribute_handle;
|
||||
server_event_callback_t _event_cb;
|
||||
uint16_t _mtu_size;
|
||||
};
|
||||
|
||||
TEST_F(TestGattServerEvent, event_callback_shall_be_called_on_indication) {
|
||||
auto value = make_char_value(5000);
|
||||
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 10) {
|
||||
for (uint16_t len = 0; len < (_mtu_size - 3); len += 10) {
|
||||
|
||||
EXPECT_CALL(
|
||||
_event_cb, call(Pointee(GattHVXCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
HVXType_t::BLE_HVX_INDICATION,
|
||||
len,
|
||||
value.data()
|
||||
}))
|
||||
);
|
||||
|
||||
_mock_client.on_server_event(
|
||||
_connection_handle,
|
||||
AttHandleValueIndication(
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(value.data(), len)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGattServerEvent, event_callback_shall_be_called_on_notification) {
|
||||
auto value = make_char_value(5000);
|
||||
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 10) {
|
||||
for (uint16_t len = 0; len < (_mtu_size - 3); len += 10) {
|
||||
|
||||
EXPECT_CALL(
|
||||
_event_cb, call(Pointee(GattHVXCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
HVXType_t::BLE_HVX_NOTIFICATION,
|
||||
len,
|
||||
value.data()
|
||||
}))
|
||||
);
|
||||
|
||||
_mock_client.on_server_event(
|
||||
_connection_handle,
|
||||
AttHandleValueNotification(
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(value.data(), len)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,691 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ble/generic/GenericGattClient.h"
|
||||
#include "ble/pal/AttServerMessage.h"
|
||||
#include "ble/pal/SimpleAttServerMessage.h"
|
||||
#include "ble/DiscoveredService.h"
|
||||
|
||||
#include "mock/MockPalGattClient.h"
|
||||
#include "mock/MockCallbacks.h"
|
||||
|
||||
#include "util/PrettyPrinter.h"
|
||||
#include "util/Equality.h"
|
||||
|
||||
using ble::pal::AttErrorResponse;
|
||||
using ble::pal::AttributeOpcode;
|
||||
using ble::pal::AttWriteResponse;
|
||||
using ble::pal::AttPrepareWriteResponse;
|
||||
using ble::pal::AttExecuteWriteResponse;
|
||||
using ble::generic::GenericGattClient;
|
||||
using ble::pal::vendor::mock::MockPalGattClient;
|
||||
|
||||
using ble::connection_handle_t;
|
||||
using ble::attribute_handle_t;
|
||||
using ble::ArrayView;
|
||||
using ble::make_const_ArrayView;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::InSequence;
|
||||
using ::testing::Mock;
|
||||
using ::testing::Pointee;
|
||||
|
||||
using std::vector;
|
||||
using std::tuple;
|
||||
|
||||
static vector<uint8_t> make_char_value(uint16_t length) {
|
||||
vector<uint8_t> characteristic_value(length);
|
||||
for (auto& byte : characteristic_value) {
|
||||
byte = std::rand();
|
||||
}
|
||||
return characteristic_value;
|
||||
}
|
||||
|
||||
class TestGattClientWrite : public ::testing::Test {
|
||||
protected:
|
||||
TestGattClientWrite() :
|
||||
_mock_client(),
|
||||
_gatt_client(&_mock_client),
|
||||
_connection_handle(0xDEAD),
|
||||
_attribute_handle(0x5645),
|
||||
_write_cb(),
|
||||
_mtu_size(23) {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
_gatt_client.onDataWritten(
|
||||
makeFunctionPointer(&_write_cb, &write_callback_t::call)
|
||||
);
|
||||
|
||||
ON_CALL(
|
||||
_mock_client, get_mtu_size(_connection_handle, _)
|
||||
).WillByDefault(Invoke([&](auto, auto& mtu_size){
|
||||
mtu_size = _mtu_size;
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
}
|
||||
|
||||
void set_mtu_size_stub() {
|
||||
EXPECT_CALL(_mock_client, get_mtu_size(_connection_handle, _))
|
||||
.WillRepeatedly(::testing::DoDefault());
|
||||
}
|
||||
|
||||
MockPalGattClient _mock_client;
|
||||
GenericGattClient _gatt_client;
|
||||
const Gap::Handle_t _connection_handle;
|
||||
uint16_t _attribute_handle;
|
||||
write_callback_t _write_cb;
|
||||
uint16_t _mtu_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a payload shorter than the current MTU size - 2.
|
||||
* When the payload is sent in a write command
|
||||
* Then the payload is sent to the pal without error.
|
||||
*/
|
||||
TEST_F(TestGattClientWrite, write_command_should_succeed_if_it_fit_in_the_payload) {
|
||||
auto value = make_char_value(1000);
|
||||
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 10) {
|
||||
for(uint16_t len = 0; len <= (_mtu_size - 3); len += 10) {
|
||||
set_mtu_size_stub();
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, write_without_response(
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(value.data(), len)
|
||||
)
|
||||
);
|
||||
|
||||
ble_error_t error = _gatt_client.write(
|
||||
GattClient::GATT_OP_WRITE_CMD,
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
len,
|
||||
value.data()
|
||||
);
|
||||
|
||||
EXPECT_EQ(error, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue =
|
||||
Mock::VerifyAndClearExpectations(&_write_cb) &&
|
||||
Mock::VerifyAndClearExpectations(&_mock_client);
|
||||
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a payload with a length longer than MTU size -1
|
||||
* When the payload is sent in a write command
|
||||
* Then the function returns an error.
|
||||
*/
|
||||
TEST_F(TestGattClientWrite, write_command_should_fail_if_it_doesnt_fit_in_the_payload) {
|
||||
auto value = make_char_value(1000);
|
||||
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 10) {
|
||||
for(size_t len = (_mtu_size - 3) + 1; len < 517; len += 10) {
|
||||
set_mtu_size_stub();
|
||||
|
||||
ble_error_t error = _gatt_client.write(
|
||||
GattClient::GATT_OP_WRITE_CMD,
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
len,
|
||||
value.data()
|
||||
);
|
||||
|
||||
EXPECT_EQ(error, BLE_ERROR_PARAM_OUT_OF_RANGE);
|
||||
|
||||
bool can_continue =
|
||||
Mock::VerifyAndClearExpectations(&_write_cb) &&
|
||||
Mock::VerifyAndClearExpectations(&_mock_client) &&
|
||||
(error == BLE_ERROR_PARAM_OUT_OF_RANGE);
|
||||
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test unexpected results from write_without_response
|
||||
|
||||
/**
|
||||
* Given a payload shorter than the current MTU size - 2.
|
||||
* When the payload is sent in a write request
|
||||
* Then if the operation succeed the write callback shall be invoked.
|
||||
*/
|
||||
TEST_F(TestGattClientWrite, write_request_short_characteristics) {
|
||||
auto value = make_char_value(1000);
|
||||
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 10) {
|
||||
for(uint16_t len = 0; len <= (_mtu_size - 3); len += 10) {
|
||||
set_mtu_size_stub();
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, write_attribute(
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(value.data(), len)
|
||||
)
|
||||
).WillOnce(Invoke([&](auto conn_handle, auto, const auto&) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttWriteResponse()
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(_write_cb, call(Pointee(GattWriteCallbackParams {
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
GattWriteCallbackParams::OP_WRITE_REQ,
|
||||
BLE_ERROR_NONE,
|
||||
/* error code */ 0,
|
||||
})));
|
||||
|
||||
ble_error_t error = _gatt_client.write(
|
||||
GattClient::GATT_OP_WRITE_REQ,
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
len,
|
||||
value.data()
|
||||
);
|
||||
|
||||
EXPECT_EQ(error, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue =
|
||||
Mock::VerifyAndClearExpectations(&_write_cb) &&
|
||||
Mock::VerifyAndClearExpectations(&_mock_client);
|
||||
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestGattClientWriteAttributeError :
|
||||
public TestGattClientWrite,
|
||||
public ::testing::WithParamInterface<tuple<AttErrorResponse::AttributeErrorCode, ble_error_t>> {
|
||||
protected:
|
||||
TestGattClientWriteAttributeError() :
|
||||
TestGattClientWrite(),
|
||||
error_code((AttErrorResponse::AttributeErrorCode) 0x00),
|
||||
status(BLE_ERROR_NONE) {
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
TestGattClientWrite::SetUp();
|
||||
std::tie(error_code, status) = GetParam();
|
||||
}
|
||||
|
||||
AttErrorResponse::AttributeErrorCode error_code;
|
||||
ble_error_t status;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a payload shorter than the current MTU size - 2.
|
||||
* When the payload is sent in a write request
|
||||
* Then if the operation fail the write callback shall report the error
|
||||
*/
|
||||
TEST_P(TestGattClientWriteAttributeError, write_request_short_characteristics) {
|
||||
auto value = make_char_value(1000);
|
||||
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 10) {
|
||||
for(uint16_t len = 0; len <= (_mtu_size - 3); len += 10) {
|
||||
set_mtu_size_stub();
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, write_attribute(
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(value.data(), len)
|
||||
)
|
||||
).WillOnce(Invoke([&](auto conn_handle, auto att_handle, const auto&) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::WRITE_REQUEST,
|
||||
att_handle,
|
||||
error_code
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(
|
||||
_write_cb, call(Pointee(GattWriteCallbackParams{
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
GattWriteCallbackParams::OP_PREP_WRITE_REQ,
|
||||
status,
|
||||
error_code
|
||||
}))
|
||||
);
|
||||
|
||||
ble_error_t error = _gatt_client.write(
|
||||
GattClient::GATT_OP_WRITE_REQ,
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
len,
|
||||
value.data()
|
||||
);
|
||||
|
||||
EXPECT_EQ(error, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue =
|
||||
Mock::VerifyAndClearExpectations(&_write_cb) &&
|
||||
Mock::VerifyAndClearExpectations(&_mock_client);
|
||||
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
TestGattClientWriteAttributeError_combination,
|
||||
TestGattClientWriteAttributeError,
|
||||
::testing::Values(
|
||||
std::make_tuple(AttErrorResponse::INVALID_ATTRIBUTE_VALUE_LENGTH, BLE_ERROR_INVALID_PARAM),
|
||||
std::make_tuple(AttErrorResponse::INVALID_HANDLE, BLE_ERROR_INVALID_PARAM),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_AUTHORIZATION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_AUTHENTICATION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_ENCRYPTION_KEY_SIZE, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_ENCRYPTION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::WRITE_NOT_PERMITTED, BLE_ERROR_OPERATION_NOT_PERMITTED),
|
||||
std::make_tuple((AttErrorResponse::AttributeErrorCode)/* application error */ 0x80, BLE_ERROR_UNSPECIFIED)
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Given a payload with a length bigger than the current MTU size - 2.
|
||||
* When the payload is written as using Gattclient::write
|
||||
* Then the payload is split into multiple chunks of a maximum size equal to
|
||||
* MTU_size - 5. Those chunks are sent sequentially using a prepare write
|
||||
* request which is answered by a prepare write response by the server.
|
||||
* Once all the chunks have been received by the server, the client sends an
|
||||
* Execute write request. Once the client received an Execute Write request,
|
||||
* the server shall invoke the write callback.
|
||||
*/
|
||||
TEST_F(TestGattClientWrite, write_request_long_characteristics) {
|
||||
auto value = make_char_value(10000);
|
||||
|
||||
for (_mtu_size = 23; _mtu_size < 517; _mtu_size += 10) {
|
||||
for(uint16_t len = (_mtu_size - 2); len <= (_mtu_size * 4); len += 10) {
|
||||
set_mtu_size_stub();
|
||||
|
||||
InSequence seq;
|
||||
|
||||
for (uint16_t offset = 0; offset < len; offset += (_mtu_size - 5)) {
|
||||
EXPECT_CALL(
|
||||
_mock_client, queue_prepare_write(
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(
|
||||
value.data() + offset,
|
||||
((len - offset) > (_mtu_size - 5)) ? (_mtu_size - 5) : (len - offset)
|
||||
),
|
||||
offset
|
||||
)
|
||||
).WillOnce(Invoke([&](auto conn_handle, auto att_handle, const auto& data, auto offset) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttPrepareWriteResponse(
|
||||
att_handle,
|
||||
offset,
|
||||
data
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
}
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, execute_write_queue(
|
||||
_connection_handle,
|
||||
true
|
||||
)
|
||||
).WillOnce(Invoke([&](auto conn_handle, bool execute) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttExecuteWriteResponse()
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(
|
||||
_write_cb, call(Pointee(GattWriteCallbackParams{
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
GattWriteCallbackParams::OP_WRITE_REQ,
|
||||
BLE_ERROR_NONE,
|
||||
/* error code */ 0,
|
||||
}))
|
||||
);
|
||||
|
||||
ble_error_t error = _gatt_client.write(
|
||||
GattClient::GATT_OP_WRITE_REQ,
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
len,
|
||||
value.data()
|
||||
);
|
||||
|
||||
EXPECT_EQ(error, BLE_ERROR_NONE);
|
||||
|
||||
bool can_continue =
|
||||
Mock::VerifyAndClearExpectations(&_write_cb) &&
|
||||
Mock::VerifyAndClearExpectations(&_mock_client);
|
||||
|
||||
if (!can_continue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestGattClientPrepareWriteAttributeError : public TestGattClientWriteAttributeError { };
|
||||
|
||||
/**
|
||||
* TODO doc
|
||||
*/
|
||||
TEST_P(TestGattClientPrepareWriteAttributeError, prepare_write_error) {
|
||||
auto value = make_char_value(10000);
|
||||
|
||||
_mtu_size = 23;
|
||||
set_mtu_size_stub();
|
||||
|
||||
InSequence seq;
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, queue_prepare_write(
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(
|
||||
value.data(),
|
||||
_mtu_size - 5
|
||||
),
|
||||
0
|
||||
)
|
||||
).WillOnce(Invoke([&](auto conn_handle, auto att_handle, const auto&, auto offset) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::PREPARE_WRITE_REQUEST,
|
||||
att_handle,
|
||||
error_code
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, execute_write_queue(_connection_handle, false)
|
||||
).WillOnce(Invoke([&](auto conn_handle, bool execute) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttExecuteWriteResponse()
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(
|
||||
_write_cb, call(Pointee(GattWriteCallbackParams{
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
GattWriteCallbackParams::OP_WRITE_REQ,
|
||||
status,
|
||||
/* error code */ error_code,
|
||||
}))
|
||||
);
|
||||
|
||||
ble_error_t error = _gatt_client.write(
|
||||
GattClient::GATT_OP_WRITE_REQ,
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
_mtu_size * 10,
|
||||
value.data()
|
||||
);
|
||||
|
||||
EXPECT_EQ(error, BLE_ERROR_NONE);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
TestGattClientPrepareWriteAttributeError_combination,
|
||||
TestGattClientPrepareWriteAttributeError,
|
||||
::testing::Values(
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_AUTHORIZATION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_AUTHENTICATION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_ENCRYPTION_KEY_SIZE, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INSUFFICIENT_ENCRYPTION, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::PREPARE_QUEUE_FULL, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INVALID_HANDLE, BLE_ERROR_INVALID_PARAM),
|
||||
std::make_tuple(AttErrorResponse::WRITE_NOT_PERMITTED, BLE_ERROR_OPERATION_NOT_PERMITTED),
|
||||
std::make_tuple((AttErrorResponse::AttributeErrorCode)/* application error */ 0x80, BLE_ERROR_UNSPECIFIED)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
||||
class TestGattClientPrepareWriteAttributeErrorInTransaction : public TestGattClientWriteAttributeError { };
|
||||
|
||||
/**
|
||||
* TODO doc
|
||||
*/
|
||||
TEST_P(TestGattClientPrepareWriteAttributeErrorInTransaction, prepare_write_error_in_transaction) {
|
||||
auto value = make_char_value(10000);
|
||||
|
||||
_mtu_size = 23;
|
||||
set_mtu_size_stub();
|
||||
|
||||
InSequence seq;
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, queue_prepare_write(
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(
|
||||
value.data(),
|
||||
_mtu_size - 5
|
||||
),
|
||||
/* offset */ 0
|
||||
)
|
||||
).WillOnce(Invoke([&](auto conn_handle, auto att_handle, const auto&, auto) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttPrepareWriteResponse(
|
||||
att_handle,
|
||||
0,
|
||||
make_const_ArrayView(value.data(), _mtu_size - 5)
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
|
||||
uint16_t offset = _mtu_size - 5;
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, queue_prepare_write(
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(
|
||||
value.data() + offset,
|
||||
_mtu_size - 5
|
||||
),
|
||||
offset
|
||||
)
|
||||
).WillOnce(Invoke([&](auto conn_handle, auto att_handle, const auto&, auto offset) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::PREPARE_WRITE_REQUEST,
|
||||
att_handle,
|
||||
error_code
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, execute_write_queue(_connection_handle, false)
|
||||
).WillOnce(Invoke([&](auto conn_handle, bool execute) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttExecuteWriteResponse()
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(
|
||||
_write_cb, call(Pointee(GattWriteCallbackParams{
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
GattWriteCallbackParams::OP_WRITE_REQ,
|
||||
status,
|
||||
/* error code */ error_code,
|
||||
}))
|
||||
);
|
||||
|
||||
ble_error_t error = _gatt_client.write(
|
||||
GattClient::GATT_OP_WRITE_REQ,
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
_mtu_size * 10,
|
||||
value.data()
|
||||
);
|
||||
|
||||
EXPECT_EQ(error, BLE_ERROR_NONE);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
TestGattClientPrepareWriteAttributeErrorInTransaction_combination,
|
||||
TestGattClientPrepareWriteAttributeErrorInTransaction,
|
||||
::testing::Values(
|
||||
std::make_tuple(AttErrorResponse::PREPARE_QUEUE_FULL, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple((AttErrorResponse::AttributeErrorCode)/* application error */ 0x80, BLE_ERROR_UNSPECIFIED)
|
||||
)
|
||||
);
|
||||
|
||||
class TestGattClientExecuteWriteRequestError : public TestGattClientWriteAttributeError { };
|
||||
|
||||
/**
|
||||
* TODO doc
|
||||
*/
|
||||
TEST_P(TestGattClientExecuteWriteRequestError, prepare_write_error_in_transaction) {
|
||||
auto value = make_char_value(10000);
|
||||
|
||||
_mtu_size = 23;
|
||||
set_mtu_size_stub();
|
||||
const uint16_t len = _mtu_size * 5;
|
||||
|
||||
InSequence seq;
|
||||
|
||||
for (uint16_t offset = 0; offset < len; offset += (_mtu_size - 5)) {
|
||||
EXPECT_CALL(
|
||||
_mock_client, queue_prepare_write(
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
make_const_ArrayView(
|
||||
value.data() + offset,
|
||||
((len - offset) > (_mtu_size - 5)) ? (_mtu_size - 5) : (len - offset)
|
||||
),
|
||||
offset
|
||||
)
|
||||
).WillOnce(Invoke([&](auto conn_handle, auto att_handle, const auto& data, auto offset) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttPrepareWriteResponse(
|
||||
att_handle,
|
||||
offset,
|
||||
data
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
}
|
||||
|
||||
EXPECT_CALL(
|
||||
_mock_client, execute_write_queue(
|
||||
_connection_handle, true
|
||||
)
|
||||
).WillOnce(Invoke([&](connection_handle_t conn_handle, bool execute) {
|
||||
_mock_client.on_server_event(
|
||||
conn_handle,
|
||||
AttErrorResponse(
|
||||
AttributeOpcode::EXECUTE_WRITE_REQUEST,
|
||||
_attribute_handle,
|
||||
error_code
|
||||
)
|
||||
);
|
||||
return BLE_ERROR_NONE;
|
||||
}));
|
||||
|
||||
EXPECT_CALL(
|
||||
_write_cb, call(Pointee(GattWriteCallbackParams{
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
GattWriteCallbackParams::OP_WRITE_REQ,
|
||||
status,
|
||||
error_code
|
||||
}))
|
||||
);
|
||||
|
||||
ble_error_t error = _gatt_client.write(
|
||||
GattClient::GATT_OP_WRITE_REQ,
|
||||
_connection_handle,
|
||||
_attribute_handle,
|
||||
len,
|
||||
value.data()
|
||||
);
|
||||
|
||||
EXPECT_EQ(error, BLE_ERROR_NONE);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
TestGattClientExecuteWriteRequestError_combination,
|
||||
TestGattClientExecuteWriteRequestError,
|
||||
::testing::Values(
|
||||
std::make_tuple(AttErrorResponse::INVALID_HANDLE, BLE_ERROR_INVALID_PARAM),
|
||||
std::make_tuple(AttErrorResponse::WRITE_NOT_PERMITTED, BLE_ERROR_OPERATION_NOT_PERMITTED),
|
||||
std::make_tuple(AttErrorResponse::PREPARE_QUEUE_FULL, BLE_ERROR_INVALID_STATE),
|
||||
std::make_tuple(AttErrorResponse::INVALID_OFFSET, BLE_ERROR_INVALID_PARAM),
|
||||
std::make_tuple(AttErrorResponse::INVALID_ATTRIBUTE_VALUE_LENGTH, BLE_ERROR_INVALID_PARAM),
|
||||
std::make_tuple(AttErrorResponse::WRITE_REQUEST_REJECTED, BLE_ERROR_OPERATION_NOT_PERMITTED),
|
||||
std::make_tuple((AttErrorResponse::AttributeErrorCode)/* application error */ 0x80, BLE_ERROR_UNSPECIFIED)
|
||||
)
|
||||
);
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MockCallbacks.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor implementation of the mocked callbacks
|
||||
//
|
||||
// WARNING: Do not remove; it speedup compile time.
|
||||
|
||||
service_callback_t::service_callback_t() { }
|
||||
service_callback_t::~service_callback_t() { }
|
||||
|
||||
characteristic_callback_t::characteristic_callback_t() { }
|
||||
characteristic_callback_t::~characteristic_callback_t() { }
|
||||
|
||||
termination_callback_t::termination_callback_t() { }
|
||||
termination_callback_t::~termination_callback_t() { }
|
||||
|
||||
read_callback_t::read_callback_t() { }
|
||||
read_callback_t::~read_callback_t() { }
|
||||
|
||||
write_callback_t::write_callback_t() { }
|
||||
write_callback_t::~write_callback_t() { }
|
||||
|
||||
server_event_callback_t::server_event_callback_t() { }
|
||||
server_event_callback_t::~server_event_callback_t() { }
|
||||
|
||||
descriptor_callback_t::descriptor_callback_t() { }
|
||||
descriptor_callback_t::~descriptor_callback_t() { }
|
||||
|
||||
descriptor_termination_callback_t::descriptor_termination_callback_t() { }
|
||||
descriptor_termination_callback_t::~descriptor_termination_callback_t() { }
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_GATTCLIENT_MOCK_MOCKCALLBACKS_H_
|
||||
#define TESTS_GENERIC_GATTCLIENT_MOCK_MOCKCALLBACKS_H_
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "ble/Gap.h"
|
||||
#include "ble/DiscoveredService.h"
|
||||
#include "ble/DiscoveredCharacteristic.h"
|
||||
#include "ble/GattCallbackParamTypes.h"
|
||||
#include "ble/CharacteristicDescriptorDiscovery.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Mocks declaration for callbacks objects used in GattClient
|
||||
//
|
||||
|
||||
/*
|
||||
* Mock of the service discovery callback.
|
||||
*/
|
||||
struct service_callback_t {
|
||||
service_callback_t();
|
||||
virtual ~service_callback_t();
|
||||
MOCK_METHOD1(call, void(const DiscoveredService*));
|
||||
};
|
||||
|
||||
/*
|
||||
* Mock of the characteristic discovery callback.
|
||||
*/
|
||||
struct characteristic_callback_t {
|
||||
characteristic_callback_t();
|
||||
virtual ~characteristic_callback_t();
|
||||
MOCK_METHOD1(call, void(const DiscoveredCharacteristic*));
|
||||
};
|
||||
|
||||
/*
|
||||
* Mock of the discovery termination callback.
|
||||
*/
|
||||
struct termination_callback_t {
|
||||
termination_callback_t();
|
||||
virtual ~termination_callback_t();
|
||||
MOCK_METHOD1(call, void(Gap::Handle_t));
|
||||
};
|
||||
|
||||
/*
|
||||
* Mock of the read attribute callback.
|
||||
*/
|
||||
struct read_callback_t {
|
||||
read_callback_t();
|
||||
virtual ~read_callback_t();
|
||||
MOCK_METHOD1(call, void(const GattReadCallbackParams*));
|
||||
};
|
||||
|
||||
/*
|
||||
* Mock of the write attribute callback.
|
||||
*/
|
||||
struct write_callback_t {
|
||||
write_callback_t();
|
||||
virtual ~write_callback_t();
|
||||
MOCK_METHOD1(call, void(const GattWriteCallbackParams*));
|
||||
};
|
||||
|
||||
/*
|
||||
* Mock of the server initiated events callback (notification and indication).
|
||||
*/
|
||||
struct server_event_callback_t {
|
||||
server_event_callback_t();
|
||||
virtual ~server_event_callback_t();
|
||||
MOCK_METHOD1(call, void(const GattHVXCallbackParams*));
|
||||
};
|
||||
|
||||
/*
|
||||
* Mock of the characteristic descriptor discovery callback.
|
||||
*/
|
||||
struct descriptor_callback_t {
|
||||
descriptor_callback_t();
|
||||
virtual ~descriptor_callback_t();
|
||||
MOCK_METHOD1(
|
||||
call,
|
||||
void(const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t*)
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
* Mock of the characteristic descriptor termination callback.
|
||||
*/
|
||||
struct descriptor_termination_callback_t {
|
||||
descriptor_termination_callback_t();
|
||||
virtual ~descriptor_termination_callback_t();
|
||||
MOCK_METHOD1(
|
||||
call,
|
||||
void(const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t*)
|
||||
);
|
||||
};
|
||||
|
||||
#endif /* TESTS_GENERIC_GATTCLIENT_MOCK_MOCKCALLBACKS_H_ */
|
|
@ -0,0 +1,35 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MockPalGattClient.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace mock {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor implementation of the mocked pal gatt client
|
||||
//
|
||||
// WARNING: Do not remove; it speedup compile time.
|
||||
|
||||
MockPalGattClient::MockPalGattClient() { }
|
||||
MockPalGattClient::~MockPalGattClient() { }
|
||||
|
||||
} // namespace ble
|
||||
} // namespace pal
|
||||
} // namespace vendor
|
||||
} // namespace mock
|
|
@ -0,0 +1,141 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_GATTCLIENT_MOCK_MOCKPALGATTCLIENT_H_
|
||||
#define TESTS_GENERIC_GATTCLIENT_MOCK_MOCKPALGATTCLIENT_H_
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "ble/pal/PalGattClient.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace mock {
|
||||
|
||||
/*
|
||||
* Mock of ble::pal::GattClient
|
||||
*/
|
||||
class MockPalGattClient : public ble::pal::GattClient {
|
||||
public:
|
||||
MockPalGattClient();
|
||||
|
||||
virtual ~MockPalGattClient();
|
||||
|
||||
MOCK_METHOD0(initialize, ble_error_t());
|
||||
|
||||
MOCK_METHOD0(terminate, ble_error_t());
|
||||
|
||||
MOCK_METHOD1(exchange_mtu, ble_error_t(connection_handle_t));
|
||||
|
||||
MOCK_METHOD2(get_mtu_size, ble_error_t(connection_handle_t, uint16_t&));
|
||||
|
||||
MOCK_METHOD2(
|
||||
discover_primary_service,
|
||||
ble_error_t(connection_handle_t, attribute_handle_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
discover_primary_service_by_service_uuid,
|
||||
ble_error_t(connection_handle_t, attribute_handle_t, const UUID&)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
find_included_service,
|
||||
ble_error_t(connection_handle_t, attribute_handle_range_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
discover_characteristics_of_a_service,
|
||||
ble_error_t(connection_handle_t, attribute_handle_range_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
discover_characteristics_descriptors,
|
||||
ble_error_t(connection_handle_t, attribute_handle_range_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
read_attribute_value,
|
||||
ble_error_t(connection_handle_t, attribute_handle_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
read_using_characteristic_uuid,
|
||||
ble_error_t(connection_handle_t, attribute_handle_range_t, const UUID&)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
read_attribute_blob,
|
||||
ble_error_t(connection_handle_t, attribute_handle_t, uint16_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
read_multiple_characteristic_values,
|
||||
ble_error_t(
|
||||
connection_handle_t, const ArrayView<const attribute_handle_t>&
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
write_without_response,
|
||||
ble_error_t(
|
||||
connection_handle_t,
|
||||
attribute_handle_t,
|
||||
const ArrayView<const uint8_t>&
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
signed_write_without_response,
|
||||
ble_error_t(
|
||||
connection_handle_t,
|
||||
attribute_handle_t,
|
||||
const ArrayView<const uint8_t>&
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
write_attribute,
|
||||
ble_error_t(
|
||||
connection_handle_t,
|
||||
attribute_handle_t,
|
||||
const ArrayView<const uint8_t>&
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD4(
|
||||
queue_prepare_write,
|
||||
ble_error_t(
|
||||
connection_handle_t,
|
||||
attribute_handle_t,
|
||||
const ArrayView<const uint8_t>&,
|
||||
uint16_t
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(execute_write_queue, ble_error_t(connection_handle_t, bool));
|
||||
|
||||
// let event injection functions be public so it can be used inside the tests.
|
||||
using pal::GattClient::on_server_event;
|
||||
using pal::GattClient::on_transaction_timeout;
|
||||
};
|
||||
|
||||
} // namespace ble
|
||||
} // namespace pal
|
||||
} // namespace vendor
|
||||
} // namespace mock
|
||||
|
||||
#endif /* TESTS_GENERIC_GATTCLIENT_MOCK_MOCKPALGATTCLIENT_H_ */
|
|
@ -0,0 +1,59 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_GATTCLIENT_UTIL_CHARACTERISTICDESCRIPTION_H_
|
||||
#define TESTS_GENERIC_GATTCLIENT_UTIL_CHARACTERISTICDESCRIPTION_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "ble/UUID.h"
|
||||
#include "ble/DiscoveredCharacteristic.h"
|
||||
|
||||
struct characteristic_description_t {
|
||||
characteristic_description_t() :
|
||||
handle(0), properties(), value_handle(0), uuid() { }
|
||||
|
||||
characteristic_description_t(
|
||||
uint16_t handle,
|
||||
DiscoveredCharacteristic::Properties_t properties,
|
||||
UUID uuid
|
||||
) : handle(handle),
|
||||
properties(properties),
|
||||
value_handle(handle + 1),
|
||||
uuid(uuid) {
|
||||
}
|
||||
|
||||
friend bool operator==(
|
||||
const characteristic_description_t& lhs, const characteristic_description_t& rhs
|
||||
) {
|
||||
return (lhs.handle == rhs.handle)
|
||||
&& (lhs.properties == rhs.properties)
|
||||
&& (lhs.value_handle == rhs.value_handle)
|
||||
&& (lhs.uuid == rhs.uuid);
|
||||
}
|
||||
|
||||
friend bool operator !=(
|
||||
const characteristic_description_t& lhs, const characteristic_description_t& rhs
|
||||
) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
uint16_t handle;
|
||||
DiscoveredCharacteristic::Properties_t properties;
|
||||
uint16_t value_handle;
|
||||
UUID uuid;
|
||||
};
|
||||
|
||||
#endif /* TESTS_GENERIC_GATTCLIENT_UTIL_CHARACTERISTICDESCRIPTION_H_ */
|
|
@ -0,0 +1,127 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ble/DiscoveredCharacteristic.h"
|
||||
#include "ble/DiscoveredCharacteristicDescriptor.h"
|
||||
#include "ble/CharacteristicDescriptorDiscovery.h"
|
||||
#include "ble/GattCallbackParamTypes.h"
|
||||
#include "ble/ArrayView.h"
|
||||
|
||||
bool operator==(
|
||||
const DiscoveredCharacteristicDescriptor& lhs,
|
||||
const DiscoveredCharacteristicDescriptor& rhs
|
||||
) {
|
||||
if (lhs.getGattClient() != rhs.getGattClient() ||
|
||||
lhs.getConnectionHandle() != rhs.getConnectionHandle() ||
|
||||
lhs.getUUID() != rhs.getUUID() ||
|
||||
lhs.getAttributeHandle() != rhs.getAttributeHandle()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const DiscoveredCharacteristicDescriptor& lhs,
|
||||
const DiscoveredCharacteristicDescriptor& rhs
|
||||
) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& lhs,
|
||||
const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& rhs
|
||||
) {
|
||||
if (lhs.characteristic != rhs.characteristic ||
|
||||
lhs.status != rhs.status ||
|
||||
lhs.error_code != rhs.error_code
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& lhs,
|
||||
const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& rhs
|
||||
) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& lhs,
|
||||
const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& rhs
|
||||
) {
|
||||
if (lhs.characteristic != rhs.characteristic ||
|
||||
lhs.descriptor != rhs.descriptor
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& lhs,
|
||||
const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& rhs
|
||||
) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator==(const GattReadCallbackParams& lhs, const GattReadCallbackParams& rhs) {
|
||||
if ((lhs.connHandle != rhs.connHandle) || (lhs.handle != rhs.handle) ||
|
||||
(lhs.offset != rhs.offset) || (lhs.status != rhs.status)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.status == BLE_ERROR_NONE) {
|
||||
if (lhs.len != rhs.len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(lhs.data, rhs.data, lhs.len) != 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (lhs.error_code != rhs.error_code) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const GattHVXCallbackParams& lhs, const GattHVXCallbackParams& rhs) {
|
||||
if (lhs.connHandle != rhs.connHandle &&
|
||||
lhs.handle != rhs.handle &&
|
||||
lhs.type != rhs.type &&
|
||||
ble::make_const_ArrayView(lhs.data, lhs.len) != ble::make_const_ArrayView(rhs.data, rhs.len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const GattWriteCallbackParams& lhs, const GattWriteCallbackParams& rhs) {
|
||||
if (lhs.connHandle != rhs.connHandle &&
|
||||
lhs.handle != rhs.handle &&
|
||||
lhs.writeOp != rhs.writeOp &&
|
||||
lhs.error_code != rhs.error_code &&
|
||||
lhs.status != rhs.status) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_GATTCLIENT_UTIL_EQUALITY_H_
|
||||
#define TESTS_GENERIC_GATTCLIENT_UTIL_EQUALITY_H_
|
||||
|
||||
#include "ble/DiscoveredCharacteristicDescriptor.h"
|
||||
#include "ble/CharacteristicDescriptorDiscovery.h"
|
||||
#include "ble/GattCallbackParamTypes.h"
|
||||
|
||||
bool operator==(
|
||||
const DiscoveredCharacteristicDescriptor& lhs,
|
||||
const DiscoveredCharacteristicDescriptor& rhs
|
||||
);
|
||||
|
||||
bool operator!=(
|
||||
const DiscoveredCharacteristicDescriptor& lhs,
|
||||
const DiscoveredCharacteristicDescriptor& rhs
|
||||
);
|
||||
|
||||
bool operator==(
|
||||
const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& lhs,
|
||||
const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& rhs
|
||||
);
|
||||
|
||||
bool operator!=(
|
||||
const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& lhs,
|
||||
const CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& rhs
|
||||
);
|
||||
|
||||
bool operator==(
|
||||
const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& lhs,
|
||||
const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& rhs
|
||||
);
|
||||
|
||||
bool operator!=(
|
||||
const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& lhs,
|
||||
const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& rhs
|
||||
);
|
||||
|
||||
bool operator==(const GattReadCallbackParams& lhs, const GattReadCallbackParams& rhs);
|
||||
|
||||
bool operator==(const GattHVXCallbackParams& lhs, const GattHVXCallbackParams& rhs);
|
||||
|
||||
bool operator==(const GattWriteCallbackParams& lhs, const GattWriteCallbackParams& rhs);
|
||||
|
||||
#endif /* TESTS_GENERIC_GATTCLIENT_UTIL_EQUALITY_H_ */
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_GATTCLIENT_UTIL_LOG_H_
|
||||
#define TESTS_GENERIC_GATTCLIENT_UTIL_LOG_H_
|
||||
|
||||
#include <ostream>
|
||||
|
||||
// logger
|
||||
class Log {
|
||||
public:
|
||||
static std::ostream& info() {
|
||||
std::cout << "[info ] ";
|
||||
return std::cout;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif /* TESTS_GENERIC_GATTCLIENT_UTIL_LOG_H_ */
|
||||
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_GATTCLIENT_UTIL_PRETTYPRINTER_H_
|
||||
#define TESTS_GENERIC_GATTCLIENT_UTIL_PRETTYPRINTER_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <ble/UUID.h>
|
||||
#include <ble/GattCallbackParamTypes.h>
|
||||
#include <ble/GattCallbackParamTypes.h>
|
||||
|
||||
/*
|
||||
* UUID pretty printer
|
||||
*/
|
||||
inline ::std::ostream& operator<<(::std::ostream& os, const UUID& uuid) {
|
||||
char str[60];
|
||||
if (uuid.shortOrLong() == UUID::UUID_TYPE_SHORT) {
|
||||
sprintf(str, "0x%04X", uuid.getShortUUID());
|
||||
} else {
|
||||
const uint8_t* data = uuid.getBaseUUID();
|
||||
sprintf(
|
||||
str,
|
||||
"\"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\"",
|
||||
data[15], data[14], data[13], data[12], data[11], data[10], data[9], data[8],
|
||||
data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0]
|
||||
);
|
||||
}
|
||||
return os << str;
|
||||
}
|
||||
|
||||
/*
|
||||
* GattReadCallbackParams pretty printer
|
||||
*/
|
||||
inline ::std::ostream& operator<<(::std::ostream& os, const GattReadCallbackParams& p) {
|
||||
os << "connection_handle: " << p.connHandle <<
|
||||
", handle: " << p.handle <<
|
||||
", offset: " << p.offset <<
|
||||
", status: " << p.status;
|
||||
|
||||
if (p.status) {
|
||||
os << ", error_code: " << p.error_code;
|
||||
} else {
|
||||
os << ", len: " << p.len;
|
||||
os << ", data: ";
|
||||
for (size_t i = 0; i < p.len; ++i) {
|
||||
os << (int) p.data[i] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
/*
|
||||
* attribute_handle_range_t pretty printer
|
||||
*/
|
||||
inline ::std::ostream& operator<<(::std::ostream& os, const ble::attribute_handle_range_t& handle_range) {
|
||||
return os << "(" << handle_range.begin << "," << handle_range.end << ")";
|
||||
}
|
||||
|
||||
/*
|
||||
* Pretty printer of an view to an array of uint8_t.
|
||||
*/
|
||||
inline ::std::ostream& operator<<(::std::ostream& os, const ble::ArrayView<const uint8_t>& array) {
|
||||
os << "size: " << array.size() << " value: { ";
|
||||
for (size_t i = 0; i <array.size(); ++i) {
|
||||
os << (int) array[i] << " ";
|
||||
}
|
||||
os << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
/*
|
||||
* DiscoveredCharacteristic pretty printer
|
||||
*/
|
||||
inline ::std::ostream& operator<<(::std::ostream& os, const DiscoveredCharacteristic& d) {
|
||||
return os << "client: " << d.getGattClient() << " " <<
|
||||
"connection_handle: " << d.getConnectionHandle() << " " <<
|
||||
"decl_handle: " << d.getDeclHandle() << " " <<
|
||||
"value_handle: " << d.getValueHandle() << " " <<
|
||||
"last_handle: " << d.getLastHandle() << " " <<
|
||||
"uuid: { " << d.getUUID() << " } ";
|
||||
}
|
||||
|
||||
/*
|
||||
* DiscoveredCharacteristicDescriptor pretty printer
|
||||
*/
|
||||
inline ::std::ostream& operator<<(::std::ostream& os, const DiscoveredCharacteristicDescriptor& d) {
|
||||
return os << "client: " << d.getGattClient() << " " <<
|
||||
"connection_handle: " << d.getConnectionHandle() << " " <<
|
||||
"attribute_handle: " << d.getAttributeHandle() << " " <<
|
||||
"uuid: { " << d.getUUID() << " }";
|
||||
}
|
||||
|
||||
/*
|
||||
* CharacteristicDescriptorDiscovery::TerminationCallbackParams_t pretty printer
|
||||
*/
|
||||
inline ::std::ostream& operator<<(::std::ostream& os, CharacteristicDescriptorDiscovery::TerminationCallbackParams_t& t) {
|
||||
return os << "characteristic: { " << t.characteristic << " } " <<
|
||||
"status: " << t.status << " " <<
|
||||
"error_code: " << t.error_code;
|
||||
}
|
||||
|
||||
/*
|
||||
* CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t pretty printer
|
||||
*/
|
||||
inline ::std::ostream& operator<<(::std::ostream& os, const CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t& p) {
|
||||
return os << "characteristic: { " << p.characteristic << " } " <<
|
||||
"descriptor: { " << p.descriptor << " } ";
|
||||
}
|
||||
|
||||
#endif /* TESTS_GENERIC_GATTCLIENT_UTIL_PRETTYPRINTER_H_ */
|
|
@ -0,0 +1,32 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_GATTCLIENT_UTIL_SERVERDESCRIPTION_H_
|
||||
#define TESTS_GENERIC_GATTCLIENT_UTIL_SERVERDESCRIPTION_H_
|
||||
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
#include "ServiceDescription.h"
|
||||
|
||||
struct server_description_t {
|
||||
server_description_t() { }
|
||||
server_description_t(std::initializer_list<service_description_t> services) :
|
||||
services(services) { }
|
||||
|
||||
std::vector<service_description_t> services;
|
||||
};
|
||||
|
||||
#endif /* TESTS_GENERIC_GATTCLIENT_UTIL_SERVERDESCRIPTION_H_ */
|
|
@ -0,0 +1,44 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_GATTCLIENT_UTIL_SERVICEDESCRIPTION_H_
|
||||
#define TESTS_GENERIC_GATTCLIENT_UTIL_SERVICEDESCRIPTION_H_
|
||||
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
#include <stdint.h>
|
||||
#include "ble/UUID.h"
|
||||
#include "CharacteristicDescription.h"
|
||||
|
||||
struct service_description_t {
|
||||
service_description_t(
|
||||
uint16_t start,
|
||||
uint16_t end,
|
||||
UUID uuid,
|
||||
std::initializer_list<characteristic_description_t> characteristics
|
||||
) : start(start),
|
||||
end(end),
|
||||
uuid(uuid),
|
||||
characteristics(characteristics) {
|
||||
}
|
||||
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
UUID uuid;
|
||||
std::vector<characteristic_description_t> characteristics;
|
||||
};
|
||||
|
||||
#endif /* TESTS_GENERIC_GATTCLIENT_UTIL_SERVICEDESCRIPTION_H_ */
|
|
@ -1,5 +1,5 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 ARM Limited
|
||||
* Copyright (c) 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.
|
||||
|
@ -14,35 +14,22 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CORDIO_SECURITY_MANAGER_H_
|
||||
#define CORDIO_SECURITY_MANAGER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "ble/SecurityManager.h"
|
||||
#include "MockPalSecurityDb.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace cordio {
|
||||
namespace mock {
|
||||
|
||||
class SecurityManager : public ::SecurityManager
|
||||
{
|
||||
public:
|
||||
static SecurityManager &getInstance()
|
||||
{
|
||||
static SecurityManager m_instance;
|
||||
return m_instance;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor implementation of the mocked pal security manager
|
||||
//
|
||||
// WARNING: Do not remove; it speedup compile time.
|
||||
|
||||
public:
|
||||
SecurityManager()
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
};
|
||||
MockPalSecurityDb::MockPalSecurityDb() { }
|
||||
MockPalSecurityDb::~MockPalSecurityDb() { }
|
||||
|
||||
} // namespace cordio
|
||||
} // namespace vendor
|
||||
} // namespace ble
|
||||
|
||||
#endif /* CORDIO_SECURITY_MANAGER_H_ */
|
||||
} // namespace pal
|
||||
} // namespace vendor
|
||||
} // namespace mock
|
|
@ -0,0 +1,192 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_SECURITYMANAGER_MOCK_MOCKPALSECURITYDB_H_
|
||||
#define TESTS_GENERIC_SECURITYMANAGER_MOCK_MOCKPALSECURITYDB_H_
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "ble/pal/SecurityDB.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace mock {
|
||||
|
||||
/*
|
||||
* Mock of ble::pal::SecurityDB
|
||||
*/
|
||||
class MockPalSecurityDb : public ble::pal::SecurityDb {
|
||||
public:
|
||||
MockPalSecurityDb();
|
||||
|
||||
virtual ~MockPalSecurityDb();
|
||||
|
||||
MOCK_METHOD1(get_entry, SecurityEntry_t*(connection_handle_t));
|
||||
|
||||
MOCK_METHOD1(get_entry, SecurityEntry_t*(const address_t &));
|
||||
|
||||
MOCK_METHOD4(
|
||||
get_entry_local_keys,
|
||||
void(
|
||||
SecurityEntryKeysDbCb_t,
|
||||
connection_handle_t,
|
||||
const ediv_t &,
|
||||
const rand_t &
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
get_entry_local_keys,
|
||||
void(SecurityEntryKeysDbCb_t, connection_handle_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
set_entry_local_ltk,
|
||||
void(connection_handle_t, const ltk_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
set_entry_local_ediv_rand,
|
||||
void(connection_handle_t, const ediv_t &, const rand_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
get_entry_peer_csrk,
|
||||
void(SecurityEntryCsrkDbCb_t, connection_handle_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
get_entry_peer_keys,
|
||||
void(SecurityEntryKeysDbCb_t, connection_handle_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
set_entry_peer_ltk,
|
||||
void(connection_handle_t, const ltk_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
set_entry_peer_ediv_rand,
|
||||
void(connection_handle_t, const ediv_t &, const rand_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
set_entry_peer_irk,
|
||||
void(connection_handle_t, const irk_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
set_entry_peer_bdaddr,
|
||||
void(connection_handle_t, bool, const address_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
set_entry_peer_csrk,
|
||||
void(connection_handle_t, const csrk_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD0(get_local_csrk, const csrk_t*());
|
||||
|
||||
MOCK_METHOD1(set_local_csrk, void(const csrk_t &));
|
||||
|
||||
MOCK_METHOD0(get_public_key_x, const public_key_t&());
|
||||
|
||||
MOCK_METHOD0(get_public_key_y, const public_key_t&());
|
||||
|
||||
MOCK_METHOD2(set_public_key, void(const public_key_t &, const public_key_t &));
|
||||
|
||||
MOCK_METHOD0(get_peer_sc_oob_address, const address_t&());
|
||||
|
||||
MOCK_METHOD0(get_peer_sc_oob_random, const oob_rand_t&());
|
||||
|
||||
MOCK_METHOD0(get_peer_sc_oob_confirm, const oob_confirm_t&());
|
||||
|
||||
MOCK_METHOD4(
|
||||
get_sc_oob_data,
|
||||
void(
|
||||
address_t &,
|
||||
oob_rand_t &,
|
||||
oob_confirm_t &,
|
||||
oob_rand_t &
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD0(get_local_sc_oob_random, const oob_rand_t&());
|
||||
|
||||
MOCK_METHOD3(
|
||||
set_peer_sc_oob_data,
|
||||
void(
|
||||
const address_t &,
|
||||
const oob_rand_t &,
|
||||
const oob_confirm_t &
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD1(set_local_sc_oob_random, void(const oob_rand_t&));
|
||||
|
||||
MOCK_METHOD4(
|
||||
connect_entry,
|
||||
SecurityEntry_t*(
|
||||
connection_handle_t,
|
||||
BLEProtocol::AddressType_t,
|
||||
const address_t &,
|
||||
const address_t &
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD1(disconnect_entry, void(connection_handle_t));
|
||||
|
||||
MOCK_METHOD1(remove_entry, void(const address_t));
|
||||
|
||||
MOCK_METHOD0(clear_entries, void());
|
||||
|
||||
MOCK_METHOD2(get_whitelist, void(WhitelistDbCb_t, Gap::Whitelist_t *));
|
||||
|
||||
MOCK_METHOD2(
|
||||
generate_whitelist_from_bond_table,
|
||||
void(WhitelistDbCb_t, Gap::Whitelist_t *)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
set_whitelist,
|
||||
void(WhitelistDbCb_t, const Gap::Whitelist_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD1(
|
||||
add_whitelist_entry,
|
||||
void(const address_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD1(
|
||||
remove_whitelist_entry,
|
||||
void(const address_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD0(clear_whitelist, void());
|
||||
|
||||
MOCK_METHOD0(restore, void());
|
||||
|
||||
MOCK_METHOD0(sync, void());
|
||||
|
||||
MOCK_METHOD1(set_restore, void(bool));
|
||||
};
|
||||
|
||||
} // namespace ble
|
||||
} // namespace pal
|
||||
} // namespace vendor
|
||||
} // namespace mock
|
||||
|
||||
#endif /* TESTS_GENERIC_SECURITYMANAGER_MOCK_MOCKPALSECURITYDB_H_ */
|
|
@ -0,0 +1,35 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MockPalSecurityManager.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace mock {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor implementation of the mocked pal security manager
|
||||
//
|
||||
// WARNING: Do not remove; it speedup compile time.
|
||||
|
||||
MockPalSecurityManager::MockPalSecurityManager() { }
|
||||
MockPalSecurityManager::~MockPalSecurityManager() { }
|
||||
|
||||
} // namespace ble
|
||||
} // namespace pal
|
||||
} // namespace vendor
|
||||
} // namespace mock
|
|
@ -0,0 +1,185 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 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 TESTS_GENERIC_SECURITYMANAGER_MOCK_MOCKPALSECURITYMANAGER_H_
|
||||
#define TESTS_GENERIC_SECURITYMANAGER_MOCK_MOCKPALSECURITYMANAGER_H_
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "ble/pal/PalSecurityManager.h"
|
||||
|
||||
namespace ble {
|
||||
namespace pal {
|
||||
namespace vendor {
|
||||
namespace mock {
|
||||
|
||||
/*
|
||||
* Mock of ble::pal::SecurityManager
|
||||
*/
|
||||
class MockPalSecurityManager : public ble::pal::SecurityManager {
|
||||
public:
|
||||
MockPalSecurityManager();
|
||||
|
||||
virtual ~MockPalSecurityManager();
|
||||
|
||||
MOCK_METHOD0(initialize, ble_error_t());
|
||||
|
||||
MOCK_METHOD0(terminate, ble_error_t());
|
||||
|
||||
MOCK_METHOD0(reset, ble_error_t());
|
||||
|
||||
MOCK_METHOD0(read_resolving_list_capacity, uint8_t());
|
||||
|
||||
MOCK_METHOD3(
|
||||
add_device_to_resolving_list,
|
||||
ble_error_t(
|
||||
advertising_peer_address_type_t,
|
||||
const address_t &,
|
||||
const irk_t &
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
remove_device_from_resolving_list,
|
||||
ble_error_t(advertising_peer_address_type_t, const address_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD0(clear_resolving_list, ble_error_t());
|
||||
|
||||
MOCK_METHOD5(
|
||||
send_pairing_request,
|
||||
ble_error_t(
|
||||
connection_handle_t,
|
||||
bool,
|
||||
AuthenticationMask,
|
||||
KeyDistribution,
|
||||
KeyDistribution
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD5(
|
||||
send_pairing_response,
|
||||
ble_error_t(
|
||||
connection_handle_t,
|
||||
bool,
|
||||
AuthenticationMask,
|
||||
KeyDistribution,
|
||||
KeyDistribution
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
cancel_pairing,
|
||||
ble_error_t(connection_handle_t, pairing_failure_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD1(get_secure_connections_support, ble_error_t(bool &));
|
||||
|
||||
MOCK_METHOD1(set_io_capability, ble_error_t(io_capability_t));
|
||||
|
||||
MOCK_METHOD2(
|
||||
set_authentication_timeout,
|
||||
ble_error_t(connection_handle_t, uint16_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
get_authentication_timeout,
|
||||
ble_error_t(connection_handle_t, uint16_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
set_encryption_key_requirements,
|
||||
ble_error_t(uint8_t, uint8_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
slave_security_request,
|
||||
ble_error_t(connection_handle_t, AuthenticationMask)
|
||||
);
|
||||
|
||||
MOCK_METHOD4(
|
||||
slave_security_request,
|
||||
ble_error_t(
|
||||
connection_handle_t,
|
||||
const ltk_t &,
|
||||
const rand_t &,
|
||||
const ediv_t &
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
enable_encryption,
|
||||
ble_error_t(connection_handle_t, const ltk_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD1(disable_encryption, ble_error_t(connection_handle_t));
|
||||
|
||||
MOCK_METHOD2(
|
||||
get_encryption_key_size,
|
||||
ble_error_t(connection_handle_t, uint8_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(
|
||||
encrypt_data,
|
||||
ble_error_t(const key_t &, encryption_block_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD1(set_private_address_timeout, ble_error_t(uint16_t));
|
||||
|
||||
MOCK_METHOD2(set_ltk, ble_error_t(connection_handle_t, const ltk_t &));
|
||||
|
||||
MOCK_METHOD1(set_ltk_not_found, ble_error_t(connection_handle_t));
|
||||
|
||||
MOCK_METHOD1(set_irk, ble_error_t(const irk_t &));
|
||||
|
||||
MOCK_METHOD1(set_csrk, ble_error_t(const csrk_t &));
|
||||
|
||||
MOCK_METHOD0(generate_public_key, ble_error_t());
|
||||
|
||||
MOCK_METHOD1(request_authentication, ble_error_t(connection_handle_t));
|
||||
|
||||
MOCK_METHOD1(get_random_data, ble_error_t(random_data_t &));
|
||||
|
||||
MOCK_METHOD1(set_display_passkey, ble_error_t(passkey_num_t));
|
||||
|
||||
MOCK_METHOD2(passkey_request_reply, ble_error_t(connection_handle_t, passkey_num_t));
|
||||
|
||||
MOCK_METHOD2(
|
||||
legacy_pairing_oob_data_request_reply,
|
||||
ble_error_t(connection_handle_t, const oob_tk_t &)
|
||||
);
|
||||
|
||||
MOCK_METHOD2(confirmation_entered, ble_error_t(connection_handle_t, bool));
|
||||
|
||||
MOCK_METHOD2(
|
||||
send_keypress_notification,
|
||||
ble_error_t(connection_handle_t, Keypress_t)
|
||||
);
|
||||
|
||||
MOCK_METHOD3(
|
||||
oob_data_verified,
|
||||
ble_error_t(connection_handle_t, const oob_rand_t &, const oob_rand_t &)
|
||||
);
|
||||
|
||||
// get the event handler present in the mock object.
|
||||
using pal::SecurityManager::get_event_handler;
|
||||
};
|
||||
|
||||
} // namespace ble
|
||||
} // namespace pal
|
||||
} // namespace vendor
|
||||
} // namespace mock
|
||||
|
||||
#endif /* TESTS_GENERIC_SECURITYMANAGER_MOCK_MOCKPALSECURITYMANAGER_H_ */
|
|
@ -0,0 +1,24 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void mbed_assert_internal(const char *expr, const char *file, int line)
|
||||
{
|
||||
printf("mbed assertation failed: %s, file: %s, line %d \n", expr, file, line);
|
||||
abort();
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* \defgroup platform_sleep Sleep functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2017 ARM Limited
|
||||
*
|
||||
|
@ -24,7 +24,7 @@
|
|||
#ifndef MBED_SLEEP_H
|
||||
#define MBED_SLEEP_H
|
||||
|
||||
#include "sleep_api.h"
|
||||
#include "hal/sleep_api.h"
|
||||
#include "mbed_toolchain.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
|
@ -128,7 +128,7 @@ void sleep_manager_sleep_auto(void);
|
|||
* Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be
|
||||
* able to access the LocalFileSystem
|
||||
*/
|
||||
__INLINE static void sleep(void)
|
||||
static inline void sleep(void)
|
||||
{
|
||||
#if !(defined(FEATURE_UVISOR) && defined(TARGET_UVISOR_SUPPORTED))
|
||||
#if DEVICE_SLEEP
|
||||
|
@ -158,7 +158,7 @@ __INLINE static void sleep(void)
|
|||
*/
|
||||
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.6", "One entry point for an application, use sleep()")
|
||||
__INLINE static void deepsleep(void)
|
||||
static inline void deepsleep(void)
|
||||
{
|
||||
#if !(defined(FEATURE_UVISOR) && defined(TARGET_UVISOR_SUPPORTED))
|
||||
#if DEVICE_SLEEP
|
||||
|
|
Loading…
Reference in New Issue