summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-12-10 12:56:45 +0100
committerSven Gothel <[email protected]>2020-12-10 12:56:45 +0100
commit5a6c1d17e6d38d60728b0d7bb4a195d2f443ecfd (patch)
treee80c3770d449536316177c8118e853d3c7f95b95
parentb5c800ecfc9d0115646f5e72d93fb458f2c8ee5b (diff)
Process MgmtEvent::Opcode::NEW_LONG_TERM_KEY: Allowing updatePairingState() if SMPKeyDist::ENC_KEY not done yet
MgmtEvent::Opcode::NEW_LONG_TERM_KEY shall become handy when using Secure Connections (SC), as the generated / derived LTKs will not be transported via SMP. Also: - DBTDevice::clearSMPStates(): Clear keys - Extract checkPairingKeyDistributionComplete(..) for hciSMPMsgCallback() and updatePairingState()
-rw-r--r--api/direct_bt/DBTAdapter.hpp1
-rw-r--r--api/direct_bt/DBTDevice.hpp7
-rw-r--r--src/direct_bt/DBTAdapter.cpp20
-rw-r--r--src/direct_bt/DBTDevice.cpp113
4 files changed, 118 insertions, 23 deletions
diff --git a/api/direct_bt/DBTAdapter.hpp b/api/direct_bt/DBTAdapter.hpp
index d3650c46..3508a98d 100644
--- a/api/direct_bt/DBTAdapter.hpp
+++ b/api/direct_bt/DBTAdapter.hpp
@@ -283,6 +283,7 @@ namespace direct_bt {
bool mgmtEvDeviceFoundHCI(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvDeviceDisconnectedMgmt(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvPairDeviceCompleteMgmt(std::shared_ptr<MgmtEvent> e) noexcept;
+ bool mgmtEvNewLongTermKeyMgmt(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvDeviceDiscoveringHCI(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvDeviceConnectedHCI(std::shared_ptr<MgmtEvent> e) noexcept;
diff --git a/api/direct_bt/DBTDevice.hpp b/api/direct_bt/DBTDevice.hpp
index d38000eb..22772080 100644
--- a/api/direct_bt/DBTDevice.hpp
+++ b/api/direct_bt/DBTDevice.hpp
@@ -113,7 +113,6 @@ namespace direct_bt {
std::mutex mtx_pairing;
jau::sc_atomic_bool sync_pairing;
-
DBTDevice(DBTAdapter & adapter, EInfoReport const & r);
/** Add advertised service (GAP discovery) */
@@ -160,6 +159,10 @@ namespace direct_bt {
*/
bool connectSMP(std::shared_ptr<DBTDevice> sthis, const BTSecurityLevel sec_level) noexcept;
+ bool checkPairingKeyDistributionComplete(const std::string& timestamp) const noexcept;
+
+ bool updatePairingState(std::shared_ptr<DBTDevice> sthis, std::shared_ptr<MgmtEvent> evt, const HCIStatusCode evtStatus, SMPPairingState claimed_state) noexcept;
+
/**
* Forwarded from HCIHandler -> DBTAdapter -> this DBTDevice
* <p>
@@ -189,8 +192,6 @@ namespace direct_bt {
*/
bool connectGATT(std::shared_ptr<DBTDevice> sthis) noexcept;
- bool updatePairingState(std::shared_ptr<DBTDevice> sthis, std::shared_ptr<MgmtEvent> evt, const HCIStatusCode evtStatus, SMPPairingState claimed_state) noexcept;
-
/**
* Will be performed within disconnect() and notifyDisconnected().
*/
diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp
index 443f7602..e607f892 100644
--- a/src/direct_bt/DBTAdapter.cpp
+++ b/src/direct_bt/DBTAdapter.cpp
@@ -187,6 +187,7 @@ bool DBTAdapter::validateDevInfo() noexcept {
ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::AUTH_FAILED, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvAuthFailedMgmt));
ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_UNPAIRED, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceUnpairedMgmt));
ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::PAIR_DEVICE_COMPLETE, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvPairDeviceCompleteMgmt));
+ ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_LONG_TERM_KEY, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvNewLongTermKeyMgmt));
if( !ok ) {
ERR_PRINT("Could not add all required MgmtEventCallbacks to DBTManager: %s", toString().c_str());
@@ -1278,6 +1279,25 @@ bool DBTAdapter::mgmtEvPairDeviceCompleteMgmt(std::shared_ptr<MgmtEvent> e) noex
return true;
}
+bool DBTAdapter::mgmtEvNewLongTermKeyMgmt(std::shared_ptr<MgmtEvent> e) noexcept {
+ const MgmtEvtNewLongTermKey& event = *static_cast<const MgmtEvtNewLongTermKey *>(e.get());
+ const MgmtLongTermKeyInfo& ltk_info = event.getLongTermKey();
+ std::shared_ptr<DBTDevice> device = findConnectedDevice(ltk_info.address, ltk_info.address_type);
+ if( nullptr != device ) {
+ const bool ok = ltk_info.enc_size > 0 && ltk_info.key_type != MgmtLTKType::NONE;
+ if( ok ) {
+ device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::COMPLETED);
+ } else {
+ WORDY_PRINT("DBTAdapter::mgmt:NewLongTermKey(dev_id %d): Invalid LTK: %s",
+ dev_id, event.toString().c_str());
+ }
+ } else {
+ WORDY_PRINT("DBTAdapter::mgmt:NewLongTermKey(dev_id %d): Device not tracked: %s",
+ dev_id, event.toString().c_str());
+ }
+ return true;
+}
+
bool DBTAdapter::mgmtEvDeviceFoundHCI(std::shared_ptr<MgmtEvent> e) noexcept {
COND_PRINT(debug_event, "DBTAdapter:hci:DeviceFound(dev_id %d): %s", dev_id, e->toString().c_str());
const MgmtEvtDeviceFound &deviceFoundEvent = *static_cast<const MgmtEvtDeviceFound *>(e.get());
diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp
index 9a46747c..1d68ae3a 100644
--- a/src/direct_bt/DBTDevice.cpp
+++ b/src/direct_bt/DBTDevice.cpp
@@ -522,6 +522,44 @@ void DBTDevice::processDeviceReady(std::shared_ptr<DBTDevice> sthis, const uint6
}
+static const SMPKeyDist _key_mask_legacy = SMPKeyDist::ENC_KEY | SMPKeyDist::ID_KEY | SMPKeyDist::SIGN_KEY;
+static const SMPKeyDist _key_mask_sc = SMPKeyDist::ID_KEY | SMPKeyDist::SIGN_KEY | SMPKeyDist::LINK_KEY;
+
+bool DBTDevice::checkPairingKeyDistributionComplete(const std::string& timestamp) const noexcept {
+ bool res = false;
+
+ if( SMPPairingState::KEY_DISTRIBUTION == pairing_data.state ) {
+ // Spec allows responder to not distribute the keys,
+ // hence distribution is complete with initiator (LL master) keys!
+ // Impact of missing responder keys: Requires new pairing each connection.
+ if( pairing_data.use_sc ) {
+ if( pairing_data.keys_init_has == ( pairing_data.keys_init_exp & _key_mask_sc ) ) {
+ // pairing_data.keys_resp_has == ( pairing_data.keys_resp_exp & key_mask_sc )
+ res = true;
+ }
+ } else {
+ if( pairing_data.keys_init_has == ( pairing_data.keys_init_exp & _key_mask_legacy ) ) {
+ // pairing_data.keys_resp_has == ( pairing_data.keys_resp_exp & key_mask_legacy )
+ res = true;
+ }
+ }
+
+ if( jau::environment::get().debug ) {
+ jau::PLAIN_PRINT(false, "[%s] Debug: DBTDevice:SMP:KEY_DISTRIBUTION: done %d, address[%s, %s]",
+ timestamp.c_str(), res,
+ address.toString().c_str(), getBDAddressTypeString(addressType).c_str());
+ jau::PLAIN_PRINT(false, "[%s] - keys[init %s / %s, resp %s / %s]",
+ timestamp.c_str(),
+ getSMPKeyDistMaskString(pairing_data.keys_init_has).c_str(),
+ getSMPKeyDistMaskString(pairing_data.keys_init_exp).c_str(),
+ getSMPKeyDistMaskString(pairing_data.keys_resp_has).c_str(),
+ getSMPKeyDistMaskString(pairing_data.keys_resp_exp).c_str());
+ }
+ }
+
+ return res;
+}
+
bool DBTDevice::updatePairingState(std::shared_ptr<DBTDevice> sthis, std::shared_ptr<MgmtEvent> evt, const HCIStatusCode evtStatus, SMPPairingState claimed_state) noexcept {
const std::lock_guard<std::mutex> lock(mtx_pairing); // RAII-style acquire and relinquish via destructor
jau::sc_atomic_critical sync(sync_pairing);
@@ -568,6 +606,49 @@ bool DBTDevice::updatePairingState(std::shared_ptr<DBTDevice> sthis, std::shared
// i.e. already paired, reusing keys and usable connection
mode = PairingMode::PRE_PAIRED;
is_device_ready = true;
+ } else if( MgmtEvent::Opcode::NEW_LONG_TERM_KEY == mgmtEvtOpcode &&
+ HCIStatusCode::SUCCESS == evtStatus &&
+ SMPPairingState::KEY_DISTRIBUTION == pairing_data.state )
+ {
+ // SMP pairing has started, mngr issued new LTK key command
+ const MgmtEvtNewLongTermKey& event = *static_cast<const MgmtEvtNewLongTermKey *>(evt.get());
+ const MgmtLongTermKeyInfo& ltk_info = event.getLongTermKey();
+ const SMPLongTermKeyInfo smp_ltk = ltk_info.toSMPLongTermKeyInfo();
+ if( smp_ltk.isValid() ) {
+ const std::string timestamp = jau::uint64DecString(jau::environment::getElapsedMillisecond(evt->getTimestamp()), ',', 9);
+ const bool responder = ( SMPLongTermKeyInfo::Property::RESPONDER & smp_ltk.properties ) != SMPLongTermKeyInfo::Property::NONE;
+
+ if( responder ) {
+ if( ( SMPKeyDist::ENC_KEY & pairing_data.keys_resp_has ) == SMPKeyDist::NONE ) { // no overwrite
+ if( jau::environment::get().debug ) {
+ jau::PLAIN_PRINT(false, "[%s] DBTDevice::updatePairingState.0: ENC_KEY responder set", timestamp.c_str());
+ jau::PLAIN_PRINT(false, "[%s] - old %s", timestamp.c_str(), pairing_data.ltk_resp.toString().c_str());
+ jau::PLAIN_PRINT(false, "[%s] - new %s", timestamp.c_str(), smp_ltk.toString().c_str());
+ }
+ pairing_data.ltk_resp = smp_ltk;
+ pairing_data.keys_resp_has |= SMPKeyDist::ENC_KEY;
+ if( checkPairingKeyDistributionComplete(timestamp) ) {
+ is_device_ready = true;
+ }
+ }
+ } else {
+ if( ( SMPKeyDist::ENC_KEY & pairing_data.keys_init_has ) == SMPKeyDist::NONE ) { // no overwrite
+ if( jau::environment::get().debug ) {
+ jau::PLAIN_PRINT(false, "[%s] DBTDevice::updatePairingState.0: ENC_KEY initiator set", timestamp.c_str());
+ jau::PLAIN_PRINT(false, "[%s] - old %s", timestamp.c_str(), pairing_data.ltk_init.toString().c_str());
+ jau::PLAIN_PRINT(false, "[%s] - new %s", timestamp.c_str(), smp_ltk.toString().c_str());
+ }
+ pairing_data.ltk_init = smp_ltk;
+ pairing_data.keys_init_has |= SMPKeyDist::ENC_KEY;
+ if( checkPairingKeyDistributionComplete(timestamp) ) {
+ is_device_ready = true;
+ }
+ }
+ }
+ if( !is_device_ready ) {
+ claimed_state = pairing_data.state; // not yet
+ }
+ }
} else {
// Ignore: Undesired event or SMP pairing is in process, which needs to be completed.
claimed_state = pairing_data.state;
@@ -608,11 +689,9 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_
const std::lock_guard<std::mutex> lock(mtx_pairing); // RAII-style acquire and relinquish via destructor
jau::sc_atomic_critical sync(sync_pairing);
- const SMPKeyDist key_mask_legacy = SMPKeyDist::ENC_KEY | SMPKeyDist::ID_KEY | SMPKeyDist::SIGN_KEY;
- const SMPKeyDist key_mask_sc = SMPKeyDist::ID_KEY | SMPKeyDist::SIGN_KEY | SMPKeyDist::LINK_KEY;
const SMPPairingState old_pstate = pairing_data.state;
const PairingMode old_pmode = pairing_data.mode;
- const std::string timestamp = jau::uint64DecString(jau::environment::getElapsedMillisecond(), ',', 9);
+ const std::string timestamp = jau::uint64DecString(jau::environment::getElapsedMillisecond(msg->ts_creation), ',', 9);
SMPPairingState pstate = old_pstate;
PairingMode pmode = old_pmode;
@@ -816,23 +895,9 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_
break;
}
- if( SMPPairingState::KEY_DISTRIBUTION == old_pstate ) {
- // Spec allows responder to not distribute the keys,
- // hence distribution is complete with initiator (LL master) keys!
- // Impact of missing responder keys: Requires new pairing each connection.
- if( pairing_data.use_sc ) {
- if( pairing_data.keys_init_has == ( pairing_data.keys_init_exp & key_mask_sc ) ) {
- // pairing_data.keys_resp_has == ( pairing_data.keys_resp_exp & key_mask_sc )
- pstate = SMPPairingState::COMPLETED;
- is_device_ready = true;
- }
- } else {
- if( pairing_data.keys_init_has == ( pairing_data.keys_init_exp & key_mask_legacy ) ) {
- // pairing_data.keys_resp_has == ( pairing_data.keys_resp_exp & key_mask_legacy )
- pstate = SMPPairingState::COMPLETED;
- is_device_ready = true;
- }
- }
+ if( checkPairingKeyDistributionComplete(timestamp) ) {
+ pstate = SMPPairingState::COMPLETED;
+ is_device_ready = true;
}
if( jau::environment::get().debug ) {
@@ -1109,6 +1174,11 @@ void DBTDevice::clearSMPStates(const bool connected) noexcept {
pairing_data.maxEncsz_resp = 0;
pairing_data.keys_resp_exp = SMPKeyDist::NONE;
pairing_data.keys_resp_has = SMPKeyDist::NONE;
+ pairing_data.ltk_resp.clear();
+ pairing_data.irk_resp.clear();
+ // pairing_data.address;
+ // pairing_data.is_static_random_address;
+ pairing_data.csrk_resp.clear();
pairing_data.authReqs_init = SMPAuthReqs::NONE;
pairing_data.ioCap_init = SMPIOCapability::NO_INPUT_NO_OUTPUT;
@@ -1116,6 +1186,9 @@ void DBTDevice::clearSMPStates(const bool connected) noexcept {
pairing_data.maxEncsz_init = 0;
pairing_data.keys_init_exp = SMPKeyDist::NONE;
pairing_data.keys_init_has = SMPKeyDist::NONE;
+ pairing_data.ltk_init.clear();
+ pairing_data.irk_init.clear();
+ pairing_data.csrk_init.clear();
}
void DBTDevice::disconnectSMP(const int caller) noexcept {