mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-24 10:56:50 +01:00
Redmi Smart Band Pro: Ignore MAC during decryption
https://codeberg.org/Freeyourgadget/Gadgetbridge/issues/3766#issuecomment-2182676
This commit is contained in:
parent
2fe53ee563
commit
d3957ddf9a
@ -577,4 +577,8 @@ public abstract class XiaomiCoordinator extends AbstractBLEDeviceCoordinator {
|
|||||||
public boolean supports(final GBDevice device, final String feature) {
|
public boolean supports(final GBDevice device, final String feature) {
|
||||||
return getPrefs(device).getBoolean(feature, false);
|
return getPrefs(device).getBoolean(feature, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean checkDecryptionMac() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,4 +66,10 @@ public class RedmiSmartBandProCoordinator extends XiaomiCoordinator {
|
|||||||
public PasswordCapabilityImpl.Mode getPasswordCapability() {
|
public PasswordCapabilityImpl.Mode getPasswordCapability() {
|
||||||
return PasswordCapabilityImpl.Mode.NUMBERS_4_DIGITS_0_TO_9;
|
return PasswordCapabilityImpl.Mode.NUMBERS_4_DIGITS_0_TO_9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkDecryptionMac() {
|
||||||
|
// https://codeberg.org/Freeyourgadget/Gadgetbridge/issues/3766
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@ -46,7 +47,6 @@ import javax.crypto.Mac;
|
|||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
@ -65,6 +65,7 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
|||||||
public static final int CMD_AUTH = 27;
|
public static final int CMD_AUTH = 27;
|
||||||
|
|
||||||
private boolean encryptionInitialized = false;
|
private boolean encryptionInitialized = false;
|
||||||
|
private boolean checkDecryptionMac = true;
|
||||||
|
|
||||||
private final byte[] secretKey = new byte[16];
|
private final byte[] secretKey = new byte[16];
|
||||||
private final byte[] nonce = new byte[16];
|
private final byte[] nonce = new byte[16];
|
||||||
@ -104,6 +105,12 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
|||||||
getSupport().sendCommand("auth step 1", command);
|
getSupport().sendCommand("auth step 1", command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContext(final Context context) {
|
||||||
|
super.setContext(context);
|
||||||
|
this.checkDecryptionMac = getCoordinator().checkDecryptionMac();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleCommand(final XiaomiProto.Command cmd) {
|
public void handleCommand(final XiaomiProto.Command cmd) {
|
||||||
if (cmd.getType() != COMMAND_TYPE) {
|
if (cmd.getType() != COMMAND_TYPE) {
|
||||||
@ -180,7 +187,7 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
|||||||
packetNonce.putInt(0);
|
packetNonce.putInt(0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return decrypt(decryptionKey, packetNonce.array(), arr);
|
return decrypt(decryptionKey, packetNonce.array(), arr, checkDecryptionMac);
|
||||||
} catch (final CryptoException e) {
|
} catch (final CryptoException e) {
|
||||||
throw new RuntimeException("failed to decrypt", e);
|
throw new RuntimeException("failed to decrypt", e);
|
||||||
}
|
}
|
||||||
@ -323,7 +330,7 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
|||||||
|
|
||||||
public static byte[] encrypt(final byte[] key, final byte[] nonce, final byte[] payload) throws
|
public static byte[] encrypt(final byte[] key, final byte[] nonce, final byte[] payload) throws
|
||||||
CryptoException {
|
CryptoException {
|
||||||
final CCMBlockCipher cipher = createBlockCipher(true, new SecretKeySpec(key, "AES"), nonce);
|
final CCMBlockCipher cipher = createBlockCipher(true, new SecretKeySpec(key, "AES"), 32, nonce);
|
||||||
final byte[] out = new byte[cipher.getOutputSize(payload.length)];
|
final byte[] out = new byte[cipher.getOutputSize(payload.length)];
|
||||||
final int outBytes = cipher.processBytes(payload, 0, payload.length, out, 0);
|
final int outBytes = cipher.processBytes(payload, 0, payload.length, out, 0);
|
||||||
cipher.doFinal(out, outBytes);
|
cipher.doFinal(out, outBytes);
|
||||||
@ -332,20 +339,24 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
|||||||
|
|
||||||
public static byte[] decrypt(final byte[] key,
|
public static byte[] decrypt(final byte[] key,
|
||||||
final byte[] nonce,
|
final byte[] nonce,
|
||||||
final byte[] encryptedPayload) throws CryptoException {
|
final byte[] encryptedPayload,
|
||||||
final CCMBlockCipher cipher = createBlockCipher(false, new SecretKeySpec(key, "AES"), nonce);
|
final boolean checkMac) throws CryptoException {
|
||||||
final byte[] decrypted = new byte[cipher.getOutputSize(encryptedPayload.length)];
|
final int macSizeBits = checkMac ? 32 : 0;
|
||||||
cipher.doFinal(decrypted, cipher.processBytes(encryptedPayload, 0, encryptedPayload.length, decrypted, 0));
|
final int actualEncryptedLength = checkMac ? encryptedPayload.length : encryptedPayload.length - 4;
|
||||||
|
final CCMBlockCipher cipher = createBlockCipher(false, new SecretKeySpec(key, "AES"), macSizeBits, nonce);
|
||||||
|
final byte[] decrypted = new byte[cipher.getOutputSize(actualEncryptedLength)];
|
||||||
|
cipher.doFinal(decrypted, cipher.processBytes(encryptedPayload, 0, actualEncryptedLength, decrypted, 0));
|
||||||
return decrypted;
|
return decrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CCMBlockCipher createBlockCipher(final boolean forEncrypt,
|
public static CCMBlockCipher createBlockCipher(final boolean forEncrypt,
|
||||||
final SecretKey secretKey,
|
final SecretKey secretKey,
|
||||||
|
final int macSizeBits,
|
||||||
final byte[] nonce) {
|
final byte[] nonce) {
|
||||||
final AESEngine aesFastEngine = new AESEngine();
|
final AESEngine aesFastEngine = new AESEngine();
|
||||||
aesFastEngine.init(forEncrypt, new KeyParameter(secretKey.getEncoded()));
|
aesFastEngine.init(forEncrypt, new KeyParameter(secretKey.getEncoded()));
|
||||||
final CCMBlockCipher blockCipher = new CCMBlockCipher(aesFastEngine);
|
final CCMBlockCipher blockCipher = new CCMBlockCipher(aesFastEngine);
|
||||||
blockCipher.init(forEncrypt, new AEADParameters(new KeyParameter(secretKey.getEncoded()), 32, nonce, null));
|
blockCipher.init(forEncrypt, new AEADParameters(new KeyParameter(secretKey.getEncoded()), macSizeBits, nonce, null));
|
||||||
return blockCipher;
|
return blockCipher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user