A lightweight transport layer with mutual authentication for IoT devices using the Noise Protocol Framework.
This project implements a secure mesh link layer based on the Noise_IK_25519_ChaChaPoly_BLAKE2b pattern, providing:
- 0-RTT handshake via IK pattern with pre-known server key
- Replay protection via sliding window nonce tracking
- Automatic rekeying after 2^32 messages or 1 hour
- Zero-copy optimization for embedded systems
- MTU-aware fragmentation for IPv6 (1280 bytes)
- Perfect Forward Secrecy via ephemeral key exchange
The codebase follows Domain-Driven Design (DDD) principles with clear layer separation:
include/noise_mesh/
βββ types.h # Core domain types and constants
βββ session.h # Application layer: session management
βββ replay_filter.h # Domain: anti-replay protection
βββ fragmentation.h # Application: MTU handling
src/
βββ session.c # Session state machine
βββ replay_filter.c # Sliding window algorithm
βββ fragmentation.c # Message fragmentation
# Clone with submodules
git clone --recursive https://github.com/your/repo.git
cd Noise_IK_25519_ChaChaPoly_BLAKE2b
# Build
mkdir build && cd build
cmake ..
make
# Run tests
./test_handshake
# Run server
./mesh_server 7000
# Run client (in another terminal)
./mesh_client 127.0.0.1 7000The IK pattern provides:
- Initiator knows K (responder's static public key)
- 0-RTT encryption capability
- Vulnerable to Key Compromise Impersonation (KCI)
Message flow:
Initiator Responder
-------- ---------
e, s
e, ee, se
s, es
- Message-based: After 2^32 messages (nonce overflow protection)
- Time-based: After 1 hour (forward secrecy)
- Manual: Via
noise_mesh_session_rekey()
256-bit sliding window tracks received nonces:
- Detects duplicate packets
- Handles limited out-of-order delivery
- O(1) memory, O(1) time complexity
Each Noise message is prefixed with a 2-byte big-endian length field:
+--------+------------------------+
| Length | Noise Protocol Payload |
| 2 bytes| (variable) |
+--------+------------------------+
Actual packet capture from lo0 interface (localhost:8000):
| Frame | Direction | TCP Payload | Description |
|---|---|---|---|
| 1 | β | 0060 |
Length prefix: 96 bytes |
| 2 | β | 96 bytes | e, s - Initiator's ephemeral + encrypted static key |
| 3 | β | 0030 |
Length prefix: 48 bytes |
| 4 | β | 48 bytes | e, ee, se - Responder's ephemeral + 2 DH results |
| 5 | β | 002d |
Length prefix: 45 bytes |
| 6 | β | 45 bytes | s, es + encrypted payload - Final handshake + app data |
| 7 | β | 002c |
Length prefix: 44 bytes |
| 8 | β | 44 bytes | Encrypted application response |
Message breakdown:
- First message (96B): X25519 ephemeral (32B) + encrypted static key + Poly1305 auth tag (16B)
- Second message (48B): X25519 ephemeral (32B) + Poly1305 auth tag (16B)
- Third message (45B): Encrypted transport data + Poly1305 tag
To analyze the protocol traffic on macOS:
# Start capture on loopback interface
tshark -i lo0 -w capture.pcap -f "port 8000"
# Use the custom Noise IK dissector for detailed parsing
tshark -r capture.pcap -X lua_script:wireshark/noise_ik.lua
# Filter for Noise IK packets
tshark -r capture.pcap -X lua_script:wireshark/noise_ik.lua -Y "noise_ik"
# Show detailed protocol breakdown
tshark -r capture.pcap -X lua_script:wireshark/noise_ik.lua -VSee wireshark/README.md for dissector installation and usage details.
The IK pattern is vulnerable to KCI attacks. If the responder's static private key is compromised, an attacker can impersonate any initiator to that responder.
To mitigate, consider switching to XX pattern for mutual KCI protection.
- All sensitive data is zeroized using
secure_zero() volatilequalifier prevents compiler optimization- Keys are cleared on session termination
The underlying noise-c library uses constant-time implementations for:
- X25519 key exchange
- ChaCha20-Poly1305 encryption
- BLAKE2b hashing
The project uses Google C++ style with 2-space indentation:
# Format code
make format
# Or use clang-format directly
clang-format -i --style=Google src/*.c include/*.hMIT License - See LICENSE file for details.
- Noise Protocol Framework
- noise-c - C implementation
- RFC 7539 - ChaCha20 and Poly1305
- RFC 7693 - BLAKE2 Cryptographic Hash Function
On macOS, localhost traffic goes through the lo0 interface:
# Install Wireshark (includes tshark)
brew install wireshark
# Start capture on loopback
tshark -i lo0 -w debug.pcap -f "port 8000"
# Or use Wireshark GUI
open -a Wireshark
# Select Loopback: lo0 interface
# Filter: tcp.port == 8000Note: Since Noise Protocol encrypts immediately after handshake, you will only see encrypted ciphertext in Wireshark. To decrypt, you would need to export session keys from your application (not standard for Noise Protocol).
Both mesh_client and mesh_server log handshake progress:
Connecting to 127.0.0.1:8000
Connected. Starting handshake...
Sent handshake message (96 bytes) # e, s
Received handshake message (48 bytes) # e, ee, se
Handshake complete!
Sent encrypted message
Received: Echo from Noise Mesh Server!
| Problem | Solution |
|---|---|
Address already in use |
Kill existing process: pkill mesh_server |
Connection refused |
Verify server is running on correct port |
| No packets captured | Ensure using lo0 interface on macOS for localhost |
| Handshake timeout | Check firewall rules allow loopback connections |