From d59dc8c2253b996ef4d697cc5fa16f7b4dca0b93 Mon Sep 17 00:00:00 2001 From: darken Date: Wed, 4 Jun 2025 20:41:08 +0200 Subject: [PATCH] Move RPA checker and key types Relocate RPAChecker to the `pods.core.apple.protocol` package as it's specific to Apple's protocol. Introduce type aliases `IdentityResolvingKey` and `ProximityEncryptionKey` for `ByteArray` to improve code clarity and type safety when dealing with these keys. Update relevant classes to use these new type aliases. --- .../eu/darken/capod/main/core/GeneralSettings.kt | 15 ++++++++++++--- .../eu/darken/capod/monitor/core/PodMonitor.kt | 1 + .../java/eu/darken/capod/pods/core/PodDevice.kt | 3 ++- .../darken/capod/pods/core/apple/AppleFactory.kt | 3 +-- .../capod/pods/core/apple/ApplePodsFactory.kt | 10 +++++++--- .../core/apple/protocol/IdentityResolvingKey.kt | 3 +++ .../core/apple/protocol/ProximityEncryptionKey.kt | 3 +++ .../pods/core/apple/protocol/ProximityMessage.kt | 2 +- .../core/apple/protocol}/RPAChecker.kt | 11 +++++++---- .../darken/capod/monitor/core/RPACheckerTest.kt | 1 + 10 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/IdentityResolvingKey.kt create mode 100644 app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/ProximityEncryptionKey.kt rename app-common/src/main/java/eu/darken/capod/{monitor/core => pods/core/apple/protocol}/RPAChecker.kt (81%) diff --git a/app-common/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt b/app-common/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt index 587278ae..a3136f1e 100644 --- a/app-common/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt +++ b/app-common/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt @@ -12,6 +12,8 @@ import eu.darken.capod.common.preferences.PreferenceStoreMapper import eu.darken.capod.common.preferences.Settings import eu.darken.capod.common.preferences.createFlowPreference import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.apple.protocol.IdentityResolvingKey +import eu.darken.capod.pods.core.apple.protocol.ProximityEncryptionKey import javax.inject.Inject import javax.inject.Singleton @@ -36,9 +38,16 @@ class GeneralSettings @Inject constructor( val mainDeviceAddress = preferences.createFlowPreference("core.maindevice.address", null) val mainDeviceModel = preferences.createFlowPreference("core.maindevice.model", PodDevice.Model.UNKNOWN, moshi) - val mainDeviceIdentityKey = preferences.createFlowPreference("core.maindevice.identitykey", null, moshi) - val mainDeviceEncryptionKey = - preferences.createFlowPreference("core.maindevice.encryptionkey", null, moshi) + val mainDeviceIdentityKey = preferences.createFlowPreference( + "core.maindevice.identitykey", + null, + moshi + ) + val mainDeviceEncryptionKey = preferences.createFlowPreference( + "core.maindevice.encryptionkey", + null, + moshi + ) val isOffloadedFilteringDisabled = preferences.createFlowPreference( "core.compat.offloaded.filtering.disabled", 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 87f598a3..244a9f1a 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 @@ -20,6 +20,7 @@ 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.pods.core.apple.protocol.ProximityPairing +import eu.darken.capod.pods.core.apple.protocol.RPAChecker import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/PodDevice.kt b/app-common/src/main/java/eu/darken/capod/pods/core/PodDevice.kt index 0de6d780..bcc09ceb 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/PodDevice.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/PodDevice.kt @@ -6,6 +6,7 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import eu.darken.capod.common.R import eu.darken.capod.common.bluetooth.BleScanResult +import eu.darken.capod.common.bluetooth.BluetoothAddress import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE import eu.darken.capod.common.debug.logging.log import java.time.Duration @@ -20,7 +21,7 @@ interface PodDevice { val model: Model - val address: String + val address: BluetoothAddress get() = scanResult.address val seenLastAt: Instant 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 b71acf7a..f77df345 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 @@ -6,13 +6,13 @@ 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.monitor.core.RPAChecker 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.ProximityMessage import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import eu.darken.capod.pods.core.apple.protocol.ProximityPayload +import eu.darken.capod.pods.core.apple.protocol.RPAChecker import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import javax.inject.Inject @@ -62,7 +62,6 @@ class AppleFactory @Inject constructor( val factory = podFactories.firstOrNull { it.isResponsible(proximityMessage) } - return@withLock (factory ?: unknownAppleFactory).create( scanResult = scanResult, payload = payload, 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 fddeac25..092d904c 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 @@ -2,6 +2,7 @@ package eu.darken.capod.pods.core.apple import android.R.id.message import eu.darken.capod.common.bluetooth.BleScanResult +import eu.darken.capod.common.bluetooth.BluetoothAddress import eu.darken.capod.common.collections.median import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE import eu.darken.capod.common.debug.logging.Logging.Priority.WARN @@ -16,7 +17,9 @@ import java.time.Duration import java.time.Instant import kotlin.math.max -abstract class ApplePodsFactory(private val tag: String) { +abstract class ApplePodsFactory( + private val tag: String, +) { data class Identifier( val device: UShort, @@ -43,7 +46,7 @@ abstract class ApplePodsFactory(private val tag: String) { val lastPayload: ProximityPayload get() = history.last().payload - val lastAddress: String + val lastAddress: BluetoothAddress get() = history.last().address val reliability: Float @@ -93,7 +96,6 @@ abstract class ApplePodsFactory(private val tag: String) { internal val knownDevices = mutableMapOf() - fun KnownDevice.getLatestCaseBattery(): Float? = this.lastCaseBattery private fun Collection.determineLatestCaseBattery(): Float? = this @@ -145,6 +147,8 @@ abstract class ApplePodsFactory(private val tag: String) { .firstOrNull { it.lastAddress == scanResult.address } ?.also { log(tag, VERBOSE) { "searchHistory1: Recovered previous ID via address: $it" } } +// if() + if (recognizedDevice == null) { val currentMarkers = payload.getFuzzyIdentifier() recognizedDevice = knownDevices.values diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/IdentityResolvingKey.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/IdentityResolvingKey.kt new file mode 100644 index 00000000..8b57b148 --- /dev/null +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/IdentityResolvingKey.kt @@ -0,0 +1,3 @@ +package eu.darken.capod.pods.core.apple.protocol + +typealias IdentityResolvingKey = ByteArray \ No newline at end of file diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/ProximityEncryptionKey.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/ProximityEncryptionKey.kt new file mode 100644 index 00000000..4ffdaaa2 --- /dev/null +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/ProximityEncryptionKey.kt @@ -0,0 +1,3 @@ +package eu.darken.capod.pods.core.apple.protocol + +typealias ProximityEncryptionKey = ByteArray \ No newline at end of file diff --git a/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/ProximityMessage.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/ProximityMessage.kt index a59ebcba..ff597732 100644 --- a/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/ProximityMessage.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/ProximityMessage.kt @@ -26,7 +26,7 @@ data class ProximityMessage( @Reusable class Decrypter @Inject constructor() { @SuppressLint("GetInstance") - fun decrypt(data: ByteArray, key: ByteArray): UByteArray? { + fun decrypt(data: ByteArray, key: ProximityEncryptionKey): UByteArray? { val decryptedData = try { val cipher = Cipher.getInstance("AES/ECB/NoPadding").apply { val secretKey = SecretKeySpec(key, "AES") diff --git a/app-common/src/main/java/eu/darken/capod/monitor/core/RPAChecker.kt b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/RPAChecker.kt similarity index 81% rename from app-common/src/main/java/eu/darken/capod/monitor/core/RPAChecker.kt rename to app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/RPAChecker.kt index 917c6bc7..fe808720 100644 --- a/app-common/src/main/java/eu/darken/capod/monitor/core/RPAChecker.kt +++ b/app-common/src/main/java/eu/darken/capod/pods/core/apple/protocol/RPAChecker.kt @@ -1,8 +1,8 @@ -package eu.darken.capod.monitor.core +package eu.darken.capod.pods.core.apple.protocol import android.annotation.SuppressLint import eu.darken.capod.common.bluetooth.BluetoothAddress -import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR +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 @@ -14,14 +14,17 @@ import javax.inject.Inject class RPAChecker @Inject constructor() { // Resolvable-Private-Address - fun verify(address: BluetoothAddress, irk: ByteArray): Boolean = try { + fun verify(address: BluetoothAddress, irk: IdentityResolvingKey): Boolean = try { val rpa = 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) hash.contentEquals(computedHash) } catch (e: Exception) { - log(TAG, ERROR) { "Failed to verify RPA\naddress=${address}\nIRK=${irk.toByteString()}\n${e.asLog()}" } + log( + TAG, + Logging.Priority.ERROR + ) { "Failed to verify RPA\naddress=${address}\nIRK=${irk.toByteString()}\n${e.asLog()}" } false } diff --git a/app-common/src/test/java/eu/darken/capod/monitor/core/RPACheckerTest.kt b/app-common/src/test/java/eu/darken/capod/monitor/core/RPACheckerTest.kt index 9f42f58d..671d49b2 100644 --- a/app-common/src/test/java/eu/darken/capod/monitor/core/RPACheckerTest.kt +++ b/app-common/src/test/java/eu/darken/capod/monitor/core/RPACheckerTest.kt @@ -1,6 +1,7 @@ package eu.darken.capod.monitor.core import eu.darken.capod.common.fromHex +import eu.darken.capod.pods.core.apple.protocol.RPAChecker import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import testhelpers.BaseTest