Compare commits

...

9 Commits

Author SHA1 Message Date
Damien 'Psolyca' Gaignon
fc857b8adb
[Huawei] Fix attribut keyword 2024-02-09 12:03:04 +01:00
Damien 'Psolyca' Gaignon
ef06fd4d5e
[Huawei] Fix test 2024-02-09 12:03:03 +01:00
Damien 'Psolyca' Gaignon
21b97a9276
[Huawei] Fix BondParamsRequest stopChain 2024-02-09 12:03:03 +01:00
Damien 'Psolyca' Gaignon
510b8096ed
[Huawei] Fix transactions encryption 2024-02-09 12:03:02 +01:00
Damien 'Psolyca' Gaignon
d5cecc4a84
[Huawei] GT2 transaction crypted 2024-02-09 12:03:02 +01:00
Damien 'Psolyca' Gaignon
bb5fe00643
[Huawei] Change bond request behaviour 2024-02-09 12:03:02 +01:00
Damien 'Psolyca' Gaignon
05c11cbd14
[Huawei] Add HiChainLite firstKey handling 2024-02-09 12:03:01 +01:00
Damien 'Psolyca' Gaignon
270212a771
[Huawei] Rename authMode to deviceSupportType 2024-02-09 12:03:01 +01:00
Damien 'Psolyca' Gaignon
2880297c51
[Huawei] Fix pin code encoding 2024-02-09 12:02:53 +01:00
25 changed files with 174 additions and 104 deletions

View File

