mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-16 11:16:12 -04:00
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.
This commit is contained in:
@@ -12,6 +12,8 @@ import eu.darken.capod.common.preferences.PreferenceStoreMapper
|
|||||||
import eu.darken.capod.common.preferences.Settings
|
import eu.darken.capod.common.preferences.Settings
|
||||||
import eu.darken.capod.common.preferences.createFlowPreference
|
import eu.darken.capod.common.preferences.createFlowPreference
|
||||||
import eu.darken.capod.pods.core.PodDevice
|
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.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -36,9 +38,16 @@ class GeneralSettings @Inject constructor(
|
|||||||
|
|
||||||
val mainDeviceAddress = preferences.createFlowPreference<BluetoothAddress?>("core.maindevice.address", null)
|
val mainDeviceAddress = preferences.createFlowPreference<BluetoothAddress?>("core.maindevice.address", null)
|
||||||
val mainDeviceModel = preferences.createFlowPreference("core.maindevice.model", PodDevice.Model.UNKNOWN, moshi)
|
val mainDeviceModel = preferences.createFlowPreference("core.maindevice.model", PodDevice.Model.UNKNOWN, moshi)
|
||||||
val mainDeviceIdentityKey = preferences.createFlowPreference<ByteArray?>("core.maindevice.identitykey", null, moshi)
|
val mainDeviceIdentityKey = preferences.createFlowPreference<IdentityResolvingKey?>(
|
||||||
val mainDeviceEncryptionKey =
|
"core.maindevice.identitykey",
|
||||||
preferences.createFlowPreference<ByteArray?>("core.maindevice.encryptionkey", null, moshi)
|
null,
|
||||||
|
moshi
|
||||||
|
)
|
||||||
|
val mainDeviceEncryptionKey = preferences.createFlowPreference<ProximityEncryptionKey?>(
|
||||||
|
"core.maindevice.encryptionkey",
|
||||||
|
null,
|
||||||
|
moshi
|
||||||
|
)
|
||||||
|
|
||||||
val isOffloadedFilteringDisabled = preferences.createFlowPreference(
|
val isOffloadedFilteringDisabled = preferences.createFlowPreference(
|
||||||
"core.compat.offloaded.filtering.disabled",
|
"core.compat.offloaded.filtering.disabled",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import eu.darken.capod.main.core.PermissionTool
|
|||||||
import eu.darken.capod.pods.core.PodDevice
|
import eu.darken.capod.pods.core.PodDevice
|
||||||
import eu.darken.capod.pods.core.PodFactory
|
import eu.darken.capod.pods.core.PodFactory
|
||||||
import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.squareup.moshi.Json
|
|||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import eu.darken.capod.common.R
|
import eu.darken.capod.common.R
|
||||||
import eu.darken.capod.common.bluetooth.BleScanResult
|
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.Logging.Priority.VERBOSE
|
||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@@ -20,7 +21,7 @@ interface PodDevice {
|
|||||||
|
|
||||||
val model: Model
|
val model: Model
|
||||||
|
|
||||||
val address: String
|
val address: BluetoothAddress
|
||||||
get() = scanResult.address
|
get() = scanResult.address
|
||||||
|
|
||||||
val seenLastAt: Instant
|
val seenLastAt: Instant
|
||||||
|
|||||||
@@ -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.log
|
||||||
import eu.darken.capod.common.debug.logging.logTag
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
import eu.darken.capod.main.core.GeneralSettings
|
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.PodDevice
|
||||||
import eu.darken.capod.pods.core.apple.misc.UnknownAppleDevice
|
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.ContinuityProtocol
|
||||||
import eu.darken.capod.pods.core.apple.protocol.ProximityMessage
|
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.ProximityPairing
|
||||||
import eu.darken.capod.pods.core.apple.protocol.ProximityPayload
|
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.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -62,7 +62,6 @@ class AppleFactory @Inject constructor(
|
|||||||
|
|
||||||
val factory = podFactories.firstOrNull { it.isResponsible(proximityMessage) }
|
val factory = podFactories.firstOrNull { it.isResponsible(proximityMessage) }
|
||||||
|
|
||||||
|
|
||||||
return@withLock (factory ?: unknownAppleFactory).create(
|
return@withLock (factory ?: unknownAppleFactory).create(
|
||||||
scanResult = scanResult,
|
scanResult = scanResult,
|
||||||
payload = payload,
|
payload = payload,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package eu.darken.capod.pods.core.apple
|
|||||||
|
|
||||||
import android.R.id.message
|
import android.R.id.message
|
||||||
import eu.darken.capod.common.bluetooth.BleScanResult
|
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.collections.median
|
||||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||||
@@ -16,7 +17,9 @@ import java.time.Duration
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
abstract class ApplePodsFactory<PodType : ApplePods>(private val tag: String) {
|
abstract class ApplePodsFactory<PodType : ApplePods>(
|
||||||
|
private val tag: String,
|
||||||
|
) {
|
||||||
|
|
||||||
data class Identifier(
|
data class Identifier(
|
||||||
val device: UShort,
|
val device: UShort,
|
||||||
@@ -43,7 +46,7 @@ abstract class ApplePodsFactory<PodType : ApplePods>(private val tag: String) {
|
|||||||
val lastPayload: ProximityPayload
|
val lastPayload: ProximityPayload
|
||||||
get() = history.last().payload
|
get() = history.last().payload
|
||||||
|
|
||||||
val lastAddress: String
|
val lastAddress: BluetoothAddress
|
||||||
get() = history.last().address
|
get() = history.last().address
|
||||||
|
|
||||||
val reliability: Float
|
val reliability: Float
|
||||||
@@ -93,7 +96,6 @@ abstract class ApplePodsFactory<PodType : ApplePods>(private val tag: String) {
|
|||||||
|
|
||||||
internal val knownDevices = mutableMapOf<PodDevice.Id, KnownDevice>()
|
internal val knownDevices = mutableMapOf<PodDevice.Id, KnownDevice>()
|
||||||
|
|
||||||
|
|
||||||
fun KnownDevice.getLatestCaseBattery(): Float? = this.lastCaseBattery
|
fun KnownDevice.getLatestCaseBattery(): Float? = this.lastCaseBattery
|
||||||
|
|
||||||
private fun Collection<ApplePods>.determineLatestCaseBattery(): Float? = this
|
private fun Collection<ApplePods>.determineLatestCaseBattery(): Float? = this
|
||||||
@@ -145,6 +147,8 @@ abstract class ApplePodsFactory<PodType : ApplePods>(private val tag: String) {
|
|||||||
.firstOrNull { it.lastAddress == scanResult.address }
|
.firstOrNull { it.lastAddress == scanResult.address }
|
||||||
?.also { log(tag, VERBOSE) { "searchHistory1: Recovered previous ID via address: $it" } }
|
?.also { log(tag, VERBOSE) { "searchHistory1: Recovered previous ID via address: $it" } }
|
||||||
|
|
||||||
|
// if()
|
||||||
|
|
||||||
if (recognizedDevice == null) {
|
if (recognizedDevice == null) {
|
||||||
val currentMarkers = payload.getFuzzyIdentifier()
|
val currentMarkers = payload.getFuzzyIdentifier()
|
||||||
recognizedDevice = knownDevices.values
|
recognizedDevice = knownDevices.values
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package eu.darken.capod.pods.core.apple.protocol
|
||||||
|
|
||||||
|
typealias IdentityResolvingKey = ByteArray
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package eu.darken.capod.pods.core.apple.protocol
|
||||||
|
|
||||||
|
typealias ProximityEncryptionKey = ByteArray
|
||||||
+1
-1
@@ -26,7 +26,7 @@ data class ProximityMessage(
|
|||||||
@Reusable
|
@Reusable
|
||||||
class Decrypter @Inject constructor() {
|
class Decrypter @Inject constructor() {
|
||||||
@SuppressLint("GetInstance")
|
@SuppressLint("GetInstance")
|
||||||
fun decrypt(data: ByteArray, key: ByteArray): UByteArray? {
|
fun decrypt(data: ByteArray, key: ProximityEncryptionKey): UByteArray? {
|
||||||
val decryptedData = try {
|
val decryptedData = try {
|
||||||
val cipher = Cipher.getInstance("AES/ECB/NoPadding").apply {
|
val cipher = Cipher.getInstance("AES/ECB/NoPadding").apply {
|
||||||
val secretKey = SecretKeySpec(key, "AES")
|
val secretKey = SecretKeySpec(key, "AES")
|
||||||
|
|||||||
+7
-4
@@ -1,8 +1,8 @@
|
|||||||
package eu.darken.capod.monitor.core
|
package eu.darken.capod.pods.core.apple.protocol
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import eu.darken.capod.common.bluetooth.BluetoothAddress
|
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.asLog
|
||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
import eu.darken.capod.common.debug.logging.logTag
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
@@ -14,14 +14,17 @@ import javax.inject.Inject
|
|||||||
class RPAChecker @Inject constructor() {
|
class RPAChecker @Inject constructor() {
|
||||||
|
|
||||||
// Resolvable-Private-Address
|
// 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 rpa = address.split(":").map { it.toInt(16).toByte() }.reversed().toByteArray()
|
||||||
val prand = rpa.copyOfRange(3, 6)
|
val prand = rpa.copyOfRange(3, 6)
|
||||||
val hash = rpa.copyOfRange(0, 3)
|
val hash = rpa.copyOfRange(0, 3)
|
||||||
val computedHash = ah(irk, prand)
|
val computedHash = ah(irk, prand)
|
||||||
hash.contentEquals(computedHash)
|
hash.contentEquals(computedHash)
|
||||||
} catch (e: Exception) {
|
} 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
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package eu.darken.capod.monitor.core
|
package eu.darken.capod.monitor.core
|
||||||
|
|
||||||
import eu.darken.capod.common.fromHex
|
import eu.darken.capod.common.fromHex
|
||||||
|
import eu.darken.capod.pods.core.apple.protocol.RPAChecker
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import testhelpers.BaseTest
|
import testhelpers.BaseTest
|
||||||
|
|||||||
Reference in New Issue
Block a user