1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-20 12:00:51 +02:00

Fix Huawei exceptions related to early packets

Also adds more robust tag checks for Huawei packets
This commit is contained in:
Martin.JM 2024-01-19 21:37:25 +01:00
parent 773132c4e1
commit 4b38a67a58
11 changed files with 64 additions and 232 deletions

View File

@ -44,6 +44,7 @@ public class HuaweiPacket {
private static final Logger LOG = LoggerFactory.getLogger(HuaweiPacket.class);
public static class ParamsProvider {
protected byte authVersion;
protected byte authMode;
protected byte[] secretKey;
@ -226,6 +227,8 @@ public class HuaweiPacket {
}
public boolean attemptDecrypt() throws ParseException {
if (paramsProvider.getSecretKey() == null)
return false;
if (this.tlv == null)
return false;
if (this.tlv.contains(0x7C) && this.tlv.getBoolean(0x7C)) {
@ -495,6 +498,8 @@ public class HuaweiPacket {
}
public HuaweiPacket parseOutgoing(byte[] data) throws ParseException {
this.isEncrypted = false; // Will be changed if decrypt has been performed
parseData(data);
if (!this.complete)
return this;

View File

@ -207,57 +207,35 @@ public class HuaweiTLV {
return this.valueMap;
}
public byte[] getBytes(int tag) {
public byte[] getBytes(int tag) throws HuaweiPacket.MissingTagException {
for (TLV item : valueMap)
if (item.getTag() == (byte) tag)
return item.getValue();
return null;
throw new HuaweiPacket.MissingTagException(tag);
}
public Byte getByte(int tag) {
byte[] bytes = getBytes(tag);
if (bytes == null) {
return null;
}
return bytes[0];
public Byte getByte(int tag) throws HuaweiPacket.MissingTagException {
return getBytes(tag)[0];
}
public Boolean getBoolean(int tag) {
byte[] bytes = getBytes(tag);
if (bytes == null) {
return null;
}
return bytes[0] == 1;
public Boolean getBoolean(int tag) throws HuaweiPacket.MissingTagException {
return getBytes(tag)[0] == 1;
}
public Integer getInteger(int tag) {
byte[] bytes = getBytes(tag);
if (bytes == null) {
return null;
}
return ByteBuffer.wrap(bytes).getInt();
public Integer getInteger(int tag) throws HuaweiPacket.MissingTagException {
return ByteBuffer.wrap(getBytes(tag)).getInt();
}
public Short getShort(int tag) {
byte[] bytes = getBytes(tag);
if (bytes == null)
return null;
return ByteBuffer.wrap(bytes).getShort();
public Short getShort(int tag) throws HuaweiPacket.MissingTagException {
return ByteBuffer.wrap(getBytes(tag)).getShort();
}
public String getString(int tag) {
byte[] bytes = getBytes(tag);
if (bytes == null) {
return null;
}
return new String(bytes, StandardCharsets.UTF_8);
public String getString(int tag) throws HuaweiPacket.MissingTagException {
return new String(getBytes(tag), StandardCharsets.UTF_8);
}
public HuaweiTLV getObject(int tag) {
public HuaweiTLV getObject(int tag) throws HuaweiPacket.MissingTagException {
byte[] bytes = getBytes(tag);
if (bytes == null) {
return null;
}
return new HuaweiTLV().parse(bytes, 0, bytes.length);
}
@ -320,7 +298,7 @@ public class HuaweiTLV {
.put(CryptoTags.cipherText, encryptedTLV);
}
public void decrypt(ParamsProvider paramsProvider) throws CryptoException {
public void decrypt(ParamsProvider paramsProvider) throws CryptoException, HuaweiPacket.MissingTagException {
byte[] key = paramsProvider.getSecretKey();
byte[] decryptedTLV = HuaweiCrypto.decrypt(paramsProvider.getAuthMode(), getBytes(CryptoTags.cipherText), key, getBytes(CryptoTags.initVector));
this.valueMap = new ArrayList<>();

View File

@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket.ParseException;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
// TODO: complete responses
@ -34,18 +35,7 @@ public class Alarms {
public byte repeat;
public String name;
public EventAlarm(HuaweiTLV tlv) throws HuaweiPacket.MissingTagException {
if (!tlv.contains(0x03))
throw new HuaweiPacket.MissingTagException(0x03);
if (!tlv.contains(0x04))
throw new HuaweiPacket.MissingTagException(0x04);
if (!tlv.contains(0x05))
throw new HuaweiPacket.MissingTagException(0x05);
if (!tlv.contains(0x06))
throw new HuaweiPacket.MissingTagException(0x06);
if (!tlv.contains(0x07))
throw new HuaweiPacket.MissingTagException(0x07);
public EventAlarm(HuaweiTLV tlv) throws ParseException {
this.index = tlv.getByte(0x03);
this.status = tlv.getBoolean(0x04);
this.startHour = (byte) ((tlv.getShort(0x05) >> 8) & 0xFF);
@ -93,18 +83,7 @@ public class Alarms {
public byte repeat;
public byte aheadTime;
public SmartAlarm(HuaweiTLV tlv) throws HuaweiPacket.MissingTagException {
if (!tlv.contains(0x03))
throw new HuaweiPacket.MissingTagException(0x03);
if (!tlv.contains(0x04))
throw new HuaweiPacket.MissingTagException(0x04);
if (!tlv.contains(0x05))
throw new HuaweiPacket.MissingTagException(0x05);
if (!tlv.contains(0x06))
throw new HuaweiPacket.MissingTagException(0x06);
if (!tlv.contains(0x07))
throw new HuaweiPacket.MissingTagException(0x07);
public SmartAlarm(HuaweiTLV tlv) throws ParseException {
this.index = tlv.getByte(0x03);
this.status = tlv.getBoolean(0x04);
this.startHour = (byte) ((tlv.getShort(0x05) >> 8) & 0xFF);
@ -224,9 +203,6 @@ public class Alarms {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x81))
throw new MissingTagException(0x81);
eventAlarms = new ArrayList<>();
HuaweiTLV tlv = this.tlv.getObject(0x81);
@ -262,9 +238,6 @@ public class Alarms {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x81))
throw new MissingTagException(0x81);
HuaweiTLV tlv = this.tlv.getObject(0x81);
if (tlv.contains(0x82)) {
this.smartAlarm = new SmartAlarm(tlv.getObject(0x82));

View File

@ -49,16 +49,12 @@ public class Calls {
@Override
public void parseTlv() throws MissingTagException {
if (this.tlv.contains(0x01)) {
if (this.tlv.getByte(0x01) == 0x01) {
this.action = Action.CALL_REJECT;
} else if (this.tlv.getByte(0x01) == 0x02) {
this.action = Action.CALL_ACCEPT;
}
// TODO: find more values, if there are any
} else {
throw new MissingTagException(0x01);
if (this.tlv.getByte(0x01) == 0x01) {
this.action = Action.CALL_REJECT;
} else if (this.tlv.getByte(0x01) == 0x02) {
this.action = Action.CALL_ACCEPT;
}
// TODO: find more values, if there are any
}
}
}

View File

@ -100,11 +100,8 @@ public class DeviceConfig {
if (this.tlv.contains(0x04))
this.interval = this.tlv.getShort(0x04);
if (this.tlv.contains(0x05)) {
System.arraycopy(this.tlv.getBytes(0x05), 2, this.serverNonce, 0, 16);
this.authVersion = (byte)this.tlv.getBytes(0x05)[1];
} else
throw new MissingTagException(0x05);
System.arraycopy(this.tlv.getBytes(0x05), 2, this.serverNonce, 0, 16);
this.authVersion = (byte)this.tlv.getBytes(0x05)[1];
if (this.tlv.contains(0x07))
this.authMode = this.tlv.getByte(0x07);
@ -152,11 +149,7 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x02)) {
this.supportedServices = this.tlv.getBytes(0x02);
} else {
throw new MissingTagException(0x02);
}
this.supportedServices = this.tlv.getBytes(0x02);
}
}
@ -175,10 +168,7 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01))
this.allSupportedServices = this.tlv.getBytes(0x01);
else
throw new MissingTagException(0x01);
this.allSupportedServices = this.tlv.getBytes(0x01);
}
}
}
@ -320,13 +310,6 @@ public class DeviceConfig {
CommandsList commandsList = null;
HuaweiTLV containerTLV = this.tlv.getObject(0x81);
if (!containerTLV.contains(0x02)) {
throw new MissingTagException(0x02);
}
if (!containerTLV.contains(0x04)) {
throw new MissingTagException(0x04);
}
for (HuaweiTLV.TLV tlv : containerTLV.get()) {
if ((int) tlv.getTag() == 0x02) {
commandsList = new CommandsList();
@ -465,10 +448,6 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x07))
throw new MissingTagException(0x07);
if (!this.tlv.contains(0x0A))
throw new MissingTagException(0x0A);
if (this.tlv.contains(0x03))
this.hardwareVersion = this.tlv.getString(0x03);
this.softwareVersion = this.tlv.getString(0x07);
@ -521,20 +500,9 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x05))
this.clientSerial = this.tlv.getBytes(0x05);
else
throw new MissingTagException(0x05);
if (this.tlv.contains(0x06))
this.bondingKey = this.tlv.getBytes(0x06);
else
throw new MissingTagException(0x06);
if (this.tlv.contains(0x07))
this.iv = this.tlv.getBytes(0x07);
else
throw new MissingTagException(0x07);
this.clientSerial = this.tlv.getBytes(0x05);
this.bondingKey = this.tlv.getBytes(0x06);
this.iv = this.tlv.getBytes(0x07);
}
}
}
@ -579,7 +547,7 @@ public class DeviceConfig {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
this.status = this.tlv.getByte(0x01);
this.encryptionCounter = this.tlv.getInteger(0x09) & 0xFFFFFFFFL;
}
@ -655,7 +623,7 @@ public class DeviceConfig {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
this.challengeResponse = this.tlv.getBytes(0x01);
}
}
@ -690,10 +658,7 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01))
this.level = this.tlv.getByte(0x01);
else
throw new MissingTagException(0x01);
this.level = this.tlv.getByte(0x01);
}
}
// TODO: implement parsing this request for the log parser support
@ -876,7 +841,7 @@ public class DeviceConfig {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
// AW70 doesn't seem to have this
if (this.tlv.contains(0x01))
this.status = this.tlv.getByte(0x01);
@ -944,7 +909,7 @@ public class DeviceConfig {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
this.dndLiftWristType = (int) this.tlv.getShort(0x01);
}
}
@ -1192,7 +1157,7 @@ public class DeviceConfig {
public static class Step4Data {
public String data;
public Step4Data(HuaweiTLV tlv) {
public Step4Data(HuaweiTLV tlv) throws ParseException {
if (tlv.contains(0x01))
this.data = tlv.getString(0x01);
}
@ -1231,11 +1196,6 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x01))
throw new MissingTagException(0x01);
if (!this.tlv.contains(0x04))
throw new MissingTagException(0x04);
this.type = this.tlv.getByte(0x04);
if (this.type == 0x00) {
@ -1377,11 +1337,6 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x01))
throw new MissingTagException(0x01);
if (!this.tlv.contains(0x02))
throw new MissingTagException(0x02);
try {
value = new JSONObject(this.tlv.getString(0x01));
payload = value.getJSONObject("payload");
@ -1440,16 +1395,9 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
byte[] message;
byte[] iv;
if (this.tlv.contains(0x01))
message = this.tlv.getBytes(0x01);
else
throw new MissingTagException(0x01);
if (this.tlv.contains(0x02))
iv = this.tlv.getBytes(0x02);
else
throw new MissingTagException(0x02);
byte[] message = this.tlv.getBytes(0x01);
byte[] iv = this.tlv.getBytes(0x02);
HuaweiCrypto huaweiCrypto = new HuaweiCrypto(paramsProvider.getAuthVersion());
try {
pinCode = huaweiCrypto.decryptPinCode(message, iv);
@ -1498,7 +1446,6 @@ public class DeviceConfig {
// Tag 4 to 6 are HMS related
}
}
}
public static class TimeZoneIdRequest extends HuaweiPacket {
@ -1628,10 +1575,7 @@ public class DeviceConfig {
@Override
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01)) {
this.expandCapabilities = this.tlv.getBytes(0x01);
} else
throw new MissingTagException(0x01);
this.expandCapabilities = this.tlv.getBytes(0x01);
}
}
@ -1666,7 +1610,6 @@ public class DeviceConfig {
public void parseTlv() throws ParseException {
}
}
}
public static class SetUpDeviceStatusRequest extends HuaweiPacket {
@ -1703,11 +1646,4 @@ public class DeviceConfig {
public static final int hours12 = 0x01;
public static final int hours24 = 0x02;
}
public static class HiChainStep {
public static final int one = 0x01;
public static final int two = 0x02;
public static final int three = 0x03;
public static final int four = 0x04;
}
}

View File

@ -37,10 +37,9 @@ public class FindPhone {
}
@Override
public void parseTlv() {
if (this.tlv.contains(0x01)) {
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01))
this.start = this.tlv.getBoolean(0x01);
}
// No missing tag exception so it will stop by default
}
}

View File

@ -104,7 +104,7 @@ public class FitnessData {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
this.count = this.tlv.getObject(0x81).getShort(0x02);
this.complete = true;
}
@ -156,7 +156,7 @@ public class FitnessData {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
HuaweiTLV container = this.tlv.getObject(0x81);
List<HuaweiTLV> subContainers = container.getObjects(0x83);
@ -256,11 +256,6 @@ public class FitnessData {
HuaweiTLV container = this.tlv.getObject(0x81);
List<HuaweiTLV> subContainers = container.getObjects(0x84);
if (!container.contains(0x02))
throw new MissingTagException(0x02);
if (!container.contains(0x03))
throw new MissingTagException(0x03);
this.number = container.getShort(0x02);
this.timestamp = container.getInteger(0x03);
this.containers = new ArrayList<>();
@ -394,7 +389,7 @@ public class FitnessData {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
HuaweiTLV container = this.tlv.getObject(0x81);
List<HuaweiTLV> containers = container.getObjects(0x83);
@ -555,6 +550,8 @@ public class FitnessData {
}
public static class Type {
// TODO: enum?
public static final byte goal = 0x01;
public static final byte motion = 0x00;
public static final byte data = 0x01;

View File

@ -51,7 +51,7 @@ public class MusicControl {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x7F) && this.tlv.getBytes(0x7F).length == 4)
this.status = this.tlv.getInteger(0x7F);
}
@ -96,7 +96,7 @@ public class MusicControl {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x7F)) {
if (this.tlv.getInteger(0x7F) == successValue) {
this.ok = true;
@ -144,7 +144,7 @@ public class MusicControl {
}
@Override
public void parseTlv() {
public void parseTlv() throws ParseException {
if (this.tlv.contains(0x01)) {
this.buttonPresent = true;
this.rawButton = this.tlv.getByte(0x01);

View File

@ -132,10 +132,12 @@ public class Notifications {
private void putByteBuffer(ByteBuffer bBuffer, byte position, byte[] value) {
ByteBuffer bValue = ByteBuffer.wrap(value);
if (bValue.capacity() == 2)
if (bValue.capacity() == 2) {
bBuffer.putShort(position, bValue.getShort());
bBuffer.put(position, (byte)0x00);
bBuffer.put(bValue.get());
} else {
bBuffer.put(position, (byte) 0x00);
bBuffer.put(position + 1, bValue.get());
}
}
@Override

View File

@ -69,32 +69,16 @@ public class Workout {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x81))
throw new MissingTagException(0x81);
HuaweiTLV container = this.tlv.getObject(0x81);
if (!container.contains(0x02))
throw new MissingTagException(0x02);
this.count = container.getShort(0x02);
this.workoutNumbers = new ArrayList<>();
if (this.count == 0)
return;
if (!container.contains(0x85))
throw new MissingTagException(0x85);
List<HuaweiTLV> subContainers = container.getObjects(0x85);
for (HuaweiTLV subContainerTlv : subContainers) {
if (!subContainerTlv.contains(0x06))
throw new MissingTagException(0x06);
if (!subContainerTlv.contains(0x07))
throw new MissingTagException(0x07);
if (!subContainerTlv.contains(0x08))
throw new MissingTagException(0x08);
WorkoutNumbers workoutNumber = new WorkoutNumbers();
workoutNumber.rawData = subContainerTlv.serialize();
workoutNumber.workoutNumber = subContainerTlv.getShort(0x06);
@ -150,18 +134,8 @@ public class Workout {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x81))
throw new MissingTagException(0x81);
HuaweiTLV container = this.tlv.getObject(0x81);
if (!container.contains(0x02))
throw new MissingTagException(0x02);
if (!container.contains(0x04))
throw new MissingTagException(0x04);
if (!container.contains(0x05))
throw new MissingTagException(0x05);
this.rawData = container.serialize();
this.number = container.getShort(0x02);
if (container.contains(0x03))
@ -319,24 +293,12 @@ public class Workout {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x81))
throw new MissingTagException(0x81);
HuaweiTLV container = this.tlv.getObject(0x81);
if (!container.contains(0x02))
throw new MissingTagException(0x02);
if (!container.contains(0x03))
throw new MissingTagException(0x03);
if (!container.contains(0x04))
throw new MissingTagException(0x04);
if (!container.contains(0x05))
throw new MissingTagException(0x05); // TODO: not sure if 5 can also be omitted
this.workoutNumber = container.getShort(0x02);
this.dataNumber = container.getShort(0x03);
this.rawHeader = container.getBytes(0x04);
this.rawData = container.getBytes(0x05);
this.rawData = container.getBytes(0x05); // TODO: not sure if 5 can also be omitted
if (container.contains(0x09))
innerBitmap = container.getShort(0x09);
@ -515,31 +477,13 @@ public class Workout {
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x81))
throw new MissingTagException(0x81);
HuaweiTLV container = this.tlv.getObject(0x81);
if (!container.contains(0x02))
throw new MissingTagException(0x02);
if (!container.contains(0x08))
throw new MissingTagException(0x08);
// TODO: not sure what happens with an empty workout here...
if (!container.contains(0x83))
throw new MissingTagException(0x83);
this.workoutNumber = container.getShort(0x02);
this.paceNumber = container.getShort(0x08);
this.blocks = new ArrayList<>();
for (HuaweiTLV blockTlv : container.getObjects(0x83)) {
if (!blockTlv.contains(0x04))
throw new MissingTagException(0x04);
if (!blockTlv.contains(0x05))
throw new MissingTagException(0x05);
if (!blockTlv.contains(0x06))
throw new MissingTagException(0x06);
Block block = new Block();
block.distance = blockTlv.getShort(0x04);
block.type = blockTlv.getByte(0x05);
@ -569,6 +513,4 @@ public class Workout {
}
}
}

View File

@ -78,6 +78,10 @@ public class AsynchronousResponse {
}
public void handleResponse(HuaweiPacket response) {
// Ignore messages if the key isn't set yet
if (support.getParamsProvider().getSecretKey() == null)
return;
try {
response.parseTlv();
} catch (HuaweiPacket.ParseException e) {