You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am trying to write a code for nRF52840. It somehow stops and disconnects the central when it needs to pair/bond.
While I run the same code on a ESP32-C3 and it runs fine, pairs, and bonds successfully. The following is the serial monitor output for ESP32-C3:
It runs store IRK/LTK hence the bonding information can be captured.
This is the nRF52840 serial monitor output:
As seen on the snapshot, it just disconnects the central without running the store IRK/LTK callback.
Is there any other step that needs to be taken for nRF52840? Or the library is somehow not ready for nRF52840 regarding the pairing and bonding?
The following is the Arduino code for both ESP32-C3 and the nRF52840. It's taken from the examples, so nothing special, but is included for the complete issue communication.
/* This file is part of the ArduinoBLE library. Copyright (c) 2018 Arduino SA. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA*/
#include"HCI.h"
#include"ATT.h"
#include"btct.h"
#include"L2CAPSignaling.h"
#include"keyDistribution.h"
#include"bitDescriptions.h"
#defineCONNECTION_PARAMETER_UPDATE_REQUEST0x12
#defineCONNECTION_PARAMETER_UPDATE_RESPONSE0x13//#define _BLE_TRACE_L2CAPSignalingClass::L2CAPSignalingClass() :
_minInterval(0),
_maxInterval(0),
_supervisionTimeout(0),
_pairing_enabled(1)
{
}
L2CAPSignalingClass::~L2CAPSignalingClass()
{
}
voidL2CAPSignalingClass::addConnection(uint16_t handle, uint8_t role, uint8_t/*peerBdaddrType*/,
uint8_t/*peerBdaddr*/[6], uint16_t interval,
uint16_t/*latency*/, uint16_t supervisionTimeout,
uint8_t/*masterClockAccuracy*/)
{
if (role != 1) {
// ignorereturn;
}
bool updateParameters = false;
uint16_t updatedMinInterval = interval;
uint16_t updatedMaxInterval = interval;
uint16_t updatedSupervisionTimeout = supervisionTimeout;
if (_minInterval && _maxInterval) {
if (interval < _minInterval || interval > _maxInterval) {
updatedMinInterval = _minInterval;
updatedMaxInterval = _maxInterval;
updateParameters = true;
}
}
if (_supervisionTimeout && supervisionTimeout != _supervisionTimeout) {
updatedSupervisionTimeout = _supervisionTimeout;
updateParameters = true;
}
if (updateParameters) {
struct__attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest {
uint8_t code;
uint8_t identifier;
uint16_t length;
uint16_t minInterval;
uint16_t maxInterval;
uint16_t latency;
uint16_t supervisionTimeout;
} request = { CONNECTION_PARAMETER_UPDATE_REQUEST, 0x01, 8,
updatedMinInterval, updatedMaxInterval, 0x0000, updatedSupervisionTimeout };
HCI.sendAclPkt(handle, SIGNALING_CID, sizeof(request), &request);
}
}
voidL2CAPSignalingClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[])
{
struct__attribute__ ((packed)) L2CAPSignalingHdr {
uint8_t code;
uint8_t identifier;
uint16_t length;
} *l2capSignalingHdr = (L2CAPSignalingHdr*)data;
if (dlen < sizeof(L2CAPSignalingHdr)) {
// too short, ignorereturn;
}
if (dlen != (sizeof(L2CAPSignalingHdr) + l2capSignalingHdr->length)) {
// invalid length, ignorereturn;
}
uint8_t code = l2capSignalingHdr->code;
uint8_t identifier = l2capSignalingHdr->identifier;
uint16_t length = l2capSignalingHdr->length;
data = &data[sizeof(L2CAPSignalingHdr)];
if (code == CONNECTION_PARAMETER_UPDATE_REQUEST) {
connectionParameterUpdateRequest(connectionHandle, identifier, length, data);
} elseif (code == CONNECTION_PARAMETER_UPDATE_RESPONSE) {
connectionParameterUpdateResponse(connectionHandle, identifier, length, data);
}
}
voidL2CAPSignalingClass::handleSecurityData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[])
{
struct__attribute__ ((packed)) L2CAPSignalingHdr {
uint8_t code;
uint8_t data[64];
} *l2capSignalingHdr = (L2CAPSignalingHdr*)data;
#ifdef _BLE_TRACE_
Serial.print("dlen: ");
Serial.println(dlen);
#endifuint8_t code = l2capSignalingHdr->code;
#ifdef _BLE_TRACE_
Serial.print("handleSecurityData: code: 0x");
Serial.println(code, HEX);
Serial.print("rx security:");
btct.printBytes(data,dlen);
#endifif (code == CONNECTION_PAIRING_REQUEST) {
if (isPairingEnabled()){
if (_pairing_enabled >= 2) _pairing_enabled = 0; // 2 = pair once only// 0x1struct__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;
KeyDistribution responseKD = KeyDistribution();
responseKD.setIdKey(true);
ATT.remoteKeyDistribution = responseKD;// KeyDistribution(pairingRequest->initiatorKeyDistribution);
ATT.localKeyDistribution = responseKD; //KeyDistribution(pairingRequest->responderKeyDistribution);// KeyDistribution rkd(pairingRequest->responderKeyDistribution);
AuthReq req(pairingRequest->authReq);
#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, ");
#endifuint8_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);
#endifstruct__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, HCI.localIOCap(), 0, HCI.localAuthreq().getOctet(), 0x10, responseKD.getOctet(), responseKD.getOctet()};
HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response);
} else {
// Pairing not enableduint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x05}; // reqect pairing
HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(ret), ret);
ATT.setPeerEncryption(connectionHandle, NO_ENCRYPTION);
}
}
elseif (code == CONNECTION_PAIRING_RANDOM)
{
struct__attribute__ ((packed)) PairingRandom {
uint8_t Na[16];
} *pairingRandom = (PairingRandom*)l2capSignalingHdr->data;
for(int i=0; i<16; i++){
HCI.Na[15-i] = pairingRandom->Na[i];
}
#ifdef _BLE_TRACE_
Serial.println("[Info] Pairing random.");
#endifstruct__attribute__ ((packed)) PairingResponse {
uint8_t code;
uint8_t Nb[16];
} response = { CONNECTION_PAIRING_RANDOM, 0};
for(int i=0; i< 16; i++) response.Nb[15-i] = HCI.Nb[i];
HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response);
// We now have all needed for compare valueuint8_t g2Result[4];
uint8_t U[32];
uint8_t V[32];
for(int i=0; i<32; i++){
U[31-i] = HCI.remotePublicKeyBuffer[i];
V[31-i] = HCI.localPublicKeyBuffer[i];
}
btct.g2(U,V,HCI.Na,HCI.Nb, g2Result);
uint32_t result = 0;
for(int i=0; i<4; i++) result += g2Result[3-i] << 8*i;
#ifdef _BLE_TRACE_
Serial.print("U : ");
btct.printBytes(U,32);
Serial.print("V : ");
btct.printBytes(V,32);
Serial.print("X : ");
btct.printBytes(X,16);
Serial.print("Y : ");
btct.printBytes(Y,16);
Serial.print("g2res : ");
btct.printBytes(g2Result,4);
Serial.print("Result : ");
Serial.println(result);
#endifif(HCI._displayCode!=0){
HCI._displayCode(result%1000000);
}
if(HCI._binaryConfirmPairing!=0){
if(!HCI._binaryConfirmPairing()){
#ifdef _BLE_TRACE_
Serial.println("User rejection");
#endifuint8_t rejection[2];
rejection[0] = CONNECTION_PAIRING_FAILED;
rejection[1] = 0x0C; // Numeric comparison failed
HCI.sendAclPkt(connectionHandle, SECURITY_CID, 2, rejection);
ATT.setPeerEncryption(connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION);
}else{
#ifdef _BLE_TRACE_
Serial.println("User did confirm");
#endif
}
}
}
elseif (code == CONNECTION_PAIRING_RESPONSE)
{
}
elseif(code == CONNECTION_PAIRING_FAILED)
{
#ifdef _BLE_TRACE_
struct__attribute__ ((packed)) PairingFailed
{
uint8_t code;
uint8_t reason;
} *pairingFailed = (PairingFailed*)data;
Serial.print("Pairing failed with code: 0x");
Serial.println(pairingFailed->reason,HEX);
#endif
ATT.setPeerEncryption(connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION);
}
elseif (code == CONNECTION_IDENTITY_INFORMATION){
struct__attribute__ ((packed)) IdentityInformation {
uint8_t code;
uint8_t PeerIRK[16];
} *identityInformation = (IdentityInformation*)data;
for(int i=0; i<16; i++) ATT.peerIRK[15-i] = identityInformation->PeerIRK[i];
#ifdef _BLE_TRACE_
Serial.println("Saved peer IRK");
#endif
}
elseif (code == CONNECTION_IDENTITY_ADDRESS){
struct__attribute__ ((packed)) IdentityAddress {
uint8_t code;
uint8_t addressType;
uint8_t address[6];
} *identityAddress = (IdentityAddress*)data;
// we can save this information now.uint8_t peerAddress[6];
for(int i=0; i<6; i++) peerAddress[5-i] = identityAddress->address[i];
HCI.saveNewAddress(identityAddress->addressType, peerAddress, ATT.peerIRK, ATT.localIRK);
if(HCI._storeLTK!=0){
HCI._storeLTK(peerAddress, HCI.LTK);
}
}
elseif (code == CONNECTION_PAIRING_PUBLIC_KEY){
/// Received a public keystruct__attribute__ ((packed)) ConnectionPairingPublicKey {
uint8_t x[32];
uint8_t y[32];
} *connectionPairingPublicKey = (ConnectionPairingPublicKey*)l2capSignalingHdr->data;
struct__attribute__ ((packed)) GenerateDHKeyCommand {
uint8_t x[32];
uint8_t y[32];
} generateDHKeyCommand = {
0x00,
0x00,
};
memcpy(generateDHKeyCommand.x,connectionPairingPublicKey->x,32);
memcpy(generateDHKeyCommand.y,connectionPairingPublicKey->y,32);
if(ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::REQUESTED_ENCRYPTION)){
#ifdef _BLE_TRACE_
Serial.println("[Info] Pairing public key");
Serial.println("Requested encryption stored.");
#endif
}else{
#ifdef _BLE_TRACE_
Serial.println("[Info] Pairing public key");
Serial.print("Failed to store encryption request with handle: 0x");
Serial.println(connectionHandle,HEX);
#endif
}
memcpy(HCI.remotePublicKeyBuffer,&generateDHKeyCommand,sizeof(generateDHKeyCommand));
HCI.sendCommand( (OGF_LE_CTL << 10 )| LE_COMMAND::READ_LOCAL_P256, 0);
}
elseif(code == CONNECTION_PAIRING_DHKEY_CHECK)
{
uint8_t RemoteDHKeyCheck[16];
for(int i=0; i<16; i++) RemoteDHKeyCheck[15-i] = l2capSignalingHdr->data[i];
#ifdef _BLE_TRACE_
Serial.println("[Info] DH Key check");
Serial.print("Remote DHKey Check: ");
btct.printBytes(RemoteDHKeyCheck, 16);
#endifuint8_t encryptionState = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::RECEIVED_DH_CHECK;
ATT.setPeerEncryption(connectionHandle, encryptionState);
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 checkmemcpy(HCI.remoteDHKeyCheckBuffer,RemoteDHKeyCheck,16);
} else {
// We've already calculated the DHKey so we can calculate our check and send it.smCalculateLTKandConfirm(connectionHandle, RemoteDHKeyCheck);
}
}
}
voidL2CAPSignalingClass::smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[])
{ // Authentication stage 2: LTK Calculationuint8_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 MacKeyuint8_t MacKey[16];
btct.f5(HCI.DHKey, HCI.Na, HCI.Nb, remoteAddress, localAddress, MacKey, HCI.LTK);
// Compute Ea and Ebuint8_t Ea[16];
uint8_t Eb[16];
uint8_t R[16];
uint8_t MasterIOCap[3];
uint8_t SlaveIOCap[3] = {HCI.localAuthreq().getOctet(), 0x0, HCI.localIOCap()};
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);
#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// Check if Ea = expectedEaif (memcmp(Ea, expectedEa, 16) == 0){
// Check ok// Send our confirmation value to complete authentication stage 2uint8_t ret[17];
ret[0] = CONNECTION_PAIRING_DHKEY_CHECK;
for(int i=0; i<sizeof(Eb); i++){
ret[sizeof(Eb)-i] = Eb[i];
}
HCI.sendAclPkt(handle, SECURITY_CID, sizeof(ret), ret );
uint8_t encryption = ATT.getPeerEncryption(handle) | PEER_ENCRYPTION::SENT_DH_CHECK;
ATT.setPeerEncryption(handle, encryption);
#ifdef _BLE_TRACE_
Serial.println("DHKey check ok - send Eb back");
#endif
} else {
// Check failed, abort pairinguint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x0B}; // 0x0B = DHKey Check Failed
HCI.sendAclPkt(handle, SECURITY_CID, sizeof(ret), ret);
ATT.setPeerEncryption(handle, NO_ENCRYPTION);
#ifdef _BLE_TRACE_
Serial.println("Error: DHKey check failed - Aborting");
#endif
}
}
voidL2CAPSignalingClass::removeConnection(uint8_t/*handle*/, uint16_t/*reason*/)
{
}
voidL2CAPSignalingClass::setConnectionInterval(uint16_t minInterval, uint16_t maxInterval)
{
_minInterval = minInterval;
_maxInterval = maxInterval;
}
voidL2CAPSignalingClass::setSupervisionTimeout(uint16_t supervisionTimeout)
{
_supervisionTimeout = supervisionTimeout;
}
voidL2CAPSignalingClass::setPairingEnabled(uint8_t enabled)
{
_pairing_enabled = enabled;
}
boolL2CAPSignalingClass::isPairingEnabled()
{
return _pairing_enabled > 0;
}
voidL2CAPSignalingClass::connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[])
{
struct__attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest {
uint16_t minInterval;
uint16_t maxInterval;
uint16_t latency;
uint16_t supervisionTimeout;
} *request = (L2CAPConnectionParameterUpdateRequest*)data;
if (dlen < sizeof(L2CAPConnectionParameterUpdateRequest)) {
// too short, ignorereturn;
}
struct__attribute__ ((packed)) L2CAPConnectionParameterUpdateResponse {
uint8_t code;
uint8_t identifier;
uint16_t length;
uint16_t value;
} response = { CONNECTION_PARAMETER_UPDATE_RESPONSE, identifier, 2, 0x0000 };
if (_minInterval && _maxInterval) {
if (request->minInterval < _minInterval || request->maxInterval > _maxInterval) {
response.value = 0x0001; // reject
}
}
if (_supervisionTimeout) {
if (request->supervisionTimeout != _supervisionTimeout) {
response.value = 0x0001; // reject
}
}
HCI.sendAclPkt(handle, SIGNALING_CID, sizeof(response), &response);
if (response.value == 0x0000) {
HCI.leConnUpdate(handle, request->minInterval, request->maxInterval, request->latency, request->supervisionTimeout);
}
}
voidL2CAPSignalingClass::connectionParameterUpdateResponse(uint16_t/*handle*/, uint8_t/*identifier*/, uint8_t/*dlen*/, uint8_t/*data*/[])
{
}
#if !defined(FAKE_L2CAP)
L2CAPSignalingClass L2CAPSignalingObj;
L2CAPSignalingClass& L2CAPSignaling = L2CAPSignalingObj;
#endif
The text was updated successfully, but these errors were encountered:
I am trying to write a code for nRF52840. It somehow stops and disconnects the central when it needs to pair/bond.
While I run the same code on a ESP32-C3 and it runs fine, pairs, and bonds successfully. The following is the serial monitor output for ESP32-C3:
It runs store IRK/LTK hence the bonding information can be captured.
This is the nRF52840 serial monitor output:
As seen on the snapshot, it just disconnects the central without running the store IRK/LTK callback.
Is there any other step that needs to be taken for nRF52840? Or the library is somehow not ready for nRF52840 regarding the pairing and bonding?
The following is the Arduino code for both ESP32-C3 and the nRF52840. It's taken from the examples, so nothing special, but is included for the complete issue communication.
The text was updated successfully, but these errors were encountered: