Skip to content

Commit 1bad60f

Browse files
NomDeTomclaude
andcommitted
Rename signer-proven -> key-proven for broadened provenance predicate
Address PR #11119 review: the copyPublicKey()/copyUser() out-parameter and the cache-path replay gate now report entry->keyProven() (XEdDSA-signed OR manually verified), so the "signerProven" name and "signer-proven" comments were misleading. Rename the public out-param to keyProven, the local cachedKeySignerProven to cachedKeyProven, and update coupled callers, log strings, docs headings, and comments to say "key-proven". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 2834691 commit 1bad60f

7 files changed

Lines changed: 29 additions & 29 deletions

File tree

docs/node_info_stores.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ independent of `hasFullUser`. Because the re-mark is only hourly, hook-driven ad
154154
an hour.
155155

156156
**Retention:** no timed eviction. Slots die only by LRU displacement on insert, ranked by
157-
trust tiers - members and signer-proven keys are stickiest; the seeding pass additionally
157+
trust tiers - members and key-proven keys are stickiest; the seeding pass additionally
158158
refuses to churn one member out for another (`spareMembers`).
159159

160160
**Key-commit funnel:** every path that writes a remote key into the hot store must route

docs/traffic_management_module.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ and the request is left to propagate normally:
111111
4. **Staleness gate (6 h)**: never vouch for a node not genuinely _heard_ within the serve
112112
window. Only a real observed frame stamps the recency bit - seeding and write-through are
113113
knowledge, not observation, so a silent node can never look alive to this path.
114-
5. **Signer-provenance gate** (`TMM_NODEINFO_REPLAY_SIGNED_GATE`, default on): vouch only for
114+
5. **Key-provenance gate** (`TMM_NODEINFO_REPLAY_SIGNED_GATE`, default on): vouch only for
115115
an identity whose key is proven - XEdDSA-verified (directly or inherited from NodeDB) **or**
116116
manually verified out-of-band. Both paths honour both channels: the cache path via
117117
`keyProven()`, the NodeDB fallback path via `HAS_XEDDSA_SIGNED | IS_KEY_MANUALLY_VERIFIED`. A

