diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java
index 9516b3f07..57f1267fb 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java
@@ -590,7 +590,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate);
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
- if (coordinator.getSupportedDeviceSpecificSettings(device) != null) {
+ if (coordinator.getSupportedDeviceSpecificSettings(device) == null) {
return true;
}
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 e7b7cd0a1..6bc42278e 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
@@ -209,15 +209,16 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
try {
- heartRateNotifyEnabled = false;
- boolean authenticate = needsAuth;
- needsAuth = false;
byte authFlags = getAuthFlags();
- new InitOperation(authenticate, authFlags, this, builder).perform();
+ byte cryptFlags = getCryptFlags();
+ heartRateNotifyEnabled = false;
+ boolean authenticate = needsAuth && (cryptFlags == 0x00);
+ needsAuth = false;
+ new InitOperation(authenticate, authFlags, cryptFlags, this, builder).perform();
characteristicHRControlPoint = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT);
characteristicChunked = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER);
} catch (IOException e) {
- GB.toast(getContext(), "Initializing Mi Band 2 failed", Toast.LENGTH_SHORT, GB.ERROR, e);
+ GB.toast(getContext(), "Initializing Huami device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
}
return builder;
}
@@ -226,6 +227,10 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
return HuamiService.AUTH_BYTE;
}
+ public byte getCryptFlags() {
+ return 0x00;
+ }
+
/**
* Returns the given date/time (calendar) as a byte sequence, suitable for sending to the
* Mi Band 2 (or derivative). The band appears to not handle DST offsets, so we simply add this
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java
index 4824039e8..d7feb5b97 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java
@@ -26,6 +26,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand
public class MiBand4Support extends MiBand3Support {
+ @Override
+ public byte getCryptFlags() {
+ return (byte) 0x80;
+ }
+
@Override
public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException {
return null;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java
index c5bdfb43c..b655a8527 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java
@@ -16,6 +16,7 @@
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.SharedPreferences;
@@ -24,9 +25,7 @@ import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.IOException;
import java.security.InvalidKeyException;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.UUID;
@@ -52,21 +51,25 @@ public class InitOperation extends AbstractBTLEOperation {
private final TransactionBuilder builder;
private final boolean needsAuth;
private final byte authFlags;
+ private final byte cryptFlags;
+ private final HuamiSupport huamiSupport;
- public InitOperation(boolean needsAuth, byte authFlags, HuamiSupport support, TransactionBuilder builder) {
+ public InitOperation(boolean needsAuth, byte authFlags, byte cryptFlags, HuamiSupport support, TransactionBuilder builder) {
super(support);
+ this.huamiSupport = support;
this.needsAuth = needsAuth;
this.authFlags = authFlags;
+ this.cryptFlags = cryptFlags;
this.builder = builder;
builder.setGattCallback(this);
}
@Override
- protected void doPerform() throws IOException {
- getSupport().enableNotifications(builder, true);
+ protected void doPerform() {
+ huamiSupport.enableNotifications(builder, true);
if (needsAuth) {
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.AUTHENTICATING, getContext()));
- // write key to miband2
+ // write key to device
byte[] sendKey = org.apache.commons.lang3.ArrayUtils.addAll(new byte[]{HuamiService.AUTH_SEND_KEY, authFlags}, getSecretKey());
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_AUTH), sendKey);
} else {
@@ -77,7 +80,11 @@ public class InitOperation extends AbstractBTLEOperation {
}
private byte[] requestAuthNumber() {
- return new byte[]{HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER, authFlags};
+ if (cryptFlags == 0x00) {
+ return new byte[]{HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER, authFlags};
+ } else {
+ return new byte[]{(byte) (cryptFlags | HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER), authFlags, 0x02};
+ }
}
private byte[] getSecretKey() {
@@ -88,13 +95,17 @@ public class InitOperation extends AbstractBTLEOperation {
String authKey = sharedPrefs.getString("authkey", null);
if (authKey != null && !authKey.isEmpty()) {
byte[] srcBytes = authKey.getBytes();
- System.arraycopy(srcBytes, 0, authKeyBytes, 0, Math.min(srcBytes.length,16));
+ if (authKey.length() == 34 && authKey.substring(0, 2).equals("0x")) {
+ srcBytes = GB.hexStringToByteArray(authKey.substring(2));
+ }
+ System.arraycopy(srcBytes, 0, authKeyBytes, 0, Math.min(srcBytes.length, 16));
}
+
return authKeyBytes;
}
@Override
- public TransactionBuilder performInitialized(String taskName) throws IOException {
+ public TransactionBuilder performInitialized(String taskName) {
throw new UnsupportedOperationException("This IS the initialization class, you cannot call this method");
}
@@ -105,41 +116,40 @@ public class InitOperation extends AbstractBTLEOperation {
if (HuamiService.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
try {
byte[] value = characteristic.getValue();
- getSupport().logMessageContent(value);
+ huamiSupport.logMessageContent(value);
if (value[0] == HuamiService.AUTH_RESPONSE &&
value[1] == HuamiService.AUTH_SEND_KEY &&
value[2] == HuamiService.AUTH_SUCCESS) {
- TransactionBuilder builder = createTransactionBuilder("Sending the secret key to the band");
+ TransactionBuilder builder = createTransactionBuilder("Sending the secret key to the device");
builder.write(characteristic, requestAuthNumber());
- getSupport().performImmediately(builder);
+ huamiSupport.performImmediately(builder);
} else if (value[0] == HuamiService.AUTH_RESPONSE &&
- value[1] == HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER &&
+ (value[1] & 0x0f) == HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER &&
value[2] == HuamiService.AUTH_SUCCESS) {
- // md5??
byte[] eValue = handleAESAuth(value, getSecretKey());
byte[] responseValue = org.apache.commons.lang3.ArrayUtils.addAll(
- new byte[]{HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER, authFlags}, eValue);
+ new byte[]{(byte) (HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER | cryptFlags), authFlags}, eValue);
- TransactionBuilder builder = createTransactionBuilder("Sending the encrypted random key to the band");
+ TransactionBuilder builder = createTransactionBuilder("Sending the encrypted random key to the device");
builder.write(characteristic, responseValue);
- getSupport().setCurrentTimeWithService(builder);
- getSupport().performImmediately(builder);
+ huamiSupport.setCurrentTimeWithService(builder);
+ huamiSupport.performImmediately(builder);
} else if (value[0] == HuamiService.AUTH_RESPONSE &&
- value[1] == HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER &&
+ (value[1] & 0x0f) == HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER &&
value[2] == HuamiService.AUTH_SUCCESS) {
TransactionBuilder builder = createTransactionBuilder("Authenticated, now initialize phase 2");
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
- getSupport().requestDeviceInfo(builder);
- getSupport().enableFurtherNotifications(builder, true);
- getSupport().phase2Initialize(builder);
- getSupport().phase3Initialize(builder);
- getSupport().setInitialized(builder);
- getSupport().performImmediately(builder);
+ huamiSupport.requestDeviceInfo(builder);
+ huamiSupport.enableFurtherNotifications(builder, true);
+ huamiSupport.phase2Initialize(builder);
+ huamiSupport.phase3Initialize(builder);
+ huamiSupport.setInitialized(builder);
+ huamiSupport.performImmediately(builder);
} else {
return super.onCharacteristicChanged(gatt, characteristic);
}
} catch (Exception e) {
- GB.toast(getContext(), "Error authenticating Mi Band 2", Toast.LENGTH_LONG, GB.ERROR, e);
+ GB.toast(getContext(), "Error authenticating Huami device", Toast.LENGTH_LONG, GB.ERROR, e);
}
return true;
} else {
@@ -148,17 +158,11 @@ public class InitOperation extends AbstractBTLEOperation {
}
}
- private byte[] getMD5(byte[] message) throws NoSuchAlgorithmException {
- MessageDigest md5 = MessageDigest.getInstance("MD5");
- return md5.digest(message);
- }
-
private byte[] handleAESAuth(byte[] value, byte[] secretKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException {
byte[] mValue = Arrays.copyOfRange(value, 3, 19);
- Cipher ecipher = Cipher.getInstance("AES/ECB/NoPadding");
+ @SuppressLint("GetInstance") Cipher ecipher = Cipher.getInstance("AES/ECB/NoPadding");
SecretKeySpec newKey = new SecretKeySpec(secretKey, "AES");
ecipher.init(Cipher.ENCRYPT_MODE, newKey);
- byte[] enc = ecipher.doFinal(mValue);
- return enc;
+ return ecipher.doFinal(mValue);
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java
index aace268af..2f6acb03a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java
@@ -194,6 +194,16 @@ public class GB {
return new String(hexChars);
}
+ public static byte[] hexStringToByteArray(String s) {
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ + Character.digit(s.charAt(i+1), 16));
+ }
+ return data;
+ }
+
public static String formatRssi(short rssi) {
return String.valueOf(rssi);
}