diff --git a/app-common/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt b/app-common/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt index 9b8dae91..c3792428 100644 --- a/app-common/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt +++ b/app-common/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt @@ -19,6 +19,7 @@ import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.main.core.PermissionTool import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.PodFactory +import eu.darken.capod.monitor.core.RPAChecker import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay diff --git a/app-common/src/main/java/eu/darken/capod/monitor/core/RPAChecker.kt b/app-common/src/main/java/eu/darken/capod/monitor/core/RPAChecker.kt index 22c8de2a..4db04bc4 100644 --- a/app-common/src/main/java/eu/darken/capod/monitor/core/RPAChecker.kt +++ b/app-common/src/main/java/eu/darken/capod/monitor/core/RPAChecker.kt @@ -1,7 +1,12 @@ package eu.darken.capod.monitor.core import android.annotation.SuppressLint +import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR +import eu.darken.capod.common.debug.logging.asLog +import eu.darken.capod.common.debug.logging.log +import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice +import okio.ByteString.Companion.toByteString import javax.crypto.Cipher import javax.crypto.spec.SecretKeySpec import javax.inject.Inject @@ -9,12 +14,15 @@ import javax.inject.Inject class RPAChecker @Inject constructor() { // Resolvable-Private-Address - fun verify(dev: PodDevice, irk: ByteArray): Boolean { + fun verify(dev: PodDevice, irk: ByteArray): Boolean = try { val rpa = dev.address.split(":").map { it.toInt(16).toByte() }.reversed().toByteArray() val prand = rpa.copyOfRange(3, 6) val hash = rpa.copyOfRange(0, 3) val computedHash = ah(irk, prand) - return hash.contentEquals(computedHash) + hash.contentEquals(computedHash) + } catch (e: Exception) { + log(TAG, ERROR) { "Failed to verify RPA\nPod=${dev}\nIRK=${irk.toByteString()}\n${e.asLog()}" } + false } // E function (Encryption function): @@ -34,4 +42,8 @@ class RPAChecker @Inject constructor() { } return e(k, rPadded).copyOfRange(0, 3) } + + companion object { + private val TAG = logTag("Monitor", "PodMonitor", "RPAChecker") + } } \ No newline at end of file diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt index dc854507..3751644c 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt @@ -5,9 +5,11 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.WARN import eu.darken.capod.common.debug.logging.asLog import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.debug.logging.logTag +import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.misc.UnknownAppleDevice import eu.darken.capod.pods.core.apple.protocol.ContinuityProtocol +import eu.darken.capod.pods.core.apple.protocol.MessageDecrypter import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -20,6 +22,8 @@ class AppleFactory @Inject constructor( private val proximityPairingDecoder: ProximityPairing.Decoder, private val podFactories: @JvmSuppressWildcards Set>, private val unknownAppleFactory: UnknownAppleDevice.Factory, + private val generalSettings: GeneralSettings, + private val messageDecrypter: MessageDecrypter, ) { private val lock = Mutex() @@ -54,9 +58,13 @@ class AppleFactory @Inject constructor( val factory = podFactories.firstOrNull { it.isResponsible(pm) } + val decryptedData = generalSettings.mainDeviceEncryptionKey.value + ?.let { messageDecrypter.decrypt(pm, it) } + return@withLock (factory ?: unknownAppleFactory).create( scanResult = scanResult, message = pm, + decrypted = decryptedData, ) } diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/ApplePods.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/ApplePods.kt index 476b10a6..84d2185e 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/ApplePods.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/ApplePods.kt @@ -10,6 +10,9 @@ interface ApplePods : PodDevice { val proximityMessage: ProximityPairing.Message + val decryptedPayload: UByteArray? + get() = null + // We start counting at the airpods prefix byte val rawPrefix: UByte get() = proximityMessage.data[0] diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt index 5633f809..dde34f60 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt @@ -202,5 +202,6 @@ abstract class ApplePodsFactory(private val tag: String) { abstract fun create( scanResult: BleScanResult, message: ProximityPairing.Message, + decrypted: UByteArray?, ): ApplePods } \ No newline at end of file diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt index 3cc17948..c27b38da 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt @@ -4,8 +4,12 @@ import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.isBitSet import eu.darken.capod.common.lowerNibble import eu.darken.capod.common.upperNibble -import eu.darken.capod.pods.core.* +import eu.darken.capod.pods.core.DualPodDevice import eu.darken.capod.pods.core.DualPodDevice.Pod +import eu.darken.capod.pods.core.HasCase +import eu.darken.capod.pods.core.HasChargeDetectionDual +import eu.darken.capod.pods.core.HasDualMicrophone +import eu.darken.capod.pods.core.HasEarDetectionDual interface DualApplePods : ApplePods, HasChargeDetectionDual, DualPodDevice, HasEarDetectionDual, HasCase, HasDualMicrophone, HasAppleColor { @@ -25,6 +29,10 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualPodDevice, HasE override val batteryLeftPodPercent: Float? get() { + decryptedPayload?.get(if (areValuesFlipped) 2 else 1)?.let { raw -> + val level = (raw and 0x7Fu).toInt() / 100f + if (level <= 1.0f) return level + } val value = when (areValuesFlipped) { true -> rawPodsBattery.upperNibble.toInt() false -> rawPodsBattery.lowerNibble.toInt() @@ -42,6 +50,10 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualPodDevice, HasE override val batteryRightPodPercent: Float? get() { + decryptedPayload?.get(if (areValuesFlipped) 1 else 2)?.let { raw -> + val level = (raw and 0x7Fu).toInt() / 100f + if (level <= 1.0f) return level + } val value = when (areValuesFlipped) { true -> rawPodsBattery.lowerNibble.toInt() false -> rawPodsBattery.upperNibble.toInt() @@ -93,30 +105,55 @@ interface DualApplePods : ApplePods, HasChargeDetectionDual, DualPodDevice, HasE get() = !rawStatus.isBitSet(5) xor isThisPodInThecase override val isLeftPodCharging: Boolean - get() = when (areValuesFlipped) { - false -> rawFlags.isBitSet(0) - true -> rawFlags.isBitSet(1) + get() { + decryptedPayload?.get(if (areValuesFlipped) 2 else 1)?.let { raw -> + val isCharging = (raw and 0x80u).toInt() != 0 + return isCharging + } + return when (areValuesFlipped) { + false -> rawFlags.isBitSet(0) + true -> rawFlags.isBitSet(1) + } } override val isRightPodCharging: Boolean - get() = when (areValuesFlipped) { - false -> rawFlags.isBitSet(1) - true -> rawFlags.isBitSet(0) + get() { + decryptedPayload?.get(if (areValuesFlipped) 1 else 2)?.let { raw -> + val isCharging = (raw and 0x80u).toInt() != 0 + return isCharging + } + return when (areValuesFlipped) { + false -> rawFlags.isBitSet(1) + true -> rawFlags.isBitSet(0) + } } override val batteryCasePercent: Float? - get() = when (val value = rawCaseBattery.toInt()) { - 15 -> null - else -> if (value > 10) { - log { "Case: Above 100% battery: $value" } - 1.0f - } else { - value / 10f + get() { + decryptedPayload?.get(3)?.let { raw -> + val level = (raw and 0x7Fu).toInt() / 100f + if (level <= 1.0f) return level + } + return when (val value = rawCaseBattery.toInt()) { + 15 -> null + else -> if (value > 10) { + log { "Case: Above 100% battery: $value" } + 1.0f + } else { + value / 10f + } } } override val isCaseCharging: Boolean - get() = rawFlags.isBitSet(2) + get() { + decryptedPayload?.get(3)?.let { raw -> + val isCharging = (raw and 0x80u).toInt() != 0 + if (batteryCasePercent != null) return isCharging + } + + return rawFlags.isBitSet(2) + } val caseLidState: LidState get() { diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt index 24deeb8b..4502acb9 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt @@ -41,7 +41,11 @@ data class AirPodsGen1 constructor( } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsGen1(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt index 7fa0c320..1c1f012a 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt @@ -40,7 +40,11 @@ data class AirPodsGen2 constructor( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsGen2(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt index f5701726..eb340d07 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt @@ -40,7 +40,11 @@ data class AirPodsGen3 constructor( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsGen3(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4.kt index f793c673..569653a8 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4.kt @@ -40,7 +40,11 @@ data class AirPodsGen4( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsGen4(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4Anc.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4Anc.kt index 36d25478..aacc1e54 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4Anc.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4Anc.kt @@ -40,7 +40,11 @@ data class AirPodsGen4Anc( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsGen4Anc(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt index 33c043e0..051957fe 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt @@ -42,7 +42,11 @@ data class AirPodsMax( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsMax(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMaxUsbc.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMaxUsbc.kt index 4b886a2f..77348bc4 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMaxUsbc.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMaxUsbc.kt @@ -42,7 +42,11 @@ data class AirPodsMaxUsbc( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsMaxUsbc(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt index 2f2d98c3..648c2e8f 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt @@ -20,6 +20,7 @@ data class AirPodsPro( override val seenCounter: Int = 1, override val scanResult: BleScanResult, override val proximityMessage: ProximityPairing.Message, + override val decryptedPayload: UByteArray?, override val reliability: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, private val cachedBatteryPercentage: Float? = null, @@ -59,8 +60,16 @@ data class AirPodsPro( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { - var basic = AirPodsPro(scanResult = scanResult, proximityMessage = message) + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { + var basic = AirPodsPro( + scanResult = scanResult, + proximityMessage = message, + decryptedPayload = decrypted + ) val result = searchHistory(basic) if (result != null) basic = basic.copy(identifier = result.id) @@ -79,7 +88,6 @@ data class AirPodsPro( cachedCaseState = result.getLatestCaseLidState(basic) ) } - } companion object { diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2.kt index 2353b09e..de40e69c 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2.kt @@ -59,7 +59,11 @@ data class AirPodsPro2( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsPro2(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2Usbc.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2Usbc.kt index 5ee90850..386c8826 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2Usbc.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2Usbc.kt @@ -59,7 +59,11 @@ data class AirPodsPro2Usbc( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = AirPodsPro2Usbc(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFitPro.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFitPro.kt index ba821e35..ce5deb2f 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFitPro.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFitPro.kt @@ -40,7 +40,11 @@ data class BeatsFitPro( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = BeatsFitPro(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt index 02dcc5e2..52a92239 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt @@ -32,7 +32,11 @@ data class BeatsFlex( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = BeatsFlex(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt index ab965acd..2e026583 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt @@ -31,7 +31,11 @@ data class BeatsSolo3( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = BeatsSolo3(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt index 43d9199a..b0c2f649 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt @@ -29,7 +29,11 @@ data class BeatsStudio3( getModelInfo().dirty == DEVICE_CODE_DIRTY && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = BeatsStudio3(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt index 879f1ebb..74c63a00 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt @@ -32,7 +32,11 @@ data class BeatsX( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = BeatsX(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt index 2ab40e8e..6b4c0d4e 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt @@ -32,7 +32,11 @@ data class PowerBeats3( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = PowerBeats3(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats4.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats4.kt index 02b30b13..5f159fb6 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats4.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats4.kt @@ -33,7 +33,11 @@ data class PowerBeats4( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = PowerBeats4(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt index aaabfe56..5a8b94a3 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt @@ -40,7 +40,11 @@ data class PowerBeatsPro( getModelInfo().dirty == DEVICE_CODE_DIRTY && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = PowerBeatsPro(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen1.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen1.kt index a4edb0c0..c07e93e1 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen1.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen1.kt @@ -124,7 +124,11 @@ data class FakeAirPodsGen1 constructor( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = FakeAirPodsGen1(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen2.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen2.kt index 53d06708..95a396cd 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen2.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen2.kt @@ -122,7 +122,11 @@ data class FakeAirPodsGen2 constructor( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = FakeAirPodsGen2(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen3.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen3.kt index c6765186..2db60339 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen3.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen3.kt @@ -124,7 +124,11 @@ data class FakeAirPodsGen3 constructor( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = FakeAirPodsGen3(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro.kt index 03dee942..06332a45 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro.kt @@ -142,7 +142,11 @@ data class FakeAirPodsPro constructor( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = FakeAirPodsPro(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro2.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro2.kt index dc3a5a00..601110ab 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro2.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro2.kt @@ -139,7 +139,11 @@ data class FakeAirPodsPro2 constructor( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + override fun create( + scanResult: BleScanResult, + message: ProximityPairing.Message, + decrypted: UByteArray? + ): ApplePods { var basic = FakeAirPodsPro2(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt index 2a6fd530..b58a2d49 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt @@ -35,6 +35,7 @@ data class UnknownAppleDevice( override fun create( scanResult: BleScanResult, message: ProximityPairing.Message, + decrypted: UByteArray?, ): ApplePods { var basic = UnknownAppleDevice(scanResult = scanResult, proximityMessage = message) val result = searchHistory(basic) diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/MessageDecrypter.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/MessageDecrypter.kt new file mode 100644 index 00000000..88adb82d --- /dev/null +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/MessageDecrypter.kt @@ -0,0 +1,44 @@ +package eu.darken.capod.pods.core.apple.protocol + +import android.annotation.SuppressLint +import eu.darken.capod.common.debug.logging.Logging +import eu.darken.capod.common.debug.logging.asLog +import eu.darken.capod.common.debug.logging.log +import eu.darken.capod.common.debug.logging.logTag +import okio.ByteString.Companion.toByteString +import javax.crypto.Cipher +import javax.crypto.spec.SecretKeySpec +import javax.inject.Inject + +class MessageDecrypter @Inject constructor() { + + @SuppressLint("GetInstance") + fun decrypt(message: ProximityPairing.Message, key: ByteArray): UByteArray? { + val data = message.data.toByteArray() + if (data.size < 16) return null + + val decryptedData = try { + val cipher = Cipher.getInstance("AES/ECB/NoPadding").apply { + val secretKey = SecretKeySpec(key, "AES") + init(Cipher.DECRYPT_MODE, secretKey) + } + cipher.doFinal(data.copyOfRange(data.size - 16, data.size)) + } catch (e: Exception) { + log(TAG, Logging.Priority.ERROR) { "Failed to decrypt $message with ${key.toByteString()}\n${e.asLog()}" } + null + } + + log( + TAG, + Logging.Priority.VERBOSE + ) { "Decrypted $message with ${key.toByteString()} to ${decryptedData?.toByteString()}" } + + if (decryptedData == null || decryptedData.size != 16) return null + + return decryptedData.toUByteArray() + } + + companion object { + private val TAG = logTag("Pod", "Factory", "Decrypter") + } +} \ No newline at end of file