src/mesh/NodeDB.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3773,7 +3773,7 @@ bool NodeDB::copyPublicKey(NodeNum n, meshtastic_NodeInfoLite_public_key_t &out)
37733773
#if HAS_TRAFFIC_MANAGEMENT
37743774
// Last resort: a key the TrafficManagement NodeInfo cache learned from an observed frame
37753775
// for a node no longer in either NodeDB tier. This extends the pool of peers we can
3776-
// encrypt to. Keys here may be trust-on-first-use (see copyPublicKey's signerProven), the
3776+
// encrypt to. Keys here may be trust-on-first-use (see copyPublicKey's keyProven), the
37773777
// same first-contact trust NodeDB itself applies via updateUser().
37783778
if (trafficManagementModule && trafficManagementModule->copyPublicKey(n, out.bytes)) {
37793779
out.size = 32;
@@ -3788,10 +3788,10 @@ bool NodeDB::copyPublicKeyForDecrypt(NodeNum n, meshtastic_NodeInfoLite_public_k
37883788
if (copyPublicKeyAuthoritative(n, out))
37893789
return true;
37903790
#if HAS_TRAFFIC_MANAGEMENT
3791-
// A cold-tier cache key backs an authenticated decrypt only when signer-proven; unverified TOFU
3791+
// A cold-tier cache key backs an authenticated decrypt only when key-proven; unverified TOFU
37923792
// cache keys must not. Outbound encryption still uses the opportunistic copyPublicKey().
3793-
bool signerProven = false;
3794-
if (trafficManagementModule && trafficManagementModule->copyPublicKey(n, out.bytes, &signerProven) && signerProven) {
3793+
bool keyProven = false;
3794+
if (trafficManagementModule && trafficManagementModule->copyPublicKey(n, out.bytes, &keyProven) && keyProven) {
37953795
out.size = 32;
37963796
return true;
37973797
}

src/mesh/NodeDB.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ class NodeDB
365365
bool copyPublicKeyAuthoritative(NodeNum n, meshtastic_NodeInfoLite_public_key_t &out);
366366

367367
/// Key for the inbound-decrypt path: authoritative (hot/warm), or a cold-tier cache key only when
368-
/// it is signer-proven. Keeps unverified TOFU cache keys from backing pki_encrypted attribution.
368+
/// it is key-proven. Keeps unverified TOFU cache keys from backing pki_encrypted attribution.
369369
bool copyPublicKeyForDecrypt(NodeNum n, meshtastic_NodeInfoLite_public_key_t &out);
370370

371371
/// True if n is a known XEdDSA signer for exactly `key32` (hot signed bitfield or warm

src/mesh/Router.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
818818
// Resolve the sender's key only for actual PKI-decrypt candidates, not every encrypted channel
819819
// packet: copyPublicKeyForDecrypt() can fall through to a linear scan of TrafficManagement's large
820820
// NodeInfo cache. It returns authoritative keys (hot/warm), or a cold-tier cache key only when it is
821-
// signer-proven - an unverified TOFU cache key must not back authenticated (pki_encrypted, p->from)
821+
// key-proven - an unverified TOFU cache key must not back authenticated (pki_encrypted, p->from)
822822
// DM attribution.
823823
meshtastic_NodeInfoLite_public_key_t remotePublic = {0, {0}};
824824
bool haveRemoteKey = nodeDB->copyPublicKeyForDecrypt(p->from, remotePublic);

src/modules/TrafficManagementModule.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ const TrafficManagementModule::NodeInfoPayloadEntry *TrafficManagementModule::fi
439439

440440
/// Find or create a NodeInfo payload entry. Victim selection is trust-tiered so the cache
441441
/// doubles as a pubkey pool: NodeDB membership outranks key trust, then keyless < TOFU key <
442-
/// signer-proven key; within a tier the oldest observation loses (never-observed = oldest).
442+
/// key-proven key; within a tier the oldest observation loses (never-observed = oldest).
443443
TrafficManagementModule::NodeInfoPayloadEntry *
444444
TrafficManagementModule::findOrCreateNodeInfoEntry(NodeNum node, bool *usedEmptySlot, bool spareMembers)
445445
{
@@ -466,7 +466,7 @@ TrafficManagementModule::findOrCreateNodeInfoEntry(NodeNum node, bool *usedEmpty
466466
}
467467
if (empty)
468468
continue; // an empty slot beats any victim; stop scoring
469-
// Eviction tier (lower loses first): 0 keyless, 1 TOFU key, 2 signer-proven key;
469+
// Eviction tier (lower loses first): 0 keyless, 1 TOFU key, 2 key-proven key;
470470
// +3 for NodeDB members - never shed a NodeDB-tier identity over a stranger.
471471
const uint8_t tier = static_cast<uint8_t>(((entry.user.public_key.size != 32) ? 0 : (entry.keyProven() ? 2 : 1)) +
472472
(entry.isMember ? 3 : 0));
@@ -724,7 +724,7 @@ void TrafficManagementModule::onNodeKeyCommitted(NodeNum node, const uint8_t key
724724
// hasObserved/obsTick untouched: a key commit is knowledge, not an observation.
725725
}
726726

727-
bool TrafficManagementModule::copyPublicKey(NodeNum node, uint8_t out[32], bool *signerProven) const
727+
bool TrafficManagementModule::copyPublicKey(NodeNum node, uint8_t out[32], bool *keyProven) const
728728
{
729729
// Same enable gate as the write-through hooks and maintenance: a disabled module stops
730730
// updating and sweeping the cache, so its frozen contents must not keep feeding PKI key
@@ -740,12 +740,12 @@ bool TrafficManagementModule::copyPublicKey(NodeNum node, uint8_t out[32], bool
740740
return false;
741741

742742
memcpy(out, entry->user.public_key.bytes, 32);
743-
if (signerProven)
744-
*signerProven = entry->keyProven();
743+
if (keyProven)
744+
*keyProven = entry->keyProven();
745745
return true;
746746
}
747747

748-
bool TrafficManagementModule::copyUser(NodeNum node, meshtastic_User &out, bool *signerProven) const
748+
bool TrafficManagementModule::copyUser(NodeNum node, meshtastic_User &out, bool *keyProven) const
749749
{
750750
// Enable gate, as in copyPublicKey(): a disabled module must not feed name rehydration
751751
// from frozen cache contents once its maintenance/write-through have stopped.
@@ -762,8 +762,8 @@ bool TrafficManagementModule::copyUser(NodeNum node, meshtastic_User &out, bool
762762
return false;
763763

764764
out = entry->user;
765-
if (signerProven)
766-
*signerProven = entry->keyProven();
765+
if (keyProven)
766+
*keyProven = entry->keyProven();
767767
return true;
768768
}
769769

@@ -1432,7 +1432,7 @@ bool TrafficManagementModule::shouldRespondToNodeInfo(const meshtastic_MeshPacke
14321432
uint8_t cachedObsTick = 0;
14331433
// Signer-proven provenance of the cached key, consumed by the replay gate below
14341434
// (maybe_unused: read only when TMM_NODEINFO_REPLAY_SIGNED_GATE is compiled in).
1435-
[[maybe_unused]] bool cachedKeySignerProven = false;
1435+
[[maybe_unused]] bool cachedKeyProven = false;
14361436
// True once we commit to answering from the NodeDB fallback (no NodeInfo cache) path. The
14371437
// response throttle no longer distinguishes the paths - the per-requester/per-target RAM
14381438
// tables cover both - but the replay gate below still keys off it.
@@ -1449,7 +1449,7 @@ bool TrafficManagementModule::shouldRespondToNodeInfo(const meshtastic_MeshPacke
14491449
cachedSourceChannel = entry->sourceChannel;
14501450
cachedHasObserved = entry->hasObserved;
14511451
cachedObsTick = entry->obsTick;
1452-
cachedKeySignerProven = entry->keyProven();
1452+
cachedKeyProven = entry->keyProven();
14531453
}
14541454
}
14551455

@@ -1500,10 +1500,10 @@ bool TrafficManagementModule::shouldRespondToNodeInfo(const meshtastic_MeshPacke
15001500
}
15011501

15021502
#if TMM_NODEINFO_REPLAY_SIGNED_GATE
1503-
// Replay provenance gate (cache path): only spoof a reply for a signer-proven cached key.
1503+
// Replay provenance gate (cache path): only spoof a reply for a key-proven cached key.
15041504
// usedFallback entries were already gated above. See TMM_NODEINFO_REPLAY_REQUIRE_SIGNED.
1505-
if (!usedFallback && !cachedKeySignerProven) {
1506-
TM_LOG_DEBUG("NodeInfo cache entry for 0x%08x not signer-proven, not responding", p->to);
1505+
if (!usedFallback && !cachedKeyProven) {
1506+
TM_LOG_DEBUG("NodeInfo cache entry for 0x%08x not key-proven, not responding", p->to);
15071507
return false;
15081508
}
15091509
#endif

src/modules/TrafficManagementModule.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#if HAS_TRAFFIC_MANAGEMENT
1010

1111
// Replay provenance gate: when 1 (default), direct responses are spoofed only for nodes whose
12-
// cached key is signer-proven (XEdDSA-verified), not for trust-on-first-use identities.
12+
// cached key is key-proven (XEdDSA-signed or manually verified), not for trust-on-first-use identities.
1313
// Define as 0 to also serve fresh TOFU-only nodes; bypassed entirely when PKI is excluded.
1414
#ifndef TMM_NODEINFO_REPLAY_REQUIRE_SIGNED
1515
#define TMM_NODEINFO_REPLAY_REQUIRE_SIGNED 1
@@ -65,14 +65,14 @@ class TrafficManagementModule : public MeshModule, private concurrency::OSThread
6565
bool preloadNextHopsFromNodeDB();
6666

6767
/// Last-resort key source for NodeDB::copyPublicKey() after the hot and warm tiers miss.
68-
/// Copies the 32-byte key for `node` into out[32]; `signerProven` (optional) reports whether
69-
/// the key was XEdDSA-verified vs trust-on-first-use. Thread-safe.
70-
bool copyPublicKey(NodeNum node, uint8_t out[32], bool *signerProven = nullptr) const;
68+
/// Copies the 32-byte key for `node` into out[32]; `keyProven` (optional) reports whether
69+
/// the key is proven (XEdDSA-signed or manually verified) vs trust-on-first-use. Thread-safe.
70+
bool copyPublicKey(NodeNum node, uint8_t out[32], bool *keyProven = nullptr) const;
7171

7272
/// Copy the full cached User for `node` (used by NodeDB to rehydrate a re-admitted node's
7373
/// name - the warm tier keeps keys but not names). False on miss or key-only records.
74-
/// `signerProven` (optional) reports the cached key's provenance. Thread-safe.
75-
bool copyUser(NodeNum node, meshtastic_User &out, bool *signerProven = nullptr) const;
74+
/// `keyProven` (optional) reports the cached key's provenance. Thread-safe.
75+
bool copyUser(NodeNum node, meshtastic_User &out, bool *keyProven = nullptr) const;
7676

7777
/// Write-through hook from NodeDB::updateUser(): upsert the committed identity immediately
7878
/// (the reconcile sweep remains the backstop). NodeDB's key is authoritative, but a keyless
@@ -125,7 +125,7 @@ class TrafficManagementModule : public MeshModule, private concurrency::OSThread
125125
/// (distinguishes "not tracked" from CLIENT == 0).
126126
int peekCachedRole(NodeNum node);
127127

128-
/// Test hook: force a cached NodeInfo entry's key to signer-proven so replay-gate tests
128+
/// Test hook: force a cached NodeInfo entry's key to XEdDSA-signed so replay-gate tests
129129
/// can skip a full XEdDSA verification. No-op if absent.
130130
void markKeyXeddsaSignedForTest(NodeNum node);
131131

@@ -285,7 +285,7 @@ class TrafficManagementModule : public MeshModule, private concurrency::OSThread
285285
// stickiest under LRU; the bit is the keep-alive (no TTL).
286286
uint8_t isMember : 1;
287287

288-
// Possession proven by either channel - the "signer-proven" predicate the replay gate,
288+
// Possession proven by either channel - the "key-proven" predicate the replay gate,
289289
// eviction tiering, and NodeDB pubkey-pool callers consume.
290290
bool keyProven() const { return keyXeddsaSigned || keyManuallyVerified; }
291291
};

0 commit comments

Comments
 (0)