mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-10-12 16:18:10 +02:00
Mi Band 6 auth: first full but nonworking version
This commit is contained in:
parent
1c6f055bef
commit
d352624629
@ -5,10 +5,26 @@ JNIEXPORT jbyteArray JNICALL
|
|||||||
Java_nodomain_freeyourgadget_gadgetbridge_service_devices_huami_operations_InitOperation2021_ecdh_1generate_1public(
|
Java_nodomain_freeyourgadget_gadgetbridge_service_devices_huami_operations_InitOperation2021_ecdh_1generate_1public(
|
||||||
JNIEnv *env, jobject thiz, jbyteArray private_ec) {
|
JNIEnv *env, jobject thiz, jbyteArray private_ec) {
|
||||||
jboolean isCopy;
|
jboolean isCopy;
|
||||||
jbyte *a = (*env)->GetByteArrayElements(env, private_ec, &isCopy);
|
jbyte *privec = (*env)->GetByteArrayElements(env, private_ec, &isCopy);
|
||||||
jbyte public_ec[48];
|
jbyte public_ec[48];
|
||||||
ecdh_generate_keys((unsigned char *) public_ec, (unsigned char *) a);
|
ecdh_generate_keys((unsigned char *) public_ec, (unsigned char *) privec);
|
||||||
jbyteArray return_array = (*env)->NewByteArray(env, 48);
|
jbyteArray return_array = (*env)->NewByteArray(env, 48);
|
||||||
(*env)->SetByteArrayRegion(env, return_array, 0, 48, public_ec);
|
(*env)->SetByteArrayRegion(env, return_array, 0, 48, public_ec);
|
||||||
return return_array;
|
return return_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jbyteArray JNICALL
|
||||||
|
Java_nodomain_freeyourgadget_gadgetbridge_service_devices_huami_operations_InitOperation2021_ecdh_1generate_1shared(
|
||||||
|
JNIEnv *env, jobject thiz, jbyteArray private_ec, jbyteArray remote_public_ec) {
|
||||||
|
|
||||||
|
jboolean isCopy;
|
||||||
|
jbyte *privec = (*env)->GetByteArrayElements(env, private_ec, &isCopy);
|
||||||
|
jbyte *rempubec = (*env)->GetByteArrayElements(env, remote_public_ec, &isCopy);
|
||||||
|
jbyte shared_ec[48];
|
||||||
|
|
||||||
|
ecdh_shared_secret((unsigned char *) privec, (unsigned char *) rempubec,
|
||||||
|
(unsigned char *) shared_ec);
|
||||||
|
jbyteArray return_array = (*env)->NewByteArray(env, 48);
|
||||||
|
(*env)->SetByteArrayRegion(env, return_array, 0, 48, shared_ec);
|
||||||
|
return return_array;
|
||||||
|
}
|
@ -87,7 +87,7 @@ public class InitOperation extends AbstractBTLEOperation<HuamiSupport> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getSecretKey() {
|
protected byte[] getSecretKey() {
|
||||||
byte[] authKeyBytes = new byte[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45};
|
byte[] authKeyBytes = new byte[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45};
|
||||||
|
|
||||||
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
|
||||||
|
@ -45,10 +45,17 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
public class InitOperation2021 extends InitOperation {
|
public class InitOperation2021 extends InitOperation {
|
||||||
|
private final byte[] privateEC = new byte[24];
|
||||||
private byte[] privateEC = new byte[24];
|
|
||||||
private byte[] publicEC;
|
private byte[] publicEC;
|
||||||
|
private byte[] remotePublicEC = new byte[48];
|
||||||
|
private byte[] remoteRandom = new byte[16];
|
||||||
private byte[] sharedEC;
|
private byte[] sharedEC;
|
||||||
|
private byte[] finalSharedSessionAES = new byte[16];
|
||||||
|
|
||||||
|
private final byte[] reassembleBuffer = new byte[512];
|
||||||
|
private int lastSequenceNumber = 0;
|
||||||
|
private int reassembleBuffer_pointer = 0;
|
||||||
|
private int reassembleBuffer_expectedBytes = 0;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.loadLibrary("tiny-edhc");
|
System.loadLibrary("tiny-edhc");
|
||||||
@ -78,29 +85,15 @@ public class InitOperation2021 extends InitOperation {
|
|||||||
|
|
||||||
private native byte[] ecdh_generate_public(byte[] privateEC);
|
private native byte[] ecdh_generate_public(byte[] privateEC);
|
||||||
|
|
||||||
|
private native byte[] ecdh_generate_shared(byte[] privateEC, byte[] remotePublicEC);
|
||||||
|
|
||||||
|
|
||||||
private void generateKeyPair() {
|
private void generateKeyPair() {
|
||||||
Random r = new Random();
|
Random r = new Random();
|
||||||
r.nextBytes(privateEC);
|
r.nextBytes(privateEC);
|
||||||
publicEC = ecdh_generate_public(privateEC);
|
publicEC = ecdh_generate_public(privateEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getSecretKey() {
|
|
||||||
byte[] authKeyBytes = new byte[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45};
|
|
||||||
|
|
||||||
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
|
|
||||||
|
|
||||||
String authKey = sharedPrefs.getString("authkey", null);
|
|
||||||
if (authKey != null && !authKey.isEmpty()) {
|
|
||||||
byte[] srcBytes = authKey.trim().getBytes();
|
|
||||||
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
|
@Override
|
||||||
public TransactionBuilder performInitialized(String taskName) {
|
public TransactionBuilder performInitialized(String taskName) {
|
||||||
throw new UnsupportedOperationException("This IS the initialization class, you cannot call this method");
|
throw new UnsupportedOperationException("This IS the initialization class, you cannot call this method");
|
||||||
@ -112,6 +105,58 @@ public class InitOperation2021 extends InitOperation {
|
|||||||
UUID characteristicUUID = characteristic.getUuid();
|
UUID characteristicUUID = characteristic.getUuid();
|
||||||
if (HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_READ.equals(characteristicUUID)) {
|
if (HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_READ.equals(characteristicUUID)) {
|
||||||
byte[] value = characteristic.getValue();
|
byte[] value = characteristic.getValue();
|
||||||
|
// 0x03 0x01 0x00 0x64 0x00 0x43 0x00 0x00 0x00 0x82 0x00 0x10 0x04 0x01 0x36 0x41 0xf2 0x5a 0x8f 0xb3
|
||||||
|
if (value.length > 1 && value[0] == 0x03) {
|
||||||
|
int sequenceNumber = value[4];
|
||||||
|
int headerSize;
|
||||||
|
if (sequenceNumber == 0 && value[9] == (byte) 0x82 && value[10] == 0x00 && value[11] == 0x10 && value[12] == 0x04 && value[13] == 0x01) {
|
||||||
|
reassembleBuffer_pointer = 0;
|
||||||
|
headerSize = 14;
|
||||||
|
reassembleBuffer_expectedBytes = value[5] - 3;
|
||||||
|
|
||||||
|
} else if (sequenceNumber > 0) {
|
||||||
|
if (sequenceNumber != lastSequenceNumber + 1) {
|
||||||
|
LOG.warn("unexpected sequence number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
headerSize = 5;
|
||||||
|
} else {
|
||||||
|
LOG.info("Unhandled characteristic changed: " + characteristicUUID);
|
||||||
|
return super.onCharacteristicChanged(gatt, characteristic);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesToCopy = value.length - headerSize;
|
||||||
|
System.arraycopy(value, headerSize, reassembleBuffer, reassembleBuffer_pointer, bytesToCopy);
|
||||||
|
reassembleBuffer_pointer += bytesToCopy;
|
||||||
|
|
||||||
|
lastSequenceNumber = sequenceNumber;
|
||||||
|
if (reassembleBuffer_pointer == reassembleBuffer_expectedBytes) {
|
||||||
|
System.arraycopy(reassembleBuffer, 0, remoteRandom, 0, 16);
|
||||||
|
System.arraycopy(reassembleBuffer, 16, remotePublicEC, 0, 48);
|
||||||
|
sharedEC = ecdh_generate_shared(privateEC, remotePublicEC);
|
||||||
|
byte[] secretKey = getSecretKey();
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
finalSharedSessionAES[i] = (byte) (sharedEC[i + 8] ^ secretKey[i]);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
byte[] encryptedRandom1 = encryptAES(remoteRandom, secretKey);
|
||||||
|
byte[] encryptedRandom2 = encryptAES(remoteRandom, finalSharedSessionAES);
|
||||||
|
if (encryptedRandom1.length == 16 && encryptedRandom2.length == 16) {
|
||||||
|
byte[] command = new byte[33];
|
||||||
|
command[0] = 0x05;
|
||||||
|
System.arraycopy(encryptedRandom1, 0, command, 1, 16);
|
||||||
|
System.arraycopy(encryptedRandom2, 0, command, 17, 16);
|
||||||
|
TransactionBuilder builder = createTransactionBuilder("Sending double encryted random to device");
|
||||||
|
huamiSupport.writeToChunked2021(builder, (short) 0x82, (byte) 0x67, command);
|
||||||
|
huamiSupport.performImmediately(builder);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("AES encryption failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
huamiSupport.logMessageContent(value);
|
huamiSupport.logMessageContent(value);
|
||||||
return super.onCharacteristicChanged(gatt, characteristic);
|
return super.onCharacteristicChanged(gatt, characteristic);
|
||||||
} else {
|
} else {
|
||||||
@ -121,8 +166,8 @@ public class InitOperation2021 extends InitOperation {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] handleAESAuth(byte[] value, byte[] secretKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException {
|
private byte[] encryptAES(byte[] value, byte[] secretKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException {
|
||||||
byte[] mValue = Arrays.copyOfRange(value, 3, 19);
|
byte[] mValue = Arrays.copyOfRange(value, 0, 16);
|
||||||
@SuppressLint("GetInstance") Cipher ecipher = Cipher.getInstance("AES/ECB/NoPadding");
|
@SuppressLint("GetInstance") Cipher ecipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||||
SecretKeySpec newKey = new SecretKeySpec(secretKey, "AES");
|
SecretKeySpec newKey = new SecretKeySpec(secretKey, "AES");
|
||||||
ecipher.init(Cipher.ENCRYPT_MODE, newKey);
|
ecipher.init(Cipher.ENCRYPT_MODE, newKey);
|
||||||
|
Loading…
Reference in New Issue
Block a user