Handle node public key mismatch and show warning

- Add a mismatchKey flag to Node and MessageTopBar to indicate a public key mismatch.
- Set the public key to a default error value (all zeros) when a node's public key changes.
- Display a warning in the MessageTopBar when a key mismatch is detected in PKC.
- Only clear all nodes when a different mynode number is present.
pull/1720/head
James Rich 2025-03-25 15:37:36 -05:00
rodzic 53c240198c
commit 879a367ff5
5 zmienionych plików z 35 dodań i 10 usunięć

Wyświetl plik

@ -107,9 +107,12 @@ class NodeRepository @Inject constructor(
} }
suspend fun installNodeDB(mi: MyNodeEntity, nodes: List<NodeEntity>) = withContext(dispatchers.io) { suspend fun installNodeDB(mi: MyNodeEntity, nodes: List<NodeEntity>) = withContext(dispatchers.io) {
val isDifferentNode = myNodeInfo.value?.myNodeNum == mi.myNodeNum
nodeInfoDao.clearMyNodeInfo() nodeInfoDao.clearMyNodeInfo()
nodeInfoDao.setMyNodeInfo(mi) // set MyNodeEntity first nodeInfoDao.setMyNodeInfo(mi) // set MyNodeEntity first
if (isDifferentNode) {
nodeInfoDao.clearNodeInfo() nodeInfoDao.clearNodeInfo()
}
nodeInfoDao.putAll(nodes) nodeInfoDao.putAll(nodes)
} }

Wyświetl plik

