diff options
author | Sven Gothel <[email protected]> | 2021-01-27 22:44:44 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-01-27 22:44:44 +0100 |
commit | c8c6f05c17725f84cecef99be0f9186b8b82e536 (patch) | |
tree | 059e5320101a1606c03ddae0b2deb711f2e85677 | |
parent | f9250fd02eba1a0e2fa2c52457398198549edf1b (diff) |
Example Scanner10 (C++/Java): Support Security setting per device BDAddressAndType; Tested reconnect reusing stored LTK
Same code in C++ dbt_scanner10.cpp and DBTScanner10.java.
-rw-r--r-- | api/direct_bt/BTAddress.hpp | 6 | ||||
-rw-r--r-- | examples/direct_bt_scanner10/dbt_scanner10.cpp | 118 | ||||
-rw-r--r-- | examples/java/DBTScanner10.java | 106 | ||||
-rw-r--r-- | java/org/direct_bt/BTFactory.java | 2 |
4 files changed, 192 insertions, 40 deletions
diff --git a/api/direct_bt/BTAddress.hpp b/api/direct_bt/BTAddress.hpp index eb1a4715..e57a232a 100644 --- a/api/direct_bt/BTAddress.hpp +++ b/api/direct_bt/BTAddress.hpp @@ -58,6 +58,12 @@ namespace direct_bt { /** Undefined */ BDADDR_UNDEFINED = 0xff }; + constexpr BDAddressType getBDAddressType(const uint8_t v) noexcept { + if( v <= 2 ) { + return static_cast<BDAddressType>(v); + } + return BDAddressType::BDADDR_UNDEFINED; + } constexpr uint8_t number(const BDAddressType rhs) noexcept { return static_cast<uint8_t>(rhs); } diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp index 891febf3..46f6cd88 100644 --- a/examples/direct_bt_scanner10/dbt_scanner10.cpp +++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp @@ -86,9 +86,6 @@ static bool QUIET = false; static jau::darray<BDAddressAndType> waitForDevices; const static int NO_PASSKEY = -1; -static int pairing_passkey = NO_PASSKEY; -static BTSecurityLevel sec_level = BTSecurityLevel::UNSET; -static SMPIOCapability io_capabilities = SMPIOCapability::UNSET; static void connectDiscoveredDevice(std::shared_ptr<BTDevice> device); @@ -199,6 +196,7 @@ struct MyLongTermKeyInfo { bool read(const std::string filename) { std::ifstream file(filename, std::ios::binary); if (!file.is_open() ) { + fprintf(stderr, "****** READ LTK [%s] failed\n", filename.c_str()); return false; } file.read((char*)&address_and_type.address, sizeof(address_and_type.address)); @@ -232,6 +230,7 @@ struct MySignatureResolvingKeyInfo { bool read(const std::string filename) { std::ifstream file(filename, std::ios::binary); if (!file.is_open() ) { + fprintf(stderr, "****** READ CSRK [%s] failed\n", filename.c_str()); return false; } file.read((char*)&address_and_type.address, sizeof(address_and_type.address)); @@ -246,6 +245,67 @@ struct MySignatureResolvingKeyInfo { } }; +struct MyBTSecurityDetail { + BDAddressAndType addrAndType; + + BTSecurityLevel sec_level { BTSecurityLevel::UNSET }; + SMPIOCapability io_cap { SMPIOCapability::UNSET }; + int passkey = NO_PASSKEY; + + MyBTSecurityDetail(const BDAddressAndType& addrAndType_) + : addrAndType(addrAndType_) {} + + const BTSecurityLevel& getSecLevel() { return sec_level; } + + const SMPIOCapability& getIOCapability() { return io_cap; } + + int getPairingPasskey() { return passkey; } + + int getPairingNumericComparison() { return 1; } + + std::string toString() const noexcept { + return "MyBTSecurityDetail["+addrAndType.toString()+", lvl "+ + getBTSecurityLevelString(sec_level)+", io "+getSMPIOCapabilityString(io_cap)+ + ", passkey "+std::to_string(passkey)+"]"; + } + + private: + static std::unordered_map<BDAddressAndType, MyBTSecurityDetail> devicesSecDetail; + + public: + + static MyBTSecurityDetail* get(const BDAddressAndType& addrAndType) { + auto s = devicesSecDetail.find(addrAndType); + if( s != devicesSecDetail.end() ) { + return &(s->second); + } else { + return nullptr; + } + } + static MyBTSecurityDetail* getOrCreate(const BDAddressAndType& addrAndType) { + MyBTSecurityDetail* sec_detail = get(addrAndType); + if( nullptr != sec_detail ) { + return sec_detail; + } else { + auto i = devicesSecDetail.insert( devicesSecDetail.end(), { addrAndType, MyBTSecurityDetail(addrAndType) } ); + return &(i->second); + } + } + static std::string allToString() { + std::string res; + int i=0; + for(auto iter = devicesSecDetail.begin(); iter != devicesSecDetail.end(); ++iter, ++i) { + const MyBTSecurityDetail& sec = iter->second; + if( 0 < i ) { + res += ", "; + } + res += sec.toString(); + } + return res; + } +}; +std::unordered_map<BDAddressAndType, MyBTSecurityDetail> MyBTSecurityDetail::devicesSecDetail; + class MyAdapterStatusListener : public AdapterStatusListener { void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask, @@ -342,8 +402,9 @@ class MyAdapterStatusListener : public AdapterStatusListener { // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION break; case SMPPairingState::PASSKEY_EXPECTED: { - if( pairing_passkey != NO_PASSKEY ) { - std::thread dc(&BTDevice::setPairingPasskey, device, static_cast<uint32_t>(pairing_passkey)); // @suppress("Invalid arguments") + const MyBTSecurityDetail* sec = MyBTSecurityDetail::get(device->getAddressAndType()); + if( nullptr != sec && sec->passkey != NO_PASSKEY ) { + std::thread dc(&BTDevice::setPairingPasskey, device, static_cast<uint32_t>(sec->passkey)); // @suppress("Invalid arguments") dc.detach(); } /* else { std::thread dc(&BTDevice::setPairingPasskeyNegative, device); // @suppress("Invalid arguments") @@ -471,12 +532,19 @@ static void connectDiscoveredDevice(std::shared_ptr<BTDevice> device) { HCIStatusCode::SUCCESS == device->setLongTermKeyInfo(my_ltk_init.smp_ltk) && HCIStatusCode::SUCCESS == device->setLongTermKeyInfo(my_ltk_resp.smp_ltk) ) { fprintf(stderr, "****** Connecting Device: Loaded LTKs from file successfully\n"); + } + } + { + // Always reuse same sec setting if reusing LTK + const MyBTSecurityDetail* sec = MyBTSecurityDetail::get(device->getAddressAndType()); + if( nullptr != sec ) { + bool res = device->setConnSecurityBest(sec->sec_level, sec->io_cap); + fprintf(stderr, "****** Connecting Device: Using SecurityDetail %s, set OK %d\n", sec->toString().c_str(), res); } else { - fprintf(stderr, "****** Connecting Device: Error loading LTKs from file\n"); + fprintf(stderr, "****** Connecting Device: No SecurityDetail for %s\n", device->getAddressAndType().toString().c_str()); } } - device->setConnSecurityBest(sec_level, io_capabilities); HCIStatusCode res; if( !USE_WHITELIST ) { @@ -866,12 +934,27 @@ int main(int argc, char *argv[]) fprintf(stderr, "Whitelist + %s\n", wle.toString().c_str()); WHITELIST.push_back( wle ); USE_WHITELIST = true; - } else if( !strcmp("-passkey", argv[i]) && argc > (i+1) ) { - pairing_passkey = atoi(argv[++i]); - } else if( !strcmp("-seclevel", argv[i]) && argc > (i+1) ) { - sec_level = getBTSecurityLevel(atoi(argv[++i])); - } else if( !strcmp("-iocap", argv[i]) && argc > (i+1) ) { - io_capabilities = getSMPIOCapability(atoi(argv[++i])); + } else if( !strcmp("-passkey", argv[i]) && argc > (i+3) ) { + const char* mac = argv[++i]; + const uint8_t atype = (uint8_t) ( atoi(argv[++i]) & 0xff ); + const BDAddressAndType macAndType(EUI48(mac), getBDAddressType(atype)); + MyBTSecurityDetail* sec = MyBTSecurityDetail::getOrCreate(macAndType); + sec->passkey = atoi(argv[++i]); + fprintf(stderr, "Set passkey in %s\n", sec->toString().c_str()); + } else if( !strcmp("-seclevel", argv[i]) && argc > (i+3) ) { + const char* mac = argv[++i]; + const uint8_t atype = (uint8_t) ( atoi(argv[++i]) & 0xff ); + const BDAddressAndType macAndType(EUI48(mac), getBDAddressType(atype)); + MyBTSecurityDetail* sec = MyBTSecurityDetail::getOrCreate(macAndType); + sec->sec_level = getBTSecurityLevel(atoi(argv[++i])); + fprintf(stderr, "Set sec_level in %s\n", sec->toString().c_str()); + } else if( !strcmp("-iocap", argv[i]) && argc > (i+3) ) { + const char* mac = argv[++i]; + const uint8_t atype = (uint8_t) ( atoi(argv[++i]) & 0xff ); + const BDAddressAndType macAndType(EUI48(mac), getBDAddressType(atype)); + MyBTSecurityDetail* sec = MyBTSecurityDetail::getOrCreate(macAndType); + sec->io_cap = getSMPIOCapability(atoi(argv[++i])); + fprintf(stderr, "Set io_cap in %s\n", sec->toString().c_str()); } else if( !strcmp("-unpairPre", argv[i]) ) { UNPAIR_DEVICE_PRE = true; } else if( !strcmp("-unpairPost", argv[i]) ) { @@ -900,7 +983,9 @@ int main(int argc, char *argv[]) "[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] " "[-resetEachCon connectionCount] " "(-mac <device_address>)* (-wl <device_address>)* " - "[-seclevel <int>] [-iocap <int>] [-passkey <digits>] " + "[-seclevel <device_address> <(int)address_type> <int>] " + "[-iocap <device_address> <(int)address_type> <int>] " + "[-passkey <device_address> <(int)address_type> <digits>] " "[-unpairPre] [-unpairPost] " "[-charid <uuid>] [-charval <byte-val>] " "[-dbt_verbose true|false] " @@ -920,16 +1005,15 @@ int main(int argc, char *argv[]) fprintf(stderr, "SHOW_UPDATE_EVENTS %d\n", SHOW_UPDATE_EVENTS); fprintf(stderr, "QUIET %d\n", QUIET); fprintf(stderr, "btmode %s\n", getBTModeString(btMode).c_str()); - fprintf(stderr, "passkey %d\n", pairing_passkey); - fprintf(stderr, "seclevel %s\n", getBTSecurityLevelString(sec_level).c_str()); - fprintf(stderr, "iocap %s\n", getSMPIOCapabilityString(io_capabilities).c_str()); fprintf(stderr, "UNPAIR_DEVICE_PRE %d\n", UNPAIR_DEVICE_PRE); fprintf(stderr, "UNPAIR_DEVICE_POST %d\n", UNPAIR_DEVICE_POST); fprintf(stderr, "characteristic-id: %s\n", charIdentifier.c_str()); fprintf(stderr, "characteristic-value: %d\n", charValue); + fprintf(stderr, "security-details: %s\n", MyBTSecurityDetail::allToString().c_str()); printList( "waitForDevice: ", waitForDevices); + if( waitForEnter ) { fprintf(stderr, "Press ENTER to continue\n"); getchar(); diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java index 08ae5e95..d37ed4de 100644 --- a/examples/java/DBTScanner10.java +++ b/examples/java/DBTScanner10.java @@ -34,13 +34,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import java.util.function.Predicate; import org.direct_bt.AdapterSettings; import org.direct_bt.AdapterStatusListener; @@ -87,9 +86,6 @@ public class DBTScanner10 { final List<BDAddressAndType> waitForDevices = new ArrayList<BDAddressAndType>(); static final int NO_PASSKEY = -1; - int pairing_passkey = NO_PASSKEY; - BTSecurityLevel sec_level = BTSecurityLevel.UNSET; - SMPIOCapability io_capabilities = SMPIOCapability.UNSET; long timestamp_t0; @@ -183,6 +179,7 @@ public class DBTScanner10 { println("****** WRITE LTK ["+address_and_type+", valid, written]: "+smp_ltk); return true; } catch (final Exception ex) { + println("****** WRITE LTK "+address_and_type+" failed: "+ex.getMessage()); ex.printStackTrace(); } finally { try { @@ -212,7 +209,7 @@ public class DBTScanner10 { println("****** READ LTK ["+address_and_type+", valid "+smp_ltk.isValid()+"]: "+smp_ltk); return smp_ltk.isValid(); } catch (final Exception ex) { - ex.printStackTrace(); + println("****** READ LTK ["+filename+"] failed: "+ex.getMessage()); } finally { try { if( null != in ) { @@ -252,6 +249,7 @@ public class DBTScanner10 { println("****** WRITE CSRK ["+address_and_type+", written]: "+smp_csrk); return true; } catch (final Exception ex) { + println("****** WRITE CSRK "+address_and_type+" failed: "+ex.getMessage()); ex.printStackTrace(); } finally { try { @@ -278,10 +276,10 @@ public class DBTScanner10 { address_and_type.address.putStream(buffer, 0); address_and_type.type = BDAddressType.get(buffer[6]); smp_csrk.putStream(buffer, 6+1); - println("****** READ CSRK ["+address_and_type+"]: "+smp_csrk); + println("****** READ CSRK "+address_and_type+": "+smp_csrk); return true; } catch (final Exception ex) { - ex.printStackTrace(); + println("****** READ CSRK ["+filename+"] failed: "+ex.getMessage()); } finally { try { if( null != in ) { @@ -294,6 +292,44 @@ public class DBTScanner10 { return false; } } + static public class MyBTSecurityDetail { + public final BDAddressAndType addrAndType; + + BTSecurityLevel sec_level = BTSecurityLevel.UNSET; + SMPIOCapability io_cap = SMPIOCapability.UNSET; + int passkey = NO_PASSKEY; + + public MyBTSecurityDetail(final BDAddressAndType addrAndType) { this.addrAndType = addrAndType; } + + public BTSecurityLevel getSecLevel() { return sec_level; } + + public SMPIOCapability getIOCapability() { return io_cap; } + + public int getPairingPasskey() { return passkey; } + + public int getPairingNumericComparison() { return 1; } + + @Override + public String toString() { + return "BTSecurityDetail["+addrAndType+", lvl "+sec_level+", io "+io_cap+", passkey "+passkey+"]"; + } + + static private HashMap<BDAddressAndType, MyBTSecurityDetail> devicesSecDetail = new HashMap<BDAddressAndType, MyBTSecurityDetail>(); + + static public MyBTSecurityDetail get(final BDAddressAndType addrAndType) { + return devicesSecDetail.get(addrAndType); + } + static public MyBTSecurityDetail getOrCreate(final BDAddressAndType addrAndType) { + MyBTSecurityDetail sec_detail = devicesSecDetail.get(addrAndType); + if( null == sec_detail ) { + sec_detail = new MyBTSecurityDetail(addrAndType); + devicesSecDetail.put(addrAndType, sec_detail); + } + return sec_detail; + } + static public String allToString() { return Arrays.toString( devicesSecDetail.values().toArray() ); } + } + Collection<BDAddressAndType> devicesInProcessing = Collections.synchronizedCollection(new HashSet<>()); Collection<BDAddressAndType> devicesProcessed = Collections.synchronizedCollection(new HashSet<>()); @@ -388,8 +424,9 @@ public class DBTScanner10 { // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION break; case PASSKEY_EXPECTED: { - if( pairing_passkey != NO_PASSKEY ) { - executeOffThread( () -> { device.setPairingPasskey(pairing_passkey); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */); + final MyBTSecurityDetail sec = MyBTSecurityDetail.get(device.getAddressAndType()); + if( null != sec && sec.passkey != NO_PASSKEY ) { + executeOffThread( () -> { device.setPairingPasskey(sec.passkey); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */); } // next: KEY_DISTRIBUTION or FAILED } break; @@ -471,11 +508,18 @@ public class DBTScanner10 { HCIStatusCode.SUCCESS == device.setLongTermKeyInfo(my_ltk_init.smp_ltk) && HCIStatusCode.SUCCESS == device.setLongTermKeyInfo(my_ltk_resp.smp_ltk) ) { println("****** Connecting Device: Loaded LTKs from file successfully\n"); + } + } + { + // Always reuse same sec setting if reusing LTK + final MyBTSecurityDetail sec = MyBTSecurityDetail.get(device.getAddressAndType()); + if( null != sec ) { + final boolean res = device.setConnSecurityBest(sec.sec_level, sec.io_cap); + println("****** Connecting Device: Using SecurityDetail "+sec+" -> set OK "+res); } else { - println("****** Connecting Device: Error loading LTKs from file\n"); + println("****** Connecting Device: No SecurityDetail for "+device.getAddressAndType()); } } - device.setConnSecurityBest(sec_level, io_capabilities); HCIStatusCode res; if( !USE_WHITELIST ) { @@ -938,12 +982,29 @@ public class DBTScanner10 { println("Whitelist + "+wle); test.whitelist.add(wle); test.USE_WHITELIST = true; - } else if( arg.equals("-passkey") && args.length > (i+1) ) { - test.pairing_passkey = Integer.valueOf(args[++i]).intValue(); - } else if( arg.equals("-seclevel") && args.length > (i+1) ) { - test.sec_level = BTSecurityLevel.get( (byte)Integer.valueOf(args[++i]).intValue() ); - } else if( arg.equals("-iocap") && args.length > (i+1) ) { - test.io_capabilities = SMPIOCapability.get( (byte)Integer.valueOf(args[++i]).intValue() ); + } else if( arg.equals("-passkey") && args.length > (i+3) ) { + final String mac = args[++i]; + final byte atype = (byte) ( Integer.valueOf(args[++i]).intValue() & 0xff ); + final BDAddressAndType macAndType = new BDAddressAndType(new EUI48(mac), BDAddressType.get(atype)); + final MyBTSecurityDetail sec = MyBTSecurityDetail.getOrCreate(macAndType); + sec.passkey = Integer.valueOf(args[++i]).intValue(); + System.err.println("Set passkey in "+sec); + } else if( arg.equals("-seclevel") && args.length > (i+3) ) { + final String mac = args[++i]; + final byte atype = (byte) ( Integer.valueOf(args[++i]).intValue() & 0xff ); + final BDAddressAndType macAndType = new BDAddressAndType(new EUI48(mac), BDAddressType.get(atype)); + final MyBTSecurityDetail sec = MyBTSecurityDetail.getOrCreate(macAndType); + final int sec_level_i = Integer.valueOf(args[++i]).intValue(); + sec.sec_level = BTSecurityLevel.get( (byte)( sec_level_i & 0xff ) ); + System.err.println("Set sec_level "+sec_level_i+" in "+sec); + } else if( arg.equals("-iocap") && args.length > (i+3) ) { + final String mac = args[++i]; + final byte atype = (byte) ( Integer.valueOf(args[++i]).intValue() & 0xff ); + final BDAddressAndType macAndType = new BDAddressAndType(new EUI48(mac), BDAddressType.get(atype)); + final MyBTSecurityDetail sec = MyBTSecurityDetail.getOrCreate(macAndType); + final int io_cap_i = Integer.valueOf(args[++i]).intValue(); + sec.io_cap = SMPIOCapability.get( (byte)( io_cap_i & 0xff ) ); + System.err.println("Set io_cap "+io_cap_i+" in "+sec); } else if( arg.equals("-unpairPre") ) { test.UNPAIR_DEVICE_PRE = true; } else if( arg.equals("-unpairPost") ) { @@ -971,7 +1032,9 @@ public class DBTScanner10 { "[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] "+ "[-resetEachCon connectionCount] "+ "(-mac <device_address>)* (-wl <device_address>)* "+ - "[-seclevel <int>] [-iocap <int>] [-passkey <digits>] " + + "[-seclevel <device_address> <(int)address_type> <int>] "+ + "[-iocap <device_address> <(int)address_type> <int>] "+ + "[-passkey <device_address> <(int)address_type> <digits>] " + "[-unpairPre] [-unpairPost] "+ "[-charid <uuid>] [-charval <byte-val>] "+ "[-verbose] [-debug] "+ @@ -992,14 +1055,13 @@ public class DBTScanner10 { println("USE_WHITELIST "+test.USE_WHITELIST); println("SHOW_UPDATE_EVENTS "+test.SHOW_UPDATE_EVENTS); println("QUIET "+test.QUIET); - println("passkey "+test.pairing_passkey); - println("seclevel "+test.sec_level); - println("iocap "+test.io_capabilities); println("UNPAIR_DEVICE_PRE "+ test.UNPAIR_DEVICE_PRE); println("UNPAIR_DEVICE_POST "+ test.UNPAIR_DEVICE_POST); println("characteristic-id: "+test.charIdentifier); println("characteristic-value: "+test.charValue); + println("security-details: "+MyBTSecurityDetail.allToString() ); + println("waitForDevice: "+Arrays.toString(test.waitForDevices.toArray())); if( waitForEnter ) { diff --git a/java/org/direct_bt/BTFactory.java b/java/org/direct_bt/BTFactory.java index eca45f31..44fc11b9 100644 --- a/java/org/direct_bt/BTFactory.java +++ b/java/org/direct_bt/BTFactory.java @@ -203,8 +203,8 @@ public class BTFactory { try { isJaulibAvail = null != Class.forName("org.jau.sys.PlatformProps", true /* initializeClazz */, BTFactory.class.getClassLoader()); } catch( final Throwable t ) { - System.err.println("Caught: "+t.getMessage()); if( DEBUG ) { + System.err.println("BTFactory Caught: "+t.getMessage()); t.printStackTrace(); } } |