diff --git a/examples/Peripheral/EncryptedBatteryMonitor/EncryptedBatteryMonitor.ino b/examples/Peripheral/EncryptedBatteryMonitor/EncryptedBatteryMonitor.ino index d4953b03..fd40f5ee 100644 --- a/examples/Peripheral/EncryptedBatteryMonitor/EncryptedBatteryMonitor.ino +++ b/examples/Peripheral/EncryptedBatteryMonitor/EncryptedBatteryMonitor.ino @@ -18,6 +18,14 @@ #include +#define PAIR_BUTTON D3 // button for pairing +#define PAIR_LED 24 // LED used to signal pairing +#define PAIR_LED_ON LOW // Blue LED on Nano BLE has inverted logic +#define PAIR_INTERVAL 30000 // interval for pairing after button press in ms + +#define CTRL_LED LED_BUILTIN + + // BLE Battery Service BLEService batteryService("180F"); @@ -31,13 +39,17 @@ BLEStringCharacteristic stringcharacteristic("183E", BLERead | BLEWrite, 31); BLEUnsignedCharCharacteristic secretValue("2a3F", BLERead | BLEWrite | BLEEncryption); int oldBatteryLevel = 0; // last battery level reading from analog input -long previousMillis = 0; // last time the battery level was checked, in ms +unsigned long previousMillis = 0; // last time the battery level was checked, in ms +unsigned long pairingStarted = 0; // pairing start time when button is pressed +bool wasConnected = 0; void setup() { Serial.begin(9600); // initialize serial communication while (!Serial); - pinMode(LED_BUILTIN, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected + pinMode(CTRL_LED, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected + pinMode(PAIR_LED, OUTPUT); + pinMode(PAIR_BUTTON, INPUT_PULLUP); Serial.println("Serial connected"); @@ -145,6 +157,9 @@ void setup() { secretValue.writeValue(0); delay(1000); + + // prevent pairing until button is pressed (will show a pairing rejected message) + BLE.setPairable(false); /* Start advertising BLE. It will start continuously transmitting BLE advertising packets and will be visible to remote BLE central devices @@ -169,30 +184,44 @@ void loop() { // wait for a BLE central BLEDevice central = BLE.central(); + + // If button is pressed, allow pairing for 30 sec + if (!BLE.pairable() && digitalRead(PAIR_BUTTON) == LOW){ + pairingStarted = millis(); + BLE.setPairable(Pairable::ONCE); + Serial.println("Accepting pairing for 30s"); + } else if (BLE.pairable() && millis() > pairingStarted + PAIR_INTERVAL){ + BLE.setPairable(false); + Serial.println("No longer accepting pairing"); + } + // Make LED blink while pairing is allowed + digitalWrite(PAIR_LED, (BLE.pairable() ? (millis()%400)<200 : BLE.paired()) ? PAIR_LED_ON : !PAIR_LED_ON); + + // if a central is connected to the peripheral: - if (central) { - Serial.print("Connected to central: "); - // print the central's BT address: - Serial.println(central.address()); + if (central && central.connected()) { + if (!wasConnected){ + wasConnected = true; + Serial.print("Connected to central: "); + // print the central's BT address: + Serial.println(central.address()); + } // check the battery level every 200ms // while the central is connected: - while (central.connected()) { - long currentMillis = millis(); - // if 200ms have passed, check the battery level: - if (currentMillis - previousMillis >= 1000) { - previousMillis = currentMillis; - updateBatteryLevel(); - if(secretValue.value()>0){ - digitalWrite(13,HIGH); - }else{ - digitalWrite(13,LOW); - } - } + long currentMillis = millis(); + // if 200ms have passed, check the battery level: + if (currentMillis - previousMillis >= 1000) { + previousMillis = currentMillis; + updateBatteryLevel(); + digitalWrite(CTRL_LED, secretValue.value()>0 ? HIGH : LOW); } + } else if (wasConnected){ + wasConnected = false; Serial.print("Disconnected from central: "); Serial.println(central.address()); } + } void updateBatteryLevel() { @@ -208,4 +237,4 @@ void updateBatteryLevel() { batteryLevelChar.writeValue(batteryLevel); // and update the battery level characteristic oldBatteryLevel = batteryLevel; // save the level for next comparison } -} +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index 464cac72..effdfbca 100644 --- a/keywords.txt +++ b/keywords.txt @@ -77,9 +77,12 @@ setEventHandler KEYWORD2 setAdvertisingInterval KEYWORD2 setConnectionInterval KEYWORD2 setConnectable KEYWORD2 +setPairable KEYWORD2 setTimeout KEYWORD2 debug KEYWORD2 noDebug KEYWORD2 +pairable KEYWORD2 +paired KEYWORD2 properties KEYWORD2 valueSize KEYWORD2 diff --git a/src/local/BLELocalDevice.cpp b/src/local/BLELocalDevice.cpp index 49cc6603..029fb488 100644 --- a/src/local/BLELocalDevice.cpp +++ b/src/local/BLELocalDevice.cpp @@ -214,6 +214,16 @@ bool BLELocalDevice::connected() const return ATT.connected(); } +/* + * Whether there is at least one paired device + */ +bool BLELocalDevice::paired() +{ + HCI.poll(); + + return ATT.paired(); +} + bool BLELocalDevice::disconnect() { return ATT.disconnect(); @@ -395,6 +405,22 @@ void BLELocalDevice::setTimeout(unsigned long timeout) ATT.setTimeout(timeout); } +/* + * Control whether pairing is allowed or rejected + * Use true/false or the Pairable enum + */ +void BLELocalDevice::setPairable(uint8_t pairable) +{ + L2CAPSignaling.setPairingEnabled(pairable); +} + +/* + * Whether pairing is currently allowed + */ +bool BLELocalDevice::pairable() +{ + return L2CAPSignaling.isPairingEnabled(); +} void BLELocalDevice::setGetIRKs(int (*getIRKs)(uint8_t* nIRKs, uint8_t** BADDR_type, uint8_t*** BADDRs, uint8_t*** IRKs)){ HCI._getIRKs = getIRKs; diff --git a/src/local/BLELocalDevice.h b/src/local/BLELocalDevice.h index 22a5f589..0c471938 100644 --- a/src/local/BLELocalDevice.h +++ b/src/local/BLELocalDevice.h @@ -24,6 +24,12 @@ #include "BLEService.h" #include "BLEAdvertisingData.h" +enum Pairable { + NO = 0, + YES = 1, + ONCE = 2, +}; + class BLELocalDevice { public: BLELocalDevice(); @@ -80,6 +86,10 @@ class BLELocalDevice { virtual void debug(Stream& stream); virtual void noDebug(); + + virtual void setPairable(uint8_t pairable); + virtual bool pairable(); + virtual bool paired(); /// TODO: Put in actual variable names virtual void setStoreIRK(int (*storeIRK)(uint8_t*, uint8_t*)); diff --git a/src/utility/ATT.cpp b/src/utility/ATT.cpp index 18556413..a5166217 100644 --- a/src/utility/ATT.cpp +++ b/src/utility/ATT.cpp @@ -497,6 +497,32 @@ bool ATTClass::connected(uint16_t handle) const return false; } +/* + * Return true if any of the known devices is paired (peer encrypted) + * Does not check if the paired device is also connected + */ +bool ATTClass::paired() const +{ + for(int i=0; i 0){ + return true; + } + } + return false; +} + +/* + * Return true if the specified device is paired (peer encrypted) + */ +bool ATTClass::paired(uint16_t handle) const +{ + for(int i=0; i 0; + } + return false; // unknown handle +} + uint16_t ATTClass::mtu(uint16_t handle) const { for (int i = 0; i < ATT_MAX_PEERS; i++) { diff --git a/src/utility/ATT.h b/src/utility/ATT.h index 1c8910c3..c9f007f9 100644 --- a/src/utility/ATT.h +++ b/src/utility/ATT.h @@ -75,6 +75,8 @@ class ATTClass { virtual bool connected() const; virtual bool connected(uint8_t addressType, const uint8_t address[6]) const; virtual bool connected(uint16_t handle) const; + virtual bool paired() const; + virtual bool paired(uint16_t handle) const; virtual uint16_t mtu(uint16_t handle) const; virtual bool disconnect(); diff --git a/src/utility/HCI.cpp b/src/utility/HCI.cpp index 4dc78460..4c97f79d 100644 --- a/src/utility/HCI.cpp +++ b/src/utility/HCI.cpp @@ -24,6 +24,8 @@ #include "btct.h" #include "HCI.h" +//#define _BLE_TRACE_ + #define HCI_COMMAND_PKT 0x01 #define HCI_ACLDATA_PKT 0x02 #define HCI_EVENT_PKT 0x04 @@ -1335,7 +1337,6 @@ void HCIClass::handleEventPkt(uint8_t /*plen*/, uint8_t pdata[]) break; } - uint8_t encryption = ATT.getPeerEncryption(connectionHandle); for(int i=0; i<32; i++) DHKey[31-i] = evtLeDHKeyComplete->DHKey[i]; @@ -1343,15 +1344,22 @@ void HCIClass::handleEventPkt(uint8_t /*plen*/, uint8_t pdata[]) Serial.println("Stored our DHKey:"); btct.printBytes(DHKey,32); #endif - encryption |= PEER_ENCRYPTION::DH_KEY_CALULATED; + uint8_t encryption = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::DH_KEY_CALULATED; ATT.setPeerEncryption(connectionHandle, encryption); + + if((encryption & PEER_ENCRYPTION::RECEIVED_DH_CHECK) > 0){ #ifdef _BLE_TRACE_ - if(encryption | PEER_ENCRYPTION::RECEIVED_DH_CHECK){ - Serial.println("Recieved DHKey check already so calculate f5, f6."); + Serial.println("Recieved DHKey check already so calculate f5, f6 now."); +#endif + L2CAPSignaling.smCalculateLTKandConfirm(connectionHandle, HCI.remoteDHKeyCheckBuffer); + }else{ +#ifdef _BLE_TRACE_ Serial.println("Waiting on other DHKey check before calculating."); +#endif } }else{ +#ifdef _BLE_TRACE_ Serial.print("Key generation error: 0x"); Serial.println(evtLeDHKeyComplete->status, HEX); #endif diff --git a/src/utility/HCI.h b/src/utility/HCI.h index de15bdf8..522570d3 100644 --- a/src/utility/HCI.h +++ b/src/utility/HCI.h @@ -22,6 +22,8 @@ #include +#include "L2CAPSignaling.h" + #define OGF_LINK_CTL 0x01 #define OGF_HOST_CTL 0x03 #define OGF_INFO_PARAM 0x04 @@ -111,6 +113,7 @@ class HCIClass { // TODO: Send command be private again & use ATT implementation within ATT. virtual int sendCommand(uint16_t opcode, uint8_t plen = 0, void* parameters = NULL); uint8_t remotePublicKeyBuffer[64]; + uint8_t remoteDHKeyCheckBuffer[16]; uint8_t Na[16]; uint8_t Nb[16]; uint8_t DHKey[32]; diff --git a/src/utility/L2CAPSignaling.cpp b/src/utility/L2CAPSignaling.cpp index f7f93b84..622f2178 100644 --- a/src/utility/L2CAPSignaling.cpp +++ b/src/utility/L2CAPSignaling.cpp @@ -26,10 +26,13 @@ #define CONNECTION_PARAMETER_UPDATE_REQUEST 0x12 #define CONNECTION_PARAMETER_UPDATE_RESPONSE 0x13 +//#define _BLE_TRACE_ + L2CAPSignalingClass::L2CAPSignalingClass() : _minInterval(0), _maxInterval(0), _supervisionTimeout(0) + ,_pairing_enabled(1) { } @@ -129,53 +132,64 @@ void L2CAPSignalingClass::handleSecurityData(uint16_t connectionHandle, uint8_t btct.printBytes(data,dlen); #endif if (code == CONNECTION_PAIRING_REQUEST) { - // 0x1 - struct __attribute__ ((packed)) PairingRequest { - uint8_t ioCapability; - uint8_t oobDataFlag; - uint8_t authReq; - uint8_t maxEncSize; - uint8_t initiatorKeyDistribution; - uint8_t responderKeyDistribution; - } *pairingRequest = (PairingRequest*)l2capSignalingHdr->data; - - - ATT.remoteKeyDistribution = KeyDistribution(pairingRequest->initiatorKeyDistribution); - ATT.localKeyDistribution = KeyDistribution(pairingRequest->responderKeyDistribution); - KeyDistribution rkd(pairingRequest->responderKeyDistribution); - AuthReq req(pairingRequest->authReq); - KeyDistribution responseKD = KeyDistribution(); - responseKD.setIdKey(true); + + if (isPairingEnabled()){ + if (_pairing_enabled >= 2) _pairing_enabled = 0; // 2 = pair once only + + // 0x1 + struct __attribute__ ((packed)) PairingRequest { + uint8_t ioCapability; + uint8_t oobDataFlag; + uint8_t authReq; + uint8_t maxEncSize; + uint8_t initiatorKeyDistribution; + uint8_t responderKeyDistribution; + } *pairingRequest = (PairingRequest*)l2capSignalingHdr->data; + + + ATT.remoteKeyDistribution = KeyDistribution(pairingRequest->initiatorKeyDistribution); + ATT.localKeyDistribution = KeyDistribution(pairingRequest->responderKeyDistribution); + KeyDistribution rkd(pairingRequest->responderKeyDistribution); + AuthReq req(pairingRequest->authReq); + KeyDistribution responseKD = KeyDistribution(); + responseKD.setIdKey(true); #ifdef _BLE_TRACE_ - Serial.print("Req has properties: "); - Serial.print(req.Bonding()?"bonding, ":"no bonding, "); - Serial.print(req.CT2()?"CT2, ":"no CT2, "); - Serial.print(req.KeyPress()?"KeyPress, ":"no KeyPress, "); - Serial.print(req.MITM()?"MITM, ":"no MITM, "); - Serial.print(req.SC()?"SC, ":"no SC, "); + Serial.print("Req has properties: "); + Serial.print(req.Bonding()?"bonding, ":"no bonding, "); + Serial.print(req.CT2()?"CT2, ":"no CT2, "); + Serial.print(req.KeyPress()?"KeyPress, ":"no KeyPress, "); + Serial.print(req.MITM()?"MITM, ":"no MITM, "); + Serial.print(req.SC()?"SC, ":"no SC, "); #endif - uint8_t peerIOCap[3]; - peerIOCap[0] = pairingRequest->authReq; - peerIOCap[1] = pairingRequest->oobDataFlag; - peerIOCap[2] = pairingRequest->ioCapability; - ATT.setPeerIOCap(connectionHandle, peerIOCap); - ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::PAIRING_REQUEST); + uint8_t peerIOCap[3]; + peerIOCap[0] = pairingRequest->authReq; + peerIOCap[1] = pairingRequest->oobDataFlag; + peerIOCap[2] = pairingRequest->ioCapability; + ATT.setPeerIOCap(connectionHandle, peerIOCap); + ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::PAIRING_REQUEST); #ifdef _BLE_TRACE_ - Serial.print("Peer encryption : 0b"); - Serial.println(ATT.getPeerEncryption(connectionHandle), BIN); + Serial.print("Peer encryption : 0b"); + Serial.println(ATT.getPeerEncryption(connectionHandle), BIN); #endif - struct __attribute__ ((packed)) PairingResponse { - uint8_t code; - uint8_t ioCapability; - uint8_t oobDataFlag; - uint8_t authReq; - uint8_t maxEncSize; - uint8_t initiatorKeyDistribution; - uint8_t responderKeyDistribution; - } response = { CONNECTION_PAIRING_RESPONSE, LOCAL_IOCAP, 0, LOCAL_AUTHREQ, 0x10, responseKD.getOctet(), responseKD.getOctet()}; - - HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response); + struct __attribute__ ((packed)) PairingResponse { + uint8_t code; + uint8_t ioCapability; + uint8_t oobDataFlag; + uint8_t authReq; + uint8_t maxEncSize; + uint8_t initiatorKeyDistribution; + uint8_t responderKeyDistribution; + } response = { CONNECTION_PAIRING_RESPONSE, LOCAL_IOCAP, 0, LOCAL_AUTHREQ, 0x10, responseKD.getOctet(), responseKD.getOctet()}; + + HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response); + + } else { + // Pairing not enabled + uint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x05}; // reqect pairing + HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(ret), ret); + ATT.setPeerEncryption(connectionHandle, NO_ENCRYPTION); + } } else if (code == CONNECTION_PAIRING_RANDOM) { @@ -276,11 +290,8 @@ void L2CAPSignalingClass::handleSecurityData(uint16_t connectionHandle, uint8_t else if(code == CONNECTION_PAIRING_DHKEY_CHECK) { uint8_t RemoteDHKeyCheck[16]; - uint8_t BD_ADDR_REMOTE[7]; - ATT.getPeerAddrWithType(connectionHandle, BD_ADDR_REMOTE); for(int i=0; i<16; i++) RemoteDHKeyCheck[15-i] = l2capSignalingHdr->data[i]; - uint8_t encryptionState = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::RECEIVED_DH_CHECK; - + #ifdef _BLE_TRACE_ Serial.println("[Info] DH Key check"); @@ -288,72 +299,93 @@ void L2CAPSignalingClass::handleSecurityData(uint16_t connectionHandle, uint8_t btct.printBytes(RemoteDHKeyCheck, 16); #endif - HCI.readBdAddr(); + + + uint8_t encryptionState = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::RECEIVED_DH_CHECK; ATT.setPeerEncryption(connectionHandle, encryptionState); - if((encryptionState & PEER_ENCRYPTION::DH_KEY_CALULATED) > 0){ + if((encryptionState & PEER_ENCRYPTION::DH_KEY_CALULATED) == 0){ +#ifdef _BLE_TRACE_ + Serial.println("DHKey not yet ready, will calculate f5, f6 later"); +#endif + // store RemoteDHKeyCheck for later check + memcpy(HCI.remoteDHKeyCheckBuffer,RemoteDHKeyCheck,16); + + } else { // We've already calculated the DHKey so we can calculate our check and send it. + smCalculateLTKandConfirm(connectionHandle, RemoteDHKeyCheck); - uint8_t MacKey[16]; - uint8_t localAddress[7]; - - memcpy(&localAddress[1],HCI.localAddr,6); - localAddress[0] = 0; // IOT 33 uses a static address + } + } +} - btct.f5(HCI.DHKey,HCI.Na,HCI.Nb,BD_ADDR_REMOTE,localAddress,MacKey,HCI.LTK); +void L2CAPSignalingClass::smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[]) +{ // Authentication stage 2: LTK Calculation + + uint8_t localAddress[7]; + uint8_t remoteAddress[7]; + ATT.getPeerAddrWithType(handle, remoteAddress); + + HCI.readBdAddr(); + memcpy(&localAddress[1],HCI.localAddr,6); + localAddress[0] = 0; // IOT 33 uses a static address // TODO: confirm for Nano BLE + + // Compute the LTK and MacKey + uint8_t MacKey[16]; + btct.f5(HCI.DHKey, HCI.Na, HCI.Nb, remoteAddress, localAddress, MacKey, HCI.LTK); + + // Compute Ea and Eb + uint8_t Ea[16]; + uint8_t Eb[16]; + uint8_t R[16]; + uint8_t MasterIOCap[3]; + uint8_t SlaveIOCap[3] = {LOCAL_AUTHREQ, 0x0, LOCAL_IOCAP}; + + ATT.getPeerIOCap(handle, MasterIOCap); + for(int i=0; i<16; i++) R[i] = 0; + + btct.f6(MacKey, HCI.Na,HCI.Nb,R, MasterIOCap, remoteAddress, localAddress, Ea); + btct.f6(MacKey, HCI.Nb,HCI.Na,R, SlaveIOCap, localAddress, remoteAddress, Eb); - uint8_t Ea[16]; - uint8_t Eb[16]; - uint8_t R[16]; - uint8_t MasterIOCap[3]; - uint8_t SlaveIOCap[3] = {LOCAL_AUTHREQ, 0x0, LOCAL_IOCAP}; - - ATT.getPeerIOCap(connectionHandle, MasterIOCap); - for(int i=0; i<16; i++) R[i] = 0; +#ifdef _BLE_TRACE_ + Serial.println("Calculate and confirm LTK via f5, f6:"); + Serial.print("DHKey : "); btct.printBytes(HCI.DHKey,32); + Serial.print("Na : "); btct.printBytes(HCI.Na,16); + Serial.print("Nb : "); btct.printBytes(HCI.Nb,16); + Serial.print("MacKey : "); btct.printBytes(MacKey,16); + Serial.print("LTK : "); btct.printBytes(HCI.LTK,16); + Serial.print("Expected Ea: "); btct.printBytes(expectedEa, 16); + Serial.print("Ea : "); btct.printBytes(Ea, 16); + Serial.print("Eb : "); btct.printBytes(Eb,16); + Serial.print("Local Addr : "); btct.printBytes(localAddress, 7); + Serial.print("LocalIOCap : "); btct.printBytes(SlaveIOCap, 3); + Serial.print("MasterAddr : "); btct.printBytes(remoteAddress, 7); + Serial.print("MasterIOCAP: "); btct.printBytes(MasterIOCap, 3); +#endif - btct.f6(MacKey, HCI.Na,HCI.Nb,R, MasterIOCap, BD_ADDR_REMOTE, localAddress, Ea); - btct.f6(MacKey, HCI.Nb,HCI.Na,R, SlaveIOCap, localAddress, BD_ADDR_REMOTE, Eb); - - + // Check if Ea = expectedEa + if (memcmp(Ea, expectedEa, 16) == 0){ + // Check ok + // Send our confirmation value to complete authentication stage 2 + uint8_t ret[17]; + ret[0] = CONNECTION_PAIRING_DHKEY_CHECK; + for(int i=0; i 0; +} + void L2CAPSignalingClass::connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]) { struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest { diff --git a/src/utility/L2CAPSignaling.h b/src/utility/L2CAPSignaling.h index b7177be3..f8a361ee 100644 --- a/src/utility/L2CAPSignaling.h +++ b/src/utility/L2CAPSignaling.h @@ -41,8 +41,15 @@ #define CONNECTION_PAIRING_DHKEY_CHECK 0x0D #define CONNECTION_PAIRING_KEYPRESS 0x0E +#define IOCAP_DISPLAY_ONLY 0x00 +#define IOCAP_DISPLAY_YES_NO 0x01 +#define IOCAP_KEYBOARD_ONLY 0x02 +#define IOCAP_NO_INPUT_NO_OUTPUT 0x03 +#define IOCAP_KEYBOARD_DISPLAY 0x04 + + #define LOCAL_AUTHREQ 0b00101101 -#define LOCAL_IOCAP 0x3 +#define LOCAL_IOCAP IOCAP_NO_INPUT_NO_OUTPUT // will use JustWorks pairing class L2CAPSignalingClass { public: @@ -63,15 +70,25 @@ class L2CAPSignalingClass { virtual void setConnectionInterval(uint16_t minInterval, uint16_t maxInterval); virtual void setSupervisionTimeout(uint16_t supervisionTimeout); + + virtual void setPairingEnabled(uint8_t enabled); + virtual bool isPairingEnabled(); + + + + virtual void smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[]); + private: virtual void connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]); virtual void connectionParameterUpdateResponse(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]); + private: uint16_t _minInterval; uint16_t _maxInterval; uint16_t _supervisionTimeout; + uint8_t _pairing_enabled; }; extern L2CAPSignalingClass& L2CAPSignaling;