@ -17,7 +17,9 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huawei; package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
import nodomain.freeyourgadget.gadgetbridge.util.CryptoUtils; import nodomain.freeyourgadget.gadgetbridge.util.CryptoUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
@ -79,16 +81,16 @@ public class HuaweiCrypto {
public static final long ENCRYPTION_COUNTER_MAX = 0xFFFFFFFF; public static final long ENCRYPTION_COUNTER_MAX = 0xFFFFFFFF;
protected int authVersion; protected int authVersion;
protected int authMode; protected int deviceSupportType;
protected byte authAlgo; protected byte authAlgo;
public HuaweiCrypto(int authVersion) { public HuaweiCrypto(int authVersion) {
this.authVersion = authVersion; this.authVersion = authVersion;
} }
public HuaweiCrypto(int authVersion, byte authAlgo, int authMode) { public HuaweiCrypto(int authVersion, byte authAlgo, int deviceSupportType) {
this(authVersion); this(authVersion);
this.authMode = authMode; this.deviceSupportType = deviceSupportType;
this.authAlgo = authAlgo; this.authAlgo = authAlgo;
} }
@ -100,13 +102,15 @@ public class HuaweiCrypto {
} }
private byte[] getDigestSecret() { private byte[] getDigestSecret() {
byte[] digest;
if (authVersion == 1) { if (authVersion == 1) {
return DIGEST_SECRET_v1; digest = DIGEST_SECRET_v1.clone();
} else if (authVersion == 2) { } else if (authVersion == 2) {
return DIGEST_SECRET_v2; digest = DIGEST_SECRET_v2.clone();
} else { } else {
return DIGEST_SECRET_v3; digest = DIGEST_SECRET_v3.clone();
} }
return digest;
} }
public byte[] computeDigest(byte[] message, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException { public byte[] computeDigest(byte[] message, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException {
byte[] digestSecret = getDigestSecret(); byte[] digestSecret = getDigestSecret();
@ -115,12 +119,16 @@ public class HuaweiCrypto {
.put(message) .put(message)
.array(); .array();
byte[] digestStep1 = CryptoUtils.calcHmacSha256(msgToDigest, nonce); byte[] digestStep1 = CryptoUtils.calcHmacSha256(msgToDigest, nonce);
return CryptoUtils.calcHmacSha256(digestStep1, nonce); byte[] challenge = ByteBuffer.allocate(0x40)
.put(CryptoUtils.calcHmacSha256(digestStep1, nonce))
.put(digestStep1)
.array();
return challenge;
} }
public byte[] computeDigestHiChainLite(byte[] message, byte[] key, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException { public byte[] computeDigestHiChainLite(byte[] message, byte[] key, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, UnsupportedEncodingException {
byte[] digestStep1; byte[] digestStep1;
byte[] hashKey = CryptoUtils.digest(key); byte[] hashKey = CryptoUtils.digest(GB.hexdump(key).getBytes("UTF-8"));
byte[] digestSecret = getDigestSecret(); byte[] digestSecret = getDigestSecret();
for (int i = 0; i < digestSecret.length; i++) { for (int i = 0; i < digestSecret.length; i++) {
digestSecret[i] = (byte) (((0xFF & hashKey[i]) ^ (digestSecret[i] & 0xFF)) & 0xFF); digestSecret[i] = (byte) (((0xFF & hashKey[i]) ^ (digestSecret[i] & 0xFF)) & 0xFF);
@ -130,15 +138,19 @@ public class HuaweiCrypto {
.put(message) .put(message)
.array(); .array();
if (authAlgo == 0x01) { if (authAlgo == 0x01) {
digestStep1 = CryptoUtils.pbkdf2Sha256(msgToDigest, nonce, 0x3e8, 0x100); digestStep1 = CryptoUtils.pbkdf2Sha256(GB.hexdump(msgToDigest), GB.hexdump(nonce), 0x3e8, 0x100);
} else { } else {
digestStep1 = CryptoUtils.calcHmacSha256(msgToDigest, nonce); digestStep1 = CryptoUtils.calcHmacSha256(msgToDigest, nonce);
} }
return CryptoUtils.calcHmacSha256(digestStep1, nonce); byte[] challenge = ByteBuffer.allocate(0x40)
.put(CryptoUtils.calcHmacSha256(digestStep1, nonce))
.put(digestStep1)
.array();
return challenge;
} }
public byte[] digestChallenge(byte[] secretKey, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException { public byte[] digestChallenge(byte[] secretKey, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, UnsupportedEncodingException {
if (authMode == 0x02) { if (deviceSupportType == 0x02) {
if (secretKey == null) if (secretKey == null)
return null; return null;
if (authVersion == 0x02) { if (authVersion == 0x02) {
@ -146,15 +158,19 @@ public class HuaweiCrypto {
.put(secretKey) .put(secretKey)
.put(MESSAGE_CHALLENGE) .put(MESSAGE_CHALLENGE)
.array(); .array();
return CryptoUtils.calcHmacSha256(key, nonce); byte[] challenge = ByteBuffer.allocate(0x40)
.put(CryptoUtils.calcHmacSha256(key, nonce))
.put(key)
.array();
return challenge;
} }
return computeDigestHiChainLite(MESSAGE_CHALLENGE, secretKey, nonce); return computeDigestHiChainLite(MESSAGE_CHALLENGE, secretKey, nonce);
} }
return computeDigest(MESSAGE_CHALLENGE, nonce); return computeDigest(MESSAGE_CHALLENGE, nonce);
} }
public byte[] digestResponse(byte[] secretKey, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException { public byte[] digestResponse(byte[] secretKey, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, UnsupportedEncodingException {
if (authMode == 0x02) { if (deviceSupportType == 0x02) {
if (secretKey == null) if (secretKey == null)
return null; return null;
if (authVersion == 0x02) { if (authVersion == 0x02) {
@ -162,7 +178,11 @@ public class HuaweiCrypto {
.put(secretKey) .put(secretKey)
.put(MESSAGE_RESPONSE) .put(MESSAGE_RESPONSE)
.array(); .array();
return CryptoUtils.calcHmacSha256(key, nonce); byte[] challenge = ByteBuffer.allocate(0x40)
.put(CryptoUtils.calcHmacSha256(key, nonce))
.put(key)
.array();
return challenge;
} }
return computeDigestHiChainLite(MESSAGE_RESPONSE, secretKey, nonce); return computeDigestHiChainLite(MESSAGE_RESPONSE, secretKey, nonce);
} }
@ -206,8 +226,9 @@ public class HuaweiCrypto {
return Arrays.copyOfRange(finalMixedKeyHash, 0, 16); return Arrays.copyOfRange(finalMixedKeyHash, 0, 16);
} }
public byte[] encryptBondingKey(byte[] data, String mac, byte[] iv) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IllegalArgumentException { public byte[] encryptBondingKey(byte encryptMethod, byte[] data, byte[] encryptionKey, byte[] iv) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IllegalArgumentException {
byte[] encryptionKey = createSecretKey(mac); if (encryptMethod == 0x01)
return CryptoUtils.encryptAES_GCM_NoPad(data, encryptionKey, iv, null);
return CryptoUtils.encryptAES_CBC_Pad(data, encryptionKey, iv); return CryptoUtils.encryptAES_CBC_Pad(data, encryptionKey, iv);
} }
@ -216,34 +237,32 @@ public class HuaweiCrypto {
return CryptoUtils.decryptAES_CBC_Pad(data, encryptionKey, iv); return CryptoUtils.decryptAES_CBC_Pad(data, encryptionKey, iv);
} }
public byte[] decryptPinCode(byte[] message, byte[] iv) throws CryptoException { public byte[] decryptPinCode(byte encryptMethod, byte[] message, byte[] iv) throws CryptoException {
byte[] secretKey = getDigestSecret(); byte[] secretKey = getDigestSecret();
try { try {
if (encryptMethod == 0x1)
return CryptoUtils.decryptAES_GCM_NoPad(message, secretKey, iv, null);
return CryptoUtils.decryptAES_CBC_Pad(message, secretKey, iv); return CryptoUtils.decryptAES_CBC_Pad(message, secretKey, iv);
} catch (Exception e) { } catch (Exception e) {
throw new CryptoException(e); throw new CryptoException(e);
} }
} }
public static byte[] encrypt(byte authMode, byte[] message, byte[] key, byte[] iv) throws CryptoException { public static byte[] encrypt(byte encryptMethod, byte[] message, byte[] key, byte[] iv) throws CryptoException {
try { try {
if (authMode == 0x04) { if (encryptMethod == 0x01)
return CryptoUtils.encryptAES_GCM_NoPad(message, key, iv, null); return CryptoUtils.encryptAES_GCM_NoPad(message, key, iv, null);
} else { return CryptoUtils.encryptAES_CBC_Pad(message, key, iv);
return CryptoUtils.encryptAES_CBC_Pad(message, key, iv);
}
} catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | BadPaddingException | InvalidKeyException | IllegalArgumentException e) { } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | BadPaddingException | InvalidKeyException | IllegalArgumentException e) {
throw new CryptoException(e); throw new CryptoException(e);
} }
} }
public static byte[] decrypt(byte authMode, byte[] message, byte[] key, byte[] iv) throws CryptoException { public static byte[] decrypt(byte encryptMethod, byte[] message, byte[] key, byte[] iv) throws CryptoException {
try { try {
if (authMode == 0x04) { if (encryptMethod == 0x01)
return CryptoUtils.decryptAES_GCM_NoPad(message, key, iv, null); return CryptoUtils.decryptAES_GCM_NoPad(message, key, iv, null);
} else { return CryptoUtils.decryptAES_CBC_Pad(message, key, iv);
return CryptoUtils.decryptAES_CBC_Pad(message, key, iv);
}
} catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | BadPaddingException | InvalidKeyException | IllegalArgumentException e) { } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | BadPaddingException | InvalidKeyException | IllegalArgumentException e) {
throw new CryptoException(e); throw new CryptoException(e);
} }

View File

@ -46,7 +46,7 @@ public class HuaweiPacket {
public static class ParamsProvider { public static class ParamsProvider {
protected byte authVersion; protected byte authVersion;
protected byte authMode; protected byte deviceSupportType;
protected byte[] secretKey; protected byte[] secretKey;
protected int slicesize = 0xf4; protected int slicesize = 0xf4;
protected boolean transactionsCrypted = true; protected boolean transactionsCrypted = true;
@ -57,6 +57,8 @@ public class HuaweiPacket {
protected byte interval; protected byte interval;
protected byte authAlgo; protected byte authAlgo;
protected byte encryptMethod;
protected byte[] firstKey;
public void setAuthVersion(byte authVersion) { public void setAuthVersion(byte authVersion) {
this.authVersion = authVersion; this.authVersion = authVersion;
@ -66,12 +68,12 @@ public class HuaweiPacket {
return this.authVersion; return this.authVersion;
} }
public void setAuthMode(byte authMode) { public void setDeviceSupportType(byte deviceSupportType) {
this.authMode = authMode; this.deviceSupportType = deviceSupportType;
} }
public byte getAuthMode(){ public byte getDeviceSupportType(){
return this.authMode; return this.deviceSupportType;
} }
public void setSecretKey(byte[] secretKey) { public void setSecretKey(byte[] secretKey) {
@ -123,7 +125,7 @@ public class HuaweiPacket {
public byte[] getIv() { public byte[] getIv() {
byte[] iv = null; byte[] iv = null;
if (this.authMode == 0x04) { if (this.deviceSupportType == 0x04) {
iv = HuaweiCrypto.generateNonce(); iv = HuaweiCrypto.generateNonce();
} else { } else {
ByteBuffer ivCounter = HuaweiCrypto.initializationVector(this.encryptionCounter); ByteBuffer ivCounter = HuaweiCrypto.initializationVector(this.encryptionCounter);
@ -144,6 +146,22 @@ public class HuaweiPacket {
public byte getAuthAlgo () { public byte getAuthAlgo () {
return this.authAlgo; return this.authAlgo;
} }
public void setEncryptMethod(byte encryptMethod) {
this.encryptMethod = encryptMethod;
}
public byte getEncryptMethod () {
return this.encryptMethod;
}
public void setFirstKey(byte[] firstKey) {
this.firstKey = firstKey;
}
public byte[] getFirstKey() {
return firstKey;
}
} }
public static abstract class ParseException extends Exception { public static abstract class ParseException extends Exception {

View File

@ -291,7 +291,7 @@ public class HuaweiTLV {
byte[] serializedTLV = serialize(); byte[] serializedTLV = serialize();
byte[] key = paramsProvider.getSecretKey(); byte[] key = paramsProvider.getSecretKey();
byte[] nonce = paramsProvider.getIv(); byte[] nonce = paramsProvider.getIv();
byte[] encryptedTLV = HuaweiCrypto.encrypt(paramsProvider.getAuthMode(), serializedTLV, key, nonce); byte[] encryptedTLV = HuaweiCrypto.encrypt(paramsProvider.getEncryptMethod(), serializedTLV, key, nonce);
return new HuaweiTLV() return new HuaweiTLV()
.put(CryptoTags.encryption, (byte) 0x01) .put(CryptoTags.encryption, (byte) 0x01)
.put(CryptoTags.initVector, nonce) .put(CryptoTags.initVector, nonce)
@ -300,7 +300,7 @@ public class HuaweiTLV {
public void decrypt(ParamsProvider paramsProvider) throws CryptoException, HuaweiPacket.MissingTagException { public void decrypt(ParamsProvider paramsProvider) throws CryptoException, HuaweiPacket.MissingTagException {
byte[] key = paramsProvider.getSecretKey(); byte[] key = paramsProvider.getSecretKey();
byte[] decryptedTLV = HuaweiCrypto.decrypt(paramsProvider.getAuthMode(), getBytes(CryptoTags.cipherText), key, getBytes(CryptoTags.initVector)); byte[] decryptedTLV = HuaweiCrypto.decrypt(paramsProvider.getEncryptMethod(), getBytes(CryptoTags.cipherText), key, getBytes(CryptoTags.initVector));
this.valueMap = new ArrayList<>(); this.valueMap = new ArrayList<>();
parse(decryptedTLV); parse(decryptedTLV);
} }

View File

@ -37,7 +37,6 @@ public class HuaweiWatchGT2Coordinator extends HuaweiBRCoordinator {
public HuaweiWatchGT2Coordinator() { public HuaweiWatchGT2Coordinator() {
super(); super();
getHuaweiCoordinator().setTransactionCrypted(false);
} }
@Override @Override

View File

@ -74,10 +74,11 @@ public class DeviceConfig {
public short sliceSize = 0x00f4; public short sliceSize = 0x00f4;
public byte authVersion = 0x00; public byte authVersion = 0x00;
public byte[] serverNonce = new byte[16]; public byte[] serverNonce = new byte[16];
public byte authMode = 0x00; public byte deviceSupportType = 0x00;
public byte authAlgo = 0x00; public byte authAlgo = 0x00;
public byte bondState = 0x00; public byte bondState = 0x00;
public short interval = 0x0; public short interval = 0x0;
public byte encryptMethod = 0x00;
public Response(ParamsProvider paramsProvider) { public Response(ParamsProvider paramsProvider) {
super(paramsProvider); super(paramsProvider);
@ -104,13 +105,16 @@ public class DeviceConfig {
this.authVersion = (byte)this.tlv.getBytes(0x05)[1]; this.authVersion = (byte)this.tlv.getBytes(0x05)[1];
if (this.tlv.contains(0x07)) if (this.tlv.contains(0x07))
this.authMode = this.tlv.getByte(0x07); this.deviceSupportType = this.tlv.getByte(0x07);
if (this.tlv.contains(0x08)) if (this.tlv.contains(0x08))
this.authAlgo = this.tlv.getByte(0x08); this.authAlgo = this.tlv.getByte(0x08);
if (this.tlv.contains(0x09)) if (this.tlv.contains(0x09))
this.bondState = this.tlv.getByte(0x09); this.bondState = this.tlv.getByte(0x09);
if (this.tlv.contains(0x0C))
this.encryptMethod = this.tlv.getByte(0x0C);
} }
} }
} }
@ -465,26 +469,21 @@ public class DeviceConfig {
public Request( public Request(
ParamsProvider paramsProvider, ParamsProvider paramsProvider,
byte[] clientSerial, byte[] clientSerial,
String mac, byte[] key,
HuaweiCrypto huaweiCrypto byte[] iv
) throws CryptoException { ) {
super(paramsProvider); super(paramsProvider);
this.serviceId = DeviceConfig.id; this.serviceId = DeviceConfig.id;
this.commandId = id; this.commandId = id;
byte[] iv = paramsProvider.getIv();
try { this.tlv = new HuaweiTLV()
this.tlv = new HuaweiTLV() .put(0x01)
.put(0x01) .put(0x03, (byte) 0x00)
.put(0x03, (byte) 0x00) .put(0x05, clientSerial)
.put(0x05, clientSerial) .put(0x06, key)
.put(0x06, huaweiCrypto.encryptBondingKey(paramsProvider.getSecretKey(), mac, iv)) .put(0x07, iv);
.put(0x07, iv); this.isEncrypted = false;
this.isEncrypted = false; this.complete = true;
this.complete = true;
} catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | BadPaddingException | InvalidKeyException e) {
throw new CryptoException("Bonding key creation exception", e);
}
} }
} }
@ -603,7 +602,7 @@ public class DeviceConfig {
this.tlv = new HuaweiTLV() this.tlv = new HuaweiTLV()
.put(0x01, challenge) .put(0x01, challenge)
.put(0x02, nonce); .put(0x02, nonce);
if (paramsProvider.getAuthMode() == 0x02) if (paramsProvider.getDeviceSupportType() == 0x02)
this.tlv.put(0x03, paramsProvider.getAuthAlgo()); this.tlv.put(0x03, paramsProvider.getAuthAlgo());
this.isEncrypted = false; this.isEncrypted = false;
this.complete = true; this.complete = true;
@ -1400,7 +1399,7 @@ public class DeviceConfig {
HuaweiCrypto huaweiCrypto = new HuaweiCrypto(paramsProvider.getAuthVersion()); HuaweiCrypto huaweiCrypto = new HuaweiCrypto(paramsProvider.getAuthVersion());
try { try {
pinCode = huaweiCrypto.decryptPinCode(message, iv); pinCode = huaweiCrypto.decryptPinCode(paramsProvider.getEncryptMethod(), message, iv);
} catch (HuaweiCrypto.CryptoException e) { } catch (HuaweiCrypto.CryptoException e) {
throw new CryptoException("Could not decrypt pinCode", e); throw new CryptoException("Could not decrypt pinCode", e);
} }
@ -1487,7 +1486,7 @@ public class DeviceConfig {
this.tlv = new HuaweiTLV() this.tlv = new HuaweiTLV()
.put(0x01, authMode); .put(0x01, authMode);
if (authMode == 0x02 || authMode == 0x04) if (authMode == 0x02 || authMode == 0x04)
this.tlv.put(0x02, (byte)0x01); this.tlv.put(0x02, (byte)0x01); //force to not reconnected else 0x02
this.tlv.put(0x05, deviceUUID) this.tlv.put(0x05, deviceUUID)
.put(0x03, (byte)0x01) .put(0x03, (byte)0x01)
.put(0x04, (byte)0x00); .put(0x04, (byte)0x00);

View File

@ -306,12 +306,12 @@ public class HuaweiSupportProvider {
// 1 or 3 : HiChain // 1 or 3 : HiChain
// 2 or 8 : HiChainLite -> normal mode // 2 or 8 : HiChainLite -> normal mode
// 4 : HiChain3 // 4 : HiChain3
byte authMode = paramsProvider.getAuthMode(); byte authMode = paramsProvider.getDeviceSupportType();
return authMode == 0x01 || authMode == 0x03 || authMode == 0x04 || isHiChainLite(); return authMode == 0x01 || authMode == 0x03 || authMode == 0x04 || isHiChainLite();
} }
protected boolean isHiChainLite() { protected boolean isHiChainLite() {
byte authMode = paramsProvider.getAuthMode(); byte authMode = paramsProvider.getDeviceSupportType();
return authMode == 0x02; return authMode == 0x02;
} }
@ -331,6 +331,7 @@ public class HuaweiSupportProvider {
initializeDeviceHiChainMode(linkParamsReq); initializeDeviceHiChainMode(linkParamsReq);
} else if (securityNegoReq.authType == 0x01 || securityNegoReq.authType == 0x02) { } else if (securityNegoReq.authType == 0x01 || securityNegoReq.authType == 0x02) {
LOG.debug("HiChain Lite mode"); LOG.debug("HiChain Lite mode");
// Keep track the gadget is connected
initializeDeviceHiChainLiteMode(linkParamsReq); initializeDeviceHiChainLiteMode(linkParamsReq);
} }
} }

View File

@ -19,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -30,6 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DeviceConfig; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DeviceConfig;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class GetAuthRequest extends Request { public class GetAuthRequest extends Request {
@ -40,7 +42,7 @@ public class GetAuthRequest extends Request {
protected byte authAlgo; protected byte authAlgo;
protected byte[] doubleNonce; protected byte[] doubleNonce;
protected byte[] key = null; protected byte[] key = null;
protected byte authMode; protected byte deviceSupportType;
public GetAuthRequest(HuaweiSupportProvider support, public GetAuthRequest(HuaweiSupportProvider support,
Request linkParamsReq) { Request linkParamsReq) {
@ -54,16 +56,16 @@ public class GetAuthRequest extends Request {
.array(); .array();
this.authVersion = paramsProvider.getAuthVersion(); this.authVersion = paramsProvider.getAuthVersion();
this.authAlgo = paramsProvider.getAuthAlgo(); this.authAlgo = paramsProvider.getAuthAlgo();
this.authMode = paramsProvider.getAuthMode(); this.deviceSupportType = paramsProvider.getDeviceSupportType();
this.huaweiCrypto = new HuaweiCrypto(authVersion, authAlgo, deviceSupportType);
} }
@Override @Override
protected List<byte[]> createRequest() throws RequestCreationException { protected List<byte[]> createRequest() throws RequestCreationException {
huaweiCrypto = new HuaweiCrypto(authVersion, authAlgo, authMode);
byte[] nonce; byte[] nonce;
try { try {
if (authMode == 0x02) { if (deviceSupportType == 0x02) {
key = paramsProvider.getPinCode(); key = paramsProvider.getPinCode();
if (authVersion == 0x02) if (authVersion == 0x02)
key = paramsProvider.getSecretKey(); key = paramsProvider.getSecretKey();
@ -72,13 +74,19 @@ public class GetAuthRequest extends Request {
.putShort(authVersion) .putShort(authVersion)
.put(clientNonce) .put(clientNonce)
.array(); .array();
byte[] challenge = huaweiCrypto.digestChallenge(key, doubleNonce); ByteBuffer digestedChallenge = ByteBuffer.wrap(huaweiCrypto.digestChallenge(key, doubleNonce));
byte[] challenge = new byte[0x20];
digestedChallenge.get(challenge, 0x00, 0x20);
LOG.debug("challenge: " + GB.hexdump(challenge));
byte[] firstKey = new byte[0x10];
digestedChallenge.get(firstKey, 0x00, 0x10);
paramsProvider.setFirstKey(firstKey);
if (challenge == null) if (challenge == null)
throw new RequestCreationException("Challenge null"); throw new RequestCreationException("Challenge null");
return new DeviceConfig.Auth.Request(paramsProvider, challenge, nonce).serialize(); return new DeviceConfig.Auth.Request(paramsProvider, challenge, nonce).serialize();
} catch (HuaweiPacket.CryptoException e) { } catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e); throw new RequestCreationException(e);
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException e) { } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | UnsupportedEncodingException e) {
throw new RequestCreationException("Digest exception", e); throw new RequestCreationException("Digest exception", e);
} }
} }
@ -91,7 +99,10 @@ public class GetAuthRequest extends Request {
throw new ResponseTypeMismatchException(receivedPacket, DeviceConfig.Auth.Response.class); throw new ResponseTypeMismatchException(receivedPacket, DeviceConfig.Auth.Response.class);
try { try {
byte[] expectedAnswer = huaweiCrypto.digestResponse(key, doubleNonce); ByteBuffer digestedChallenge = ByteBuffer.wrap(huaweiCrypto.digestResponse(key, doubleNonce));
byte[] expectedAnswer = new byte[0x20];
digestedChallenge.get(expectedAnswer, 0x00, 0x20);
LOG.debug("challenge: " + GB.hexdump(expectedAnswer));
if (expectedAnswer == null) if (expectedAnswer == null)
throw new ResponseParseException("Challenge null"); throw new ResponseParseException("Challenge null");
byte[] actualAnswer = ((DeviceConfig.Auth.Response) receivedPacket).challengeResponse; byte[] actualAnswer = ((DeviceConfig.Auth.Response) receivedPacket).challengeResponse;
@ -102,7 +113,7 @@ public class GetAuthRequest extends Request {
+ StringUtils.bytesToHex(expectedAnswer) + StringUtils.bytesToHex(expectedAnswer)
); );
} }
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException e) { } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | UnsupportedEncodingException e) {
throw new ResponseParseException("Challenge response digest exception"); throw new ResponseParseException("Challenge response digest exception");
} }
} }

View File

@ -55,9 +55,10 @@ public class GetBondParamsRequest extends Request {
throw new ResponseTypeMismatchException(receivedPacket, DeviceConfig.BondParams.Response.class); throw new ResponseTypeMismatchException(receivedPacket, DeviceConfig.BondParams.Response.class);
paramsProvider.setEncryptionCounter(((DeviceConfig.BondParams.Response) receivedPacket).encryptionCounter); paramsProvider.setEncryptionCounter(((DeviceConfig.BondParams.Response) receivedPacket).encryptionCounter);
int status = ((DeviceConfig.BondParams.Response) receivedPacket).status; if (paramsProvider.getDeviceSupportType() != 0x02) {
if (status == 1) { if (((DeviceConfig.BondParams.Response) receivedPacket).status == 1) {
stopChain(this); stopChain(this);
}
} }
} }
} }

View File

@ -19,11 +19,20 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List; import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DeviceConfig; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DeviceConfig;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class GetBondRequest extends Request { public class GetBondRequest extends Request {
private static final Logger LOG = LoggerFactory.getLogger(GetBondRequest.class); private static final Logger LOG = LoggerFactory.getLogger(GetBondRequest.class);
@ -39,14 +48,25 @@ public class GetBondRequest extends Request {
@Override @Override
protected List<byte[]> createRequest() throws RequestCreationException { protected List<byte[]> createRequest() throws RequestCreationException {
try { try {
byte[] iv = paramsProvider.getIv();
huaweiCrypto = new HuaweiCrypto(paramsProvider.getAuthVersion());
byte[] encryptionKey;
if (paramsProvider.getDeviceSupportType() == 0x02) { //HiChainLite
encryptionKey = paramsProvider.getFirstKey();
} else {
encryptionKey = huaweiCrypto.createSecretKey(supportProvider.getDeviceMac());
}
byte[] key = huaweiCrypto.encryptBondingKey(paramsProvider.getEncryptMethod(), paramsProvider.getSecretKey(), encryptionKey, iv);
LOG.debug("key: " + GB.hexdump(key));
return new DeviceConfig.Bond.Request( return new DeviceConfig.Bond.Request(
paramsProvider, paramsProvider,
supportProvider.getSerial(), supportProvider.getSerial(),
supportProvider.getDeviceMac(), key,
huaweiCrypto iv
).serialize(); ).serialize();
} catch (HuaweiPacket.CryptoException e) { } catch (HuaweiPacket.CryptoException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | IllegalBlockSizeException |
throw new RequestCreationException(e); BadPaddingException e) {
throw new RequestCreationException(e.toString());
} }
} }

View File

@ -73,7 +73,7 @@ public class GetLinkParamsRequest extends Request {
throw new ResponseTypeMismatchException(receivedPacket, LinkParams.Response.class); throw new ResponseTypeMismatchException(receivedPacket, LinkParams.Response.class);
supportProvider.setProtocolVersion(((LinkParams.Response) receivedPacket).protocolVersion); supportProvider.setProtocolVersion(((LinkParams.Response) receivedPacket).protocolVersion);
paramsProvider.setAuthMode(((LinkParams.Response) receivedPacket).authMode); paramsProvider.setDeviceSupportType(((LinkParams.Response) receivedPacket).deviceSupportType);
paramsProvider.setSliceSize(((LinkParams.Response) receivedPacket).sliceSize); paramsProvider.setSliceSize(((LinkParams.Response) receivedPacket).sliceSize);
paramsProvider.setMtu(((LinkParams.Response) receivedPacket).mtu); paramsProvider.setMtu(((LinkParams.Response) receivedPacket).mtu);
@ -84,5 +84,6 @@ public class GetLinkParamsRequest extends Request {
this.bondState = ((LinkParams.Response) receivedPacket).bondState; this.bondState = ((LinkParams.Response) receivedPacket).bondState;
paramsProvider.setAuthAlgo(((LinkParams.Response) receivedPacket).authAlgo); paramsProvider.setAuthAlgo(((LinkParams.Response) receivedPacket).authAlgo);
paramsProvider.setEncryptMethod(((LinkParams.Response) receivedPacket).encryptMethod);
} }
} }

View File

@ -42,7 +42,7 @@ public class GetSecurityNegotiationRequest extends Request {
try { try {
return new DeviceConfig.SecurityNegotiation.Request( return new DeviceConfig.SecurityNegotiation.Request(
paramsProvider, paramsProvider,
paramsProvider.getAuthMode(), paramsProvider.getDeviceSupportType(),
supportProvider.getAndroidId(), supportProvider.getAndroidId(),
Build.MODEL Build.MODEL
).serialize(); ).serialize();

View File

@ -102,7 +102,7 @@ public class Request {
protected RequestCallback finalizeReq = null; protected RequestCallback finalizeReq = null;
// Stop chaining requests and clean support.inProgressRequests from these requests // Stop chaining requests and clean support.inProgressRequests from these requests
protected boolean stopChain = false; protected boolean stopChain = false;
protected static HuaweiCrypto huaweiCrypto = null; protected HuaweiCrypto huaweiCrypto = null;
protected boolean addToResponse = true; protected boolean addToResponse = true;
public static class RequestCallback { public static class RequestCallback {

View File

@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.util;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
@ -133,10 +134,9 @@ public class CryptoUtils {
return result; return result;
} }
public static byte[] pbkdf2Sha256(byte[] key, byte[] iv, int count, int length) throws InvalidKeySpecException, NoSuchAlgorithmException { public static byte[] pbkdf2Sha256(String key, String iv, int count, int length) throws InvalidKeySpecException, NoSuchAlgorithmException, UnsupportedEncodingException {
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
String keyStr = new String(key, StandardCharsets.UTF_8); PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray(), iv.getBytes("utf-8"), count, length);
PBEKeySpec keySpec = new PBEKeySpec(keyStr.toCharArray(), iv, count, length);
return secretKeyFactory.generateSecret(keySpec).getEncoded(); return secretKeyFactory.generateSecret(keySpec).getEncoded();
} }
} }

View File

@ -29,7 +29,7 @@ public class TestHuaweiPacket {
HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }
@ -62,7 +62,7 @@ public class TestHuaweiPacket {
HuaweiPacket.ParamsProvider paramsProviderEncrypt = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider paramsProviderEncrypt = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }
@ -95,7 +95,7 @@ public class TestHuaweiPacket {
HuaweiPacket.ParamsProvider paramsProviderSmallSlice = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider paramsProviderSmallSlice = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -26,7 +26,7 @@ public class TestHuaweiTLV {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -29,7 +29,7 @@ public class TestAlarms {
HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -42,7 +42,7 @@ public class TestDeviceConfig {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }
@ -413,21 +413,23 @@ public class TestDeviceConfig {
HuaweiCrypto huaweiCrypto = new HuaweiCrypto(0x01); HuaweiCrypto huaweiCrypto = new HuaweiCrypto(0x01);
Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); Field tlvField = HuaweiPacket.class.getDeclaredField("tlv");
tlvField.setAccessible(true); tlvField.setAccessible(true);
try { try {
byte[] encryptionKey = huaweiCrypto.createSecretKey(mac);
byte[] iv = secretsProvider.getIv();
byte[] key = huaweiCrypto.encryptBondingKey(secretsProvider.getEncryptMethod(), secretsProvider.getSecretKey(), encryptionKey, iv);
HuaweiTLV expectedTlv = new HuaweiTLV() HuaweiTLV expectedTlv = new HuaweiTLV()
.put(0x01) .put(0x01)
.put(0x03, (byte) 0x00) .put(0x03, (byte) 0x00)
.put(0x05, clientSerial) .put(0x05, clientSerial)
.put(0x06, huaweiCrypto.encryptBondingKey(secretsProvider.getSecretKey(), mac, secretsProvider.getIv())) .put(0x06, key)
.put(0x07, secretsProvider.getIv()); .put(0x07, iv);
byte[] serialized = new byte[]{(byte) 0x5A, (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x01, (byte) 0x0E, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x05, (byte) 0x06, (byte) 0x54, (byte) 0x56, (byte) 0x64, (byte) 0x54, (byte) 0x4D, (byte) 0x44, (byte) 0x06, (byte) 0x20, (byte) 0x88, (byte) 0x45, (byte) 0xAA, (byte) 0xB5, (byte) 0x9C, (byte) 0x84, (byte) 0x39, (byte) 0xAE, (byte) 0xD8, (byte) 0xE9, (byte) 0x71, (byte) 0x01, (byte) 0x5D, (byte) 0xC8, (byte) 0x34, (byte) 0x05, (byte) 0xC5, (byte) 0x9A, (byte) 0x6B, (byte) 0xDB, (byte) 0x62, (byte) 0x7D, (byte) 0xC8, (byte) 0xC3, (byte) 0xF4, (byte) 0xCC, (byte) 0x30, (byte) 0x74, (byte) 0x21, (byte) 0xD4, (byte) 0x45, (byte) 0x0E, (byte) 0x07, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x72, (byte) 0xFC}; byte[] serialized = new byte[]{(byte) 0x5A, (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x01, (byte) 0x0E, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x05, (byte) 0x06, (byte) 0x54, (byte) 0x56, (byte) 0x64, (byte) 0x54, (byte) 0x4D, (byte) 0x44, (byte) 0x06, (byte) 0x20, (byte) 0x88, (byte) 0x45, (byte) 0xAA, (byte) 0xB5, (byte) 0x9C, (byte) 0x84, (byte) 0x39, (byte) 0xAE, (byte) 0xD8, (byte) 0xE9, (byte) 0x71, (byte) 0x01, (byte) 0x5D, (byte) 0xC8, (byte) 0x34, (byte) 0x05, (byte) 0xC5, (byte) 0x9A, (byte) 0x6B, (byte) 0xDB, (byte) 0x62, (byte) 0x7D, (byte) 0xC8, (byte) 0xC3, (byte) 0xF4, (byte) 0xCC, (byte) 0x30, (byte) 0x74, (byte) 0x21, (byte) 0xD4, (byte) 0x45, (byte) 0x0E, (byte) 0x07, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x72, (byte) 0xFC};
DeviceConfig.Bond.Request request = new DeviceConfig.Bond.Request( DeviceConfig.Bond.Request request = new DeviceConfig.Bond.Request(
secretsProvider, secretsProvider,
clientSerial, clientSerial,
mac, key,
huaweiCrypto iv
); );
Assert.assertEquals(0x01, request.serviceId); Assert.assertEquals(0x01, request.serviceId);
@ -489,8 +491,7 @@ public class TestDeviceConfig {
DeviceConfig.Auth.Request request = new DeviceConfig.Auth.Request( DeviceConfig.Auth.Request request = new DeviceConfig.Auth.Request(
secretsProvider, secretsProvider,
challenge, challenge,
nonce, nonce
false
); );
Assert.assertEquals(0x01, request.serviceId); Assert.assertEquals(0x01, request.serviceId);

View File

@ -29,7 +29,7 @@ public class TestDisconnectNotification {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -28,7 +28,7 @@ public class TestFindPhone {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -30,7 +30,7 @@ public class TestFitnessData {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -29,7 +29,7 @@ public class TestLocaleConfig {
HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -32,7 +32,7 @@ public class TestMusicControl {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -30,7 +30,7 @@ public class TestNotifications {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -29,7 +29,7 @@ public class TestWorkMode {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }

View File

@ -30,7 +30,7 @@ public class TestWorkout {
HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() {
@Override @Override
public byte getAuthMode() { public byte getDeviceSupportType() {
return 0; return 0;
} }