[noise] NoiseSocket status and plans

Trevor Perrin trevp at trevp.net
Sat Nov 18 09:13:27 PST 2017


Hi all,

Let's discuss the status of NoiseSocket - which parts seem good, which
need more thought, and plans.

This reflects some offlist discussion with Alexey.  Other people
(David, Piotr) are starting to look at NoiseSocket too.  Hopefully we
can assemble a group of implementers to discuss and experiment with
this, and work towards interop.


Document status
----------------
Per the document status thread [0], I'd like to make NoiseSocket an
"official/unstable" spec.  This reflects that it represents a
"probable good idea" and "direction that the Noise ecosystem should
evolve in".

Please comment if you disagree, otherwise we'll make this transition
at end of month (move the document into noiseprotocol repo, and I'll
become responsible for merging changes).

[We are still experimenting with this process stuff - I think having 2
weeks notice before document status changes might be a good general
policy?]


Length fields
--------------
NoiseSocket uses 2-byte lengths for:
 (a) negotiation_data in handshake messages (for extensibility)
 (b) Noise message lengths (for framing)
 (c) The body length of encrypted payloads (for length-hiding)

The main alternative is to allow 4-byte lengths for (b) and (c) for
"jumbo" transport messages, if both parties agree [1].  This might
improve efficiency for high-speed implementations [2].

I propose we DON'T support jumbo messages, for now:
 * Even if there are good uses for jumbo messages, supporting them is
likely to tempt people into bad uses - e.g., trying to send each
application message in a separate Noise message instead of using
packetized/streaming encryption.
 * It's not clear how significant the performance savings are.
 * A protocol designer who's determined to use jumbo messages could
use the Noise handshake phase, then take the output keys from Split()
and use them for whatever non-Noise protocol they want.  (We should
probably discuss "Handshake-only" protocols as an Advanced Use in the
Noise spec, since this could be useful for things like EAP).
 * Less options = more simple
 * We could revisit this later as a separate extension, if the need
becomes clearer


Negotiation
------------
The NoiseSocket spec discusses the responder having 5 options based on
the initial message:
 - Acceptance
 - Change protocol and send fallback message
 - Change protocol and sent reinitialization request
 - Explicit rejection
 - Silent rejection

I think we can clarify this list, and generalize it a bit:
 - Accept
 - Switch (instead of fallback)
 - Retry (instead of reinitialization request)
 - Explicit reject
 - Silent reject

This generalizes "fallback" to the responder "switching" protocols and
sending an initial message - perhaps from a fallback protocol, or some
other "Bob-initiated" protocol [3].

We could rename "Reinitialization" to "Retry" since this is simpler
language and more aligned with "Retry Request" from TLS.

The responder's choice determines whether the responder populates the
negotiation_data and/or noise_message fields in the response, as
explained in NoiseSocket spec:

                    negotiation_data      noise_message
Accept                                         *
Switch                     *                   *
Retry                      *
Explicit Reject            *
Silent Reject


Prologue
---------
The curent spec has 2 different prologue constructions:
 - Accept = "NoiseSocket1" || initial_negotiation_data
 - Retry or Fallback = "NoiseSocket2" || initial_negotiation_data ||
initial_noise_message || response_negotiation_data

Using a single "NoiseSocket2" prefix for retry or fallback was OK
because "fallback" protocols wouldn't be confused with the
non-fallback retry case, and the response_negotiation_data probably
differentiates the cases anyways.   But if we generalize fallback ->
switch, then it's probably safer to more explicitly differentiate
Retry and Switch cases.  I'll propose:

 - Accept = "NoiseSocket1" || initial_negotiation_data
 - Switch = "NoiseSocket2" || initial_negotiation_data ||
initial_noise_message || response_negotiation_data
 - Retry = "NoiseSocket3" || " " "

The number then indicates when the "final" Noise protocol is initiated:

1 ->  (Accept = Bob accepts Alice's initial protocol)
<- 2  (Switch = Bob switches to a different protocol)
3 ->  (Retry = Alice retries a different protocol based on Bob's request)


Another issue:  Should we allow the NoiseSocket-using application to
provide some "application prologue" that gets mixed in somehow?  One
option is to simply append an "application prologue" to the above
NoiseSocket prologues.  The above prologues are length-terminated, so
it will be clear where the NoiseSocket prologue ends and the
application prologue begins.


API
----
The current spec emphasizes a message-based API (WriteMessage /
ReadMessage), instead of a more TCP or TLS-like stream API (Write /
Read / Flush, something like that).

The rationale is that you could implement a stream API on top of a
message API but not vice versa, so this seemed the most general.

OTOH, a stream API is going to be most useful over TCP, and
NoiseSocket's length/framing fields are designed for such a case.  So
maybe we'd be better off emphasizing a stream API, here?


Trevor

[0] https://moderncrypto.org/mail-archive/noise/2017/001334.html
[1] https://moderncrypto.org/mail-archive/noise/2017/001223.html
[2]
https://moderncrypto.org/mail-archive/noise/2017/001220.html
https://moderncrypto.org/mail-archive/noise/2017/001221.html
[3] https://moderncrypto.org/mail-archive/noise/2017/001321.html


More information about the Noise mailing list