@@ -71,7 +71,7 @@ void MyMesh::pushPostToClient(ClientInfo *client, PostInfo &post) {
7171 mesh::Utils::sha256 ((uint8_t *)&client->extra .room .pending_ack , 4 , reply_data, len, client->id .pub_key , PUB_KEY_SIZE );
7272 client->extra .room .push_post_timestamp = post .post_timestamp ;
7373
74- auto reply = createDatagram (PAYLOAD_TYPE_TXT_MSG , client->id , client->shared_secret , reply_data, len, client-> nextAeadNonce ( ));
74+ auto reply = createDatagram (PAYLOAD_TYPE_TXT_MSG , client->id , client->shared_secret , reply_data, len, acl. nextAeadNonceFor (*client ));
7575 if (reply) {
7676 if (client->out_path_len == OUT_PATH_UNKNOWN ) {
7777 unsigned long delay_millis = 0 ;
@@ -424,7 +424,7 @@ uint8_t MyMesh::getPeerFlags(int peer_idx) {
424424uint16_t MyMesh::getPeerNextAeadNonce (int peer_idx) {
425425 int i = matching_peer_indexes[peer_idx];
426426 if (i >= 0 && i < acl.getNumClients ())
427- return acl.getClientByIdx (i)-> nextAeadNonce ( );
427+ return acl.nextAeadNonceFor (*acl. getClientByIdx (i));
428428 return 0 ;
429429}
430430
@@ -434,7 +434,10 @@ void MyMesh::onPeerAeadDetected(int peer_idx) {
434434 auto c = acl.getClientByIdx (i);
435435 if (!(c->flags & CONTACT_FLAG_AEAD )) {
436436 c->flags |= CONTACT_FLAG_AEAD ;
437- getRNG ()->random ((uint8_t *)&c->aead_nonce , sizeof (c->aead_nonce ));
437+ if (c->aead_nonce == 0 ) { // no persisted nonce — seed from RNG to avoid deterministic start
438+ getRNG ()->random ((uint8_t *)&c->aead_nonce , sizeof (c->aead_nonce ));
439+ if (c->aead_nonce == 0 ) c->aead_nonce = 1 ;
440+ }
438441 }
439442 }
440443}
@@ -532,7 +535,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
532535 // mesh::Utils::sha256((uint8_t *)&expected_ack_crc, 4, temp, 5 + text_len, self_id.pub_key,
533536 // PUB_KEY_SIZE);
534537
535- auto reply = createDatagram (PAYLOAD_TYPE_TXT_MSG , client->id , secret, temp, 5 + text_len, client-> nextAeadNonce ( ));
538+ auto reply = createDatagram (PAYLOAD_TYPE_TXT_MSG , client->id , secret, temp, 5 + text_len, acl. nextAeadNonceFor (*client ));
536539 if (reply) {
537540 if (client->out_path_len == OUT_PATH_UNKNOWN ) {
538541 sendFloodReply (reply, delay_millis + SERVER_RESPONSE_DELAY , packet->getPathHashSize ());
@@ -589,10 +592,10 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
589592 if (packet->isRouteFlood ()) {
590593 // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
591594 mesh::Packet *path = createPathReturn (client->id , secret, packet->path , packet->path_len ,
592- PAYLOAD_TYPE_RESPONSE , reply_data, reply_len, client-> nextAeadNonce ( ));
595+ PAYLOAD_TYPE_RESPONSE , reply_data, reply_len, acl. nextAeadNonceFor (*client ));
593596 if (path) sendFloodReply (path, SERVER_RESPONSE_DELAY , packet->getPathHashSize ());
594597 } else {
595- mesh::Packet *reply = createDatagram (PAYLOAD_TYPE_RESPONSE , client->id , secret, reply_data, reply_len, client-> nextAeadNonce ( ));
598+ mesh::Packet *reply = createDatagram (PAYLOAD_TYPE_RESPONSE , client->id , secret, reply_data, reply_len, acl. nextAeadNonceFor (*client ));
596599 if (reply) {
597600 if (client->out_path_len != OUT_PATH_UNKNOWN ) { // we have an out_path, so send DIRECT
598601 sendDirect (reply, client->out_path , client->out_path_len , SERVER_RESPONSE_DELAY );
@@ -647,6 +650,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
647650 uptime_millis = 0 ;
648651 next_local_advert = next_flood_advert = 0 ;
649652 dirty_contacts_expiry = 0 ;
653+ next_nonce_persist = 0 ;
650654 _logging = false ;
651655 region_load_active = false ;
652656 set_radio_at = revert_radio_at = 0 ;
@@ -702,6 +706,12 @@ void MyMesh::begin(FILESYSTEM *fs) {
702706
703707 acl.load (_fs, self_id);
704708 region_map.load (_fs);
709+ acl.setRNG (getRNG ());
710+ acl.loadNonces ();
711+ bool dirty_reset = wasDirtyReset (board);
712+ acl.finalizeNonceLoad (dirty_reset);
713+ if (dirty_reset) acl.saveNonces (); // persist bumped nonces immediately
714+ next_nonce_persist = futureMillis (60000 );
705715
706716 // establish default-scope
707717 {
@@ -1048,6 +1058,12 @@ void MyMesh::loop() {
10481058 dirty_contacts_expiry = 0 ;
10491059 }
10501060
1061+ // persist dirty AEAD nonces
1062+ if (next_nonce_persist && millisHasNowPassed (next_nonce_persist)) {
1063+ if (acl.isNonceDirty ()) { acl.saveNonces (); }
1064+ next_nonce_persist = futureMillis (60000 );
1065+ }
1066+
10511067 // TODO: periodically check for OLD/inactive entries in known_clients[], and evict
10521068
10531069 // update uptime
0 commit comments