import type * as Protobuf from "@meshtastic/protobufs"; import type { Node } from "../domain/Node.ts"; import type { NodeErrorType } from "../domain/NodeError.ts"; /** * Byte-equal compare for two public keys. Empty / undefined keys never * match each other. */ export function equalKey( a?: Uint8Array | null, b?: Uint8Array | null, ): boolean { if (!a || !b) return false; if (a === b) return true; if (a.byteLength !== b.byteLength) return false; for (let i = 0; i < a.byteLength; i++) { if (a[i] !== b[i]) return false; } return true; } export interface ValidatedNodeInfo { /** The NodeInfo to commit to the store. `undefined` when the incoming node should be dropped entirely. */ readonly accepted: Protobuf.Mesh.NodeInfo | undefined; /** Optional error to record against the incoming node's number. */ readonly error: NodeErrorType | undefined; } /** * Validates an incoming NodeInfo against the existing snapshot of nodes. * Pure — no side effects. Caller decides what to do with the verdict. * * Mirrors the previous web-side validation in * `packages/web/src/core/stores/nodeDBStore/nodeValidation.ts` but returns * the verdict instead of pushing into a Zustand store. */ export function validateIncomingNode( newNode: Protobuf.Mesh.NodeInfo, existingByNum: ReadonlyMap, ): ValidatedNodeInfo { const num = newNode.num; const existing = existingByNum.get(num); const newKey = newNode.user?.publicKey; if (!existing) { if (newKey && newKey.length > 0) { // Reject if another node already presents this public key — // potential impersonation. for (const node of existingByNum.values()) { const otherKey = node.user?.publicKey; if (otherKey && otherKey.length > 0 && equalKey(otherKey, newKey)) { return { accepted: undefined, error: "DUPLICATE_PKI" }; } } } return { accepted: newNode, error: undefined }; } const oldKey = existing.user?.publicKey; const oldKeyEmpty = !oldKey || oldKey.length === 0; if (oldKeyEmpty || equalKey(oldKey, newKey)) { // Trust the incoming update. return { accepted: newNode, error: undefined }; } if (newKey && newKey.length > 0) { // Existing node has a key, incoming has a different key — mismatch. return { accepted: undefined, error: "MISMATCH_PKI" }; } // Incoming has no key, existing does — silently drop the update. return { accepted: undefined, error: undefined }; }