diff options
author | Sven Gothel <[email protected]> | 2021-10-29 05:49:43 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-10-29 05:49:43 +0200 |
commit | 88456111f8020081eeea719143f42584a62f8ac6 (patch) | |
tree | fb77f5cf0a287cb0b982ba7570231868579685a7 | |
parent | 4f0f232301a2a2ffc1e396e29ccf8ab423edece6 (diff) |
Add BTAdapter's Slave Peripheral SMP Key Management
To have full SMP key persitency in peripheral slave mode,
BTAdapter requires fine grained control over
- Passing stored keys to BTDevice's PairingData (w/o uploading them)
- Uploading BTDevice's PairingData to the adapter
This required interaction in certain places,
only enabled if adapter is BTRole::Slave (peripheral):
- BTAdapter::mgmtEvDeviceConnectedHCI()
Only issue unpairDevice() if not pre-paired.
Unpairing is required for new pairing to avoid DHKey Check failures!
- BTAdapter::mgmtEvDeviceDisconnectedHCI()
- First unpairDevice() will be issued via notifyDisconnect()
- Set and upload stored keys for disconnected device (if existing),
preparing for next connect.
- BTAdapter::sendDevicePairingState()
- SMPPairingState::COMPLETED && not SMPPairingState::PRE_PAIRED: Store keys
- SMPPairingState::COMPLETED && SMPPairingState::PRE_PAIRED: Refresh keys to BTDevice (set), no upload!
- SMPPairingState::FAILED: Remove and delete keys
+++
BTAdapter::setSMPKeyPath(path) allows user to enable the persistent key storage
by setting its local filesystem path.
It will also read all key files (SMPKeyBin) and if valid and matching with the adapter,
uploads them for pre-pairing.
See dbt_peripheral00.cpp:
adapter->setSMPKeyPath(ADAPTER_KEY_PATH);
+++
-rw-r--r-- | api/direct_bt/BTAdapter.hpp | 43 | ||||
-rw-r--r-- | examples/dbt_constants.hpp | 2 | ||||
-rw-r--r-- | examples/dbt_peripheral00.cpp | 25 | ||||
-rw-r--r-- | java/jau/direct_bt/DBTAdapter.java | 3 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTAdapter.cxx | 11 | ||||
-rw-r--r-- | java/org/direct_bt/BTAdapter.java | 18 | ||||
-rwxr-xr-x | scripts/run-native-example.sh | 1 | ||||
-rw-r--r-- | src/direct_bt/BTAdapter.cpp | 239 |
8 files changed, 233 insertions, 109 deletions
diff --git a/api/direct_bt/BTAdapter.hpp b/api/direct_bt/BTAdapter.hpp index 06d64478..cd3ba90a 100644 --- a/api/direct_bt/BTAdapter.hpp +++ b/api/direct_bt/BTAdapter.hpp @@ -332,12 +332,19 @@ namespace direct_bt { typedef jau::cow_darray<impl::StatusListenerPair> statusListenerList_t; statusListenerList_t statusListenerList; + // Storing SMPKeyBin entries, referenced by their remote address, i.e. BTDevice address. + std::string key_path; + typedef std::shared_ptr<SMPKeyBin> SMPKeyBinRef; + typedef jau::darray<SMPKeyBinRef> key_list_t; + key_list_t key_list; + DBGattServerRef gattServerData = nullptr; mutable std::mutex mtx_discoveredDevices; mutable std::mutex mtx_connectedDevices; mutable std::mutex mtx_discovery; mutable std::mutex mtx_sharedDevices; // final mutex of all BTDevice lifecycle + mutable std::mutex mtx_keys; mutable jau::sc_atomic_bool sync_data; bool updateDataFromHCI() noexcept; @@ -405,6 +412,13 @@ namespace direct_bt { std::shared_ptr<BTDevice> getSharedDevice(const BTDevice & device) noexcept; void removeSharedDevice(const BTDevice & device) noexcept; + static SMPKeyBinRef findSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress) noexcept; + static bool removeSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress, const bool remove_file, const std::string key_path_) noexcept; + SMPKeyBinRef findSMPKeyBin(BDAddressAndType const & remoteAddress) noexcept; + /** Adding a SMPKeyBin will remove previous entry. */ + bool addSMPKeyBin(const SMPKeyBinRef& key, const bool write_file) noexcept; + bool removeSMPKeyBin(BDAddressAndType const & remoteAddress, const bool remove_file) noexcept; + bool mgmtEvNewSettingsMgmt(const MgmtEvent& e) noexcept; void updateAdapterSettings(const bool off_thread, const AdapterSetting new_settings, const bool sendEvent, const uint64_t timestamp) noexcept; bool mgmtEvDeviceDiscoveringMgmt(const MgmtEvent& e) noexcept; @@ -650,20 +664,41 @@ namespace direct_bt { bool setSecureConnections(const bool enable) noexcept; /** + * Set the adapter's persistent storage directory for SMPKeyBin files. + * - if set, all SMPKeyBin instances will be managed and persistent. + * - if not set, all SMPKeyBin instances will be transient only. + * + * When called, all keys within the path will be loaded, + * i.e. issuing uploadKeys() for all keys belonging to this BTAdapter. + * + * Persistent SMPKeyBin management is only functional when BTAdapter is in BTRole::Slave peripheral mode. + * + * For each SMPKeyBin file one shared BTDevice in BTRole::Master will be instantiated + * when uploadKeys() is called. + * + * @param path persistent storage path to SMPKeyBin files + * @see uploadKeys() + */ + void setSMPKeyPath(const std::string path) noexcept; + + /** * Associate the given SMPKeyBin with the contained remote address, i.e. SMPKeyBin::getRemoteAddrAndType(). * * Further uploads the Long Term Key (LTK) and Link Key (LK) for a potential upcoming connection, * if they are contained in the given SMPKeyBin file. * * This method is provided to support BTRole::Slave peripheral adapter mode, - * allowing user to inject all required keys before a connection occurs. + * allowing user to inject all required keys after initialize() * - * FIXME: Pass keys to BTDevice instance after connection! + * One shared BTDevice in BTRole::Master is instantiated. * - * @param keys SMPKeyBin file + * @param bin SMPKeyBin instance, might be persistent in filesystem + * @param write if true, write file to persistent storage, otherwise not * @return HCIStatusCode::SUCCESS or an error state on failure + * @see setSMPKeyPath() + * @see BTDevice::uploadKeys() */ - HCIStatusCode setSMPKeyBin(const SMPKeyBin& keys) noexcept; + HCIStatusCode uploadKeys(SMPKeyBin& bin, const bool write) noexcept; /** * Initialize the adapter with default values, including power-on. diff --git a/examples/dbt_constants.hpp b/examples/dbt_constants.hpp index 2ff4aaa4..71d6f8f1 100644 --- a/examples/dbt_constants.hpp +++ b/examples/dbt_constants.hpp @@ -38,4 +38,6 @@ */ constexpr const char KEY_PATH[] = "keys"; +constexpr const char ADAPTER_KEY_PATH[] = "dbt_keys"; + #endif /* DBT_CONSTANTS_HPP */ diff --git a/examples/dbt_peripheral00.cpp b/examples/dbt_peripheral00.cpp index 09fa7a58..8fe81ff9 100644 --- a/examples/dbt_peripheral00.cpp +++ b/examples/dbt_peripheral00.cpp @@ -70,7 +70,6 @@ static bool SHOW_UPDATE_EVENTS = false; static bool startAdvertising(BTAdapter *a, std::string msg); static bool stopAdvertising(BTAdapter *a, std::string msg); -static void processConnectedDevice(std::shared_ptr<BTDevice> device); static void processReadyDevice(std::shared_ptr<BTDevice> device); static void processDisconnectedDevice(std::shared_ptr<BTDevice> device); @@ -216,9 +215,6 @@ class MyAdapterStatusListener : public AdapterStatusListener { void deviceConnected(std::shared_ptr<BTDevice> device, const uint16_t handle, const uint64_t timestamp) override { fprintf_td(stderr, "****** CONNECTED: %s\n", device->toString(true).c_str()); - std::thread sd(::processConnectedDevice, device); // @suppress("Invalid arguments") - sd.detach(); - (void)handle; (void)timestamp; } @@ -232,9 +228,6 @@ class MyAdapterStatusListener : public AdapterStatusListener { // next: deviceReady(..) break; case SMPPairingState::FAILED: { - const bool res = SMPKeyBin::remove(KEY_PATH, *device); - fprintf_td(stderr, "****** PAIRING_STATE: state %s; Remove key file %s, res %d\n", - to_string(state).c_str(), SMPKeyBin::getFilename(KEY_PATH, *device).c_str(), res); // next: deviceReady() or deviceDisconnected(..) } break; case SMPPairingState::REQUESTED_BY_RESPONDER: @@ -479,16 +472,6 @@ static bool stopAdvertising(BTAdapter *a, std::string msg) { return HCIStatusCode::SUCCESS == status; } -static void processConnectedDevice(std::shared_ptr<BTDevice> device) { - fprintf_td(stderr, "****** Connected Device: Start %s\n", device->toString().c_str()); - - // Already Connected - Can't Do! - // HCIStatusCode res = SMPKeyBin::readAndApply(KEY_PATH, *device, BTSecurityLevel::UNSET, true /* verbose */); - // fprintf_td(stderr, "****** CONNECTED: SMPKeyBin::readAndApply(..) result %s\n", to_string(res).c_str()); - - fprintf_td(stderr, "****** Connected Device: End %s\n", device->toString().c_str()); -} - static void processDisconnectedDevice(std::shared_ptr<BTDevice> device) { fprintf_td(stderr, "****** Disconnected Device: Start %s\n", device->toString().c_str()); @@ -505,8 +488,6 @@ static void processDisconnectedDevice(std::shared_ptr<BTDevice> device) { static void processReadyDevice(std::shared_ptr<BTDevice> device) { fprintf_td(stderr, "****** Processing Ready Device: Start %s\n", device->toString().c_str()); - SMPKeyBin::createAndWrite(*device, KEY_PATH, false /* overwrite */, true /* verbose */); - fprintf_td(stderr, "****** Processing Ready Device: End %s\n", device->toString().c_str()); } @@ -562,11 +543,7 @@ static bool initAdapter(std::shared_ptr<BTAdapter>& adapter) { fprintf_td(stderr, "initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n", to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str()); } - { - std::vector<SMPKeyBin> keys = SMPKeyBin::readAllForLocalAdapter(adapter->getAddressAndType(), KEY_PATH, true /* verbose_ */); - jau::nsize_t count = SMPKeyBin::applyAll(keys, *adapter); - fprintf_td(stderr, "initAdapter: Set %d SMPKeyBin, successfully taken %d SMPKeyBin entries\n", keys.size(), count); - } + adapter->setSMPKeyPath(ADAPTER_KEY_PATH); std::shared_ptr<AdapterStatusListener> asl( std::make_shared<MyAdapterStatusListener>() ); adapter->addStatusListener( asl ); diff --git a/java/jau/direct_bt/DBTAdapter.java b/java/jau/direct_bt/DBTAdapter.java index a6dd1254..a4d36c85 100644 --- a/java/jau/direct_bt/DBTAdapter.java +++ b/java/jau/direct_bt/DBTAdapter.java @@ -274,6 +274,9 @@ public class DBTAdapter extends DBTObject implements BTAdapter public native boolean setSecureConnections(final boolean enable); @Override + public native void setSMPKeyPath(final String path); + + @Override public final HCIStatusCode initialize() { return initialize(BTMode.DUAL); } diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx index 524f2064..9af80f24 100644 --- a/java/jni/direct_bt/DBTAdapter.cxx +++ b/java/jni/direct_bt/DBTAdapter.cxx @@ -910,6 +910,17 @@ jboolean Java_jau_direct_1bt_DBTAdapter_setSecureConnections(JNIEnv *env, jobjec return JNI_FALSE; } +void Java_jau_direct_1bt_DBTAdapter_setSMPKeyPath(JNIEnv *env, jobject obj, jstring jpath) { + try { + BTAdapter *adapter = jau::getJavaUplinkObject<BTAdapter>(env, obj); + jau::JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + std::string path = jau::from_jstring_to_string(env, jpath); + adapter->setSMPKeyPath(path); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + jbyte Java_jau_direct_1bt_DBTAdapter_initializeImpl(JNIEnv *env, jobject obj, jbyte jbtMode) { try { BTAdapter *adapter = jau::getJavaUplinkObject<BTAdapter>(env, obj); diff --git a/java/org/direct_bt/BTAdapter.java b/java/org/direct_bt/BTAdapter.java index f9d222f5..1dc32f17 100644 --- a/java/org/direct_bt/BTAdapter.java +++ b/java/org/direct_bt/BTAdapter.java @@ -477,6 +477,24 @@ public interface BTAdapter extends BTObject boolean setSecureConnections(final boolean enable); /** + * Set the adapter's persistent storage directory for {@link SMPKeyBin} files. + * - if set, all {@link SMPKeyBin} instances will be managed and persistent. + * - if not set, all {@link SMPKeyBin} instances will be transient only. + * + * When called, all keys within the path will be loaded, + * i.e. issuing {@link BTDevice#uploadKeys(SMPKeyBin, BTSecurityLevel) for all keys belonging to this BTAdapter. + * + * Persistent {@link SMPKeyBin} management is only functional when {@link BTAdapter} is in {@link BTRole#Slave} peripheral mode. + * + * For each {@link SMPKeyBin} file one shared {@link BTDevice} in {@link BTRole#Master} will be instantiated + * when uploadKeys() is called. + * + * @param path persistent storage path to {@link SMPKeyBin} files + * @see BTDevice#uploadKeys(SMPKeyBin, BTSecurityLevel) + */ + void setSMPKeyPath(final String path); + + /** * Initialize the adapter with default values, including power-on. * <p> * Method shall be issued on the desired adapter found via {@link BTManager.ChangedAdapterSetListener}. diff --git a/scripts/run-native-example.sh b/scripts/run-native-example.sh index 192c74b3..e7f93e62 100755 --- a/scripts/run-native-example.sh +++ b/scripts/run-native-example.sh @@ -172,6 +172,7 @@ runit() { echo LD_LIBRARY_PATH=`pwd`/lib $EXE_WRAPPER bin/${exename} "$@" mkdir -p keys + mkdir -p dbt_keys if [ "${run_setcap}" -eq "1" ]; then runit_setcap "$@" diff --git a/src/direct_bt/BTAdapter.cpp b/src/direct_bt/BTAdapter.cpp index cec26514..a033ee1c 100644 --- a/src/direct_bt/BTAdapter.cpp +++ b/src/direct_bt/BTAdapter.cpp @@ -336,6 +336,11 @@ void BTAdapter::close() noexcept { const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor sharedDevices.clear(); } + { + const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor + key_list.clear(); + key_path.clear(); + } valid = false; DBG_PRINT("BTAdapter::close: XXX"); } @@ -448,88 +453,59 @@ bool BTAdapter::setSecureConnections(const bool enable) noexcept { return enable == isAdapterSettingBitSet(new_settings, AdapterSetting::SECURE_CONN); } -HCIStatusCode BTAdapter::setSMPKeyBin(const SMPKeyBin& keys) noexcept { - if( keys.getLocalAddrAndType() != adapterInfo.addressAndType ) { - if( keys.getVerbose() ) { +void BTAdapter::setSMPKeyPath(const std::string path) noexcept { + jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc + key_path = path; + + std::vector<SMPKeyBin> keys = SMPKeyBin::readAllForLocalAdapter(getAddressAndType(), key_path, jau::environment::get().debug /* verbose_ */); + for(SMPKeyBin f : keys) { + uploadKeys(f, false /* write */); + } +} + +HCIStatusCode BTAdapter::uploadKeys(SMPKeyBin& bin, const bool write) noexcept { + if( bin.getLocalAddrAndType() != adapterInfo.addressAndType ) { + if( bin.getVerbose() ) { jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Adapter address not matching: %s, %s", - keys.toString().c_str(), toString().c_str()); + bin.toString().c_str(), toString().c_str()); } return HCIStatusCode::INVALID_PARAMS; } - /** - if( isConnected ) { - ERR_PRINT("BTDevice::setLongTermKeyInfo: Already connected: %s", toString().c_str()); - return HCIStatusCode::CONNECTION_ALREADY_EXISTS; - } */ - // FIXME: Pass keys to BTDevice instance after connection! -#if USE_LINUX_BT_SECURITY + EInfoReport ad_report; + { + ad_report.setSource( EInfoReport::Source::NA ); + ad_report.setTimestamp( jau::getCurrentMilliseconds() ); + ad_report.setAddressType( bin.getRemoteAddrAndType().type ); + ad_report.setAddress( bin.getRemoteAddrAndType().address ); + } + // Enforce BTRole::Master on new device, + // since this functionality is only for local being BTRole::Slave peripheral! + std::shared_ptr<BTDevice> device = BTDevice::make_shared(*this, ad_report); + device->btRole = BTRole::Master; + addSharedDevice(device); + HCIStatusCode res; BTManager & mngr = getManager(); - res = mngr.unpairDevice(dev_id, keys.getRemoteAddrAndType(), false /* disconnect */); + res = mngr.unpairDevice(dev_id, bin.getRemoteAddrAndType(), false /* disconnect */); if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) { ERR_PRINT("BTAdapter::setSMPKeyBin: Unpair device failed: %s, %s", - keys.getRemoteAddrAndType().toString().c_str(), toString().c_str()); - } - if( keys.hasLTKInit() ) { - res = mngr.uploadLongTermKey(dev_id, keys.getRemoteAddrAndType(), keys.getLTKInit()); - if( HCIStatusCode::SUCCESS != res ) { - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload LTK initiator failed: %s, %s", - keys.toString().c_str(), toString().c_str()); - } - return res; - } + bin.getRemoteAddrAndType().toString().c_str(), toString().c_str()); } - if( keys.hasLTKResp() ) { - res = mngr.uploadLongTermKey(dev_id, keys.getRemoteAddrAndType(), keys.getLTKResp()); - if( HCIStatusCode::SUCCESS != res ) { - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload LTK responder failed: %s, %s", - keys.toString().c_str(), toString().c_str()); - } - return res; + + res = device->uploadKeys(bin, BTSecurityLevel::NONE); + if( HCIStatusCode::SUCCESS != res ) { + WARN_PRINT("(dev_id %d): Upload SMPKeyBin failed %s, %s (removing file)", + dev_id, to_string(res).c_str(), bin.toString().c_str()); + if( key_path.size() > 0 ) { + bin.remove(key_path); } - } - if( BDAddressType::BDADDR_BREDR != keys.getRemoteAddrAndType().type ) { - // Not supported - DBG_PRINT("BTAdapter::setSMPKeyBin: Upload LK for LE address not supported -> ignored"); + return res; } else { - if( BTRole::Master == btRole ) { - // Remote device is slave (peripheral), we are master (initiator) - if( keys.hasLKInit() ) { - res = mngr.uploadLinkKey(dev_id, keys.getRemoteAddrAndType(), keys.getLKInit()); - if( HCIStatusCode::SUCCESS != res ) { - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload LK initiator failed: %s, %s", - keys.toString().c_str(), toString().c_str()); - } - return res; - } - } - } else { - // Remote device is master (central), we are slave (peripheral) - if( keys.hasLKResp() ) { - res = mngr.uploadLinkKey(dev_id, keys.getRemoteAddrAndType(), keys.getLKResp()); - if( HCIStatusCode::SUCCESS != res ) { - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload LK responder failed: %s, %s", - keys.toString().c_str(), toString().c_str()); - } - return res; - } - } - } - } - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload OK: %s, %s", - keys.toString().c_str(), toString().c_str()); + DBG_PRINT("BTAdapter::setSMPKeyBin(dev_id %d): Upload OK: %s, %s", + dev_id, bin.toString().c_str(), toString().c_str()); } + addSMPKeyBin( std::make_shared<SMPKeyBin>(bin), write); // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS return HCIStatusCode::SUCCESS; -#elif SMP_SUPPORTED_BY_OS - return HCIStatusCode::NOT_SUPPORTED; -#else - return HCIStatusCode::NOT_SUPPORTED; -#endif } HCIStatusCode BTAdapter::initialize(const BTMode btMode) noexcept { @@ -1086,7 +1062,6 @@ bool BTAdapter::removeDiscoveredDevice(const BDAddressAndType & addressAndType) return false; } - int BTAdapter::removeDiscoveredDevices() noexcept { const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc @@ -1117,6 +1092,8 @@ jau::darray<std::shared_ptr<BTDevice>> BTAdapter::getDiscoveredDevices() const n return res; } +// ************************************************* + bool BTAdapter::addSharedDevice(std::shared_ptr<BTDevice> const &device) noexcept { const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor if( nullptr != findDevice(sharedDevices, *device) ) { @@ -1149,6 +1126,8 @@ std::shared_ptr<BTDevice> BTAdapter::findSharedDevice (const EUI48 & address, co return findDevice(sharedDevices, address, addressType); } +// ************************************************* + void BTAdapter::removeDevice(BTDevice & device) noexcept { WORDY_PRINT("DBTAdapter::removeDevice: Start %s", toString().c_str()); removeAllStatusListener(device); @@ -1168,6 +1147,61 @@ void BTAdapter::removeDevice(BTDevice & device) noexcept { // ************************************************* +BTAdapter::SMPKeyBinRef BTAdapter::findSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress) noexcept { + const jau::nsize_t size = keys.size(); + for (jau::nsize_t i = 0; i < size; ++i) { + SMPKeyBinRef& k = keys[i]; + if ( nullptr != k && remoteAddress == k->getRemoteAddrAndType() ) { + return k; + } + } + return nullptr; +} +bool BTAdapter::removeSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress, const bool remove_file, const std::string key_path_) noexcept { + for (auto it = keys.begin(); it != keys.end(); ++it) { + const SMPKeyBinRef& k = *it; + if ( nullptr != k && remoteAddress == k->getRemoteAddrAndType() ) { + DBG_PRINT("BTAdapter::removeSMPKeyBin(file %d): %s", remove_file, k->toString().c_str()); + if( remove_file && key_path_.size() > 0 ) { + if( !k->remove(key_path_) ) { + WARN_PRINT("Failed removal of SMPKeyBin file: %s", k->getFilename(key_path_).c_str()); + } + } + keys.erase(it); + return true; + } + } + return false; +} +BTAdapter::SMPKeyBinRef BTAdapter::findSMPKeyBin(BDAddressAndType const & remoteAddress) noexcept { + const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor + jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc + return findSMPKeyBin(key_list, remoteAddress); +} +bool BTAdapter::addSMPKeyBin(const SMPKeyBinRef& key, const bool write_file) noexcept { + const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor + jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc + removeSMPKeyBin(key_list, key->getRemoteAddrAndType(), write_file /* remove_file */, key_path); + if( jau::environment::get().debug ) { + key->setVerbose(true); + DBG_PRINT("BTAdapter::addSMPKeyBin(file %d): %s", write_file, key->toString().c_str()); + } + key_list.push_back( key ); + if( write_file && key_path.size() > 0 ) { + if( !key->write(key_path, true /* overwrite */) ) { + WARN_PRINT("Failed write of SMPKeyBin file: %s", key->getFilename(key_path).c_str()); + } + } + return true; +} +bool BTAdapter::removeSMPKeyBin(BDAddressAndType const & remoteAddress, const bool remove_file) noexcept { + const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor + jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc + return removeSMPKeyBin(key_list, remoteAddress, remove_file, key_path); +} + +// ************************************************* + HCIStatusCode BTAdapter::startAdvertising(DBGattServerRef gattServerData_, const uint16_t adv_interval_min, const uint16_t adv_interval_max, const AD_PDU_Type adv_type, @@ -1492,6 +1526,7 @@ bool BTAdapter::mgmtEvDeviceConnectedHCI(const MgmtEvent& e) noexcept { ad_report.read_data(event.getData(), event.getDataSize()); } int new_connect = 0; + bool slave_unpair = false; std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType()); if( nullptr == device ) { device = findDiscoveredDevice(event.getAddress(), event.getAddressType()); @@ -1505,25 +1540,30 @@ bool BTAdapter::mgmtEvDeviceConnectedHCI(const MgmtEvent& e) noexcept { if( nullptr != device ) { addDiscoveredDevice(device); // connected devices must be in shared + discovered list new_connect = 2; + slave_unpair = BTRole::Slave == getRole(); } } if( nullptr == device ) { + // (new_connect = 3) a whitelist auto-connect w/o previous discovery, or + // (new_connect = 4) we are a peripheral being connected by a remote client device = BTDevice::make_shared(*this, ad_report); addDiscoveredDevice(device); addSharedDevice(device); - if( BTRole::Slave == getRole() ) { - new_connect = 4; // slave adapter (peripheral), got connected by remote client - /** - * Without unpair in SC mode, the peripheral fails the DHKey Check. - * TODO: Provide a persistent pre-pairing mechanism via SMPKeyBin. - */ - HCIStatusCode res = mgmt.unpairDevice(dev_id, device->getAddressAndType(), false /* disconnect */); + new_connect = BTRole::Master == getRole() ? 3 : 4; + slave_unpair = BTRole::Slave == getRole(); + } + if( slave_unpair ) { + /** + * Without unpair in SC mode (or key pre-pairing), the peripheral fails the DHKey Check. + */ + const SMPKeyBinRef key = findSMPKeyBin( device->getAddressAndType() ); // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS + if( nullptr == key ) { + // No pre-pairing -> unpair + HCIStatusCode res = mgmt.unpairDevice(dev_id, device->getAddressAndType(), false /* disconnect */); if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) { - WARN_PRINT("BTAdapter::EventHCI:DeviceConnected(dev_id %d, new_connect %d): Unpair device failed: %s, %s", + WARN_PRINT("(dev_id %d, new_connect %d): Unpair device failed: %s, %s", dev_id, new_connect, to_string(res).c_str(), device->getAddressAndType().toString().c_str()); } - } else { - new_connect = 3; // master adapter, whitelist auto-connect w/o previous discovery } } @@ -1672,7 +1712,7 @@ bool BTAdapter::mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept { device->toString().c_str()); unlockConnect(*device); - device->notifyDisconnected(); + device->notifyDisconnected(); // -> unpair() removeConnectedDevice(*device); if( !device->isConnSecurityAutoEnabled() ) { @@ -1691,6 +1731,18 @@ bool BTAdapter::mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept { }); removeDiscoveredDevice(device->addressAndType); // ensure device will cause a deviceFound event after disconnect } + if( BTRole::Slave == getRole() ) { + // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS + SMPKeyBinRef key = findSMPKeyBin(device->getAddressAndType()); + if( nullptr != key ) { + const HCIStatusCode res = device->uploadKeys(*key, BTSecurityLevel::NONE); + if( HCIStatusCode::SUCCESS != res ) { + WARN_PRINT("(dev_id %d): Upload SMPKeyBin failed %s, %s (removing file)", + dev_id, to_string(res).c_str(), key->toString().c_str()); + removeSMPKeyBin(device->getAddressAndType(), true /* remove_file */); + } + } + } } else { WORDY_PRINT("BTAdapter::EventHCI:DeviceDisconnected(dev_id %d): Device not tracked: %s", dev_id, event.toString().c_str()); @@ -2080,6 +2132,31 @@ bool BTAdapter::hciSMPMsgCallback(const BDAddressAndType & addressAndType, void BTAdapter::sendDevicePairingState(std::shared_ptr<BTDevice> device, const SMPPairingState state, const PairingMode mode, uint64_t timestamp) noexcept { + if( BTRole::Slave == getRole() ) { + // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS + if( SMPPairingState::COMPLETED == state ) { + // Pairing completed + if( PairingMode::PRE_PAIRED != mode ) { + // newly paired -> store keys + SMPKeyBin key = SMPKeyBin::create(*device); + if( key.isValid() ) { + addSMPKeyBin( std::make_shared<SMPKeyBin>(key), true /* write_file */ ); + } + } else { + // pre-paired, refresh PairingData of BTDevice (perhaps a new instance) + const SMPKeyBinRef key = findSMPKeyBin( device->getAddressAndType() ); + if( nullptr != key ) { + bool res = device->setSMPKeyBin(*key); + if( !res ) { + WARN_PRINT("(dev_id %d): device::setSMPKeyBin() failed %d, %s", res, key->toString().c_str()); + } + } + } + } else if( SMPPairingState::FAILED == state ) { + // Pairing failed + removeSMPKeyBin(device->getAddressAndType(), true /* remove_file */); + } + } int i=0; jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { try { |