@@ -5,6 +5,8 @@ namespace mesh {
55
66void Mesh::begin () {
77 Dispatcher::begin ();
8+ // Initialize Ascon counter with random starting value for replay protection
9+ Utils::initAsconCounter (*_rng);
810}
911
1012void Mesh::loop () {
@@ -46,6 +48,11 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
4648
4749 if (pkt->isRouteDirect () && pkt->getPayloadType () == PAYLOAD_TYPE_TRACE) {
4850 if (pkt->path_len < MAX_PATH_SIZE) {
51+ // TRACE packet minimum: trace_tag(4) + auth_code(4) + flags(1) = 9 bytes
52+ if (pkt->payload_len < 9 ) {
53+ MESH_DEBUG_PRINTLN (" %s Mesh::onRecvPacket(): incomplete TRACE packet" , getLogDateTime ());
54+ return ACTION_RELEASE;
55+ }
4956 uint8_t i = 0 ;
5057 uint32_t trace_tag;
5158 memcpy (&trace_tag, &pkt->payload [i], 4 ); i += 4 ;
@@ -56,9 +63,10 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
5663
5764 uint8_t len = pkt->payload_len - i;
5865 uint8_t offset = pkt->path_len << path_sz;
66+ uint8_t hash_size = 1 << path_sz;
5967 if (offset >= len) { // TRACE has reached end of given path
6068 onTraceRecv (pkt, trace_tag, auth_code, flags, pkt->path , &pkt->payload [i], len);
61- } else if (self_id.isHashMatch (&pkt->payload [i + offset], 1 << path_sz ) && allowPacketForward (pkt) && !_tables->hasSeen (pkt)) {
69+ } else if (i + offset + hash_size <= pkt-> payload_len && self_id.isHashMatch (&pkt->payload [i + offset], hash_size ) && allowPacketForward (pkt) && !_tables->hasSeen (pkt)) {
6270 // append SNR (Not hash!)
6371 pkt->path [pkt->path_len ++] = (int8_t ) (pkt->getSNR ()*4 );
6472
@@ -80,10 +88,9 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
8088 if (pkt->isRouteDirect () && pkt->path_len >= PATH_HASH_SIZE) {
8189 // check for 'early received' ACK
8290 if (pkt->getPayloadType () == PAYLOAD_TYPE_ACK) {
83- int i = 0 ;
84- uint32_t ack_crc;
85- memcpy (&ack_crc, &pkt->payload [i], 4 ); i += 4 ;
86- if (i <= pkt->payload_len ) {
91+ if (pkt->payload_len >= 4 ) {
92+ uint32_t ack_crc;
93+ memcpy (&ack_crc, &pkt->payload [0 ], 4 );
8794 onAckRecv (pkt, ack_crc);
8895 }
8996 }
@@ -115,12 +122,11 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
115122
116123 switch (pkt->getPayloadType ()) {
117124 case PAYLOAD_TYPE_ACK: {
118- int i = 0 ;
119- uint32_t ack_crc;
120- memcpy (&ack_crc, &pkt->payload [i], 4 ); i += 4 ;
121- if (i > pkt->payload_len ) {
125+ if (pkt->payload_len < 4 ) {
122126 MESH_DEBUG_PRINTLN (" %s Mesh::onRecvPacket(): incomplete ACK packet" , getLogDateTime ());
123127 } else if (!_tables->hasSeen (pkt)) {
128+ uint32_t ack_crc;
129+ memcpy (&ack_crc, &pkt->payload [0 ], 4 );
124130 onAckRecv (pkt, ack_crc);
125131 action = routeRecvPacket (pkt);
126132 }
@@ -134,8 +140,10 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
134140 uint8_t dest_hash = pkt->payload [i++];
135141 uint8_t src_hash = pkt->payload [i++];
136142
137- uint8_t * macAndData = &pkt->payload [i]; // MAC + encrypted data
138- if (i + CIPHER_MAC_SIZE >= pkt->payload_len ) {
143+ uint8_t * encryptedData = &pkt->payload [i]; // encrypted data (AES: MAC + ciphertext, Ascon: counter + ciphertext + tag)
144+ int encrypted_len = pkt->payload_len - i;
145+
146+ if (encrypted_len < CIPHER_MAC_SIZE) {
139147 MESH_DEBUG_PRINTLN (" %s Mesh::onRecvPacket(): incomplete data packet" , getLogDateTime ());
140148 } else if (!_tables->hasSeen (pkt)) {
141149 // NOTE: this is a 'first packet wins' impl. When receiving from multiple paths, the first to arrive wins.
@@ -151,10 +159,15 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
151159 uint8_t secret[PUB_KEY_SIZE];
152160 getPeerSharedSecret (secret, j);
153161
154- // decrypt, checking MAC is valid
162+ // Unified decryption: tries Ascon first, falls back to legacy AES-ECB+HMAC
155163 uint8_t data[MAX_PACKET_PAYLOAD];
156- int len = Utils::MACThenDecrypt (secret, data, macAndData, pkt->payload_len - i);
164+ bool was_ascon = false ;
165+ int len = Utils::decryptAuto (secret, data, encryptedData, encrypted_len, &was_ascon);
166+
157167 if (len > 0 ) { // success!
168+ // Notify subclass of detected crypto capability (for auto-upgrade to Ascon)
169+ onPeerAsconCapabilityDetected (j, was_ascon);
170+
158171 if (pkt->getPayloadType () == PAYLOAD_TYPE_PATH) {
159172 int k = 0 ;
160173 uint8_t path_len = data[k++];
@@ -164,17 +177,23 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
164177 uint8_t extra_len = len - k; // remainder of packet (may be padded with zeroes!)
165178 if (onPeerPathRecv (pkt, j, secret, path, path_len, extra_type, extra, extra_len)) {
166179 if (pkt->isRouteFlood ()) {
167- // send a reciprocal return path to sender, but send DIRECTLY!
168- mesh::Packet* rpath = createPathReturn (&src_hash, secret, pkt->path , pkt->path_len , 0 , NULL , 0 );
180+ // send a reciprocal return path to sender using same crypto they used , but send DIRECTLY!
181+ mesh::Packet* rpath = createPathReturn (&src_hash, secret, pkt->path , pkt->path_len , 0 , NULL , 0 , was_ascon );
169182 if (rpath) sendDirect (rpath, path, path_len, 500 );
170183 }
171184 }
172185 } else {
173186 onPeerDataRecv (pkt, pkt->getPayloadType (), j, secret, data, len);
174187 }
188+ // SECURITY: Clear sensitive data from stack before breaking
189+ memset (data, 0 , sizeof (data));
190+ memset (secret, 0 , sizeof (secret));
175191 found = true ;
176192 break ;
177193 }
194+ // SECURITY: Clear buffers even if decryption failed
195+ memset (data, 0 , sizeof (data));
196+ memset (secret, 0 , sizeof (secret));
178197 }
179198 if (found) {
180199 pkt->markDoNotRetransmit (); // packet was for this node, so don't retransmit
@@ -191,8 +210,10 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
191210 uint8_t dest_hash = pkt->payload [i++];
192211 uint8_t * sender_pub_key = &pkt->payload [i]; i += PUB_KEY_SIZE;
193212
194- uint8_t * macAndData = &pkt->payload [i]; // MAC + encrypted data
195- if (i + 2 >= pkt->payload_len ) {
213+ uint8_t * encryptedData = &pkt->payload [i]; // encrypted data (AES: MAC + ciphertext, Ascon: counter + ciphertext + tag)
214+ int encrypted_len = pkt->payload_len - i;
215+
216+ if (encrypted_len < CIPHER_MAC_SIZE + 1 ) {
196217 MESH_DEBUG_PRINTLN (" %s Mesh::onRecvPacket(): incomplete data packet" , getLogDateTime ());
197218 } else if (!_tables->hasSeen (pkt)) {
198219 if (self_id.isHashMatch (&dest_hash)) {
@@ -201,39 +222,51 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
201222 uint8_t secret[PUB_KEY_SIZE];
202223 self_id.calcSharedSecret (secret, sender);
203224
204- // decrypt, checking MAC is valid
225+ // Unified decryption: tries Ascon first, falls back to legacy AES-ECB+HMAC
205226 uint8_t data[MAX_PACKET_PAYLOAD];
206- int len = Utils::MACThenDecrypt (secret, data, macAndData, pkt->payload_len - i);
227+ bool was_ascon = false ;
228+ int len = Utils::decryptAuto (secret, data, encryptedData, encrypted_len, &was_ascon);
229+
207230 if (len > 0 ) { // success!
208- onAnonDataRecv (pkt, secret, sender, data, len);
231+ onAnonDataRecv (pkt, secret, sender, data, len, was_ascon );
209232 pkt->markDoNotRetransmit ();
210233 }
234+ // SECURITY: Clear sensitive data from stack
235+ memset (data, 0 , sizeof (data));
236+ memset (secret, 0 , sizeof (secret));
211237 }
212238 action = routeRecvPacket (pkt);
213239 }
214240 break ;
215241 }
216- case PAYLOAD_TYPE_GRP_DATA:
242+ case PAYLOAD_TYPE_GRP_DATA:
217243 case PAYLOAD_TYPE_GRP_TXT: {
218244 int i = 0 ;
219245 uint8_t channel_hash = pkt->payload [i++];
220246
221- uint8_t * macAndData = &pkt->payload [i]; // MAC + encrypted data
222- if (i + 2 >= pkt->payload_len ) {
247+ uint8_t * encryptedData = &pkt->payload [i]; // encrypted data (AES: MAC + ciphertext, Ascon: counter + ciphertext + tag)
248+ int encrypted_len = pkt->payload_len - i;
249+
250+ if (encrypted_len < 2 ) {
223251 MESH_DEBUG_PRINTLN (" %s Mesh::onRecvPacket(): incomplete data packet" , getLogDateTime ());
224252 } else if (!_tables->hasSeen (pkt)) {
225253 // scan channels DB, for all matching hashes of 'channel_hash' (max 4 matches supported ATM)
226254 GroupChannel channels[4 ];
227255 int num = searchChannelsByHash (&channel_hash, channels, 4 );
228256 // for each matching channel, try to decrypt data
229257 for (int j = 0 ; j < num; j++) {
230- // decrypt, checking MAC is valid
258+ // Unified decryption: tries Ascon first, falls back to legacy AES-ECB+HMAC
231259 uint8_t data[MAX_PACKET_PAYLOAD];
232- int len = Utils::MACThenDecrypt (channels[j].secret , data, macAndData, pkt->payload_len - i);
260+ int len = Utils::decryptAuto (channels[j].secret , data, encryptedData, encrypted_len);
261+
233262 if (len > 0 ) { // success!
234263 onGroupDataRecv (pkt, pkt->getPayloadType (), channels[j], data, len);
264+ // SECURITY: Clear sensitive data from stack before breaking
265+ memset (data, 0 , sizeof (data));
235266 break ;
236267 }
268+ // SECURITY: Clear buffer even if decryption failed
269+ memset (data, 0 , sizeof (data));
237270 }
238271 action = routeRecvPacket (pkt);
239272 }
@@ -430,23 +463,38 @@ Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, siz
430463 return packet;
431464}
432465
433- #define MAX_COMBINED_PATH (MAX_PACKET_PAYLOAD - 2 - CIPHER_BLOCK_SIZE)
434-
435- Packet* Mesh::createPathReturn (const Identity& dest, const uint8_t * secret, const uint8_t * path, uint8_t path_len, uint8_t extra_type, const uint8_t *extra, size_t extra_len) {
466+ Packet* Mesh::createPathReturn (const Identity& dest, const uint8_t * secret, const uint8_t * path, uint8_t path_len, uint8_t extra_type, const uint8_t *extra, size_t extra_len, bool use_ascon) {
436467 uint8_t dest_hash[PATH_HASH_SIZE];
437468 dest.copyHashTo (dest_hash);
438- return createPathReturn (dest_hash, secret, path, path_len, extra_type, extra, extra_len);
469+ return createPathReturn (dest_hash, secret, path, path_len, extra_type, extra, extra_len, use_ascon );
439470}
440471
441- Packet* Mesh::createPathReturn (const uint8_t * dest_hash, const uint8_t * secret, const uint8_t * path, uint8_t path_len, uint8_t extra_type, const uint8_t *extra, size_t extra_len) {
442- if (path_len + extra_len + 5 > MAX_COMBINED_PATH) return NULL ; // too long!!
472+ Packet* Mesh::createPathReturn (const uint8_t * dest_hash, const uint8_t * secret, const uint8_t * path, uint8_t path_len, uint8_t extra_type, const uint8_t *extra, size_t extra_len, bool use_ascon) {
473+ // Plaintext layout: [path_len (1)] [path (path_len)] [extra_type (1)] [extra (extra_len)]
474+ // Or if no extra: [dummy_type (1)] [rand (4)]
475+ const int plain_len = 1 + (int )path_len + ((extra_len > 0 ) ? (1 + (int )extra_len) : (1 + 4 ));
476+
477+ // Payload prefix: dest_hash (1) + src_hash (1)
478+ const int prefix_len = 2 ;
479+
480+ // Check size based on encryption mode
481+ if (use_ascon) {
482+ // V2: prefix + counter + ciphertext + tag
483+ if (prefix_len + ASCON_COUNTER_SIZE + plain_len + ASCON_TAG_SIZE > MAX_PACKET_PAYLOAD) return NULL ;
484+ } else {
485+ // V1: prefix + MAC + ciphertext (rounded up to 16)
486+ int enc_len = ((plain_len + 15 ) / 16 ) * 16 ;
487+ if (prefix_len + CIPHER_MAC_SIZE + enc_len > MAX_PACKET_PAYLOAD) return NULL ;
488+ }
443489
444490 Packet* packet = obtainNewPacket ();
445491 if (packet == NULL ) {
446492 MESH_DEBUG_PRINTLN (" %s Mesh::createPathReturn(): error, packet pool empty" , getLogDateTime ());
447493 return NULL ;
448494 }
449- packet->header = (PAYLOAD_TYPE_PATH << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later
495+
496+ // Always use PAYLOAD_VER_1 header for backwards compatibility with old repeaters
497+ packet->header = (PAYLOAD_TYPE_PATH << PH_TYPE_SHIFT);
450498
451499 int len = 0 ;
452500 memcpy (&packet->payload [len], dest_hash, PATH_HASH_SIZE); len += PATH_HASH_SIZE; // dest hash
@@ -467,17 +515,32 @@ Packet* Mesh::createPathReturn(const uint8_t* dest_hash, const uint8_t* secret,
467515 getRNG ()->random (&data[data_len], 4 ); data_len += 4 ;
468516 }
469517
470- len += Utils::encryptThenMAC (secret, &packet->payload [len], data, data_len);
518+ // Encrypt based on peer capability
519+ if (use_ascon) {
520+ len += Utils::encryptAscon (secret, &packet->payload [len], data, data_len);
521+ } else {
522+ len += Utils::encryptThenMAC (secret, &packet->payload [len], data, data_len);
523+ }
471524 }
472525
473526 packet->payload_len = len;
474527
475528 return packet;
476529}
477530
478- Packet* Mesh::createDatagram (uint8_t type, const Identity& dest, const uint8_t * secret, const uint8_t * data, size_t data_len) {
531+ Packet* Mesh::createDatagram (uint8_t type, const Identity& dest, const uint8_t * secret, const uint8_t * data, size_t data_len, bool use_ascon ) {
479532 if (type == PAYLOAD_TYPE_TXT_MSG || type == PAYLOAD_TYPE_REQ || type == PAYLOAD_TYPE_RESPONSE) {
480- if (data_len + CIPHER_MAC_SIZE + CIPHER_BLOCK_SIZE-1 > MAX_PACKET_PAYLOAD) return NULL ;
533+ // Payload prefix: dest_hash (1) + src_hash (1)
534+ const int prefix_len = 2 ;
535+ // Check size based on encryption mode
536+ if (use_ascon) {
537+ // V2: prefix + counter + ciphertext + tag
538+ if (prefix_len + (int )data_len + ASCON_COUNTER_SIZE + ASCON_TAG_SIZE > MAX_PACKET_PAYLOAD) return NULL ;
539+ } else {
540+ // V1: prefix + MAC + ciphertext (rounded up to 16)
541+ int enc_len = (((int )data_len + 15 ) / 16 ) * 16 ;
542+ if (prefix_len + CIPHER_MAC_SIZE + enc_len > MAX_PACKET_PAYLOAD) return NULL ;
543+ }
481544 } else {
482545 return NULL ; // invalid type
483546 }
@@ -487,21 +550,37 @@ Packet* Mesh::createDatagram(uint8_t type, const Identity& dest, const uint8_t*
487550 MESH_DEBUG_PRINTLN (" %s Mesh::createDatagram(): error, packet pool empty" , getLogDateTime ());
488551 return NULL ;
489552 }
490- packet->header = (type << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later
553+
554+ // Always use PAYLOAD_VER_1 header for backwards compatibility with old repeaters
555+ packet->header = (type << PH_TYPE_SHIFT);
491556
492557 int len = 0 ;
493558 len += dest.copyHashTo (&packet->payload [len]); // dest hash
494559 len += self_id.copyHashTo (&packet->payload [len]); // src hash
495- len += Utils::encryptThenMAC (secret, &packet->payload [len], data, data_len);
560+
561+ // Encrypt based on peer capability
562+ if (use_ascon) {
563+ len += Utils::encryptAscon (secret, &packet->payload [len], data, data_len);
564+ } else {
565+ len += Utils::encryptThenMAC (secret, &packet->payload [len], data, data_len);
566+ }
496567
497568 packet->payload_len = len;
498569
499570 return packet;
500571}
501572
502- Packet* Mesh::createAnonDatagram (uint8_t type, const LocalIdentity& sender, const Identity& dest, const uint8_t * secret, const uint8_t * data, size_t data_len) {
573+ Packet* Mesh::createAnonDatagram (uint8_t type, const LocalIdentity& sender, const Identity& dest, const uint8_t * secret, const uint8_t * data, size_t data_len, bool use_ascon ) {
503574 if (type == PAYLOAD_TYPE_ANON_REQ) {
504- if (data_len + 1 + PUB_KEY_SIZE + CIPHER_BLOCK_SIZE-1 > MAX_PACKET_PAYLOAD) return NULL ;
575+ // Check size based on encryption mode
576+ if (use_ascon) {
577+ // V2: dest_hash + pub_key + counter + ciphertext + tag
578+ if (data_len + 1 + PUB_KEY_SIZE + ASCON_COUNTER_SIZE + ASCON_TAG_SIZE > MAX_PACKET_PAYLOAD) return NULL ;
579+ } else {
580+ // V1: dest_hash + pub_key + MAC + ciphertext (rounded up to 16)
581+ int enc_len = ((data_len + 15 ) / 16 ) * 16 ;
582+ if (1 + PUB_KEY_SIZE + CIPHER_MAC_SIZE + enc_len > MAX_PACKET_PAYLOAD) return NULL ;
583+ }
505584 } else {
506585 return NULL ; // invalid type
507586 }
@@ -511,7 +590,9 @@ Packet* Mesh::createAnonDatagram(uint8_t type, const LocalIdentity& sender, cons
511590 MESH_DEBUG_PRINTLN (" %s Mesh::createAnonDatagram(): error, packet pool empty" , getLogDateTime ());
512591 return NULL ;
513592 }
514- packet->header = (type << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later
593+
594+ // Always use PAYLOAD_VER_1 header for backwards compatibility with old repeaters
595+ packet->header = (type << PH_TYPE_SHIFT);
515596
516597 int len = 0 ;
517598 if (type == PAYLOAD_TYPE_ANON_REQ) {
@@ -520,27 +601,52 @@ Packet* Mesh::createAnonDatagram(uint8_t type, const LocalIdentity& sender, cons
520601 } else {
521602 // FUTURE:
522603 }
523- len += Utils::encryptThenMAC (secret, &packet->payload [len], data, data_len);
604+
605+ // Encrypt based on peer capability
606+ if (use_ascon) {
607+ len += Utils::encryptAscon (secret, &packet->payload [len], data, data_len);
608+ } else {
609+ len += Utils::encryptThenMAC (secret, &packet->payload [len], data, data_len);
610+ }
524611
525612 packet->payload_len = len;
526613
527614 return packet;
528615}
529616
530- Packet* Mesh::createGroupDatagram (uint8_t type, const GroupChannel& channel, const uint8_t * data, size_t data_len) {
617+ Packet* Mesh::createGroupDatagram (uint8_t type, const GroupChannel& channel, const uint8_t * data, size_t data_len, bool use_ascon ) {
531618 if (!(type == PAYLOAD_TYPE_GRP_TXT || type == PAYLOAD_TYPE_GRP_DATA)) return NULL ; // invalid type
532- if (data_len + 1 + CIPHER_BLOCK_SIZE-1 > MAX_PACKET_PAYLOAD) return NULL ; // too long
619+
620+ // Payload prefix: channel_hash (PATH_HASH_SIZE, currently 1)
621+ const int prefix_len = PATH_HASH_SIZE;
622+ // Check size based on encryption mode
623+ if (use_ascon) {
624+ // V2: prefix + counter + ciphertext + tag
625+ if (prefix_len + (int )data_len + ASCON_COUNTER_SIZE + ASCON_TAG_SIZE > MAX_PACKET_PAYLOAD) return NULL ;
626+ } else {
627+ // V1: prefix + MAC + ciphertext (rounded up to 16)
628+ int enc_len = (((int )data_len + 15 ) / 16 ) * 16 ;
629+ if (prefix_len + CIPHER_MAC_SIZE + enc_len > MAX_PACKET_PAYLOAD) return NULL ;
630+ }
533631
534632 Packet* packet = obtainNewPacket ();
535633 if (packet == NULL ) {
536634 MESH_DEBUG_PRINTLN (" %s Mesh::createGroupDatagram(): error, packet pool empty" , getLogDateTime ());
537635 return NULL ;
538636 }
539- packet->header = (type << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later
637+
638+ // Always use PAYLOAD_VER_1 header for backwards compatibility with old repeaters
639+ packet->header = (type << PH_TYPE_SHIFT);
540640
541641 int len = 0 ;
542642 memcpy (&packet->payload [len], channel.hash , PATH_HASH_SIZE); len += PATH_HASH_SIZE;
543- len += Utils::encryptThenMAC (channel.secret , &packet->payload [len], data, data_len);
643+
644+ // Encrypt based on channel flag or explicit request
645+ if (use_ascon) {
646+ len += Utils::encryptAscon (channel.secret , &packet->payload [len], data, data_len);
647+ } else {
648+ len += Utils::encryptThenMAC (channel.secret , &packet->payload [len], data, data_len);
649+ }
544650
545651 packet->payload_len = len;
546652
0 commit comments