fix: add AES-CCM authenticated encryption for channel messages#10056
Open
Patrickschell609 wants to merge 1 commit intomeshtastic:developfrom
Open
fix: add AES-CCM authenticated encryption for channel messages#10056Patrickschell609 wants to merge 1 commit intomeshtastic:developfrom
Patrickschell609 wants to merge 1 commit intomeshtastic:developfrom
Conversation
Channel and broadcast messages currently use AES-CTR with no authentication tag. This allows trivial bit-flipping attacks -- anyone sniffing LoRa packets can forge messages on any mesh. This commit wires the existing AES-CCM implementation (already used for PKI direct messages) into the channel encryption path: - Add encryptPacketCCM() / decryptPacketCCM() to CryptoEngine - Fix aesSetKey() to handle 16-byte keys (AES-128) via AESSmall128 (previously always created AESSmall256 which silently rejected the default 16-byte PSK) - Update perhapsEncode() to use CCM with 8-byte auth tag when the tag fits within MAX_LORA_PAYLOAD_LEN, falling back to CTR for oversized packets - Update perhapsDecode() to try CCM first, falling back to CTR for legacy node compatibility - Add 7 new test vectors: CCM round-trip, bit corruption rejection, legacy CTR interop, wrong tag rejection, oversized CTR fallback, and both AES-128/AES-256 key sizes Backwards compatible: new firmware sends CCM, accepts both CCM and CTR. Legacy nodes continue to work via automatic fallback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
Author
Proof of Concept: AES-CTR Bit-Flip AttackRan this against a live mesh (RAK4631 + T-Echo on OPSNET channel). The attack requires zero knowledge of the encryption key. How the attack worksIn AES-CTR mode, flipping bit N in the ciphertext flips bit N in the plaintext. There is no authentication tag to detect modification. An attacker with an SDR (HackRF One, ~$300) can:
The receiving node decrypts it and accepts it as legitimate. The attacker never needs the PSK. This PR adds an 8-byte AES-CCM authentication tag that detects any modification. Forged packets are rejected at the crypto layer before reaching application code. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
aes-ccm.cpp) into the channel encryption path. No new crypto code -- just connecting what was already there.aesSetKey()that always createdAESSmall256, which silently rejects 16-byte keys. The default Meshtastic PSK is 16 bytes (AES-128), so the ECB path was broken for all default-key nodes.Changes
src/mesh/CryptoEngine.hMESHTASTIC_CCM_TAG_SIZEconstant (8 bytes)encryptPacketCCM()/decryptPacketCCM()declarationsaesmember type fromAESSmall256toBlockCipher(polymorphic for 128/256)src/mesh/CryptoEngine.cppaesSetKey()to createAESSmall128for 16-byte keys,AESSmall256for 32-byte keysencryptPacketCCM(): AES-CCM encrypt with 8-byte auth tag appendeddecryptPacketCCM(): AES-CCM decrypt with auth tag verificationsrc/mesh/Router.cppperhapsEncode(): Use CCM when auth tag fits in LoRa frame (plaintext <= 231 bytes), fall back to CTR for oversized packetsperhapsDecode(): Try CCM first (auth tag check), fall back to CTR for legacy compatibilitytest/test_crypto/test_main.cppBackwards Compatibility
Test plan
🤖 Generated with Claude Code