@@ -900,7 +900,7 @@ bool BaseChatMesh::removeContact(ContactInfo& contact) {
900900 }
901901 if (idx >= num_contacts) return false ; // not found
902902
903- session_keys. remove (contact.id .pub_key ); // also remove session key if any
903+ removeSessionKey (contact.id .pub_key ); // also remove session key if any
904904
905905 // remove from contacts array and parallel nonce tracking
906906 num_contacts--;
@@ -1015,6 +1015,41 @@ void BaseChatMesh::loop() {
10151015 }
10161016}
10171017
1018+ // --- Session key flash-backed wrappers ---
1019+
1020+ SessionKeyEntry* BaseChatMesh::findSessionKey (const uint8_t * pub_key) {
1021+ auto entry = session_keys.findByPrefix (pub_key);
1022+ if (entry) return entry;
1023+
1024+ // Cache miss — try flash
1025+ uint8_t flags; uint16_t nonce;
1026+ uint8_t sk[SESSION_KEY_SIZE ], psk[SESSION_KEY_SIZE ];
1027+ if (!loadSessionKeyRecordFromFlash (pub_key, &flags, &nonce, sk, psk)) return nullptr ;
1028+
1029+ // Save dirty evictee before overwriting
1030+ if (session_keys.isFull () && session_keys_dirty) {
1031+ mergeAndSaveSessionKeys ();
1032+ }
1033+ session_keys.applyLoaded (pub_key, flags, nonce, sk, psk);
1034+ return session_keys.findByPrefix (pub_key);
1035+ }
1036+
1037+ SessionKeyEntry* BaseChatMesh::allocateSessionKey (const uint8_t * pub_key) {
1038+ // Check RAM and flash first
1039+ auto entry = findSessionKey (pub_key);
1040+ if (entry) return entry;
1041+
1042+ // Not found anywhere — save dirty evictee before allocating
1043+ if (session_keys.isFull () && session_keys_dirty) {
1044+ mergeAndSaveSessionKeys ();
1045+ }
1046+ return session_keys.allocate (pub_key);
1047+ }
1048+
1049+ void BaseChatMesh::removeSessionKey (const uint8_t * pub_key) {
1050+ session_keys.remove (pub_key);
1051+ }
1052+
10181053// --- Session key support (Phase 2 — initiator) ---
10191054
10201055static bool canUseSessionKey (const SessionKeyEntry* entry) {
@@ -1030,7 +1065,7 @@ static bool canUseSessionKey(const SessionKeyEntry* entry) {
10301065}
10311066
10321067const uint8_t * BaseChatMesh::getEncryptionKeyFor (const ContactInfo& contact) {
1033- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1068+ auto entry = findSessionKey (contact.id .pub_key );
10341069 if (canUseSessionKey (entry)) {
10351070 return entry->session_key ;
10361071 }
@@ -1039,7 +1074,7 @@ const uint8_t* BaseChatMesh::getEncryptionKeyFor(const ContactInfo& contact) {
10391074
10401075uint16_t BaseChatMesh::getEncryptionNonceFor (const ContactInfo& contact) {
10411076 uint16_t nonce = 0 ;
1042- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1077+ auto entry = findSessionKey (contact.id .pub_key );
10431078 if (canUseSessionKey (entry)) {
10441079 ++entry->nonce ;
10451080 if (entry->sends_since_last_recv < 255 ) entry->sends_since_last_recv ++;
@@ -1053,7 +1088,7 @@ uint16_t BaseChatMesh::getEncryptionNonceFor(const ContactInfo& contact) {
10531088 int idx = &contact - contacts;
10541089 if (idx >= 0 && idx < num_contacts)
10551090 contacts[idx].flags &= ~CONTACT_FLAG_AEAD ;
1056- session_keys. remove (contact.id .pub_key );
1091+ removeSessionKey (contact.id .pub_key );
10571092 onSessionKeysUpdated ();
10581093 // nonce = 0 (ECB)
10591094 } else if (entry->sends_since_last_recv >= SESSION_KEY_ECB_THRESHOLD ) {
@@ -1082,7 +1117,7 @@ bool BaseChatMesh::shouldInitiateSessionKey(const ContactInfo& contact) {
10821117 // Need a known path to send the request
10831118 if (contact.out_path_len < 0 ) return false ;
10841119
1085- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1120+ auto entry = findSessionKey (contact.id .pub_key );
10861121
10871122 // Don't trigger if negotiation already in progress
10881123 if (entry && entry->state == SESSION_STATE_INIT_SENT ) return false ;
@@ -1118,7 +1153,7 @@ bool BaseChatMesh::shouldInitiateSessionKey(const ContactInfo& contact) {
11181153}
11191154
11201155bool BaseChatMesh::initiateSessionKeyNegotiation (const ContactInfo& contact) {
1121- auto entry = session_keys. allocate (contact.id .pub_key );
1156+ auto entry = allocateSessionKey (contact.id .pub_key );
11221157 if (!entry) return false ;
11231158
11241159 // Don't start a new negotiation if one is already pending
@@ -1154,7 +1189,7 @@ bool BaseChatMesh::handleSessionKeyResponse(ContactInfo& contact, const uint8_t*
11541189 if (len < 5 + PUB_KEY_SIZE ) return false ;
11551190 if (data[4 ] != RESP_TYPE_SESSION_KEY_ACCEPT ) return false ;
11561191
1157- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1192+ auto entry = findSessionKey (contact.id .pub_key );
11581193 if (!entry || entry->state != SESSION_STATE_INIT_SENT ) return false ;
11591194
11601195 const uint8_t * ephemeral_pub_B = &data[5 ];
@@ -1216,7 +1251,7 @@ uint8_t BaseChatMesh::handleIncomingSessionKeyInit(ContactInfo& from, const uint
12161251 memset (ephemeral_secret, 0 , PUB_KEY_SIZE );
12171252
12181253 // 4. Store in pool (dual-decode: new key active, old key still valid)
1219- auto entry = session_keys. allocate (from.id .pub_key );
1254+ auto entry = allocateSessionKey (from.id .pub_key );
12201255 if (!entry) return 0 ;
12211256
12221257 if (entry->state == SESSION_STATE_ACTIVE || entry->state == SESSION_STATE_DUAL_DECODE ) {
@@ -1279,7 +1314,7 @@ void BaseChatMesh::checkSessionKeyTimeouts() {
12791314const uint8_t * BaseChatMesh::getPeerSessionKey (int peer_idx) {
12801315 int i = matching_peer_indexes[peer_idx];
12811316 if (i >= 0 && i < num_contacts) {
1282- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1317+ auto entry = findSessionKey (contacts[i].id .pub_key );
12831318 // Also try decode during INIT_SENT renegotiation (nonce > 1 means prior key exists)
12841319 if (entry && (entry->state == SESSION_STATE_ACTIVE || entry->state == SESSION_STATE_DUAL_DECODE
12851320 || (entry->state == SESSION_STATE_INIT_SENT && entry->nonce > 1 )))
@@ -1291,7 +1326,7 @@ const uint8_t* BaseChatMesh::getPeerSessionKey(int peer_idx) {
12911326const uint8_t * BaseChatMesh::getPeerPrevSessionKey (int peer_idx) {
12921327 int i = matching_peer_indexes[peer_idx];
12931328 if (i >= 0 && i < num_contacts) {
1294- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1329+ auto entry = findSessionKey (contacts[i].id .pub_key );
12951330 if (entry && entry->state == SESSION_STATE_DUAL_DECODE )
12961331 return entry->prev_session_key ;
12971332 }
@@ -1301,7 +1336,7 @@ const uint8_t* BaseChatMesh::getPeerPrevSessionKey(int peer_idx) {
13011336void BaseChatMesh::onSessionKeyDecryptSuccess (int peer_idx) {
13021337 int i = matching_peer_indexes[peer_idx];
13031338 if (i >= 0 && i < num_contacts) {
1304- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1339+ auto entry = findSessionKey (contacts[i].id .pub_key );
13051340 if (entry) {
13061341 bool changed = (entry->state == SESSION_STATE_DUAL_DECODE );
13071342 if (changed) {
0 commit comments