ranch_noise

Noise Protocol Framework transport adapter for Ranch.

Allows any Ranch-based protocol (e.g., Cowboy, MochiWeb) to run transparently over an encrypted Noise channel.

Features

Requirements

Installation

Add ranch_noise to your project dependencies in rebar.config:

{deps, [
ranch_noise
]}.

Or clone and compile:

$ rebar3 compile

Usage

Server

Start a Ranch listener using ranch_noise as the transport:

%% Create keypairs for server and client.
ServerKP = enoise_keypair:new(dh25519),
ClientKP = enoise_keypair:new(dh25519),
%% Start listener — note the transport is `ranch_noise`.
NoiseOpts = [{noise, "Noise_XX_25519_ChaChaPoly_BLAKE2b"},
{s, ServerKP}, {role, responder}],
{ok, _} = ranch:start_listener(my_listener, ranch_noise,
#{socket_opts => [{port, 8765}],
num_acceptors => 2,
handshake_timeout => 5000},
my_protocol, #{noise_opts => NoiseOpts}).

Inside the Ranch protocol handler, perform the Noise handshake:

start_link(Ref, Transport, #{noise_opts := NoiseOpts}) ->
Pid = spawn_link(fun() -> init(Ref, Transport, NoiseOpts) end),
{ok, Pid}.
init(Ref, Transport, NoiseOpts) ->
{ok, Socket} = ranch:handshake(Ref, NoiseOpts),
%% Socket is now a noise-encrypted handle — use Transport:recv/3
%% and Transport:send/2 as normal.
loop(Socket, Transport).

Client

{ok, Sock} = ranch_noise:connect("127.0.0.1", 8765,
[{noise, "Noise_XX_25519_ChaChaPoly_BLAKE2b"},
{s, ClientKP}, {role, initiator}],
5000).
%% Send / recv work exactly like gen_tcp after the handshake.
ok = ranch_noise:send(Sock, <<"hello">>),
{ok, <<"hello">>} = ranch_noise:recv(Sock, 0, 5000).

Socket type transitions

PhaseSocket type
Before handshakeinet:socket() — raw TCP managed by Ranch
After handshakepid()ranch_noise_socket gen_server holding Noise CipherState

Active mode

Set active options post-handshake to receive encrypted messages as messages:

%% Passive (default) — call recv/3 to pull data.
%% Active once — one {noise, Socket, Data} message, then reverts to passive.
ok = ranch_noise:setopts(Sock, [{active, once}]),
%% Active — every decrypted message delivered immediately.
ok = ranch_noise:setopts(Sock, [{active, true}]),
%% Messages arrive as:
%% {noise, Socket, Data}
%% {noise_closed, Socket}
%% {noise_error, Socket, Reason}

Running tests

$ rebar3 eunit

License

Apache-2.0