View Source Decibel (decibel v0.1.0)
Decibel
is an implementation of The Noise Protocol Framework.
Noise is a framework for building crypto protocols. Noise protocols support mutual and optional authentication, identity hiding, forward secrecy, zero round-trip encryption, and other advanced features.
For more information about Noise, its rationale, supported protocols etc, please refer to The Noise Specification.
The rest of this document assumes the reader is familiar with the above specification.
overview
Overview
Decibel encrypts and decrypts messages according to the Noise Protocol, and the client's selection of handshake and cryptographic primitives. It does not act as a transport, nor does it say anything about how Noise messages should be transmitted between participants.
Each party - either the initiator (the party that starts the handshake) or responder (the other party) - advances the handshake until it completes, at which point a secure, symmetric channel is established that either party may use to encrypt and decrypt outbound and inbound messages respectively.
Decibel supports all the handshake patterns outlined in r34 of the specification including the fundamental patterns, deferred patterns and one-way patterns. It also supports pre-shared keys as outlined in the specification.
example
Example
Consider the following handshake defined in the Noise Protocol:
NN:
-> e
<- e, ee
The parties agree on this handshake and its cryptographic parameters and
express this in a protocol name, e.g. Noise_NN_25519_AESGCM_SHA256
. The
initiator's code may look something like this:
# Create the protocol instance
ini = Decibel.new("Noise_NN_25519_AESGCM_SHA256", :ini)
# Perform the first stage of the handshake
msg1 = Decibel.handshake_encrypt(ini)
# Somehow send this message to the responder and get the response
magically_send_msg(rsp_proc, msg1)
msg2 = magically_recv_msg(rsp_proc)
# Process the response through the second stage
Decibel.handshake_decrypt(ini, msg2)
# At this point, the 'NN' handshake has completed for the initiator
# and regular messages may be sent and received
msg3 = Decibel.encrypt(ini, "Hello, world")
magically_send_msg(rsp_proc, msg3)
The responder's code may look something like this:
# Create the protocol instance
rsp = Decibel.new("Noise_NN_25519_AESGCM_SHA256", :rsp)
# Receive the first-stage message from the initiator
msg1 = magically_recv_msg(ini_proc)
# Process the message through the protocol
Decibel.handshake_decrypt(rsp, msg1)
# Send the second stage to the initiator
msg2 = Decibel.handshake_encrypt(rsp)
magically_send_msg(ini_proc, msg2)
# At this point, the 'NN' handshake has completed for the responder
# and regular messages may be sent and received
msg3 = magically_recv_msg(ini_proc)
"Hello, world" = Decibel.decrypt(rsp, msg3)
lifecycle
Lifecycle
creation
Creation
Each party begins by creating a new handshake, via new/4
, specifying the
protocol name,
the role the party plays in the handshake (:ini
for initiator, :rsp
for
responder), and optionally any pre-message keys.
# In the IK handshake, the responder's public (static) key is known to the
# initiator prior to the handshake.
keys = %{rs: <<...>>}
ini = Decibel.new("Noise_IK_448_ChaChaPoly_BLAKE2b", :ini, keys)
The result of new/4
is a reference used for the rest of the session.
handshake
Handshake
During the handshake phase, the protocol is advanced by each party in turn. For
initiators, this typically starts with calling handshake_encrypt/2
and sending
the result to the responder. In turn, the responder calls handshake_decrypt/2
before typically encrypting its own handshake message and sending that to the
initiator.
This sequence continues until the handshake is complete. If the selected protocol
is known at compile time, the parties can just assume its completion in the
absence of an error (as in the example above). Alternatively,
each party can call is_handshake_complete?/1
after each handshake
encryption/decryption.
Once the handshake is complete, a secure channel is established with the properties of the selected protocol.
Additionally, once the handshake is complete, a unique 'session-hash' is available
via get_handshake_hash/1
- see the channel-binding
section of the specification for more details.
session
Session
Once the handshake is complete, the parties use encrypt/3
and decrypt/3
to
exchange 'application' messages between each other. Both functions provide for optional
'associated authenticated data' to be specified, that provides message-integrity
assurance for the application data.
Once the session is complete, each party should call close/1
to free the
resources associated with the it.
Link to this section Summary
Functions
Release the resources associated with the session.
Decrypts a message over an established session, using an optionally provided AAD for message integrity.
Encrypts a message over an established session, using an optionally provided AAD for message integrity.
Returns a 32-byte handshake hash, unique to the established session.
Decrypt an inbound handshake message, returning any optionally provided application data.
Encrypt an outbound handshake message, optionally folding in application data.
Returns true
if the handshake is complete, false
otherwise.
Start a new handshake.
Rekey the inbound or outbound channel of the session.
Link to this section Types
@type role() :: :ini | :rsp
The role the party plays in the protocol.
Link to this section Functions
@spec close(reference()) :: :ok
Release the resources associated with the session.
These resources are automatically released when the process terminates, but this call may be used to eagerly clean them up.
Decrypts a message over an established session, using an optionally provided AAD for message integrity.
Returns the decrypted message, or raises a RuntimeException
if the
message cannot be decrypted.
Encrypts a message over an established session, using an optionally provided AAD for message integrity.
Returns the encrypted message.
Returns a 32-byte handshake hash, unique to the established session.
Returns nil
if the handshake is not yet completed.
Decrypt an inbound handshake message, returning any optionally provided application data.
The function will raise a RuntimeException
if the handshake data does not decrypt
correctly.
Encrypt an outbound handshake message, optionally folding in application data.
The reader is encouraged to understand the ramifications of providing application data during the handshake. As the handshake is not yet completed, the properties of any secure channel have not yet been established. Such data may even be sent in the clear. Consult the Payload Security Properties in the specification for more information.
Returns true
if the handshake is complete, false
otherwise.
Start a new handshake.
The caller should provide a protocol name and the role the caller will play in the protocol. The caller should provide any keys required by the protocol prior to advancing the handshake. This are typically either static keys or pre-shared keys (PSKs), but ephemeral keys may also be provided. The list of provided keys should be identified as follows:
:s
: the party's public-private static key pair as a tuple.:rs
: the peer's public static key as a binary.:psks
: a list of pre-shared symmetric keys (as binaries), one for each psk modifier.:prologue
: any prologue data
This function will raise an exception if any required keys are missing.
Returns a reference representing the handshake.
@spec rekey(reference(), :in | :out) :: :ok
Rekey the inbound or outbound channel of the session.