From 7682782405c799bbc5b888318d15ab3e8d608b0a Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Fri, 13 Feb 2026 00:26:42 +0100 Subject: [PATCH] Harden AEAD-4 bounds checks and add nonce wrap logging - Fix potential unsigned overflow in createDatagram size check by subtracting constants from MAX_PACKET_PAYLOAD instead of adding to data_len - Add upper-bound validation on src_len and assoc_len in aeadEncrypt and aeadDecrypt - Log peer name on AEAD nonce wraparound for debug builds --- src/Mesh.cpp | 3 ++- src/Utils.cpp | 6 ++++-- src/helpers/ContactInfo.h | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index e8b5f3b87..c88940388 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -531,7 +531,8 @@ Packet* Mesh::createDatagram(uint8_t type, const Identity& dest, const uint8_t* if (type == PAYLOAD_TYPE_TXT_MSG || type == PAYLOAD_TYPE_REQ || type == PAYLOAD_TYPE_RESPONSE) { size_t hash_prefix = PATH_HASH_SIZE * 2; // dest_hash + src_hash size_t max_overhead = aead_nonce ? (AEAD_NONCE_SIZE + AEAD_TAG_SIZE) : (CIPHER_MAC_SIZE + CIPHER_BLOCK_SIZE-1); - if (data_len + hash_prefix + max_overhead > MAX_PACKET_PAYLOAD) return NULL; + size_t max_payload_data = MAX_PACKET_PAYLOAD - hash_prefix - max_overhead; + if (data_len > max_payload_data) return NULL; } else { return NULL; // invalid type } diff --git a/src/Utils.cpp b/src/Utils.cpp index a0c98b880..82882b2c4 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -122,7 +122,8 @@ int Utils::aeadEncrypt(const uint8_t* shared_secret, const uint8_t* assoc_data, int assoc_len, uint16_t nonce_counter, uint8_t dest_hash, uint8_t src_hash) { - if (src_len <= 0) return 0; + if (src_len <= 0 || src_len > MAX_PACKET_PAYLOAD) return 0; + if (assoc_len < 0 || assoc_len > MAX_PACKET_PAYLOAD) return 0; // Write nonce to output dest[0] = (uint8_t)(nonce_counter >> 8); @@ -166,7 +167,8 @@ int Utils::aeadDecrypt(const uint8_t* shared_secret, const uint8_t* assoc_data, int assoc_len, uint8_t dest_hash, uint8_t src_hash) { // Minimum: nonce(2) + at least 1 byte ciphertext + tag(4) - if (src_len < AEAD_NONCE_SIZE + 1 + AEAD_TAG_SIZE) return 0; + if (src_len < AEAD_NONCE_SIZE + 1 + AEAD_TAG_SIZE || src_len > MAX_PACKET_PAYLOAD) return 0; + if (assoc_len < 0 || assoc_len > MAX_PACKET_PAYLOAD) return 0; int ct_len = src_len - AEAD_NONCE_SIZE - AEAD_TAG_SIZE; diff --git a/src/helpers/ContactInfo.h b/src/helpers/ContactInfo.h index 595c464c6..da2b8b1f4 100644 --- a/src/helpers/ContactInfo.h +++ b/src/helpers/ContactInfo.h @@ -23,7 +23,10 @@ struct ContactInfo { // When 0, callers use ECB encryption. uint16_t nextAeadNonce() const { if (flags & CONTACT_FLAG_AEAD) { - if (++aead_nonce == 0) ++aead_nonce; // skip 0 (sentinel for ECB) + if (++aead_nonce == 0) { + ++aead_nonce; // skip 0 (sentinel for ECB) + MESH_DEBUG_PRINTLN("AEAD nonce wrapped for peer: %s", name); + } return aead_nonce; } return 0;