@ -17,6 +17,7 @@
package com.geeksville.mesh.database.dao package com.geeksville.mesh.database.dao
import android.util.Log
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.MapColumn import androidx.room.MapColumn
@ -26,10 +27,11 @@ import androidx.room.Transaction
import androidx.room.Upsert import androidx.room.Upsert
import com.geeksville.mesh.database.entity.MetadataEntity import com.geeksville.mesh.database.entity.MetadataEntity
import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.MyNodeEntity
import com.geeksville.mesh.database.entity.NodeWithRelations
import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.database.entity.NodeEntity
import com.geeksville.mesh.database.entity.NodeWithRelations
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
private const val TAG = "NodeInfoDao"
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
@Dao @Dao
interface NodeInfoDao { interface NodeInfoDao {
@ -108,8 +110,22 @@ interface NodeInfoDao {
@Upsert @Upsert
fun upsert(node: NodeEntity) fun upsert(node: NodeEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE) @Transaction
fun putAll(nodes: List<NodeEntity>) fun putAll(nodes: List<NodeEntity>) {
val existingNodes = nodes.mapNotNull {
getNodeByNum(it.num)
}
nodes.forEach { newNode ->
val existingNode = existingNodes.firstOrNull { it.num == newNode.num }
if (existingNode != null && existingNode.user.publicKey != newNode.user.publicKey) {
Log.w(TAG, "Node ${newNode.num} has changed its public key")
val user =
newNode.user.toBuilder().setPublicKey(NodeEntity.ERROR_BYTE_STRING).build()
newNode.user = user
}
upsert(newNode)
}
}
@Query("DELETE FROM nodes") @Query("DELETE FROM nodes")
fun clearNodeInfo() fun clearNodeInfo()
@ -122,4 +138,7 @@ interface NodeInfoDao {
@Query("DELETE FROM metadata WHERE num=:num") @Query("DELETE FROM metadata WHERE num=:num")
fun deleteMetadata(num: Int) fun deleteMetadata(num: Int)
@Query("SELECT * FROM nodes WHERE num = :num")
fun getNodeByNum(num: Int): NodeEntity?
} }

Wyświetl plik

@ -151,7 +151,7 @@ data class NodeEntity(
val isUnknownUser get() = user.hwModel == MeshProtos.HardwareModel.UNSET val isUnknownUser get() = user.hwModel == MeshProtos.HardwareModel.UNSET
val hasPKC get() = !user.publicKey.isEmpty val hasPKC get() = !user.publicKey.isEmpty
val errorByteString: ByteString get() = ByteString.copyFrom(ByteArray(32) { 0 }) val errorByteString: ByteString get() = ERROR_BYTE_STRING
fun setPosition(p: MeshProtos.Position, defaultTime: Int = currentTime()) { fun setPosition(p: MeshProtos.Position, defaultTime: Int = currentTime()) {
position = p.copy { time = if (p.time != 0) p.time else defaultTime } position = p.copy { time = if (p.time != 0) p.time else defaultTime }
@ -174,6 +174,7 @@ data class NodeEntity(
fun degD(i: Int) = i * 1e-7 fun degD(i: Int) = i * 1e-7
fun degI(d: Double) = (d * 1e7).toInt() fun degI(d: Double) = (d * 1e7).toInt()
val ERROR_BYTE_STRING: ByteString = ByteString.copyFrom(ByteArray(32) { 0 })
fun currentTime() = (System.currentTimeMillis() / 1000).toInt() fun currentTime() = (System.currentTimeMillis() / 1000).toInt()
} }

Wyświetl plik

@ -24,10 +24,10 @@ import com.geeksville.mesh.PaxcountProtos
import com.geeksville.mesh.TelemetryProtos.DeviceMetrics import com.geeksville.mesh.TelemetryProtos.DeviceMetrics
import com.geeksville.mesh.TelemetryProtos.EnvironmentMetrics import com.geeksville.mesh.TelemetryProtos.EnvironmentMetrics
import com.geeksville.mesh.TelemetryProtos.PowerMetrics import com.geeksville.mesh.TelemetryProtos.PowerMetrics
import com.geeksville.mesh.database.entity.NodeEntity
import com.geeksville.mesh.util.GPSFormat import com.geeksville.mesh.util.GPSFormat
import com.geeksville.mesh.util.latLongToMeter import com.geeksville.mesh.util.latLongToMeter
import com.geeksville.mesh.util.toDistanceString import com.geeksville.mesh.util.toDistanceString
import com.google.protobuf.ByteString
@Suppress("MagicNumber") @Suppress("MagicNumber")
data class Node( data class Node(
@ -59,8 +59,7 @@ data class Node(
val isUnknownUser get() = user.hwModel == MeshProtos.HardwareModel.UNSET val isUnknownUser get() = user.hwModel == MeshProtos.HardwareModel.UNSET
val hasPKC get() = !user.publicKey.isEmpty val hasPKC get() = !user.publicKey.isEmpty
val errorByteString: ByteString get() = ByteString.copyFrom(ByteArray(32) { 0 }) val mismatchKey get() = user.publicKey == NodeEntity.ERROR_BYTE_STRING
val mismatchKey get() = user.publicKey == errorByteString
val hasEnvironmentMetrics: Boolean val hasEnvironmentMetrics: Boolean
get() = environmentMetrics != EnvironmentMetrics.getDefaultInstance() get() = environmentMetrics != EnvironmentMetrics.getDefaultInstance()

Wyświetl plik

@ -184,6 +184,8 @@ internal fun MessageScreen(
DataPacket.ID_BROADCAST -> channelName DataPacket.ID_BROADCAST -> channelName
else -> viewModel.getUser(nodeId).longName else -> viewModel.getUser(nodeId).longName
} }
val mismatchKey =
DataPacket.PKC_CHANNEL_INDEX == channelIndex && viewModel.getNode(nodeId).mismatchKey
// if (channelIndex != DataPacket.PKC_CHANNEL_INDEX && nodeId != DataPacket.ID_BROADCAST) { // if (channelIndex != DataPacket.PKC_CHANNEL_INDEX && nodeId != DataPacket.ID_BROADCAST) {
// subtitle = "(ch: $channelIndex - $channelName)" // subtitle = "(ch: $channelIndex - $channelName)"
@ -242,7 +244,7 @@ internal fun MessageScreen(
} }
} }
} else { } else {
MessageTopBar(title, channelIndex, onNavigateBack) MessageTopBar(title, channelIndex, mismatchKey, onNavigateBack)
} }
}, },
bottomBar = { bottomBar = {
@ -368,6 +370,7 @@ private fun ActionModeTopBar(
private fun MessageTopBar( private fun MessageTopBar(
title: String, title: String,
channelIndex: Int?, channelIndex: Int?,
mismatchKey: Boolean = false,
onNavigateBack: () -> Unit onNavigateBack: () -> Unit
) = TopAppBar( ) = TopAppBar(
title = { Text(text = title) }, title = { Text(text = title) },
@ -381,7 +384,7 @@ private fun MessageTopBar(
}, },
actions = { actions = {
if (channelIndex == DataPacket.PKC_CHANNEL_INDEX) { if (channelIndex == DataPacket.PKC_CHANNEL_INDEX) {
NodeKeyStatusIcon(hasPKC = true, mismatchKey = false) NodeKeyStatusIcon(hasPKC = true, mismatchKey = mismatchKey)
} }
} }
) )