-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add AEAD (AES-CCM) authenticated encryption for PSK channels #9749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
001cd28
b623cb2
34e653c
30b708a
d83a33a
b9fdf80
72e6484
1512b25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,11 @@ | |
| #include "serialization/MeshPacketSerializer.h" | ||
| #endif | ||
|
|
||
| // The size checks below budget for the tag that encryptPacketCCM actually appends, so the | ||
| // two constants must not drift apart. | ||
| static_assert(MESHTASTIC_AEAD_OVERHEAD == CryptoEngine::AEAD_TAG_SIZE, | ||
| "MESHTASTIC_AEAD_OVERHEAD must match CryptoEngine::AEAD_TAG_SIZE"); | ||
|
|
||
| #define MAX_RX_FROMRADIO \ | ||
| 4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big | ||
|
|
||
|
|
@@ -923,15 +928,32 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p) | |
| // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf. Create a | ||
| // fresh copy for each decrypt attempt. | ||
| memcpy(bytes, p->encrypted.bytes, rawSize); | ||
| // Try to decrypt the packet if we can | ||
| crypto->decrypt(p->from, p->id, rawSize, bytes); | ||
|
|
||
| size_t decryptedSize = rawSize; | ||
|
|
||
| if (channels.isAEADEnabled(chIndex)) { | ||
| // AEAD decryption — no CTR fallback | ||
| if (rawSize <= MESHTASTIC_AEAD_OVERHEAD) { | ||
| LOG_ERROR("Packet too small for AEAD (size=%d)", rawSize); | ||
| continue; | ||
| } | ||
| CryptoKey k = channels.getKey(chIndex); | ||
| if (!crypto->decryptPacketCCM(k, p->from, p->id, rawSize, p->encrypted.bytes, bytes)) { | ||
| LOG_WARN("AEAD authentication failed for ch %d", chIndex); | ||
|
Comment on lines
+937
to
+942
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Use hex formatting for the one-byte channel index in log messages.
📝 Proposed fix- LOG_WARN("AEAD authentication failed for ch %d", chIndex);
+ LOG_WARN("AEAD authentication failed for ch 0x%x", chIndex);- LOG_ERROR("AEAD encryption failed for ch %d", chIndex);
+ LOG_ERROR("AEAD encryption failed for ch 0x%x", chIndex);(apply to both Based on coding guidelines: "format one-byte values, flags, and reason codes as Also applies to: 1216-1216, 1248-1248 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| continue; // reject — no fallback to CTR | ||
| } | ||
| decryptedSize = rawSize - MESHTASTIC_AEAD_OVERHEAD; | ||
| } else { | ||
| // Standard AES-CTR decryption | ||
| crypto->decrypt(p->from, p->id, rawSize, bytes); | ||
| } | ||
|
|
||
| // printBytes("plaintext", bytes, p->encrypted.size); | ||
|
|
||
| // Take those raw bytes and convert them back into a well structured protobuf we can understand | ||
| meshtastic_Data decodedtmp; | ||
| memset(&decodedtmp, 0, sizeof(decodedtmp)); | ||
| if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &decodedtmp)) { | ||
| if (!pb_decode_from_bytes(bytes, decryptedSize, &meshtastic_Data_msg, &decodedtmp)) { | ||
| LOG_DEBUG("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)", p->id); | ||
| } else if (decodedtmp.portnum == meshtastic_PortNum_UNKNOWN_APP) { | ||
| LOG_DEBUG("Invalid portnum (bad psk?)"); | ||
|
|
@@ -1184,32 +1206,64 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) | |
| // Client specifically requested PKI encryption | ||
| return meshtastic_Routing_Error_PKI_FAILED; | ||
| } | ||
| hash = channels.setActiveByIndex(chIndex); | ||
|
|
||
| // Now that we are encrypting the packet channel should be the hash (no longer the index) | ||
| p->channel = hash; | ||
| if (hash < 0) { | ||
| // No suitable channel could be found for | ||
| return meshtastic_Routing_Error_NO_CHANNEL; | ||
| if (channels.isAEADEnabled(chIndex)) { | ||
| // AEAD (AES-CCM) authenticated encryption path | ||
| if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_AEAD_OVERHEAD > MAX_LORA_PAYLOAD_LEN) | ||
| return meshtastic_Routing_Error_TOO_LARGE; | ||
|
|
||
| hash = channels.setActiveByIndex(chIndex); | ||
| p->channel = hash; | ||
| if (hash < 0) | ||
| return meshtastic_Routing_Error_NO_CHANNEL; | ||
|
|
||
| CryptoKey k = channels.getKey(chIndex); | ||
| if (!crypto->encryptPacketCCM(k, getFrom(p), p->id, numbytes, bytes, p->encrypted.bytes)) { | ||
| LOG_ERROR("AEAD encryption failed for ch %d", chIndex); | ||
| return meshtastic_Routing_Error_BAD_REQUEST; | ||
| } | ||
| numbytes += MESHTASTIC_AEAD_OVERHEAD; | ||
| } else { | ||
| // Standard AES-CTR encryption path | ||
| hash = channels.setActiveByIndex(chIndex); | ||
| p->channel = hash; | ||
| if (hash < 0) | ||
| return meshtastic_Routing_Error_NO_CHANNEL; | ||
|
|
||
| crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); | ||
| memcpy(p->encrypted.bytes, bytes, numbytes); | ||
| } | ||
| crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); | ||
| memcpy(p->encrypted.bytes, bytes, numbytes); | ||
| } | ||
| #else | ||
| if (p->pki_encrypted == true) { | ||
| // Client specifically requested PKI encryption | ||
| return meshtastic_Routing_Error_PKI_FAILED; | ||
| } | ||
| hash = channels.setActiveByIndex(chIndex); | ||
| if (channels.isAEADEnabled(chIndex)) { | ||
| // AEAD (AES-CCM) authenticated encryption path | ||
| if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_AEAD_OVERHEAD > MAX_LORA_PAYLOAD_LEN) | ||
| return meshtastic_Routing_Error_TOO_LARGE; | ||
|
|
||
| hash = channels.setActiveByIndex(chIndex); | ||
| p->channel = hash; | ||
| if (hash < 0) | ||
| return meshtastic_Routing_Error_NO_CHANNEL; | ||
|
|
||
| CryptoKey k = channels.getKey(chIndex); | ||
| if (!crypto->encryptPacketCCM(k, getFrom(p), p->id, numbytes, bytes, p->encrypted.bytes)) { | ||
| LOG_ERROR("AEAD encryption failed for ch %d", chIndex); | ||
| return meshtastic_Routing_Error_BAD_REQUEST; | ||
| } | ||
| numbytes += MESHTASTIC_AEAD_OVERHEAD; | ||
| } else { | ||
| // Standard AES-CTR encryption path | ||
| hash = channels.setActiveByIndex(chIndex); | ||
| p->channel = hash; | ||
| if (hash < 0) | ||
| return meshtastic_Routing_Error_NO_CHANNEL; | ||
|
|
||
| // Now that we are encrypting the packet channel should be the hash (no longer the index) | ||
| p->channel = hash; | ||
| if (hash < 0) { | ||
| // No suitable channel could be found for | ||
| return meshtastic_Routing_Error_NO_CHANNEL; | ||
| crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); | ||
| memcpy(p->encrypted.bytes, bytes, numbytes); | ||
| } | ||
| crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); | ||
| memcpy(p->encrypted.bytes, bytes, numbytes); | ||
| #endif | ||
|
|
||
| // Copy back into the packet and set the variant type | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,8 @@ | ||
| #pragma once | ||
| #include "CryptoEngine.h" | ||
| #if !MESHTASTIC_EXCLUDE_PKI | ||
|
|
||
| int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len, | ||
| const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth); | ||
|
|
||
| bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len, | ||
| const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain); | ||
| #endif | ||
| const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain); |
Uh oh!
There was an error while loading. Please reload this page.