-
Notifications
You must be signed in to change notification settings - Fork 964
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from meshtastic/master
Update
- Loading branch information
Showing
20 changed files
with
420 additions
and
237 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Encryption in Meshtastic | ||
|
||
Cryptography is tricky, so we've tried to 'simply' apply standard crypto solutions to our implementation. However, | ||
the project developers are not cryptography experts. Therefore we ask two things: | ||
|
||
- If you are a cryptography expert, please review these notes and our questions below. Can you help us by reviewing our | ||
notes below and offering advice? We will happily give as much or as little credit as you wish as our thanks ;-). | ||
- Consider our existing solution 'alpha' and probably fairly secure against an not very aggressive adversary. But until | ||
it is reviewed by someone smarter than us, assume it might have flaws. | ||
|
||
## Notes on implementation | ||
|
||
- We do all crypto at the SubPacket (payload) level only, so that all meshtastic nodes will route for others - even those channels which are encrypted with a different key. | ||
- Mostly based on reading [Wikipedia](<https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)>) and using the modes the ESP32 provides support for in hardware. | ||
- We use AES256-CTR as a stream cypher (with zero padding on the last BLOCK) because it is well supported with hardware acceleration. | ||
|
||
Parameters for our CTR implementation: | ||
|
||
- Our AES key is 256 bits, shared as part of the 'Channel' specification. | ||
- Each SubPacket will be sent as a series of 16 byte BLOCKS. | ||
- The node number concatenated with the packet number is used as the NONCE. This counter will be stored in flash in the device and should essentially never repeat. If the user makes a new 'Channel' (i.e. picking a new random 256 bit key), the packet number will start at zero. The packet number is sent | ||
in cleartext with each packet. The node number can be derived from the "from" field of each packet. | ||
- Each BLOCK for a packet has an incrementing COUNTER. COUNTER starts at zero for the first block of each packet. | ||
- The IV for each block is constructed by concatenating the NONCE as the upper 96 bits of the IV and the COUNTER as the bottom 32 bits. Note: since our packets are small counter will really never be higher than 32 (five bits). | ||
|
||
``` | ||
You can encrypt separate messages by dividing the nonce_counter buffer in two areas: the first one used for a per-message nonce, handled by yourself, and the second one updated by this function internally. | ||
For example, you might reserve the first 12 bytes for the per-message nonce, and the last 4 bytes for internal use. In that case, before calling this function on a new message you need to set the first 12 bytes of nonce_counter to your chosen nonce value, the last 4 to 0, and nc_off to 0 (which will cause stream_block to be ignored). That way, you can encrypt at most 2**96 messages of up to 2**32 blocks each with the same key. | ||
The per-message nonce (or information sufficient to reconstruct it) needs to be communicated with the ciphertext and must be unique. The recommended way to ensure uniqueness is to use a message counter. An alternative is to generate random nonces, but this limits the number of messages that can be securely encrypted: for example, with 96-bit random nonces, you should not encrypt more than 2**32 messages with the same key. | ||
Note that for both stategies, sizes are measured in blocks and that an AES block is 16 bytes. | ||
``` | ||
|
||
## Remaining todo | ||
|
||
- Make the packet numbers 32 bit | ||
- Implement for NRF52 [NRF52](https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v15.0.0/lib_crypto_aes.html#sub_aes_ctr) |
This file contains 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
Submodule proto
updated
from b35e7f to 5e2df6
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#include "CryptoEngine.h" | ||
#include "configuration.h" | ||
|
||
#include "crypto/includes.h" | ||
|
||
#include "crypto/common.h" | ||
|
||
// #include "esp_system.h" | ||
|
||
#include "crypto/aes.h" | ||
#include "crypto/aes_wrap.h" | ||
#include "mbedtls/aes.h" | ||
|
||
#define MAX_BLOCKSIZE 256 | ||
|
||
class ESP32CryptoEngine : public CryptoEngine | ||
{ | ||
|
||
mbedtls_aes_context aes; | ||
|
||
/// How many bytes in our key | ||
uint8_t keySize = 0; | ||
|
||
public: | ||
ESP32CryptoEngine() { mbedtls_aes_init(&aes); } | ||
|
||
~ESP32CryptoEngine() { mbedtls_aes_free(&aes); } | ||
|
||
/** | ||
* Set the key used for encrypt, decrypt. | ||
* | ||
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. | ||
* | ||
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) | ||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the | ||
* provided pointer) | ||
*/ | ||
virtual void setKey(size_t numBytes, uint8_t *bytes) | ||
{ | ||
keySize = numBytes; | ||
DEBUG_MSG("Installing AES%d key!\n", numBytes * 8); | ||
if (numBytes != 0) { | ||
auto res = mbedtls_aes_setkey_enc(&aes, bytes, numBytes * 8); | ||
assert(!res); | ||
} | ||
} | ||
|
||
/** | ||
* Encrypt a packet | ||
* | ||
* @param bytes is updated in place | ||
*/ | ||
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) | ||
{ | ||
if (keySize != 0) { | ||
uint8_t stream_block[16]; | ||
static uint8_t scratch[MAX_BLOCKSIZE]; | ||
size_t nc_off = 0; | ||
|
||
// DEBUG_MSG("ESP32 encrypt!\n"); | ||
initNonce(fromNode, packetNum); | ||
assert(numBytes <= MAX_BLOCKSIZE); | ||
memcpy(scratch, bytes, numBytes); | ||
memset(scratch + numBytes, 0, | ||
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) | ||
|
||
auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes); | ||
assert(!res); | ||
} | ||
} | ||
|
||
virtual void decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) | ||
{ | ||
// DEBUG_MSG("ESP32 decrypt!\n"); | ||
|
||
// For CTR, the implementation is the same | ||
encrypt(fromNode, packetNum, numBytes, bytes); | ||
} | ||
|
||
private: | ||
}; | ||
|
||
CryptoEngine *crypto = new ESP32CryptoEngine(); |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#include "CryptoEngine.h" | ||
#include "configuration.h" | ||
|
||
void CryptoEngine::setKey(size_t numBytes, uint8_t *bytes) | ||
{ | ||
DEBUG_MSG("WARNING: Using stub crypto - all crypto is sent in plaintext!\n"); | ||
} | ||
|
||
/** | ||
* Encrypt a packet | ||
* | ||
* @param bytes is updated in place | ||
*/ | ||
void CryptoEngine::encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) | ||
{ | ||
DEBUG_MSG("WARNING: noop encryption!\n"); | ||
} | ||
|
||
void CryptoEngine::decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) | ||
{ | ||
DEBUG_MSG("WARNING: noop decryption!\n"); | ||
} | ||
|
||
/** | ||
* Init our 128 bit nonce for a new packet | ||
*/ | ||
void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetNum) | ||
{ | ||
memset(nonce, 0, sizeof(nonce)); | ||
*((uint64_t *)&nonce[0]) = packetNum; | ||
*((uint32_t *)&nonce[8]) = fromNode; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#pragma once | ||
|
||
#include <Arduino.h> | ||
|
||
/** | ||
* see docs/software/crypto.md for details. | ||
* | ||
*/ | ||
|
||
class CryptoEngine | ||
{ | ||
protected: | ||
/** Our per packet nonce */ | ||
uint8_t nonce[16]; | ||
|
||
public: | ||
virtual ~CryptoEngine() {} | ||
|
||
/** | ||
* Set the key used for encrypt, decrypt. | ||
* | ||
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. | ||
* | ||
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) | ||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the | ||
* provided pointer) | ||
*/ | ||
virtual void setKey(size_t numBytes, uint8_t *bytes); | ||
|
||
/** | ||
* Encrypt a packet | ||
* | ||
* @param bytes is updated in place | ||
*/ | ||
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes); | ||
virtual void decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes); | ||
|
||
protected: | ||
/** | ||
* Init our 128 bit nonce for a new packet | ||
* | ||
* The NONCE is constructed by concatenating (from MSB to LSB): | ||
* a 64 bit packet number (stored in little endian order) | ||
* a 32 bit sending node number (stored in little endian order) | ||
* a 32 bit block counter (starts at zero) | ||
*/ | ||
void initNonce(uint32_t fromNode, uint64_t packetNum); | ||
}; | ||
|
||
extern CryptoEngine *crypto; |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.