aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2021-01-27 22:44:44 +0100
committerSven Gothel <[email protected]>2021-01-27 22:44:44 +0100
commitc8c6f05c17725f84cecef99be0f9186b8b82e536 (patch)
tree059e5320101a1606c03ddae0b2deb711f2e85677
parentf9250fd02eba1a0e2fa2c52457398198549edf1b (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.hpp6
-rw-r--r--examples/direct_bt_scanner10/dbt_scanner10.cpp118
-rw-r--r--examples/java/DBTScanner10.java106
-rw-r--r--java/org/direct_bt/BTFactory.java2
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();
}
}