@@ -967,7 +967,7 @@ bool BaseChatMesh::removeContact(ContactInfo& contact) {
967967 }
968968 if (idx >= num_contacts) return false ; // not found
969969
970- session_keys. remove (contact.id .pub_key ); // also remove session key if any
970+ removeSessionKey (contact.id .pub_key ); // also remove session key if any
971971
972972 // remove from contacts array and parallel nonce tracking
973973 num_contacts--;
@@ -1082,6 +1082,41 @@ void BaseChatMesh::loop() {
10821082 }
10831083}
10841084
1085+ // --- Session key flash-backed wrappers ---
1086+
1087+ SessionKeyEntry* BaseChatMesh::findSessionKey (const uint8_t * pub_key) {
1088+ auto entry = session_keys.findByPrefix (pub_key);
1089+ if (entry) return entry;
1090+
1091+ // Cache miss — try flash
1092+ uint8_t flags; uint16_t nonce;
1093+ uint8_t sk[SESSION_KEY_SIZE ], psk[SESSION_KEY_SIZE ];
1094+ if (!loadSessionKeyRecordFromFlash (pub_key, &flags, &nonce, sk, psk)) return nullptr ;
1095+
1096+ // Save dirty evictee before overwriting
1097+ if (session_keys.isFull () && session_keys_dirty) {
1098+ mergeAndSaveSessionKeys ();
1099+ }
1100+ session_keys.applyLoaded (pub_key, flags, nonce, sk, psk);
1101+ return session_keys.findByPrefix (pub_key);
1102+ }
1103+
1104+ SessionKeyEntry* BaseChatMesh::allocateSessionKey (const uint8_t * pub_key) {
1105+ // Check RAM and flash first
1106+ auto entry = findSessionKey (pub_key);
1107+ if (entry) return entry;
1108+
1109+ // Not found anywhere — save dirty evictee before allocating
1110+ if (session_keys.isFull () && session_keys_dirty) {
1111+ mergeAndSaveSessionKeys ();
1112+ }
1113+ return session_keys.allocate (pub_key);
1114+ }
1115+
1116+ void BaseChatMesh::removeSessionKey (const uint8_t * pub_key) {
1117+ session_keys.remove (pub_key);
1118+ }
1119+
10851120// --- Session key support (Phase 2 — initiator) ---
10861121
10871122static bool canUseSessionKey (const SessionKeyEntry* entry) {
@@ -1097,7 +1132,7 @@ static bool canUseSessionKey(const SessionKeyEntry* entry) {
10971132}
10981133
10991134const uint8_t * BaseChatMesh::getEncryptionKeyFor (const ContactInfo& contact) {
1100- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1135+ auto entry = findSessionKey (contact.id .pub_key );
11011136 if (canUseSessionKey (entry)) {
11021137 return entry->session_key ;
11031138 }
@@ -1106,7 +1141,7 @@ const uint8_t* BaseChatMesh::getEncryptionKeyFor(const ContactInfo& contact) {
11061141
11071142uint16_t BaseChatMesh::getEncryptionNonceFor (const ContactInfo& contact) {
11081143 uint16_t nonce = 0 ;
1109- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1144+ auto entry = findSessionKey (contact.id .pub_key );
11101145 if (canUseSessionKey (entry)) {
11111146 ++entry->nonce ;
11121147 if (entry->sends_since_last_recv < 255 ) entry->sends_since_last_recv ++;
@@ -1120,7 +1155,7 @@ uint16_t BaseChatMesh::getEncryptionNonceFor(const ContactInfo& contact) {
11201155 int idx = &contact - contacts;
11211156 if (idx >= 0 && idx < num_contacts)
11221157 contacts[idx].flags &= ~CONTACT_FLAG_AEAD ;
1123- session_keys. remove (contact.id .pub_key );
1158+ removeSessionKey (contact.id .pub_key );
11241159 onSessionKeysUpdated ();
11251160 // nonce = 0 (ECB)
11261161 } else if (entry->sends_since_last_recv >= SESSION_KEY_ECB_THRESHOLD ) {
@@ -1149,7 +1184,7 @@ bool BaseChatMesh::shouldInitiateSessionKey(const ContactInfo& contact) {
11491184 // Need a known path to send the request
11501185 if (contact.out_path_len < 0 ) return false ;
11511186
1152- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1187+ auto entry = findSessionKey (contact.id .pub_key );
11531188
11541189 // Don't trigger if negotiation already in progress
11551190 if (entry && entry->state == SESSION_STATE_INIT_SENT ) return false ;
@@ -1185,7 +1220,7 @@ bool BaseChatMesh::shouldInitiateSessionKey(const ContactInfo& contact) {
11851220}
11861221
11871222bool BaseChatMesh::initiateSessionKeyNegotiation (const ContactInfo& contact) {
1188- auto entry = session_keys. allocate (contact.id .pub_key );
1223+ auto entry = allocateSessionKey (contact.id .pub_key );
11891224 if (!entry) return false ;
11901225
11911226 // Don't start a new negotiation if one is already pending
@@ -1221,7 +1256,7 @@ bool BaseChatMesh::handleSessionKeyResponse(ContactInfo& contact, const uint8_t*
12211256 if (len < 5 + PUB_KEY_SIZE ) return false ;
12221257 if (data[4 ] != RESP_TYPE_SESSION_KEY_ACCEPT ) return false ;
12231258
1224- auto entry = session_keys. findByPrefix (contact.id .pub_key );
1259+ auto entry = findSessionKey (contact.id .pub_key );
12251260 if (!entry || entry->state != SESSION_STATE_INIT_SENT ) return false ;
12261261
12271262 const uint8_t * ephemeral_pub_B = &data[5 ];
@@ -1283,7 +1318,7 @@ uint8_t BaseChatMesh::handleIncomingSessionKeyInit(ContactInfo& from, const uint
12831318 memset (ephemeral_secret, 0 , PUB_KEY_SIZE );
12841319
12851320 // 4. Store in pool (dual-decode: new key active, old key still valid)
1286- auto entry = session_keys. allocate (from.id .pub_key );
1321+ auto entry = allocateSessionKey (from.id .pub_key );
12871322 if (!entry) return 0 ;
12881323
12891324 if (entry->state == SESSION_STATE_ACTIVE || entry->state == SESSION_STATE_DUAL_DECODE ) {
@@ -1346,7 +1381,7 @@ void BaseChatMesh::checkSessionKeyTimeouts() {
13461381const uint8_t * BaseChatMesh::getPeerSessionKey (int peer_idx) {
13471382 int i = matching_peer_indexes[peer_idx];
13481383 if (i >= 0 && i < num_contacts) {
1349- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1384+ auto entry = findSessionKey (contacts[i].id .pub_key );
13501385 // Also try decode during INIT_SENT renegotiation (nonce > 1 means prior key exists)
13511386 if (entry && (entry->state == SESSION_STATE_ACTIVE || entry->state == SESSION_STATE_DUAL_DECODE
13521387 || (entry->state == SESSION_STATE_INIT_SENT && entry->nonce > 1 )))
@@ -1358,7 +1393,7 @@ const uint8_t* BaseChatMesh::getPeerSessionKey(int peer_idx) {
13581393const uint8_t * BaseChatMesh::getPeerPrevSessionKey (int peer_idx) {
13591394 int i = matching_peer_indexes[peer_idx];
13601395 if (i >= 0 && i < num_contacts) {
1361- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1396+ auto entry = findSessionKey (contacts[i].id .pub_key );
13621397 if (entry && entry->state == SESSION_STATE_DUAL_DECODE )
13631398 return entry->prev_session_key ;
13641399 }
@@ -1368,7 +1403,7 @@ const uint8_t* BaseChatMesh::getPeerPrevSessionKey(int peer_idx) {
13681403void BaseChatMesh::onSessionKeyDecryptSuccess (int peer_idx) {
13691404 int i = matching_peer_indexes[peer_idx];
13701405 if (i >= 0 && i < num_contacts) {
1371- auto entry = session_keys. findByPrefix (contacts[i].id .pub_key );
1406+ auto entry = findSessionKey (contacts[i].id .pub_key );
13721407 if (entry) {
13731408 bool changed = (entry->state == SESSION_STATE_DUAL_DECODE );
13741409 if (changed) {
0 commit comments