Bug #5 Patch -- Random Padding in AES-ECB Last Block (non-breaking)
====================================================================

This patch replaces zero-padding with random-padding in the last
(partial) AES-ECB block. This is a PARTIAL mitigation that makes the
last ciphertext block non-deterministic, preventing pattern leakage
for short messages and reducing information leakage for all messages.

NON-BREAKING: The wire format, packet layout, ciphertext length, and
HMAC computation are unchanged. Patched and unpatched nodes interoperate
fully -- decryption does not depend on padding content.

Limitations:
  - Full 16-byte-aligned plaintext blocks are still ECB-encrypted
    (identical blocks produce identical ciphertext).
  - The first-block codebook attack (timestamp + type byte) is NOT
    mitigated for messages longer than 11 bytes.
  - A complete fix (AES-CTR or AES-CBC with per-packet IV) requires
    a protocol version bump -- documented as future work.

Coverage:
  - Short messages (< 16 bytes plaintext): FULLY mitigated -- the
    single ciphertext block is now non-deterministic.
  - Longer messages: last block mitigated, earlier blocks unchanged.


--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ encrypt()
   if (src_len > 0) {  // remaining partial block
     uint8_t tmp[16];
-    memset(tmp, 0, 16);
+    random(tmp, 16);             // random padding instead of zeros
     memcpy(tmp, src, src_len);
     aes.encryptBlock(dp, tmp);
     dp += 16;
   }


=== Safety Analysis ===

All callers of MACThenDecrypt() are safe with random padding bytes:

1. TEXT MESSAGES (TXT_MSG, GRP_TXT): Callers write data[len]=0 for
   null termination, then use the text as a C string via strlen().
   The original null terminator inside the text stops string parsing
   before any padding bytes. Random padding is never read.

2. GROUP DATA (GRP_DATA): Has an explicit data_len field at data[2].
   The ECB-padded len is only used for bounds validation; actual data
   extent is determined by the inner length field. Padding ignored.

3. REQUESTS (REQ): Handlers read fixed offsets (data[0], data[1])
   based on request type codes. The padded len is not used to
   determine how much data to read. Padding irrelevant.

4. RESPONSES (RESPONSE): memcpy(&out_frame[i], &data[4], len-4)
   copies padding bytes into the serial frame. This already happens
   with zero-padding (up to 15 extra bytes). Random bytes instead of
   zeros are functionally equivalent -- the companion app parses
   response data by type code and inner structure, not raw frame
   length. The meshcore_py reader uses explicit per-response-type
   parsing.

5. PATH (extra=ACK): Only reads first 4 bytes. Padding irrelevant.

6. PATH (extra=RESPONSE): Delegates to onContactResponse -- same as
   case 4 above.


=== Future Work: Full Fix (Protocol v2) ===

A complete fix requires replacing AES-ECB with a nonce-based mode
(AES-CTR recommended). This adds an 8-byte nonce per encrypted
payload and eliminates block-level pattern leakage entirely. It
requires a protocol version negotiation mechanism so that updated
nodes can fall back to ECB when communicating with legacy nodes.

Recommended approach for protocol v2:
  - Add CIPHER_NONCE_SIZE = 8 constant
  - encrypt(): prepend random 8-byte nonce, use AES-CTR (XOR keystream)
  - decrypt(): extract nonce, reconstruct CTR keystream
  - Update size checks in createDatagram/createAnonDatagram/createGroupDatagram
  - Negotiate version via advertisement flags or handshake
