diff options
author | Sven Gothel <[email protected]> | 2020-12-10 12:56:45 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2020-12-10 12:56:45 +0100 |
commit | 5a6c1d17e6d38d60728b0d7bb4a195d2f443ecfd (patch) | |
tree | e80c3770d449536316177c8118e853d3c7f95b95 | |
parent | b5c800ecfc9d0115646f5e72d93fb458f2c8ee5b (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.hpp | 1 | ||||
-rw-r--r-- | api/direct_bt/DBTDevice.hpp | 7 | ||||
-rw-r--r-- | src/direct_bt/DBTAdapter.cpp | 20 | ||||
-rw-r--r-- | src/direct_bt/DBTDevice.cpp | 113 |
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 { |