From 3ad5412d04608fbf31d66b3607e523840867c47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Fri, 4 Apr 2025 23:32:21 +0100 Subject: [PATCH] Huami2021ChunkedEncoder: Remove dependency on BLE characteristic --- .idea/dictionaries/t.xml | 1 + .../huami/Huami2021ChunkedEncoder.java | 49 ++++++++----------- .../service/devices/huami/HuamiSupport.java | 6 +-- .../operations/init/InitOperation2021.java | 7 ++- .../devices/huami/zeppos/ZeppOsSupport.java | 4 +- 5 files changed, 32 insertions(+), 35 deletions(-) diff --git a/.idea/dictionaries/t.xml b/.idea/dictionaries/t.xml index 18f9cb697..c25502eb5 100644 --- a/.idea/dictionaries/t.xml +++ b/.idea/dictionaries/t.xml @@ -39,6 +39,7 @@ drobnič dulfo elagin + encryptable eomäe ericsson exrizu diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/Huami2021ChunkedEncoder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/Huami2021ChunkedEncoder.java index 04908ef8b..033f4de9f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/Huami2021ChunkedEncoder.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/Huami2021ChunkedEncoder.java @@ -16,20 +16,17 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami; -import android.bluetooth.BluetoothGattCharacteristic; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import java.util.function.Consumer; + import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; import nodomain.freeyourgadget.gadgetbridge.util.CryptoUtils; public class Huami2021ChunkedEncoder { private static final Logger LOG = LoggerFactory.getLogger(Huami2021ChunkedEncoder.class); - private final BluetoothGattCharacteristic characteristicChunked2021Write; - private byte writeHandle; // These must be volatile, since they are set by a different thread. Sometimes, GB might @@ -37,15 +34,9 @@ public class Huami2021ChunkedEncoder { // to that thread later. private volatile int encryptedSequenceNr; private volatile byte[] sharedSessionKey; + private volatile int mMTU; - private final boolean force2021Protocol; - private volatile int mMTU = 23; - - public Huami2021ChunkedEncoder(final BluetoothGattCharacteristic characteristicChunked2021Write, - final boolean force2021Protocol, - final int mMTU) { - this.characteristicChunked2021Write = characteristicChunked2021Write; - this.force2021Protocol = force2021Protocol; + public Huami2021ChunkedEncoder(final int mMTU) { this.mMTU = mMTU; } @@ -58,9 +49,9 @@ public class Huami2021ChunkedEncoder { this.mMTU = mMTU; } - public synchronized void write(final TransactionBuilder builder, + public synchronized void write(final Consumer chunkWriter, final short type, - byte[] data, + final byte[] data, final boolean extended_flags, final boolean encrypt) { if (encrypt && sharedSessionKey == null) { @@ -71,7 +62,7 @@ public class Huami2021ChunkedEncoder { writeHandle++; int remaining = data.length; - int length = data.length; + final int length = data.length; byte count = 0; int header_size = 10; @@ -79,10 +70,11 @@ public class Huami2021ChunkedEncoder { header_size++; } + final byte[] dataToSend; if (extended_flags && encrypt) { - byte[] messagekey = new byte[16]; + final byte[] messageKey = new byte[16]; for (int i = 0; i < 16; i++) { - messagekey[i] = (byte) (sharedSessionKey[i] ^ writeHandle); + messageKey[i] = (byte) (sharedSessionKey[i] ^ writeHandle); } int encrypted_length = length + 8; int overflow = encrypted_length % 16; @@ -90,7 +82,7 @@ public class Huami2021ChunkedEncoder { encrypted_length += (16 - overflow); } - byte[] encryptable_payload = new byte[encrypted_length]; + final byte[] encryptable_payload = new byte[encrypted_length]; System.arraycopy(data, 0, encryptable_payload, 0, length); encryptable_payload[length] = (byte) (encryptedSequenceNr & 0xff); encryptable_payload[length + 1] = (byte) ((encryptedSequenceNr >> 8) & 0xff); @@ -104,18 +96,19 @@ public class Huami2021ChunkedEncoder { encryptable_payload[length + 7] = (byte) ((checksum >> 24) & 0xff); remaining = encrypted_length; try { - data = CryptoUtils.encryptAES(encryptable_payload, messagekey); + dataToSend = CryptoUtils.encryptAES(encryptable_payload, messageKey); } catch (Exception e) { LOG.error("error while encrypting", e); return; } - + } else { + dataToSend = data; } while (remaining > 0) { - int MAX_CHUNKLENGTH = mMTU - 3 - header_size; - int copybytes = Math.min(remaining, MAX_CHUNKLENGTH); - byte[] chunk = new byte[copybytes + header_size]; + final int maxChunkLength = mMTU - 3 - header_size; + int copyBytes = Math.min(remaining, maxChunkLength); + byte[] chunk = new byte[copyBytes + header_size]; byte flags = 0; if (encrypt) { @@ -134,7 +127,7 @@ public class Huami2021ChunkedEncoder { chunk[i++] = (byte) (type & 0xff); chunk[i] = (byte) ((type >> 8) & 0xff); } - if (remaining <= MAX_CHUNKLENGTH) { + if (remaining <= maxChunkLength) { flags |= 0x06; // last chunk? } chunk[0] = 0x03; @@ -148,9 +141,9 @@ public class Huami2021ChunkedEncoder { chunk[3] = count; } - System.arraycopy(data, data.length - remaining, chunk, header_size, copybytes); - builder.write(characteristicChunked2021Write, chunk); - remaining -= copybytes; + System.arraycopy(dataToSend, dataToSend.length - remaining, chunk, header_size, copyBytes); + chunkWriter.accept(chunk); + remaining -= copyBytes; header_size = 4; if (extended_flags) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java index 12734a3b2..b2dfe6427 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java @@ -396,11 +396,11 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements } characteristicChunked2021Write = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_WRITE); if (characteristicChunked2021Write != null && huami2021ChunkedEncoder == null) { - huami2021ChunkedEncoder = new Huami2021ChunkedEncoder(characteristicChunked2021Write, force2021Protocol(), mMTU); + huami2021ChunkedEncoder = new Huami2021ChunkedEncoder(mMTU); } if (force2021Protocol()) { if (characteristicChunked2021Write != null && characteristicChunked2021Read != null) { - new InitOperation2021(authenticate, authFlags, cryptFlags, this, builder, huami2021ChunkedEncoder, huami2021ChunkedDecoder).perform(); + new InitOperation2021(authenticate, authFlags, cryptFlags, this, builder, characteristicChunked2021Write, huami2021ChunkedEncoder, huami2021ChunkedDecoder).perform(); } else { LOG.warn("Chunked 2021 characteristics are null, will attempt to reconnect"); builder.add(new SetDeviceStateAction(getDevice(), State.WAITING_FOR_RECONNECT, getContext())); @@ -4091,7 +4091,7 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements } public void writeToChunked2021(TransactionBuilder builder, short type, byte[] data, boolean encrypt) { - huami2021ChunkedEncoder.write(builder, type, data, force2021Protocol(), encrypt); + huami2021ChunkedEncoder.write(chunk -> builder.write(characteristicChunked2021Write, chunk), type, data, force2021Protocol(), encrypt); } public void writeToChunked2021(final String taskName, short type, byte data, boolean encrypt) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/init/InitOperation2021.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/init/InitOperation2021.java index 921eb11bb..2cce2ec0f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/init/InitOperation2021.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/init/InitOperation2021.java @@ -49,6 +49,7 @@ public class InitOperation2021 extends InitOperation implements Huami2021Handler private final byte[] privateEC = new byte[24]; private byte[] publicEC; + private final BluetoothGattCharacteristic characteristicChunked2021Write; private final Huami2021ChunkedEncoder huami2021ChunkedEncoder; private final Huami2021ChunkedDecoder huami2021ChunkedDecoder; @@ -59,9 +60,11 @@ public class InitOperation2021 extends InitOperation implements Huami2021Handler final byte cryptFlags, final HuamiSupport support, final TransactionBuilder builder, + final BluetoothGattCharacteristic characteristicChunked2021Write, final Huami2021ChunkedEncoder huami2021ChunkedEncoder, final Huami2021ChunkedDecoder huami2021ChunkedDecoder) { super(needsAuth, authFlags, cryptFlags, support, builder); + this.characteristicChunked2021Write = characteristicChunked2021Write; this.huami2021ChunkedEncoder = huami2021ChunkedEncoder; this.huami2021ChunkedDecoder = huami2021ChunkedDecoder; this.huami2021ChunkedDecoder.setHuami2021Handler(this); @@ -79,7 +82,7 @@ public class InitOperation2021 extends InitOperation implements Huami2021Handler sendPubKeyCommand[2] = 0x00; sendPubKeyCommand[3] = 0x02; System.arraycopy(publicEC, 0, sendPubKeyCommand, 4, 48); - huami2021ChunkedEncoder.write(builder, Huami2021Service.CHUNKED2021_ENDPOINT_AUTH, sendPubKeyCommand, true, false); + huami2021ChunkedEncoder.write(chunk -> builder.write(characteristicChunked2021Write, chunk), Huami2021Service.CHUNKED2021_ENDPOINT_AUTH, sendPubKeyCommand, true, false); } private void generateKeyPair() { @@ -148,7 +151,7 @@ public class InitOperation2021 extends InitOperation implements Huami2021Handler System.arraycopy(encryptedRandom1, 0, command, 1, 16); System.arraycopy(encryptedRandom2, 0, command, 17, 16); TransactionBuilder builder = createTransactionBuilder("Sending double encryted random to device"); - huami2021ChunkedEncoder.write(builder, Huami2021Service.CHUNKED2021_ENDPOINT_AUTH, command, true, false); + huami2021ChunkedEncoder.write(chunk -> builder.write(characteristicChunked2021Write, chunk), Huami2021Service.CHUNKED2021_ENDPOINT_AUTH, command, true, false); huamiSupport.performImmediately(builder); } } catch (final Exception e) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/ZeppOsSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/ZeppOsSupport.java index c5576317f..c0a98d9d2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/ZeppOsSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/ZeppOsSupport.java @@ -290,7 +290,7 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer characteristicChunked2021Write = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_WRITE); if (characteristicChunked2021Write != null && huami2021ChunkedEncoder == null) { - huami2021ChunkedEncoder = new Huami2021ChunkedEncoder(characteristicChunked2021Write, force2021Protocol(), getMTU()); + huami2021ChunkedEncoder = new Huami2021ChunkedEncoder(getMTU()); } if (characteristicChunked2021Write == null || characteristicChunked2021Read == null) { @@ -1150,7 +1150,7 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer public void writeToChunked2021(final TransactionBuilder builder, final short endpoint, final byte[] data, final boolean encryptIgnored) { // Ensure communication for all services contains the encrypted flag reported by the service, since not all // watches have the same services encrypted (eg. #3308). - huami2021ChunkedEncoder.write(builder, endpoint, data, force2021Protocol(), mIsEncrypted.contains(endpoint)); + huami2021ChunkedEncoder.write(chunk -> builder.write(characteristicChunked2021Write, chunk), endpoint, data, force2021Protocol(), mIsEncrypted.contains(endpoint)); } @Override