diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCrypto.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCrypto.java index 08ef0a60f..80a9fb6d2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCrypto.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCrypto.java @@ -17,7 +17,9 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huawei; import nodomain.freeyourgadget.gadgetbridge.util.CryptoUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; @@ -100,13 +102,15 @@ public class HuaweiCrypto { } private byte[] getDigestSecret() { + byte[] digest; if (authVersion == 1) { - return DIGEST_SECRET_v1; + digest = DIGEST_SECRET_v1.clone(); } else if (authVersion == 2) { - return DIGEST_SECRET_v2; + digest = DIGEST_SECRET_v2.clone(); } else { - return DIGEST_SECRET_v3; + digest = DIGEST_SECRET_v3.clone(); } + return digest; } public byte[] computeDigest(byte[] message, byte[] nonce) throws NoSuchAlgorithmException, InvalidKeyException { byte[] digestSecret = getDigestSecret(); @@ -118,9 +122,9 @@ public class HuaweiCrypto { return CryptoUtils.calcHmacSha256(digestStep1, nonce); } - 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[] hashKey = CryptoUtils.digest(key); + byte[] hashKey = CryptoUtils.digest(GB.hexdump(key).getBytes("UTF-8")); byte[] digestSecret = getDigestSecret(); for (int i = 0; i < digestSecret.length; i++) { digestSecret[i] = (byte) (((0xFF & hashKey[i]) ^ (digestSecret[i] & 0xFF)) & 0xFF); @@ -130,14 +134,14 @@ public class HuaweiCrypto { .put(message) .array(); if (authAlgo == 0x01) { - digestStep1 = CryptoUtils.pbkdf2Sha256(msgToDigest, nonce, 0x3e8, 0x100); + digestStep1 = CryptoUtils.pbkdf2Sha256(GB.hexdump(msgToDigest), GB.hexdump(nonce), 0x3e8, 0x100); } else { digestStep1 = CryptoUtils.calcHmacSha256(msgToDigest, nonce); } return CryptoUtils.calcHmacSha256(digestStep1, nonce); } - 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 (secretKey == null) return null; @@ -153,7 +157,7 @@ public class HuaweiCrypto { 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 (secretKey == null) return null; @@ -216,9 +220,11 @@ public class HuaweiCrypto { 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(); try { + if (encryptMethod == 0x1) + return CryptoUtils.decryptAES_GCM_NoPad(message, secretKey, iv, null); return CryptoUtils.decryptAES_CBC_Pad(message, secretKey, iv); } catch (Exception e) { throw new CryptoException(e); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java index e7f7efaf6..314b29760 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java @@ -57,6 +57,7 @@ public class HuaweiPacket { protected byte interval; protected byte authAlgo; + protected byte encryptMethod; public void setAuthVersion(byte authVersion) { this.authVersion = authVersion; @@ -144,6 +145,14 @@ public class HuaweiPacket { public byte getAuthAlgo () { return this.authAlgo; } + + public void setEncryptMethod(byte encryptMethod) { + this.encryptMethod = encryptMethod; + } + + public byte getEncryptMethod () { + return this.encryptMethod; + } } public static abstract class ParseException extends Exception { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java index 91281404a..3bc32a01e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/DeviceConfig.java @@ -78,6 +78,7 @@ public class DeviceConfig { public byte authAlgo = 0x00; public byte bondState = 0x00; public short interval = 0x0; + public byte encryptMethod = 0x00; public Response(ParamsProvider paramsProvider) { super(paramsProvider); @@ -111,6 +112,9 @@ public class DeviceConfig { if (this.tlv.contains(0x09)) this.bondState = this.tlv.getByte(0x09); + + if (this.tlv.contains(0x0C)) + this.encryptMethod = this.tlv.getByte(0x0C); } } } @@ -1400,7 +1404,7 @@ public class DeviceConfig { HuaweiCrypto huaweiCrypto = new HuaweiCrypto(paramsProvider.getAuthVersion()); try { - pinCode = huaweiCrypto.decryptPinCode(message, iv); + pinCode = huaweiCrypto.decryptPinCode(paramsProvider.getEncryptMethod(), message, iv); } catch (HuaweiCrypto.CryptoException e) { throw new CryptoException("Could not decrypt pinCode", e); } @@ -1487,7 +1491,7 @@ public class DeviceConfig { this.tlv = new HuaweiTLV() .put(0x01, authMode); 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) .put(0x03, (byte)0x01) .put(0x04, (byte)0x00); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java index eed8a0a7d..477bead17 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java @@ -331,6 +331,7 @@ public class HuaweiSupportProvider { initializeDeviceHiChainMode(linkParamsReq); } else if (securityNegoReq.authType == 0x01 || securityNegoReq.authType == 0x02) { LOG.debug("HiChain Lite mode"); + // Keep track the gadget is connected initializeDeviceHiChainLiteMode(linkParamsReq); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetAuthRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetAuthRequest.java index e4a98141a..81e070b6f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetAuthRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetAuthRequest.java @@ -19,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.security.InvalidKeyException; 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.packets.DeviceConfig; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; +import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; public class GetAuthRequest extends Request { @@ -73,12 +75,13 @@ public class GetAuthRequest extends Request { .put(clientNonce) .array(); byte[] challenge = huaweiCrypto.digestChallenge(key, doubleNonce); + LOG.debug("challenge: " + GB.hexdump(challenge)); if (challenge == null) throw new RequestCreationException("Challenge null"); return new DeviceConfig.Auth.Request(paramsProvider, challenge, nonce).serialize(); } catch (HuaweiPacket.CryptoException e) { throw new RequestCreationException(e); - } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException e) { + } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | UnsupportedEncodingException e) { throw new RequestCreationException("Digest exception", e); } } @@ -102,7 +105,7 @@ public class GetAuthRequest extends Request { + StringUtils.bytesToHex(expectedAnswer) ); } - } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException e) { + } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | UnsupportedEncodingException e) { throw new ResponseParseException("Challenge response digest exception"); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetLinkParamsRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetLinkParamsRequest.java index 8c135eb86..7ba643f3b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetLinkParamsRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetLinkParamsRequest.java @@ -84,5 +84,6 @@ public class GetLinkParamsRequest extends Request { this.bondState = ((LinkParams.Response) receivedPacket).bondState; paramsProvider.setAuthAlgo(((LinkParams.Response) receivedPacket).authAlgo); + paramsProvider.setEncryptMethod(((LinkParams.Response) receivedPacket).encryptMethod); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CryptoUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CryptoUtils.java index 7dde79c70..c5e4cc686 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CryptoUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CryptoUtils.java @@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.util; import android.annotation.SuppressLint; +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; @@ -133,10 +134,9 @@ public class CryptoUtils { 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"); - String keyStr = new String(key, StandardCharsets.UTF_8); - PBEKeySpec keySpec = new PBEKeySpec(keyStr.toCharArray(), iv, count, length); + PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray(), iv.getBytes("utf-8"), count, length); return secretKeyFactory.generateSecret(keySpec).getEncoded(); } }