1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-24 07:38:45 +02:00

Display toast on authentication failures

This commit is contained in:
José Rebelo 2024-06-10 16:29:53 +01:00
parent 76d633cde6
commit 4926f449ec
8 changed files with 82 additions and 34 deletions

View File

@ -220,7 +220,10 @@ public class CmfCharacteristic {
return;
}
} catch (final GeneralSecurityException e) {
LOG.error("Failed to decrypt payload for {}", cmd, e);
LOG.error("Failed to decrypt payload for {} ({}/{})", cmd, String.format("0x%04x", cmd1), String.format("0x%04x", cmd2), e);
if (cmd == CmfCommand.AUTH_FAILED) {
handler.onCommand(cmd, new byte[0]);
}
if (chunkCount > 1) {
chunkBuffers.remove(cmd);
}

View File

@ -33,6 +33,7 @@ public enum CmfCommand {
AUTH_PAIR_REQUEST(0xffff, 0x8047),
AUTH_PHONE_NAME(0xffff, 0x8049),
AUTH_WATCH_MAC(0xffff, 0x0049),
AUTH_FAILED(0xffff, 0xa061),
AUTHENTICATED_CONFIRM_REPLY(0xffff, 0x0004),
AUTHENTICATED_CONFIRM_REQUEST(0xffff, 0x804d),
BATTERY(0x005c, 0x0001),

View File

@ -24,6 +24,7 @@ import android.content.SharedPreferences;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.widget.Toast;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
@ -41,6 +42,7 @@ import java.util.TimeZone;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
@ -219,6 +221,14 @@ public class CmfWatchProSupport extends AbstractBTLEDeviceSupport implements Cmf
}
switch (cmd) {
case AUTH_FAILED:
LOG.error("Authentication failed, disconnecting");
GB.toast(getContext(), R.string.authentication_failed_check_key, Toast.LENGTH_LONG, GB.WARN);
final GBDevice device = getDevice();
if (device != null) {
GBApplication.deviceService(device).disconnect();
}
return;
case AUTH_WATCH_MAC:
LOG.debug("Got auth watch mac, requesting nonce");
sendCommand("auth request nonce", CmfCommand.AUTH_NONCE_REQUEST, A5);

View File

@ -38,6 +38,7 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation;
@ -114,30 +115,34 @@ public class InitOperation extends AbstractBTLEOperation<HuamiSupport> {
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
UUID characteristicUUID = characteristic.getUuid();
if (HuamiService.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
try {
byte[] value = characteristic.getValue();
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 device");
builder.write(characteristic, requestAuthNumber());
huamiSupport.performImmediately(builder);
} else if (value[0] == HuamiService.AUTH_RESPONSE &&
(value[1] & 0x0f) == HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER &&
value[2] == HuamiService.AUTH_SUCCESS) {
byte[] eValue = handleAESAuth(value, getSecretKey());
byte[] responseValue = org.apache.commons.lang3.ArrayUtils.addAll(
new byte[]{(byte) (HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER | cryptFlags), authFlags}, eValue);
if (!HuamiService.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
LOG.info("Unhandled characteristic changed: {}", characteristicUUID);
return super.onCharacteristicChanged(gatt, characteristic);
}
TransactionBuilder builder = createTransactionBuilder("Sending the encrypted random key to the device");
builder.write(characteristic, responseValue);
huamiSupport.setCurrentTimeWithService(builder);
huamiSupport.performImmediately(builder);
} else if (value[0] == HuamiService.AUTH_RESPONSE &&
(value[1] & 0x0f) == HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER &&
value[2] == HuamiService.AUTH_SUCCESS) {
try {
final byte[] value = characteristic.getValue();
huamiSupport.logMessageContent(value);
if (value[0] != HuamiService.AUTH_RESPONSE) {
LOG.warn("Got a non-response: {}", GB.hexdump(value));
return super.onCharacteristicChanged(gatt, characteristic);
}
if (value[1] == HuamiService.AUTH_SEND_KEY && value[2] == HuamiService.AUTH_SUCCESS) {
TransactionBuilder builder = createTransactionBuilder("Sending the secret key to the device");
builder.write(characteristic, requestAuthNumber());
huamiSupport.performImmediately(builder);
} else if ((value[1] & 0x0f) == HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER && value[2] == HuamiService.AUTH_SUCCESS) {
byte[] eValue = handleAESAuth(value, getSecretKey());
byte[] responseValue = org.apache.commons.lang3.ArrayUtils.addAll(
new byte[]{(byte) (HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER | cryptFlags), authFlags}, eValue);
TransactionBuilder builder = createTransactionBuilder("Sending the encrypted random key to the device");
builder.write(characteristic, responseValue);
huamiSupport.setCurrentTimeWithService(builder);
huamiSupport.performImmediately(builder);
} else if ((value[1] & 0x0f) == HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER) {
if (value[2] == HuamiService.AUTH_SUCCESS) {
TransactionBuilder builder = createTransactionBuilder("Authenticated, now initialize phase 2");
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
huamiSupport.enableFurtherNotifications(builder, true);
@ -146,17 +151,23 @@ public class InitOperation extends AbstractBTLEOperation<HuamiSupport> {
huamiSupport.phase3Initialize(builder);
huamiSupport.setInitialized(builder);
huamiSupport.performImmediately(builder);
} else if (value[2] == HuamiService.AUTH_FAIL) {
LOG.error("Authentication failed, disconnecting");
GB.toast(getContext(), R.string.authentication_failed_check_key, Toast.LENGTH_LONG, GB.WARN);
final GBDevice device = getDevice();
if (device != null) {
GBApplication.deviceService(device).disconnect();
}
} else {
return super.onCharacteristicChanged(gatt, characteristic);
}
} catch (Exception e) {
GB.toast(getContext(), "Error authenticating Huami device", Toast.LENGTH_LONG, GB.ERROR, e);
} else {
return super.onCharacteristicChanged(gatt, characteristic);
}
return true;
} else {
LOG.info("Unhandled characteristic changed: " + characteristicUUID);
return super.onCharacteristicChanged(gatt, characteristic);
} catch (Exception e) {
GB.toast(getContext(), "Error authenticating Huami device", Toast.LENGTH_LONG, GB.ERROR, e);
}
return true;
}
private byte[] handleAESAuth(byte[] value, byte[] secretKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException {

View File

@ -21,6 +21,7 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.SU
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -29,6 +30,8 @@ import java.util.Random;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Service;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -193,10 +196,15 @@ public class InitOperation2021 extends InitOperation implements Huami2021Handler
} catch (Exception e) {
LOG.error("failed initializing device", e);
}
return;
} else if (payload[0] == RESPONSE && payload[1] == 0x05 && payload[2] == 0x25) {
LOG.error("Authentication failed, disconnecting");
GB.toast(getContext(), R.string.authentication_failed_check_key, Toast.LENGTH_LONG, GB.WARN);
final GBDevice device = getDevice();
if (device != null) {
GBApplication.deviceService(device).disconnect();
}
} else {
LOG.info("Unhandled auth payload: {}", GB.hexdump(payload));
return;
LOG.warn("Unhandled auth payload: {}", GB.hexdump(payload));
}
}
}

View File

@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi;
import android.content.SharedPreferences;
import android.os.Build;
import android.widget.Toast;
import androidx.annotation.Nullable;
@ -47,6 +48,7 @@ import javax.crypto.spec.SecretKeySpec;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.AbstractXiaomiService;
@ -116,6 +118,7 @@ public class XiaomiAuthService extends AbstractXiaomiService {
final XiaomiProto.Command command = handleWatchNonce(cmd.getAuth().getWatchNonce());
if (command == null) {
GB.toast(getSupport().getContext(), R.string.authentication_failed_check_key, Toast.LENGTH_LONG, GB.WARN);
LOG.error("handleWatchNonce returned null, disconnecting");
final GBDevice device = getSupport().getDevice();
@ -142,7 +145,13 @@ public class XiaomiAuthService extends AbstractXiaomiService {
getSupport().onAuthSuccess();
} else {
LOG.warn("could not authenticate");
LOG.warn("Authentication failed, subtype={}, status={}", cmd.getSubtype(), cmd.getStatus());
GB.toast(getSupport().getContext(), R.string.authentication_failed_check_key, Toast.LENGTH_LONG, GB.WARN);
final GBDevice device = getSupport().getDevice();
if (device != null) {
GBApplication.deviceService(device).disconnect();
}
}
break;
}

View File

@ -38,6 +38,7 @@ import android.text.SpannableString;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@ -446,6 +447,10 @@ public class GB {
toast(context, message, displayTime, severity, null);
}
public static void toast(final Context context, @StringRes final int message, final int displayTime, final int severity) {
toast(context, context.getString(message), displayTime, severity, null);
}
/**
* Creates and display a Toast message using the application context
* Can be called from any thread.

View File

@ -1137,6 +1137,7 @@
<string name="activity_prefs_chart_min_session_length">Minimal activity length (minutes)</string>
<string name="authenticating">Authenticating</string>
<string name="authentication_required">Authentication required</string>
<string name="authentication_failed_check_key">Authentication failed, please check auth key</string>
<string name="activity_prefs_sleep_duration">Preferred sleep duration in hours</string>
<string name="device_hw">Hardware revision: %1$s</string>
<string name="device_fw">Firmware version: %1$s</string>