📬 Radikant-Noise-C

Noise Protocol Framework

NVS

Introduction

Radikant Noise is a experimental tcp implementation of The Noise Protocol Framework to establish a “secure connection” between an initiator and responder following the various patterns outlined in the noise standard. Noise vastly simplifies the establishment of a secure channel in comparison to TLS. This implementation of noise supports primitive selection like dh-ecc, hash and cipher in runtime. Runtime primitive selection is strictly in place to facilitate testing from a single testfile. In a production environment, primitive selection should ideally occur at compile-time to avoid switch-statement overhead for the responder.

⚠️ This library is a prototype and depends on Radikant-Crypto-C and Radikant-Socket-C and therefore inherits their intrinsic security flaws. In addition this library needs to be futher hardened in area’s like; digracefull disconnects, primitive mismatches, packet-loss, timeout, malicous data etc., malformed data. 

Patterns

Patterns: Supports handshakes including NN, KN, NK, KK, NX, KX, XN, XK, XX, IN, IK, and IX.

Transport

The Noise Protocol Framework is inherently transport-agnostic. It can be run over transport layers such as TCP, UDP, BLE, TLS, QUIC, ESP-NOW, I2C, SPI, or, in extreme cases, even LoRa. However, TCP is often the most practical choice because it handles packet ordering and retransmission out of the box. To keep the API simple and easy to use, this implementation is tightly coupled to a TCP socket, utilizing a custom 4-byte length prefix to frame the cryptographic messages over the TCP byte stream.

Forward Security

If an malicous actor records all encrypted network traffic for years. They can't read it, but they just store it in a massive database. If in a later stage they manage to obtain the private keys they can retroactively decrypt all past recorded traffic. With forward security the malicous actor still cannot decrypt the past traffic.

Forward security is achieved by using ephemeral (temporary) keys for every single connection. During the handshake, both parties generate a random, temporary keypair, mix them together using Diffie-Hellman to create a unique session key, and then throw the temporary private keys away when the session is over. Because the keys used to encrypt the session were destroyed, an attacker cannot re-derive the session key even if they steal the long-term static identity keys later.

Patterns that provide Forward Security: Interactive patterns like XX, NN, NK, and KK all include the ee step in their token definitions. Once the session is over and the ephemeral keys are discarded, even the client and server cannot retroactively decrypt previous traffic.

Responder (server)

FileEditViewProject
server.c — Noise Protocol Server
static void on_app_connect(noise_ctx_t *ctx, void *ud) {
printf("[Server] Handshake Complete! Secure Channel Established.\n");
const char *reply = "Server K Handshake Response - Secure Channel Established";
noise_send(ctx, reply, strlen(reply));
}
 
static void on_app_data(noise_ctx_t *ctx, void *ud, const void *data, size_t len) {
printf("[Server] Decrypted (length: %zu): %.*s\n", len, (int)len, (char*)data);
char reply[8192];
snprintf(reply, sizeof(reply), "Server confirms: '%.*s'", (int)len, (char*)data);
noise_send(ctx, reply, strlen(reply));
}
 
static void on_app_disconnect(noise_ctx_t *ctx, void *ud) {
printf("[Server] Client disconnected.\n");
}
 
int main() {
noise_ctx_t noise;
rs_init();
noise_error_t err = noise_ctx_init(&noise, NOISE_PATTERN_K, RESPONDER, AEAD_CIPHER_AES_256_GCM);
err = noise_ctx_set_keys(&noise, DH_FUNCTION_CURVE25519, &generic_x25519_server, generic_x25519_client.keypair.x25519.public_key);
rs_socket_t *server = rs_socket_create(RS_TCP);
 
noise_ctx_set_socket(&noise, server);
noise_ctx_set_callbacks(&noise, on_app_connect, on_app_data, on_app_disconnect, NULL);
noise_start_server(&noise, 12345);
 
while(1) { sleep_ms(1000); }
 
rs_cleanup();
return 0;
}
[Server] Initializing Noise KK (Responder)... [Noise] New TCP connection from 127.0.0.1:57014 [Server] Handshake Complete! Secure Channel Established. [Server] Decrypted: Hello from Client! This is a plaintext message sent over encrypted transport.

Initiator (client)

FileEditViewProject
Noise-Protocol — client.c
static void on_app_connect(noise_ctx_t ctx, void *ud) {
printf("[Client] Handshake Complete! Sending Transport Msg...\n");
const char *transport_msg = "Hello from Client";
noise_send(&noise, transport_msg, strlen(transport_msg));
}
 
static void on_app_data(noise_ctx_t ctx, void *ud, const void *data, size_t len) {
printf("[Client] Decrypted Server Msg: %.*s\n", (int)len, (char*)data);
}
 
static void on_app_disconnect(noise_ctx_t ctx, void *ud) {
printf("[Client] Disconnected.\n");
}
 
 
int main() {
rs_init();
noise_ctx_t noise;
noise_error_t err = noise_ctx_init(&noise, NOISE_PATTERN_K, INITIATOR, AEAD_CIPHER_AES_256_GCM);
err = noise_ctx_set_keys(&noise, DH_FUNCTION_CURVE25519, &generic_x25519_client, generic_x25519_server.keypair.x25519.public_key);
 
rs_socket_t *client = rs_socket_create(RS_TCP);
noise_ctx_set_socket(&noise, client);
noise_ctx_set_callbacks(&noise, on_app_connect, on_app_data, on_app_disconnect, NULL);
noise_connect(&noise, SERVER_IP, SERVER_PORT, 0);
 
while(1) { sleep_ms(1000); }
rs_cleanup();
return 0;
}
[Client] Initializing Noise KK (Initiator)... [Client] Connecting to 127.0.0.1:12345... [Client] Handshake Complete! Sending Transport Msg... [Client] Decrypted Server Msg: Server KK Handshake Response - Secure Channel Established [Client] Decrypted Server Msg: Server confirms: 'Hello from Client! This is a plaintext message sent over encrypted transport.'