1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-03 17:02:13 +01:00

[Huawei] Fix pin code encoding

This commit is contained in:
Damien 'Psolyca' Gaignon 2024-02-02 00:09:35 +01:00
parent 8aaa766dcd
commit 2880297c51
No known key found for this signature in database
GPG Key ID: 9E9404E5D9E11843
7 changed files with 40 additions and 16 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;
@ -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();
@ -118,9 +122,9 @@ public class HuaweiCrypto {
return CryptoUtils.calcHmacSha256(digestStep1, nonce); 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[] 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,14 +134,14 @@ 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); 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 (authMode == 0x02) {
if (secretKey == null) if (secretKey == null)
return null; return null;
@ -153,7 +157,7 @@ public class HuaweiCrypto {
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 (authMode == 0x02) {
if (secretKey == null) if (secretKey == null)
return null; return null;
@ -216,9 +220,11 @@ 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);

View File

@ -57,6 +57,7 @@ public class HuaweiPacket {
protected byte interval; protected byte interval;
protected byte authAlgo; protected byte authAlgo;
protected byte encryptMethod;
public void setAuthVersion(byte authVersion) { public void setAuthVersion(byte authVersion) {
this.authVersion = authVersion; this.authVersion = authVersion;
@ -144,6 +145,14 @@ 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 static abstract class ParseException extends Exception { public static abstract class ParseException extends Exception {

View File

@ -78,6 +78,7 @@ public class DeviceConfig {
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);
@ -111,6 +112,9 @@ public class DeviceConfig {
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);
} }
} }
} }
@ -1400,7 +1404,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 +1491,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

@ -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 {
@ -73,12 +75,13 @@ public class GetAuthRequest extends Request {
.put(clientNonce) .put(clientNonce)
.array(); .array();
byte[] challenge = huaweiCrypto.digestChallenge(key, doubleNonce); byte[] challenge = huaweiCrypto.digestChallenge(key, doubleNonce);
LOG.debug("challenge: " + GB.hexdump(challenge));
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);
} }
} }
@ -102,7 +105,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

@ -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

@ -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();
} }
} }