[noise] Negotiating transport message size (was: Re: NoiseSocket revision 1)

Trevor Perrin trevp at trevp.net
Fri Aug 4 09:53:04 PDT 2017


On Thu, Aug 3, 2017 at 8:46 PM, Igor Solovyov <igor.solovyov at gmail.com> wrote:
>
> I'd prefer to see device capabilities in a handshake message which IMHO
> should be portable between all devices (regardless its size).
[...]
> I think it's better to leave handshake message small and portable as much as
> possible.

Makes sense, let's focus on transport messages.


>>    - Allow 32-bit length fields if explicitly requested (i.e.
>> application calls a SetJumboLength(true) which affects all subsequent
>> length fields, including padding?)
>
> Do anybody need "Jambo"-padding? :)

The goal of padding is to hide the real length, so we should probably
keep the ability to pad a message any amount (i.e. a 2 MB transport
message could hold 2 MB of padding).


>>   * We need to define protocol layer(s) on top of NoiseSocket that
>> actually do negotiation (i.e. advertise a list of Noise protocols in
>> protobufs or something).  So we could add the ability for each party
>> to send the maximum message size it can receive, if different than
>> 65535.  If your peer advertises a limit > 65535 then you're required
>> to send transport and/or handshake messages with 32-bit length fields,
>> i.e. every implementer would be required to support this.
>
> As for me it looks like overcomplication in comparison with some
> capabilities passing over handshake message. But I could be wrong.

We might be describing the same thing.

To be more clear, I'll sketch a negotiation protocol that uses
NoiseSocket to negotiate both the Noise protocol and also a
max_transport_size.

We've tried to avoid negotiation to keep runtime behavior simple.  So
even if we support larger transport messages, it's an open question
whether we should negotiate this versus just having endpoints
configured for it.

But it's useful to see what negotiation would look like.

Anyways, the idea is:

 * This is a JSON example, but could be translated to protobufs, XML, etc.

 * The client's offer is in negotiation_data and the server's response
is in the handshake payload.  This is because the initial handshake
payload is sometimes 0-RTT encrypted and fails to be decrypted, and
the response negotiation_data is absent if the server accepts the
Noise protocol.  This means the client's offer is in the clear, and
the server's response is encrypted.

 * The initiator sends max_transport_size if it wants a different size
than 65535.  The responder either explicitly accepts this size or
sends a smaller size.

 * If a value > 65535 is agreed on, then transport messages use
"jumbo" length fields, i.e. noise_message_len and body_len are 32 bits
instead of 16 bits.

 * The parties negotiate a single max_transport_size, instead of
separate sizes for their receive buffers.  Separate sizes are
reasonable, since it's possible to do streaming encryption and
calculate the authentication tag, but it's not possible to do
streaming decryption (since it's not safe to stream decrypted
plaintext that hasn't been authenticated).  However, maybe it's
simpler just to negotiate a single value here, instead of having
separate send / receive values?

Below are example contents for a 0-RTT "Noise Pipe" attempt which the
server fails to decrypt, so uses a fallback protocol.  The client
offers a 2MB max_transport_size which the server accepts:


INITIAL MESSAGE
==========
negotiation_data:
{
  "protocol": "Noise_IK_25519_AESGCM_SHA256",
  "other_protocols": [
    "Noise_XX+fallback_25519_AESGCM_SHA256",
    "Noise_XX+fallback_25519_ChaChaPoly_BLAKE2s",
    "Noise_XX_448_AESGCM_SHA256",
    "Noise_XX_448_ChaChaPoly_BLAKE2s",
  ],
  "max_transport_size": 2197152
}

payload (encrypted):
{
}


RESPONSE MESSAGE
==========
negotiation_data:
{
  "protocol": "Noise_XX+fallback_25519_AESGCM_SHA256"
}

payload (encrypted):
{
  "max_transport_size": 2197152
}


Trevor


More information about the Noise mailing list