mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-06-05 21:49:48 +02:00
Huami2021ChunkedEncoder: Remove dependency on BLE characteristic
This commit is contained in:
1
.idea/dictionaries/t.xml
generated
1
.idea/dictionaries/t.xml
generated
@@ -39,6 +39,7 @@
|
||||
<w>drobnič</w>
|
||||
<w>dulfo</w>
|
||||
<w>elagin</w>
|
||||
<w>encryptable</w>
|
||||
<w>eomäe</w>
|
||||
<w>ericsson</w>
|
||||
<w>exrizu</w>
|
||||
|
@@ -16,20 +16,17 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
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<byte[]> 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) {
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user