@@ -991,7 +991,7 @@ bool BaseChatMesh::removeContact(ContactInfo& contact) {
991991 }
992992 if (idx >= num_contacts) return false ; // not found
993993
994- session_keys. remove (contact.id .pub_key ); // also remove session key if any
994+ removeSessionKey (contact.id .pub_key ); // also remove session key if any
995995
996996 // remove from contacts array and parallel nonce tracking
997997 num_contacts--;
@@ -1106,6 +1106,41 @@ void BaseChatMesh::loop() {
11061106 }
11071107}
11081108
1109+ // --- Session key flash-backed wrappers ---
1110+
1111+ SessionKeyEntry* BaseChatMesh::findSessionKey (const uint8_t * pub_key) {
1112+ auto entry = session_keys.findByPrefix (pub_key);
1113+ if (entry) return entry;
1114+
1115+ // Cache miss — try flash
1116+ uint8_t flags; uint16_t nonce;
1117+ uint8_t sk[SESSION_KEY_SIZE ], psk[SESSION_KEY_SIZE ];
1118+ if (!loadSessionKeyRecordFromFlash (pub_key, &flags, &nonce, sk, psk)) return nullptr ;
1119+
1120+ // Save dirty evictee before overwriting
1121+ if (session_keys.isFull () && session_keys_dirty) {
1122+ mergeAndSaveSessionKeys ();
1123+ }
1124+ session_keys.applyLoaded (pub_key, flags, nonce, sk, psk);
1125+ return session_keys.findByPrefix (pub_key);
1126+ }
1127+
1128+ SessionKeyEntry* BaseChatMesh::allocateSessionKey (const uint8_t * pub_key) {
1129+ // Check RAM and flash first
1130+ auto entry = findSessionKey (pub_key);
1131+ if (entry) return entry;
1132+
1133+ // Not found anywhere — save dirty evictee before allocating
1134+ if (session_keys.isFull () && session_keys_dirty) {
1135+ mergeAndSaveSessionKeys ();
1136+ }
1137+ return session_keys.allocate (pub_key);
1138+ }
1139+
1140+ void BaseChatMesh::removeSessionKey (const uint8_t * pub_key) {
1141+ session_keys.remove (pub_key);
1142+ }
1143+
11091144// --- Session key support (Phase 2 — initiator) ---
11101145
11111146static bool canUseSessionKey (const SessionKeyEntry* entry) {
@@ -1121,7 +1156,7 @@ static bool canUseSessionKey(const SessionKeyEntry* entry) {
11211156}
11221157
11231158const uint8_t * BaseChatMesh::getEncryptionKeyFor (const ContactInfo& contact) {
1124- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1159+ auto entry = findSessionKey (contact.id .pub_key );
11251160 if (canUseSessionKey (entry)) {
11261161 return entry->session_key ;
11271162 }
@@ -1130,7 +1165,7 @@ const uint8_t* BaseChatMesh::getEncryptionKeyFor(const ContactInfo& contact) {
11301165
11311166uint16_t BaseChatMesh::getEncryptionNonceFor (const ContactInfo& contact) {
11321167 uint16_t nonce = 0 ;
1133- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1168+ auto entry = findSessionKey (contact.id .pub_key );
11341169 if (canUseSessionKey (entry)) {
11351170 ++entry->nonce ;
11361171 if (entry->sends_since_last_recv < 255 ) entry->sends_since_last_recv ++;
@@ -1144,7 +1179,7 @@ uint16_t BaseChatMesh::getEncryptionNonceFor(const ContactInfo& contact) {
11441179 int idx = &contact - contacts;
11451180 if (idx >= 0 && idx < num_contacts)
11461181 contacts[idx].flags &= ~CONTACT_FLAG_AEAD ;
1147- session_keys. remove (contact.id .pub_key );
1182+ removeSessionKey (contact.id .pub_key );
11481183 onSessionKeysUpdated ();
11491184 // nonce = 0 (ECB)
11501185 } else if (entry->sends_since_last_recv >= SESSION_KEY_ECB_THRESHOLD ) {
@@ -1173,7 +1208,7 @@ bool BaseChatMesh::shouldInitiateSessionKey(const ContactInfo& contact) {
11731208 // Need a known path to send the request
11741209 if (contact.out_path_len < 0 ) return false ;
11751210
1176- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1211+ auto entry = findSessionKey (contact.id .pub_key );
11771212
11781213 // Don't trigger if negotiation already in progress
11791214 if (entry && entry->state == SESSION_STATE_INIT_SENT ) return false ;
@@ -1209,7 +1244,7 @@ bool BaseChatMesh::shouldInitiateSessionKey(const ContactInfo& contact) {
12091244}
12101245
12111246bool BaseChatMesh::initiateSessionKeyNegotiation (const ContactInfo& contact) {
1212- auto entry = session_keys. allocate (contact.id .pub_key );
1247+ auto entry = allocateSessionKey (contact.id .pub_key );
12131248 if (!entry) return false ;
12141249
12151250 // Don't start a new negotiation if one is already pending
@@ -1245,7 +1280,7 @@ bool BaseChatMesh::handleSessionKeyResponse(ContactInfo& contact, const uint8_t*
12451280 if (len < 5 + PUB_KEY_SIZE ) return false ;
12461281 if (data[4 ] != RESP_TYPE_SESSION_KEY_ACCEPT ) return false ;
12471282
1248- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1283+ auto entry = findSessionKey (contact.id .pub_key );
12491284 if (!entry || entry->state != SESSION_STATE_INIT_SENT ) return false ;
12501285
12511286 const uint8_t * ephemeral_pub_B = &data[5 ];
@@ -1307,7 +1342,7 @@ uint8_t BaseChatMesh::handleIncomingSessionKeyInit(ContactInfo& from, const uint
13071342 memset (ephemeral_secret, 0 , PUB_KEY_SIZE );
13081343
13091344 // 4. Store in pool (dual-decode: new key active, old key still valid)
1310- auto entry = session_keys. allocate (from.id .pub_key );
1345+ auto entry = allocateSessionKey (from.id .pub_key );
13111346 if (!entry) return 0 ;
13121347
13131348 if (entry->state == SESSION_STATE_ACTIVE || entry->state == SESSION_STATE_DUAL_DECODE ) {
@@ -1370,7 +1405,7 @@ void BaseChatMesh::checkSessionKeyTimeouts() {
13701405const uint8_t * BaseChatMesh::getPeerSessionKey (int peer_idx) {
13711406 int i = matching_peer_indexes[peer_idx];
13721407 if (i >= 0 && i < num_contacts) {
1373- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1408+ auto entry = findSessionKey (contacts[i].id .pub_key );
13741409 // Also try decode during INIT_SENT renegotiation (nonce > 1 means prior key exists)
13751410 if (entry && (entry->state == SESSION_STATE_ACTIVE || entry->state == SESSION_STATE_DUAL_DECODE
13761411 || (entry->state == SESSION_STATE_INIT_SENT && entry->nonce > 1 )))
@@ -1382,7 +1417,7 @@ const uint8_t* BaseChatMesh::getPeerSessionKey(int peer_idx) {
13821417const uint8_t * BaseChatMesh::getPeerPrevSessionKey (int peer_idx) {
13831418 int i = matching_peer_indexes[peer_idx];
13841419 if (i >= 0 && i < num_contacts) {
1385- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1420+ auto entry = findSessionKey (contacts[i].id .pub_key );
13861421 if (entry && entry->state == SESSION_STATE_DUAL_DECODE )
13871422 return entry->prev_session_key ;
13881423 }
@@ -1392,7 +1427,7 @@ const uint8_t* BaseChatMesh::getPeerPrevSessionKey(int peer_idx) {
13921427void BaseChatMesh::onSessionKeyDecryptSuccess (int peer_idx) {
13931428 int i = matching_peer_indexes[peer_idx];
13941429 if (i >= 0 && i < num_contacts) {
1395- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1430+ auto entry = findSessionKey (contacts[i].id .pub_key );
13961431 if (entry) {
13971432 bool changed = (entry->state == SESSION_STATE_DUAL_DECODE );
13981433 if (changed) {
0 commit comments