diff --git a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LockdownCoordinator.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LockdownCoordinator.kt index 329b714a3..67f5f4974 100644 --- a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LockdownCoordinator.kt +++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LockdownCoordinator.kt @@ -32,10 +32,10 @@ interface LockdownCoordinator { fun onDisconnect() /** - * Lifecycle hook called on every config_complete_id from the device. - * - * Currently a no-op; retained so implementations can react to config-complete in the future without changing the - * public contract. + * Lifecycle hook called on every config_complete_id from the device. + * + * Currently a no-op; retained so implementations can react to config-complete in the future without changing the + * public contract. */ fun onConfigComplete() diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt index 1fe042663..731e53aba 100644 --- a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt +++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt @@ -52,8 +52,7 @@ class LockdownPassphraseStoreImpl(app: Application) : LockdownPassphraseStore { } } - private fun requirePrefs(): SharedPreferences = - prefs ?: error("Encrypted passphrase store unavailable") + private fun requirePrefs(): SharedPreferences = prefs ?: error("Encrypted passphrase store unavailable") @Suppress("ReturnCount") override fun getPassphrase(deviceAddress: String): StoredPassphrase? { diff --git a/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt index 7fef1428d..8bbf4eab9 100644 --- a/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt +++ b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt @@ -34,8 +34,8 @@ import javax.crypto.spec.GCMParameterSpec * File-backed encrypted passphrase store for JVM/Desktop. * * Uses a PKCS12 KeyStore to hold an AES-256 master key and AES-256-GCM to encrypt each passphrase entry. Entries are - * stored as individual `.enc` files under `$MESHTASTIC_DATA_DIR/lockdown/` (default: `~/.meshtastic/lockdown/`), - * keyed by a sanitized device address. + * stored as individual `.enc` files under `$MESHTASTIC_DATA_DIR/lockdown/` (default: `~/.meshtastic/lockdown/`), keyed + * by a sanitized device address. * * The keystore password is fixed because the threat model mirrors Android's `EncryptedSharedPreferences`: file-system * permission is the primary access control; the encryption layer protects data at rest against casual file browsing or @@ -117,6 +117,7 @@ class LockdownPassphraseStoreImpl : LockdownPassphraseStore { private fun serialize(passphrase: String, boots: Int, hours: Int): ByteArray = "$boots\n$hours\n$passphrase".encodeToByteArray() + @Suppress("ReturnCount") private fun deserialize(plaintext: ByteArray): StoredPassphrase? { val text = plaintext.decodeToString() val lines = text.split("\n", limit = 3) @@ -163,6 +164,7 @@ class LockdownPassphraseStoreImpl : LockdownPassphraseStore { private const val KEYSTORE_FILE = "keystore.p12" private const val KEYSTORE_TYPE = "PKCS12" private const val KEY_ALIAS = "lockdown_master" + // Intentional: this mirrors the documented desktop threat model for at-rest protection only. private val KEYSTORE_PASSWORD = "meshtastic-lockdown".toCharArray() private const val AES_ALGORITHM = "AES" diff --git a/core/service/src/jvmTest/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImplTest.kt b/core/service/src/jvmTest/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImplTest.kt index e49d5d9f5..fa02d1350 100644 --- a/core/service/src/jvmTest/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImplTest.kt +++ b/core/service/src/jvmTest/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImplTest.kt @@ -56,4 +56,4 @@ class LockdownPassphraseStoreImplTest { assertNull(store.getPassphrase("AA:BB:CC:DD")) } -} \ No newline at end of file +} diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt index c942626fe..7aed6d7ad 100644 --- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt +++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt @@ -52,6 +52,7 @@ import org.meshtastic.core.model.toEventEdition import org.meshtastic.core.model.util.dispatchMeshtasticUri import org.meshtastic.core.navigation.DeepLinkRouter import org.meshtastic.core.repository.FirmwareReleaseRepository +import org.meshtastic.core.repository.LockdownPassphraseStore import org.meshtastic.core.repository.MeshLogRepository import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.NotificationManager @@ -59,7 +60,6 @@ import org.meshtastic.core.repository.PacketRepository import org.meshtastic.core.repository.RadioInterfaceService import org.meshtastic.core.repository.ServiceRepository import org.meshtastic.core.repository.UiPrefs -import org.meshtastic.core.repository.LockdownPassphraseStore import org.meshtastic.core.resources.Res import org.meshtastic.core.resources.client_notification import org.meshtastic.core.resources.compromised_keys