diff options
-rw-r--r-- | api/direct_bt/DBTDevice.hpp | 24 | ||||
-rw-r--r-- | api/direct_bt/SMPTypes.hpp | 62 | ||||
-rw-r--r-- | examples/direct_bt_scanner10/dbt_scanner10.cpp | 47 | ||||
-rw-r--r-- | examples/java/DBTScanner10.java | 109 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTDevice.java | 16 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTDevice.cxx | 36 | ||||
-rw-r--r-- | java/org/tinyb/BluetoothDevice.java | 22 | ||||
-rw-r--r-- | java/org/tinyb/SMPKeyMask.java | 165 | ||||
-rw-r--r-- | java/org/tinyb/SMPSignatureResolvingKeyInfo.java | 187 | ||||
-rw-r--r-- | java/tinyb/dbus/DBusDevice.java | 8 | ||||
-rw-r--r-- | src/direct_bt/DBTDevice.cpp | 80 | ||||
-rw-r--r-- | src/direct_bt/SMPTypes.cpp | 38 |
12 files changed, 714 insertions, 80 deletions
diff --git a/api/direct_bt/DBTDevice.hpp b/api/direct_bt/DBTDevice.hpp index 22772080..ae363599 100644 --- a/api/direct_bt/DBTDevice.hpp +++ b/api/direct_bt/DBTDevice.hpp @@ -95,8 +95,8 @@ namespace direct_bt { SMPIOCapability ioCap_init, ioCap_resp; SMPOOBDataFlag oobFlag_init, oobFlag_resp; uint8_t maxEncsz_init, maxEncsz_resp; - SMPKeyDist keys_init_exp, keys_resp_exp; - SMPKeyDist keys_init_has, keys_resp_has; + SMPKeyType keys_init_exp, keys_resp_exp; + SMPKeyType keys_init_has, keys_resp_has; // LTK: Set of Long Term Key data: ltk, ediv + rand SMPLongTermKeyInfo ltk_init, ltk_resp; @@ -107,7 +107,7 @@ namespace direct_bt { bool is_static_random_address; // CSRK - jau::uint128_t csrk_init, csrk_resp; + SMPSignatureResolvingKeyInfo csrk_init, csrk_resp; }; PairingData pairing_data; std::mutex mtx_pairing; @@ -447,7 +447,14 @@ namespace direct_bt { HCIStatusCode disconnect(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION ) noexcept; /** - * Returns a copy of the long term ket (LTK) info, valid after connection and SMP pairing has been completed. + * Returns the available ::SMPKeyType mask for the responder (LL slave) or initiator (LL master). + * @param responder if true, queries the responder (LL slave) key, otherwise the initiator (LL master) key. + * @return ::SMPKeyType mask result + */ + SMPKeyType getAvailableSMPKeys(const bool responder) const noexcept; + + /** + * Returns a copy of the Long Term Key (LTK) info, valid after connection and SMP pairing has been completed. * @param responder true will return the responder's LTK info (remote device, LL slave), otherwise the initiator's (the LL master). * @return the resulting key. SMPLongTermKeyInfo::enc_size will be zero if invalid. * @see ::SMPPairingState::COMPLETED @@ -466,6 +473,15 @@ namespace direct_bt { HCIStatusCode setLongTermKeyInfo(const SMPLongTermKeyInfo& ltk) noexcept; /** + * Returns a copy of the Signature Resolving Key (LTK) info, valid after connection and SMP pairing has been completed. + * @param responder true will return the responder's LTK info (remote device, LL slave), otherwise the initiator's (the LL master). + * @return the resulting key + * @see ::SMPPairingState::COMPLETED + * @see AdapterStatusListener::deviceReady() + */ + SMPSignatureResolvingKeyInfo getSignatureResolvingKeyInfo(const bool responder) const noexcept; + + /** * Unpairs this device from the adapter while staying connected. * <p> * All keys will be cleared within the adapter and host implementation.<br> diff --git a/api/direct_bt/SMPTypes.hpp b/api/direct_bt/SMPTypes.hpp index 42d709d8..2b30ffc4 100644 --- a/api/direct_bt/SMPTypes.hpp +++ b/api/direct_bt/SMPTypes.hpp @@ -331,7 +331,7 @@ namespace direct_bt { const SMPIOCapability ioCap_ini, const SMPIOCapability ioCap_res) noexcept; /** - * SMP Key Distribution, indicates keys distributed in the Transport Specific Key Distribution phase. + * SMP Key Type for Distribution, indicates keys distributed in the Transport Specific Key Distribution phase. * <pre> * Field format and usage: Vol 3, Part H, 3.6.1 SMP - LE Security - Key distribution and generation. * See also Vol 3, Part H, 2.4.3 SM - LE Security - Distribution of keys. @@ -342,7 +342,7 @@ namespace direct_bt { * uint8_t EncKey : 1, IdKey : 1, SignKey : 1, LinkKey : 1, RFU : 4; * </pre> */ - enum class SMPKeyDist : uint8_t { + enum class SMPKeyType : uint8_t { NONE = 0, /** * LE legacy pairing: Indicates device shall distribute LTK using the Encryption Information command, @@ -386,41 +386,41 @@ namespace direct_bt { /** Reserved for future use */ RFU_4 = 0b10000000 }; - constexpr SMPKeyDist operator ^(const SMPKeyDist lhs, const SMPKeyDist rhs) noexcept { - return static_cast<SMPKeyDist> ( static_cast<uint8_t>(lhs) ^ static_cast<uint8_t>(rhs) ); + constexpr SMPKeyType operator ^(const SMPKeyType lhs, const SMPKeyType rhs) noexcept { + return static_cast<SMPKeyType> ( static_cast<uint8_t>(lhs) ^ static_cast<uint8_t>(rhs) ); } - constexpr SMPKeyDist& operator ^=(SMPKeyDist& store, const SMPKeyDist& rhs) noexcept { - store = static_cast<SMPKeyDist> ( static_cast<uint8_t>(store) ^ static_cast<uint8_t>(rhs) ); + constexpr SMPKeyType& operator ^=(SMPKeyType& store, const SMPKeyType& rhs) noexcept { + store = static_cast<SMPKeyType> ( static_cast<uint8_t>(store) ^ static_cast<uint8_t>(rhs) ); return store; } - constexpr SMPKeyDist operator |(const SMPKeyDist lhs, const SMPKeyDist rhs) noexcept { - return static_cast<SMPKeyDist> ( static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs) ); + constexpr SMPKeyType operator |(const SMPKeyType lhs, const SMPKeyType rhs) noexcept { + return static_cast<SMPKeyType> ( static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs) ); } - constexpr SMPKeyDist& operator |=(SMPKeyDist& store, const SMPKeyDist& rhs) noexcept { - store = static_cast<SMPKeyDist> ( static_cast<uint8_t>(store) | static_cast<uint8_t>(rhs) ); + constexpr SMPKeyType& operator |=(SMPKeyType& store, const SMPKeyType& rhs) noexcept { + store = static_cast<SMPKeyType> ( static_cast<uint8_t>(store) | static_cast<uint8_t>(rhs) ); return store; } - constexpr SMPKeyDist operator &(const SMPKeyDist lhs, const SMPKeyDist rhs) noexcept { - return static_cast<SMPKeyDist> ( static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs) ); + constexpr SMPKeyType operator &(const SMPKeyType lhs, const SMPKeyType rhs) noexcept { + return static_cast<SMPKeyType> ( static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs) ); } - constexpr SMPKeyDist& operator &=(SMPKeyDist& store, const SMPKeyDist& rhs) noexcept { - store = static_cast<SMPKeyDist> ( static_cast<uint8_t>(store) & static_cast<uint8_t>(rhs) ); + constexpr SMPKeyType& operator &=(SMPKeyType& store, const SMPKeyType& rhs) noexcept { + store = static_cast<SMPKeyType> ( static_cast<uint8_t>(store) & static_cast<uint8_t>(rhs) ); return store; } - constexpr bool operator ==(const SMPKeyDist lhs, const SMPKeyDist rhs) noexcept { + constexpr bool operator ==(const SMPKeyType lhs, const SMPKeyType rhs) noexcept { return static_cast<uint8_t>(lhs) == static_cast<uint8_t>(rhs); } - constexpr bool operator !=(const SMPKeyDist lhs, const SMPKeyDist rhs) noexcept { + constexpr bool operator !=(const SMPKeyType lhs, const SMPKeyType rhs) noexcept { return !( lhs == rhs ); } - constexpr uint8_t number(const SMPKeyDist rhs) noexcept { + constexpr uint8_t number(const SMPKeyType rhs) noexcept { return static_cast<uint8_t>(rhs); } - constexpr bool isKeyDistBitSet(const SMPKeyDist mask, const SMPKeyDist bit) noexcept { - return SMPKeyDist::NONE != ( mask & bit ); + constexpr bool isKeyDistBitSet(const SMPKeyType mask, const SMPKeyType bit) noexcept { + return SMPKeyType::NONE != ( mask & bit ); } - std::string getSMPKeyDistBitString(const SMPKeyDist bit) noexcept; - std::string getSMPKeyDistMaskString(const SMPKeyDist mask) noexcept; + std::string getSMPKeyTypeBitString(const SMPKeyType bit) noexcept; + std::string getSMPKeyTypeMaskString(const SMPKeyType mask) noexcept; /** * SMP Long Term Key Info, used for platform agnostic persistence. @@ -822,16 +822,16 @@ namespace direct_bt { private: const bool request; const SMPAuthReqs authReqMask; - const SMPKeyDist initiator_key_dist; - const SMPKeyDist responder_key_dist; + const SMPKeyType initiator_key_dist; + const SMPKeyType responder_key_dist; public: SMPPairingMsg(const bool request_, const uint8_t* source, const jau::nsize_t length) : SMPPDUMsg(source, length), request(request_), authReqMask(static_cast<SMPAuthReqs>( pdu.get_uint8_nc(3) )), - initiator_key_dist(static_cast<SMPKeyDist>(pdu.get_uint8_nc(5))), - responder_key_dist(static_cast<SMPKeyDist>(pdu.get_uint8_nc(6))) + initiator_key_dist(static_cast<SMPKeyType>(pdu.get_uint8_nc(5))), + responder_key_dist(static_cast<SMPKeyType>(pdu.get_uint8_nc(6))) { checkOpcode(request? Opcode::PAIRING_REQUEST : Opcode::PAIRING_RESPONSE); } @@ -839,8 +839,8 @@ namespace direct_bt { SMPPairingMsg(const bool request_, const SMPIOCapability ioc, const SMPOOBDataFlag odf, const SMPAuthReqs auth_req_mask, const uint8_t maxEncKeySize, - const SMPKeyDist initiator_key_dist_, - const SMPKeyDist responder_key_dist_) + const SMPKeyType initiator_key_dist_, + const SMPKeyType responder_key_dist_) : SMPPDUMsg(request_? Opcode::PAIRING_REQUEST : Opcode::PAIRING_RESPONSE, 1+6), request(request_), authReqMask(auth_req_mask), initiator_key_dist(initiator_key_dist_), responder_key_dist(responder_key_dist_) @@ -911,7 +911,7 @@ namespace direct_bt { * </pre> * @see SMPKeyDistFormat */ - constexpr SMPKeyDist getInitKeyDist() const noexcept { + constexpr SMPKeyType getInitKeyDist() const noexcept { return initiator_key_dist; } /** @@ -924,7 +924,7 @@ namespace direct_bt { * </p> * @see SMPKeyDistFormat */ - constexpr SMPKeyDist getRespKeyDist() const noexcept { + constexpr SMPKeyType getRespKeyDist() const noexcept { return responder_key_dist; } @@ -938,8 +938,8 @@ namespace direct_bt { ", oob "+getSMPOOBDataFlagString(getOOBDataFlag())+ ", auth_req "+getSMPAuthReqMaskString(getAuthReqMask())+ ", max_keysz "+std::to_string(getMaxEncryptionKeySize())+ - ", key_dist[init "+getSMPKeyDistMaskString(getInitKeyDist())+ - ", resp "+getSMPKeyDistMaskString(getRespKeyDist())+ + ", key_dist[init "+getSMPKeyTypeMaskString(getInitKeyDist())+ + ", resp "+getSMPKeyTypeMaskString(getRespKeyDist())+ "]"; } }; diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp index 1336c71e..99b85e89 100644 --- a/examples/direct_bt_scanner10/dbt_scanner10.cpp +++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp @@ -195,6 +195,35 @@ __pack( struct MyLongTermKeyInfo { } } ); +__pack( struct MySignatureResolvingKeyInfo { + EUI48 address; + BDAddressType address_type; + SMPSignatureResolvingKeyInfo smp_csrk; + + bool write(const std::string filename) { + std::ofstream file(filename, std::ios::binary | std::ios::trunc); + file.write((char*)this, sizeof(*this)); + file.close(); + fprintf(stderr, "****** WRITE CSRK [%s %s, written]: %s\n", + address.toString().c_str(), getBDAddressTypeString(address_type).c_str(), + smp_csrk.toString().c_str()); + return true; + } + + bool read(const std::string filename) { + std::ifstream file(filename, std::ios::binary); + if (!file.is_open() ) { + return false; + } + file.read((char*)this, sizeof(*this)); + file.close(); + fprintf(stderr, "****** READ CSRK [%s %s]: %s\n", + address.toString().c_str(), getBDAddressTypeString(address_type).c_str(), + smp_csrk.toString().c_str()); + return true; + } +} ); + class MyAdapterStatusListener : public AdapterStatusListener { void adapterSettingsChanged(DBTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask, @@ -448,16 +477,30 @@ static void processReadyDevice(std::shared_ptr<DBTDevice> device) { const SMPPairingState pstate = device->getPairingState(); const PairingMode pmode = device->getPairingMode(); // Skip PairingMode::PRE_PAIRED (write again) if( SMPPairingState::COMPLETED == pstate && PairingMode::PRE_PAIRED != pmode ) { - { + const SMPKeyType keys_resp = device->getAvailableSMPKeys(true /* responder */); + const SMPKeyType keys_init = device->getAvailableSMPKeys(false /* responder */); + + if( ( SMPKeyType::ENC_KEY & keys_init ) != SMPKeyType::NONE ) { MyLongTermKeyInfo my_ltk { device->getAddress(), device->getAddressType(), device->getLongTermKeyInfo(false /* responder */) }; my_ltk.write(my_ltk.address.toString()+".init.ltk"); } - { + if( ( SMPKeyType::ENC_KEY & keys_resp ) != SMPKeyType::NONE ) { MyLongTermKeyInfo my_ltk { device->getAddress(), device->getAddressType(), device->getLongTermKeyInfo(true /* responder */) }; my_ltk.write(my_ltk.address.toString()+".resp.ltk"); } + + if( ( SMPKeyType::SIGN_KEY & keys_init ) != SMPKeyType::NONE ) { + MySignatureResolvingKeyInfo my_csrk { device->getAddress(), device->getAddressType(), + device->getSignatureResolvingKeyInfo(false /* responder */) }; + my_csrk.write(my_csrk.address.toString()+".init.csrk"); + } + if( ( SMPKeyType::SIGN_KEY & keys_resp ) != SMPKeyType::NONE ) { + MySignatureResolvingKeyInfo my_csrk { device->getAddress(), device->getAddressType(), + device->getSignatureResolvingKeyInfo(true /* responder */) }; + my_csrk.write(my_csrk.address.toString()+".resp.csrk"); + } } } #if 1 diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java index f995bce8..c5b1ebeb 100644 --- a/examples/java/DBTScanner10.java +++ b/examples/java/DBTScanner10.java @@ -65,8 +65,10 @@ import org.tinyb.HCIStatusCode; import org.tinyb.HCIWhitelistConnectType; import org.tinyb.PairingMode; import org.tinyb.SMPIOCapability; +import org.tinyb.SMPKeyMask; import org.tinyb.SMPLongTermKeyInfo; import org.tinyb.SMPPairingState; +import org.tinyb.SMPSignatureResolvingKeyInfo; import org.tinyb.ScanType; import direct_bt.tinyb.DBTManager; @@ -144,6 +146,11 @@ public class DBTScanner10 { BluetoothAddressType address_type; SMPLongTermKeyInfo smp_ltk; + MyLongTermKeyInfo(final EUI48 address, final BluetoothAddressType address_type, final SMPLongTermKeyInfo smp_ltk) { + this.address = address; + this.address_type = address_type; + this.smp_ltk = smp_ltk; + } MyLongTermKeyInfo() { address = new EUI48(); address_type = BluetoothAddressType.BDADDR_UNDEFINED; @@ -210,6 +217,78 @@ public class DBTScanner10 { return false; } } + static public class MySignatureResolvingKeyInfo { + EUI48 address; + BluetoothAddressType address_type; + SMPSignatureResolvingKeyInfo smp_csrk; + + MySignatureResolvingKeyInfo(final EUI48 address, final BluetoothAddressType address_type, final SMPSignatureResolvingKeyInfo smp_csrk) { + this.address = address; + this.address_type = address_type; + this.smp_csrk = smp_csrk; + } + MySignatureResolvingKeyInfo() { + address = new EUI48(); + address_type = BluetoothAddressType.BDADDR_UNDEFINED; + smp_csrk = new SMPSignatureResolvingKeyInfo(); + } + + boolean write(final String filename) { + final File file = new File(filename); + OutputStream out = null; + try { + file.delete(); // alternative to truncate, if existing + out = new FileOutputStream(file); + out.write(address.b); + out.write(address_type.value); + final byte[] smp_ltk_b = new byte[SMPSignatureResolvingKeyInfo.byte_size]; + smp_csrk.getStream(smp_ltk_b, 0); + out.write(smp_ltk_b); + println("****** WRITE CSRK ["+address+" "+address_type+", written]: "+smp_csrk); + return true; + } catch (final Exception ex) { + ex.printStackTrace(); + } finally { + try { + if( null != out ) { + out.close(); + } + } catch (final IOException e) { + e.printStackTrace(); + } + } + return false; + } + + boolean read(final String filename) { + final File file = new File(filename); + InputStream in = null; + try { + final byte[] buffer = new byte[6 + 1 + SMPSignatureResolvingKeyInfo.byte_size]; + in = new FileInputStream(file); + final int read_count = in.read(buffer, 0, buffer.length); + if( read_count != buffer.length ) { + throw new IOException("Couldn't read "+buffer.length+" bytes, only "+read_count+" from "+filename); + } + address.putStream(buffer, 0); + address_type = BluetoothAddressType.get(buffer[6]); + smp_csrk.putStream(buffer, 6+1); + println("****** READ CSRK ["+address+" "+address_type+"]: "+smp_csrk); + return true; + } catch (final Exception ex) { + ex.printStackTrace(); + } finally { + try { + if( null != in ) { + in.close(); + } + } catch (final IOException e) { + e.printStackTrace(); + } + } + return false; + } + } Collection<EUI48> devicesInProcessing = Collections.synchronizedCollection(new ArrayList<>()); Collection<EUI48> devicesProcessed = Collections.synchronizedCollection(new ArrayList<>()); @@ -442,20 +521,30 @@ public class DBTScanner10 { final SMPPairingState pstate = device.getPairingState(); final PairingMode pmode = device.getPairingMode(); // Skip PairingMode::PRE_PAIRED (write again) if( SMPPairingState.COMPLETED == pstate && PairingMode.PRE_PAIRED != pmode ) { - { - final MyLongTermKeyInfo my_ltk = new MyLongTermKeyInfo(); - my_ltk.address = device.getAddress(); - my_ltk.address_type = device.getAddressType(); - my_ltk.smp_ltk = device.getLongTermKeyInfo(false /* responder */); + final SMPKeyMask keys_resp = device.getAvailableSMPKeys(true /* responder */); + final SMPKeyMask keys_init = device.getAvailableSMPKeys(false /* responder */); + + if( keys_init.isSet(SMPKeyMask.KeyType.ENC_KEY) ) { + final MyLongTermKeyInfo my_ltk = new MyLongTermKeyInfo(device.getAddress(), device.getAddressType(), + device.getLongTermKeyInfo(false /* responder */)); my_ltk.write(my_ltk.address.toString()+".init.ltk"); } - { - final MyLongTermKeyInfo my_ltk = new MyLongTermKeyInfo(); - my_ltk.address = device.getAddress(); - my_ltk.address_type = device.getAddressType(); - my_ltk.smp_ltk = device.getLongTermKeyInfo(true /* responder */); + if( keys_resp.isSet(SMPKeyMask.KeyType.ENC_KEY) ) { + final MyLongTermKeyInfo my_ltk = new MyLongTermKeyInfo(device.getAddress(), device.getAddressType(), + device.getLongTermKeyInfo(true /* responder */)); my_ltk.write(my_ltk.address.toString()+".resp.ltk"); } + + if( keys_init.isSet(SMPKeyMask.KeyType.SIGN_KEY) ) { + final MySignatureResolvingKeyInfo my_csrk = new MySignatureResolvingKeyInfo(device.getAddress(), device.getAddressType(), + device.getSignatureResolvingKeyInfo(false /* responder */)); + my_csrk.write(my_csrk.address.toString()+".init.csrk"); + } + if( keys_resp.isSet(SMPKeyMask.KeyType.SIGN_KEY) ) { + final MySignatureResolvingKeyInfo my_csrk = new MySignatureResolvingKeyInfo(device.getAddress(), device.getAddressType(), + device.getSignatureResolvingKeyInfo(true /* responder */)); + my_csrk.write(my_csrk.address.toString()+".resp.csrk"); + } } } diff --git a/java/direct_bt/tinyb/DBTDevice.java b/java/direct_bt/tinyb/DBTDevice.java index 6f5b1f37..3a07b144 100644 --- a/java/direct_bt/tinyb/DBTDevice.java +++ b/java/direct_bt/tinyb/DBTDevice.java @@ -49,8 +49,10 @@ import org.tinyb.GATTCharacteristicListener; import org.tinyb.HCIStatusCode; import org.tinyb.PairingMode; import org.tinyb.SMPIOCapability; +import org.tinyb.SMPKeyMask; import org.tinyb.SMPLongTermKeyInfo; import org.tinyb.SMPPairingState; +import org.tinyb.SMPSignatureResolvingKeyInfo; public class DBTDevice extends DBTObject implements BluetoothDevice { @@ -329,6 +331,12 @@ public class DBTDevice extends DBTObject implements BluetoothDevice public final BluetoothDevice clone() { throw new UnsupportedOperationException(); } // FIXME @Override + public final SMPKeyMask getAvailableSMPKeys(final boolean responder) { + return new SMPKeyMask(getAvailableSMPKeysImpl(responder)); + } + private final native byte getAvailableSMPKeysImpl(final boolean responder); + + @Override public final SMPLongTermKeyInfo getLongTermKeyInfo(final boolean responder) { final byte[] stream = new byte[SMPLongTermKeyInfo.byte_size]; getLongTermKeyInfoImpl(responder, stream); @@ -345,6 +353,14 @@ public class DBTDevice extends DBTObject implements BluetoothDevice private final native byte setLongTermKeyInfoImpl(final byte[] source); @Override + public final SMPSignatureResolvingKeyInfo getSignatureResolvingKeyInfo(final boolean responder) { + final byte[] stream = new byte[SMPSignatureResolvingKeyInfo.byte_size]; + getSignatureResolvingKeyInfoImpl(responder, stream); + return new SMPSignatureResolvingKeyInfo(stream, 0); + } + private final native void getSignatureResolvingKeyInfoImpl(final boolean responder, final byte[] sink); + + @Override public final boolean pair() throws BluetoothException { return false; } diff --git a/java/jni/direct_bt/DBTDevice.cxx b/java/jni/direct_bt/DBTDevice.cxx index c61dfb03..ea821818 100644 --- a/java/jni/direct_bt/DBTDevice.cxx +++ b/java/jni/direct_bt/DBTDevice.cxx @@ -378,6 +378,18 @@ jbyte Java_direct_1bt_tinyb_DBTDevice_connectLEImpl1(JNIEnv *env, jobject obj, return (jbyte) number(HCIStatusCode::INTERNAL_FAILURE); } +jbyte Java_direct_1bt_tinyb_DBTDevice_getAvailableSMPKeysImpl(JNIEnv *env, jobject obj, jboolean responder) { + try { + DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + + return number( device->getAvailableSMPKeys(JNI_TRUE == responder) ); // assign data of new key copy to JNI critical-array + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return 0; +} + void Java_direct_1bt_tinyb_DBTDevice_getLongTermKeyInfoImpl(JNIEnv *env, jobject obj, jboolean responder, jbyteArray jsink) { try { DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj); @@ -429,6 +441,30 @@ jbyte Java_direct_1bt_tinyb_DBTDevice_setLongTermKeyInfoImpl(JNIEnv *env, jobjec return (jbyte) number(HCIStatusCode::INTERNAL_FAILURE); } +void Java_direct_1bt_tinyb_DBTDevice_getSignatureResolvingKeyInfoImpl(JNIEnv *env, jobject obj, jboolean responder, jbyteArray jsink) { + try { + DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + + if( nullptr == jsink ) { + throw IllegalArgumentException("byte array null", E_FILE_LINE); + } + const size_t sink_size = env->GetArrayLength(jsink); + if( sizeof(SMPSignatureResolvingKeyInfo) > sink_size ) { + throw IllegalArgumentException("byte array "+std::to_string(sink_size)+" < "+std::to_string(sizeof(SMPSignatureResolvingKeyInfo)), E_FILE_LINE); + } + JNICriticalArray<uint8_t, jbyteArray> criticalArray(env); // RAII - release + uint8_t * sink_ptr = criticalArray.get(jsink, criticalArray.Mode::UPDATE_AND_RELEASE); + if( NULL == sink_ptr ) { + throw InternalError("GetPrimitiveArrayCritical(byte array) is null", E_FILE_LINE); + } + SMPSignatureResolvingKeyInfo& csrk_sink = *reinterpret_cast<SMPSignatureResolvingKeyInfo *>(sink_ptr); + csrk_sink = device->getSignatureResolvingKeyInfo(JNI_TRUE == responder); // assign data of new key copy to JNI critical-array + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + jbyte Java_direct_1bt_tinyb_DBTDevice_unpairImpl(JNIEnv *env, jobject obj) { try { DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj); diff --git a/java/org/tinyb/BluetoothDevice.java b/java/org/tinyb/BluetoothDevice.java index 23eb930c..b1bcdc8d 100644 --- a/java/org/tinyb/BluetoothDevice.java +++ b/java/org/tinyb/BluetoothDevice.java @@ -181,7 +181,16 @@ public interface BluetoothDevice extends BluetoothObject boolean disconnectProfile(String arg_UUID) throws BluetoothException; /** - * Returns a copy of the long term ket (LTK) info, valid after connection and SMP pairing has been completed. + * Returns the available {@link SMPKeyMask.KeyType} {@link SMPKeyMask} for the responder (LL slave) or initiator (LL master). + * @param responder if true, queries the responder (LL slave) key, otherwise the initiator (LL master) key. + * @return {@link SMPKeyMask.KeyType} {@link SMPKeyMask} result + * @since 2.2.0 + * @implNote not implemented in tinyb.dbus + */ + SMPKeyMask getAvailableSMPKeys(final boolean responder); + + /** + * Returns a copy of the long term key (LTK) info, valid after connection and SMP pairing has been completed. * @param responder true will return the responder's LTK info (remote device, LL slave), otherwise the initiator's (the LL master). * @return the resulting key. {@link SMPLongTermKeyInfo#enc_size} will be zero if invalid. * @see {@link SMPPairingState#COMPLETED} @@ -204,6 +213,17 @@ public interface BluetoothDevice extends BluetoothObject HCIStatusCode setLongTermKeyInfo(final SMPLongTermKeyInfo ltk); /** + * Returns a copy of the Signature Resolving Key (LTK) info, valid after connection and SMP pairing has been completed. + * @param responder true will return the responder's LTK info (remote device, LL slave), otherwise the initiator's (the LL master). + * @return the resulting key + * @see {@link SMPPairingState#COMPLETED} + * @see {@link AdapterStatusListener#deviceReady(BluetoothDevice, long)} + * @since 2.2.0 + * @implNote not implemented in tinyb.dbus + */ + SMPSignatureResolvingKeyInfo getSignatureResolvingKeyInfo(final boolean responder); + + /** * A secure connection to this device is established, and the device is then paired. * <p> * For direct_bt use {@link #setConnSecurity(BTSecurityLevel, SMPIOCapability) setConnSecurity(..) or its variants} diff --git a/java/org/tinyb/SMPKeyMask.java b/java/org/tinyb/SMPKeyMask.java new file mode 100644 index 00000000..eb362956 --- /dev/null +++ b/java/org/tinyb/SMPKeyMask.java @@ -0,0 +1,165 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.tinyb; + +/** + * SMP Key Type for Distribution, indicates keys distributed in the Transport Specific Key Distribution phase. + * <p> + * {@link SMPKeyMask} {@link SMPKeyMask.KeyType} Bit Mask + * </p> + * <pre> + * Field format and usage: Vol 3, Part H, 3.6.1 SMP - LE Security - Key distribution and generation. + * See also Vol 3, Part H, 2.4.3 SM - LE Security - Distribution of keys. + * </pre> + * </p> + * Layout LSB -> MSB + * <pre> + * uint8_t EncKey : 1, IdKey : 1, SignKey : 1, LinkKey : 1, RFU : 4; + * </pre> + * @since 2.2.0 + */ +public class SMPKeyMask { + /** + * {@link SMPKeyMask} Key Type + */ + static public enum KeyType { + NONE ((byte)0), + /** + * LE legacy pairing: Indicates device shall distribute LTK using the Encryption Information command, + * followed by EDIV and Rand using the Master Identification command. + * <p> + * LE Secure Connections pairing (SMP on LE transport): Ignored, + * EDIV and Rand shall be zero and shall not be distributed. + * </p> + * <p> + * SMP on BR/EDR transport: Indicates device likes to derive LTK from BR/EDR Link Key.<br> + * When EncKey is set to 1 by both devices in the initiator and responder Key Distribution / Generation fields, + * the procedures for calculating the LTK from the BR/EDR Link Key shall be used. + * </p> + */ + ENC_KEY ((byte) 0b00000001), + /** + * Indicates that the device shall distribute IRK using the Identity Information command + * followed by its public device or status random address using Identity Address Information. + */ + ID_KEY ((byte) 0b00000010), + /** + * Indicates that the device shall distribute CSRK using the Signing Information command. + */ + SIGN_KEY ((byte) 0b00000100), + /** + * SMP on the LE transport: Indicate that the device would like to derive the Link Key from the LTK.<br> + * When LinkKey is set to 1 by both devices in the initiator and responder Key Distribution / Generation fields, + * the procedures for calculating the BR/EDR link key from the LTK shall be used.<br> + * Devices not supporting LE Secure Connections shall set this bit to zero and ignore it on reception. + * <p> + * SMP on BR/EDR transport: Reserved for future use. + * </p> + */ + LINK_KEY ((byte) 0b00001000), + /** Reserved for future use */ + RFU_1 ((byte) 0b00010000), + /** Reserved for future use */ + RFU_2 ((byte) 0b00100000), + /** Reserved for future use */ + RFU_3 ((byte) 0b01000000), + /** Reserved for future use */ + RFU_4 ((byte) 0b10000000); + + public final byte value; + + /** + * Maps the specified name to a constant of {@link KeyType}. + * <p> + * Implementation simply returns {@link #valueOf(String)}. + * This maps the constant names itself to their respective constant. + * </p> + * @param name the string name to be mapped to a constant of this enum type. + * @return the corresponding constant of this enum type. + * @throws IllegalArgumentException if the specified name can't be mapped to a constant of this enum type + * as described above. + */ + public static KeyType get(final String name) throws IllegalArgumentException { + return valueOf(name); + } + + /** + * Maps the specified integer value to a constant of {@link KeyType}. + * @param value the integer value to be mapped to a constant of this enum type. + * @return the corresponding constant of this enum type, using {@link #NONE} if not supported. + */ + public static KeyType get(final byte value) { + switch(value) { + case (byte) 0b00000001: return ENC_KEY; + case (byte) 0b00000010: return ID_KEY; + case (byte) 0b00000100: return SIGN_KEY; + case (byte) 0b00001000: return LINK_KEY; + case (byte) 0b00010000: return RFU_1; + case (byte) 0b00100000: return RFU_2; + case (byte) 0b01000000: return RFU_3; + case (byte) 0b10000000: return RFU_4; + default: return NONE; + } + } + + KeyType(final byte v) { + value = v; + } + } + + /** The {@link KeyType} bit mask */ + public byte mask; + + public SMPKeyMask(final byte v) { + mask = v; + } + + public SMPKeyMask() { + mask = (byte)0; + } + + public boolean isEmpty() { return 0 == mask; } + public boolean isSet(final KeyType bit) { return 0 != ( mask & bit.value ); } + public void set(final KeyType bit) { mask = (byte) ( mask | bit.value ); } + + @Override + public String toString() { + boolean has_pre = false; + final StringBuilder out = new StringBuilder(); + out.append("["); + for(int i=0; i<8; i++) { + final KeyType key_type = KeyType.get( (byte) ( 1 << i ) ); + if( isSet( key_type ) ) { + if( has_pre ) { out.append(", "); } + out.append( key_type.toString() ); + has_pre = true; + } + } + out.append("]"); + return out.toString(); + } + +}; diff --git a/java/org/tinyb/SMPSignatureResolvingKeyInfo.java b/java/org/tinyb/SMPSignatureResolvingKeyInfo.java new file mode 100644 index 00000000..cc9e818b --- /dev/null +++ b/java/org/tinyb/SMPSignatureResolvingKeyInfo.java @@ -0,0 +1,187 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.tinyb; + +/** + * SMP Signature Resolving Key Info, used for platform agnostic persistence. + * <p> + * Notable: No endian wise conversion shall occur on this data, + * since the encryption values are interpreted as a byte stream. + * </p> + * <p> + * Byte layout must be synchronized with native direct_bt::SMPSignatureResolvingKey + * </p> + * @since 2.2.0 + */ +public class SMPSignatureResolvingKeyInfo { + /** + * {@link SMPSignatureResolvingKeyInfo} Property Bits + */ + static public enum PropertyType { + /** No specific property */ + NONE((byte)0), + /** Responder Key (LL slave). Absence indicates Initiator Key (LL master). */ + RESPONDER((byte)0x01), + /** Authentication used. */ + AUTH((byte)0x02); + + public final byte value; + + /** + * Maps the specified name to a constant of {@link PropertyType}. + * <p> + * Implementation simply returns {@link #valueOf(String)}. + * This maps the constant names itself to their respective constant. + * </p> + * @param name the string name to be mapped to a constant of this enum type. + * @return the corresponding constant of this enum type. + * @throws IllegalArgumentException if the specified name can't be mapped to a constant of this enum type + * as described above. + */ + public static PropertyType get(final String name) throws IllegalArgumentException { + return valueOf(name); + } + + /** + * Maps the specified integer value to a constant of {@link PropertyType}. + * @param value the integer value to be mapped to a constant of this enum type. + * @return the corresponding constant of this enum type, using {@link #NONE} if not supported. + */ + public static PropertyType get(final byte value) { + switch(value) { + case (byte) 0x01: return RESPONDER; + case (byte) 0x02: return AUTH; + default: return NONE; + } + } + + PropertyType(final byte v) { + value = v; + } + } + + /** + * {@link SMPSignatureResolvingKeyInfo} {@link PropertyType} Bit Mask + */ + static public class Properties { + /** The {@link PropertyType} bit mask */ + public byte mask; + + public Properties(final byte v) { + mask = v; + } + + public boolean isEmpty() { return 0 == mask; } + public boolean isSet(final PropertyType bit) { return 0 != ( mask & bit.value ); } + public void set(final PropertyType bit) { mask = (byte) ( mask | bit.value ); } + + @Override + public String toString() { + int count = 0; + final StringBuilder out = new StringBuilder(); + if( isSet(PropertyType.RESPONDER) ) { + out.append(PropertyType.RESPONDER.name()); count++; + } + if( isSet(PropertyType.AUTH) ) { + if( 0 < count ) { out.append(", "); } + out.append(PropertyType.AUTH.name()); count++; + } + return "["+out.toString()+"]"; + } + } + + /** {@link Properties} bit mask. 1 octet or 8 bits. */ + public Properties properties; + + /** Connection Signature Resolving Key (CSRK) */ + public byte csrk[/*16*/]; + + /** + * Size of the byte stream representation in bytes + * @see #getStream(byte[], int) + */ + public static final int byte_size = 1+16; + + /** Construct instance via given source byte array */ + public SMPSignatureResolvingKeyInfo(final byte source[], final int pos) { + if( byte_size > ( source.length - pos ) ) { + throw new IllegalArgumentException("Stream ( "+source.length+" - "+pos+" ) < "+byte_size+" bytes"); + } + csrk = new byte[16]; + putStream(source, pos); + } + + /** Construct emoty unset instance. */ + public SMPSignatureResolvingKeyInfo() { + properties = new Properties((byte)0); + csrk = new byte[16]; + } + + /** + * Method transfers all bytes representing a SMPLongTermKeyInfo from the given + * source array at the given position into this instance. + * <p> + * Implementation is consistent with {@link #getStream(byte[], int)}. + * </p> + * @param source the source array + * @param pos starting position in the source array + * @see #getStream(byte[], int) + */ + public void putStream(final byte[] source, int pos) { + if( byte_size > ( source.length - pos ) ) { + throw new IllegalArgumentException("Stream ( "+source.length+" - "+pos+" ) < "+byte_size+" bytes"); + } + properties = new Properties(source[pos++]); + System.arraycopy(source, pos, csrk, 0, 16); pos+=16; + } + + /** + * Method transfers all bytes representing this instance into the given + * destination array at the given position. + * <p> + * Implementation is consistent with {@link #SMPLongTermKeyInfo(byte[], int)}. + * </p> + * @param sink the destination array + * @param pos starting position in the destination array + * @see #SMPLongTermKeyInfo(byte[], int) + * @see #putStream(byte[], int) + */ + public final void getStream(final byte[] sink, int pos) { + if( byte_size > ( sink.length - pos ) ) { + throw new IllegalArgumentException("Stream ( "+sink.length+" - "+pos+" ) < "+byte_size+" bytes"); + } + sink[pos++] = properties.mask; + System.arraycopy(csrk, 0, sink, pos, 16); pos+=16; + } + + @Override + public String toString() { // hex-fmt aligned with btmon + return "LTK[props "+properties.toString()+ + ", csrk "+BluetoothUtils.bytesHexString(csrk, 0, -1, true /* lsbFirst */, false /* leading0X */, true /* lowerCase */)+ + "]"; + } + +}; diff --git a/java/tinyb/dbus/DBusDevice.java b/java/tinyb/dbus/DBusDevice.java index a381701b..b066e0e8 100644 --- a/java/tinyb/dbus/DBusDevice.java +++ b/java/tinyb/dbus/DBusDevice.java @@ -47,8 +47,10 @@ import org.tinyb.GATTCharacteristicListener; import org.tinyb.HCIStatusCode; import org.tinyb.PairingMode; import org.tinyb.SMPIOCapability; +import org.tinyb.SMPKeyMask; import org.tinyb.SMPLongTermKeyInfo; import org.tinyb.SMPPairingState; +import org.tinyb.SMPSignatureResolvingKeyInfo; public class DBusDevice extends DBusObject implements BluetoothDevice { @@ -109,12 +111,18 @@ public class DBusDevice extends DBusObject implements BluetoothDevice public native boolean disconnectProfile(String arg_UUID) throws BluetoothException; @Override + public final SMPKeyMask getAvailableSMPKeys(final boolean responder) { return new SMPKeyMask(); } + + @Override public final SMPLongTermKeyInfo getLongTermKeyInfo(final boolean responder) { return new SMPLongTermKeyInfo(); } // FIXME @Override public final HCIStatusCode setLongTermKeyInfo(final SMPLongTermKeyInfo ltk) { return HCIStatusCode.NOT_SUPPORTED; } // FIXME @Override + public final SMPSignatureResolvingKeyInfo getSignatureResolvingKeyInfo(final boolean responder) { return new SMPSignatureResolvingKeyInfo(); } // FIXME + + @Override public native boolean pair() throws BluetoothException; @Override diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp index 1d68ae3a..f8e889a2 100644 --- a/src/direct_bt/DBTDevice.cpp +++ b/src/direct_bt/DBTDevice.cpp @@ -522,8 +522,8 @@ 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; +static const SMPKeyType _key_mask_legacy = SMPKeyType::ENC_KEY | SMPKeyType::ID_KEY | SMPKeyType::SIGN_KEY; +static const SMPKeyType _key_mask_sc = SMPKeyType::ID_KEY | SMPKeyType::SIGN_KEY | SMPKeyType::LINK_KEY; bool DBTDevice::checkPairingKeyDistributionComplete(const std::string& timestamp) const noexcept { bool res = false; @@ -550,10 +550,10 @@ bool DBTDevice::checkPairingKeyDistributionComplete(const std::string& timestamp 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()); + getSMPKeyTypeMaskString(pairing_data.keys_init_has).c_str(), + getSMPKeyTypeMaskString(pairing_data.keys_init_exp).c_str(), + getSMPKeyTypeMaskString(pairing_data.keys_resp_has).c_str(), + getSMPKeyTypeMaskString(pairing_data.keys_resp_exp).c_str()); } } @@ -619,27 +619,27 @@ bool DBTDevice::updatePairingState(std::shared_ptr<DBTDevice> sthis, std::shared 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( ( SMPKeyType::ENC_KEY & pairing_data.keys_resp_has ) == SMPKeyType::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; + pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY; if( checkPairingKeyDistributionComplete(timestamp) ) { is_device_ready = true; } } } else { - if( ( SMPKeyDist::ENC_KEY & pairing_data.keys_init_has ) == SMPKeyDist::NONE ) { // no overwrite + if( ( SMPKeyType::ENC_KEY & pairing_data.keys_init_has ) == SMPKeyType::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; + pairing_data.keys_init_has |= SMPKeyType::ENC_KEY; if( checkPairingKeyDistributionComplete(timestamp) ) { is_device_ready = true; } @@ -767,8 +767,8 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_ jau::PLAIN_PRINT(false, "[%s] - encsz: init %d", timestamp.c_str(), (int)pairing_data.maxEncsz_init); jau::PLAIN_PRINT(false, "[%s] - encsz: resp %d", timestamp.c_str(), (int)pairing_data.maxEncsz_resp); jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str()); - jau::PLAIN_PRINT(false, "[%s] - keys: init %s", timestamp.c_str(), getSMPKeyDistMaskString(pairing_data.keys_init_exp).c_str()); - jau::PLAIN_PRINT(false, "[%s] - keys: resp %s", timestamp.c_str(), getSMPKeyDistMaskString(pairing_data.keys_resp_exp).c_str()); + jau::PLAIN_PRINT(false, "[%s] - keys: init %s", timestamp.c_str(), getSMPKeyTypeMaskString(pairing_data.keys_init_exp).c_str()); + jau::PLAIN_PRINT(false, "[%s] - keys: resp %s", timestamp.c_str(), getSMPKeyTypeMaskString(pairing_data.keys_resp_exp).c_str()); } } } break; @@ -838,12 +838,12 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_ const SMPMasterIdentMsg & msg1 = *static_cast<const SMPMasterIdentMsg *>( msg.get() ); if( HCIACLData::l2cap_frame::PBFlag::START_AUTOFLUSH == source.pb_flag ) { // from responder (LL slave) - pairing_data.keys_resp_has |= SMPKeyDist::ENC_KEY; + pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY; pairing_data.ltk_resp.ediv = msg1.getEDIV(); pairing_data.ltk_resp.rand = msg1.getRand(); } else { // from initiator (LL master) - pairing_data.keys_init_has |= SMPKeyDist::ENC_KEY; + pairing_data.keys_init_has |= SMPKeyType::ENC_KEY; pairing_data.ltk_init.ediv = msg1.getEDIV(); pairing_data.ltk_init.rand = msg1.getRand(); } @@ -866,12 +866,12 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_ const SMPIdentAddrInfoMsg & msg1 = *static_cast<const SMPIdentAddrInfoMsg *>( msg.get() ); if( HCIACLData::l2cap_frame::PBFlag::START_AUTOFLUSH == source.pb_flag ) { // from responder (LL slave) - pairing_data.keys_resp_has |= SMPKeyDist::ID_KEY; + pairing_data.keys_resp_has |= SMPKeyType::ID_KEY; pairing_data.address = msg1.getAddress(); pairing_data.is_static_random_address = msg1.isStaticRandomAddress(); } else { // from initiator (LL master) - pairing_data.keys_init_has |= SMPKeyDist::ID_KEY; + pairing_data.keys_init_has |= SMPKeyType::ID_KEY; pairing_data.address = msg1.getAddress(); pairing_data.is_static_random_address = msg1.isStaticRandomAddress(); } @@ -882,12 +882,22 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_ const SMPSignInfoMsg & msg1 = *static_cast<const SMPSignInfoMsg *>( msg.get() ); if( HCIACLData::l2cap_frame::PBFlag::START_AUTOFLUSH == source.pb_flag ) { // from responder (LL slave) - pairing_data.keys_resp_has |= SMPKeyDist::SIGN_KEY; - pairing_data.csrk_resp = msg1.getCSRK(); + pairing_data.keys_resp_has |= SMPKeyType::SIGN_KEY; + + pairing_data.csrk_resp.properties |= SMPSignatureResolvingKeyInfo::Property::RESPONDER; + if( BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn ) { + pairing_data.csrk_resp.properties |= SMPSignatureResolvingKeyInfo::Property::AUTH; + } + pairing_data.csrk_resp.csrk = msg1.getCSRK(); } else { // from initiator (LL master) - pairing_data.keys_init_has |= SMPKeyDist::SIGN_KEY; - pairing_data.csrk_init = msg1.getCSRK(); + pairing_data.keys_init_has |= SMPKeyType::SIGN_KEY; + + // pairing_data.csrk_init.properties |= SMPSignatureResolvingKeyInfo::Property::INITIATOR; + if( BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn ) { + pairing_data.csrk_init.properties |= SMPSignatureResolvingKeyInfo::Property::AUTH; + } + pairing_data.csrk_init.csrk = msg1.getCSRK(); } } break; @@ -917,10 +927,10 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_ is_device_ready); 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()); + getSMPKeyTypeMaskString(pairing_data.keys_init_has).c_str(), + getSMPKeyTypeMaskString(pairing_data.keys_init_exp).c_str(), + getSMPKeyTypeMaskString(pairing_data.keys_resp_has).c_str(), + getSMPKeyTypeMaskString(pairing_data.keys_resp_exp).c_str()); } if( old_pstate == pstate /* && old_pmode == pmode */ ) { @@ -942,6 +952,15 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_ } } +SMPKeyType DBTDevice::getAvailableSMPKeys(const bool responder) const noexcept { + jau::sc_atomic_critical sync(const_cast<DBTDevice*>(this)->sync_pairing); + if( responder ) { + return pairing_data.keys_resp_has; + } else { + return pairing_data.keys_init_has; + } +} + SMPLongTermKeyInfo DBTDevice::getLongTermKeyInfo(const bool responder) const noexcept { jau::sc_atomic_critical sync(const_cast<DBTDevice*>(this)->sync_pairing); return responder ? pairing_data.ltk_resp : pairing_data.ltk_init; @@ -965,6 +984,11 @@ HCIStatusCode DBTDevice::setLongTermKeyInfo(const SMPLongTermKeyInfo& ltk) noexc return res; } +SMPSignatureResolvingKeyInfo DBTDevice::getSignatureResolvingKeyInfo(const bool responder) const noexcept { + jau::sc_atomic_critical sync(const_cast<DBTDevice*>(this)->sync_pairing); + return responder ? pairing_data.csrk_resp : pairing_data.csrk_init; +} + HCIStatusCode DBTDevice::pair(const SMPIOCapability io_cap) noexcept { /** * Experimental only. @@ -1172,8 +1196,8 @@ void DBTDevice::clearSMPStates(const bool connected) noexcept { pairing_data.ioCap_resp = SMPIOCapability::NO_INPUT_NO_OUTPUT; pairing_data.oobFlag_resp = SMPOOBDataFlag::OOB_AUTH_DATA_NOT_PRESENT; pairing_data.maxEncsz_resp = 0; - pairing_data.keys_resp_exp = SMPKeyDist::NONE; - pairing_data.keys_resp_has = SMPKeyDist::NONE; + pairing_data.keys_resp_exp = SMPKeyType::NONE; + pairing_data.keys_resp_has = SMPKeyType::NONE; pairing_data.ltk_resp.clear(); pairing_data.irk_resp.clear(); // pairing_data.address; @@ -1184,8 +1208,8 @@ void DBTDevice::clearSMPStates(const bool connected) noexcept { pairing_data.ioCap_init = SMPIOCapability::NO_INPUT_NO_OUTPUT; pairing_data.oobFlag_init = SMPOOBDataFlag::OOB_AUTH_DATA_NOT_PRESENT; pairing_data.maxEncsz_init = 0; - pairing_data.keys_init_exp = SMPKeyDist::NONE; - pairing_data.keys_init_has = SMPKeyDist::NONE; + pairing_data.keys_init_exp = SMPKeyType::NONE; + pairing_data.keys_init_has = SMPKeyType::NONE; pairing_data.ltk_init.clear(); pairing_data.irk_init.clear(); pairing_data.csrk_init.clear(); diff --git a/src/direct_bt/SMPTypes.cpp b/src/direct_bt/SMPTypes.cpp index e3294020..88f6e0d8 100644 --- a/src/direct_bt/SMPTypes.cpp +++ b/src/direct_bt/SMPTypes.cpp @@ -268,9 +268,9 @@ PairingMode direct_bt::getPairingMode(const bool use_sc, X(RFU_3) \ X(RFU_4) -#define CASE_TO_STRING_KEYDISTFMT(V) case SMPKeyDist::V: return #V; +#define CASE_TO_STRING_KEYDISTFMT(V) case SMPKeyType::V: return #V; -std::string direct_bt::getSMPKeyDistBitString(const SMPKeyDist bit) noexcept { +std::string direct_bt::getSMPKeyTypeBitString(const SMPKeyType bit) noexcept { switch(bit) { KEYDISTFMT_ENUM(CASE_TO_STRING_KEYDISTFMT) default: ; // fall through intended @@ -278,7 +278,7 @@ std::string direct_bt::getSMPKeyDistBitString(const SMPKeyDist bit) noexcept { return "Unknown SMPKeyDistFormat bit"; } -std::string direct_bt::getSMPKeyDistMaskString(const SMPKeyDist mask) noexcept { +std::string direct_bt::getSMPKeyTypeMaskString(const SMPKeyType mask) noexcept { const uint8_t one = 1; bool has_pre = false; std::string out("["); @@ -286,7 +286,7 @@ std::string direct_bt::getSMPKeyDistMaskString(const SMPKeyDist mask) noexcept { const uint8_t settingBit = one << i; if( 0 != ( static_cast<uint8_t>(mask) & settingBit ) ) { if( has_pre ) { out.append(", "); } - out.append( getSMPKeyDistBitString( static_cast<SMPKeyDist>(settingBit) ) ); + out.append( getSMPKeyTypeBitString( static_cast<SMPKeyType>(settingBit) ) ); has_pre = true; } } @@ -331,6 +331,36 @@ std::string SMPLongTermKeyInfo::getPropertyMaskString(const Property mask) noexc return out; } +#define CSRKPROP_ENUM(X) \ + X(NONE) \ + X(RESPONDER) \ + X(AUTH) + +#define CASE_TO_STRING_CSRKPROPFMT(V) case SMPSignatureResolvingKeyInfo::Property::V: return #V; + +std::string SMPSignatureResolvingKeyInfo::getPropertyBitString(const Property bit) noexcept { + switch(bit) { + CSRKPROP_ENUM(CASE_TO_STRING_CSRKPROPFMT) + default: ; // fall through intended + } + return "Unknown SMPSignatureResolvingKeyInfo::Property bit"; +} + +std::string SMPSignatureResolvingKeyInfo::getPropertyMaskString(const Property mask) noexcept { + bool has_pre = false; + std::string out("["); + if( Property::NONE != ( mask & Property::RESPONDER ) ) { + out.append( getPropertyBitString( Property::RESPONDER ) ); + has_pre = true; + } + if( Property::NONE != ( mask & Property::AUTH ) ) { + if( has_pre ) { out.append(", "); } + out.append( getPropertyBitString( Property::AUTH ) ); + has_pre = true; + } + out.append("]"); + return out; +} #define OPCODE_ENUM(X) \ X(UNDEFINED) \ |