mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-25 08:05:55 +01:00
Mi Band 8: Refactor cipher to auth service
This commit is contained in:
parent
e21b35981b
commit
f0188f3499
@ -53,12 +53,16 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.AbstractXiaomiService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class XiaomiCipher {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XiaomiCipher.class);
|
||||
public class XiaomiAuthService extends AbstractXiaomiService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XiaomiAuthService.class);
|
||||
|
||||
private final XiaomiSupport mSupport;
|
||||
public static final int COMMAND_TYPE = 1;
|
||||
|
||||
public static final int CMD_NONCE = 26;
|
||||
public static final int CMD_AUTH = 27;
|
||||
|
||||
private final byte[] secretKey = new byte[16];
|
||||
private final byte[] nonce = new byte[16];
|
||||
@ -67,55 +71,56 @@ public class XiaomiCipher {
|
||||
private final byte[] encryptionNonce = new byte[4];
|
||||
private final byte[] decryptionNonce = new byte[4];
|
||||
|
||||
public XiaomiCipher(final XiaomiSupport support) {
|
||||
this.mSupport = support;
|
||||
public XiaomiAuthService(final XiaomiSupport support) {
|
||||
super(support);
|
||||
}
|
||||
|
||||
protected void startAuthentication(final TransactionBuilder builder) {
|
||||
builder.add(new SetDeviceStateAction(mSupport.getDevice(), GBDevice.State.AUTHENTICATING, mSupport.getContext()));
|
||||
builder.add(new SetDeviceStateAction(getSupport().getDevice(), GBDevice.State.AUTHENTICATING, getSupport().getContext()));
|
||||
|
||||
System.arraycopy(getSecretKey(mSupport.getDevice()), 0, secretKey, 0, 16);
|
||||
System.arraycopy(getSecretKey(getSupport().getDevice()), 0, secretKey, 0, 16);
|
||||
new SecureRandom().nextBytes(nonce);
|
||||
|
||||
builder.write(
|
||||
mSupport.getCharacteristic(UUID_CHARACTERISTIC_XIAOMI_COMMAND_WRITE),
|
||||
getSupport().getCharacteristic(UUID_CHARACTERISTIC_XIAOMI_COMMAND_WRITE),
|
||||
ArrayUtils.addAll(XiaomiConstants.PAYLOAD_HEADER_AUTH, buildNonceCommand(nonce))
|
||||
);
|
||||
}
|
||||
|
||||
protected void handleAuthCommand(final XiaomiProto.Command cmd) {
|
||||
if (cmd.getType() != XiaomiConstants.CMD_TYPE_AUTH) {
|
||||
@Override
|
||||
public void handleCommand(final XiaomiProto.Command cmd) {
|
||||
if (cmd.getType() != COMMAND_TYPE) {
|
||||
throw new IllegalArgumentException("Not an auth command");
|
||||
}
|
||||
|
||||
switch (cmd.getSubtype()) {
|
||||
case XiaomiConstants.CMD_AUTH_NONCE: {
|
||||
case CMD_NONCE: {
|
||||
LOG.debug("Got watch nonce");
|
||||
|
||||
// Watch nonce
|
||||
final XiaomiProto.Command reply = handleWatchNonce(cmd.getAuth().getWatchNonce());
|
||||
if (reply == null) {
|
||||
mSupport.disconnect();
|
||||
getSupport().disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
final TransactionBuilder builder = mSupport.createTransactionBuilder("auth step 2");
|
||||
final TransactionBuilder builder = getSupport().createTransactionBuilder("auth step 2");
|
||||
// TODO maybe move these writes to support class?
|
||||
builder.write(
|
||||
mSupport.getCharacteristic(UUID_CHARACTERISTIC_XIAOMI_COMMAND_WRITE),
|
||||
getSupport().getCharacteristic(UUID_CHARACTERISTIC_XIAOMI_COMMAND_WRITE),
|
||||
ArrayUtils.addAll(XiaomiConstants.PAYLOAD_HEADER_AUTH, reply.toByteArray())
|
||||
);
|
||||
builder.queue(mSupport.getQueue());
|
||||
builder.queue(getSupport().getQueue());
|
||||
break;
|
||||
}
|
||||
|
||||
case XiaomiConstants.CMD_AUTH_AUTH: {
|
||||
case CMD_AUTH: {
|
||||
LOG.info("Authenticated!");
|
||||
|
||||
final TransactionBuilder builder = mSupport.createTransactionBuilder("phase 2 initialize");
|
||||
builder.add(new SetDeviceStateAction(mSupport.getDevice(), GBDevice.State.INITIALIZED, mSupport.getContext()));
|
||||
mSupport.phase2Initialize(builder);
|
||||
builder.queue(mSupport.getQueue());
|
||||
final TransactionBuilder builder = getSupport().createTransactionBuilder("phase 2 initialize");
|
||||
builder.add(new SetDeviceStateAction(getSupport().getDevice(), GBDevice.State.INITIALIZED, getSupport().getContext()));
|
||||
getSupport().phase2Initialize(builder);
|
||||
builder.queue(getSupport().getQueue());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -190,8 +195,8 @@ public class XiaomiCipher {
|
||||
.build();
|
||||
|
||||
final XiaomiProto.Command.Builder cmd = XiaomiProto.Command.newBuilder();
|
||||
cmd.setType(XiaomiConstants.CMD_TYPE_AUTH);
|
||||
cmd.setSubtype(XiaomiConstants.CMD_AUTH_AUTH);
|
||||
cmd.setType(COMMAND_TYPE);
|
||||
cmd.setSubtype(CMD_AUTH);
|
||||
|
||||
final XiaomiProto.Auth.Builder auth = XiaomiProto.Auth.newBuilder();
|
||||
auth.setAuthStep3(authStep3);
|
||||
@ -207,8 +212,8 @@ public class XiaomiCipher {
|
||||
auth.setPhoneNonce(phoneNonce.build());
|
||||
|
||||
final XiaomiProto.Command.Builder command = XiaomiProto.Command.newBuilder();
|
||||
command.setType(XiaomiConstants.CMD_TYPE_AUTH);
|
||||
command.setSubtype(XiaomiConstants.CMD_AUTH_NONCE);
|
||||
command.setType(COMMAND_TYPE);
|
||||
command.setSubtype(CMD_NONCE);
|
||||
command.setAuth(auth.build());
|
||||
return command.build().toByteArray();
|
||||
}
|
@ -39,12 +39,11 @@ public class XiaomiConstants {
|
||||
public static final UUID UUID_CHARACTERISTIC_XIAOMI_UNKNOWN_0002 = UUID.fromString((String.format(BASE_UUID, "0002")));
|
||||
public static final UUID UUID_CHARACTERISTIC_XIAOMI_UNKNOWN_0003 = UUID.fromString((String.format(BASE_UUID, "0003")));
|
||||
|
||||
public static final int CMD_TYPE_AUTH = 1;
|
||||
|
||||
public static final int CMD_AUTH_NONCE = 26;
|
||||
public static final int CMD_AUTH_AUTH = 27;
|
||||
|
||||
// TODO not like this
|
||||
public static final byte[] PAYLOAD_ACK = new byte[]{0, 0, 3, 0};
|
||||
public static final byte[] PAYLOAD_HEADER_AUTH = new byte[]{0, 0, 2, 2};
|
||||
public static final byte[] PAYLOAD_CHUNKED_START = new byte[]{0, 0, 0, 1};
|
||||
public static final byte[] PAYLOAD_CHUNKED_START_ACK = new byte[]{0, 0, 1, 1};
|
||||
public static final byte[] PAYLOAD_CHUNKED_END_ACK = new byte[]{0, 0, 1, 0};
|
||||
public static final byte[] PAYLOAD_HEADER_AUTH = new byte[]{0, 0, 2, 2};
|
||||
public static final byte[] PAYLOAD_HEADER_CMD = new byte[]{0, 0, 2, 1};
|
||||
public static final byte[] PAYLOAD_ACK = new byte[]{0, 0, 3, 0};
|
||||
}
|
||||
|
@ -69,8 +69,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XiaomiSupport.class);
|
||||
|
||||
private final XiaomiCipher cipher = new XiaomiCipher(this);
|
||||
|
||||
private final XiaomiAuthService authService = new XiaomiAuthService(this);
|
||||
private final XiaomiMusicService musicService = new XiaomiMusicService(this);
|
||||
private final XiaomiHealthService healthService = new XiaomiHealthService(this);
|
||||
private final XiaomiNotificationService notificationService = new XiaomiNotificationService(this);
|
||||
@ -79,6 +78,7 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
private final XiaomiSystemService systemService = new XiaomiSystemService(this);
|
||||
|
||||
private final Map<Integer, AbstractXiaomiService> mServiceMap = new LinkedHashMap<Integer, AbstractXiaomiService>() {{
|
||||
put(XiaomiAuthService.COMMAND_TYPE, authService);
|
||||
put(XiaomiMusicService.COMMAND_TYPE, musicService);
|
||||
put(XiaomiHealthService.COMMAND_TYPE, healthService);
|
||||
put(XiaomiNotificationService.COMMAND_TYPE, notificationService);
|
||||
@ -133,7 +133,7 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
builder.notify(getCharacteristic(UUID_CHARACTERISTIC_XIAOMI_COMMAND_READ), true);
|
||||
builder.notify(getCharacteristic(UUID_CHARACTERISTIC_XIAOMI_COMMAND_WRITE), true);
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||
cipher.startAuthentication(builder);
|
||||
authService.startAuthentication(builder);
|
||||
|
||||
return builder;
|
||||
}
|
||||
@ -168,6 +168,9 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
LOG.warn("Non-zero header not supported");
|
||||
return true;
|
||||
}
|
||||
if (type == 0) {
|
||||
// Chunked
|
||||
}
|
||||
if (type != 2) {
|
||||
LOG.warn("Unsupported type {}", type);
|
||||
return true;
|
||||
@ -175,7 +178,7 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
final byte[] plainValue;
|
||||
if (encryption == 1) {
|
||||
plainValue = cipher.decrypt(ArrayUtils.subarray(value, 4, value.length));
|
||||
plainValue = authService.decrypt(ArrayUtils.subarray(value, 4, value.length));
|
||||
} else {
|
||||
plainValue = ArrayUtils.subarray(value, 4, value.length);
|
||||
}
|
||||
@ -196,11 +199,6 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cmd.getType() == CMD_TYPE_AUTH) {
|
||||
cipher.handleAuthCommand(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.warn("Unexpected watch command type {}", cmd.getType());
|
||||
return true;
|
||||
}
|
||||
@ -441,7 +439,7 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
public void sendCommand(final TransactionBuilder builder, final XiaomiProto.Command command) {
|
||||
final byte[] commandBytes = command.toByteArray();
|
||||
final byte[] encryptedCommandBytes = cipher.encrypt(commandBytes, encryptedIndex);
|
||||
final byte[] encryptedCommandBytes = authService.encrypt(commandBytes, encryptedIndex);
|
||||
final ByteBuffer buf = ByteBuffer.allocate(6 + encryptedCommandBytes.length).order(ByteOrder.LITTLE_ENDIAN);
|
||||
buf.putShort((short) 0);
|
||||
buf.put((byte) 2); // 2 for command
|
||||
|
Loading…
Reference in New Issue
Block a user