Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending data from server -> client with NOISE_N is unsafe #45

Closed
spinda opened this Issue Apr 5, 2019 · 3 comments

Comments

Projects
None yet
2 participants
@spinda
Copy link
Contributor

spinda commented Apr 5, 2019

The documentation for hydro_kx_n_* suggests that, after a key exchange is performed, one of the generated session keys can be used to safely send data from the server (Noise "responder") and client (Noise "initiator").

// Done! session_kp.tx is the key for sending data to the server,
// and session_kp.rx is the key for receiving data from the server.
// Done! session_kp.tx is the key for sending data to the client,
// and session_kp.rx is the key for receiving data from the client.
// The session keys are the same as those computed by the client, but swapped.

The type signatures of hydro_kx_n_1 and hydro_kx_n_2—returning a key pair instead of a single session key—also suggest this.

However, the Noise specification's description of one-way handshake patterns strongly warns against this:

Following a one-way handshake the sender [client/initiator] can send a stream of transport messages, encrypting them using the first CipherState returned by Split(). The second CipherState from Split() is discarded - the recipient [server/responder] must not send any messages using it (as this would violate the rules in Section 7.3).

The rule referred to in 7.3 is:

Noise relies on DH outputs involving ephemeral keys to randomize the shared secret keys. Patterns failing this check could result in catastrophic key reuse, because the victim might send a message encrypted with a key that doesn't include a contribution from their local ephemeral key (or where the contribution from their local ephemeral was nullified by an invalid ephemeral from the other party).

In other words, the server sending a message back to the client using one of the computed session keys is bad because there's no random contribution on its part to the generation of the session keys. An active attacker could supply the same ephemeral key multiple times, resulting in key reuse. To do this safely, you'd instead want an interactive NOISE_NK handshake.

Please let me know if I've misinterpreted something here. Otherwise, the documentation should surely be updated, and (if changing the type signature is acceptable) hydro_kx_n_* should provide a single client→server session key rather than a key pair, to avoid misuse.

@jedisct1

This comment has been minimized.

Copy link
Owner

jedisct1 commented Apr 6, 2019

N is a simple DH exchange, without EDH.

While Noise strives to provide PFS everywhere, this protocol is not "unsafe". It is equivalent to the key exchange mechanism present in NaCl/libsodium.

Keys are not reused. This specific implementation forces the client to generate a new key pair for each exchange. The secret key is immediately discarded.

An active attacker can send a previously observed public key, but a reused session key is harmless as long as a nonce is not reused with different message as well. And this is something that hydrogen prevents by design. There are no ways for applications to provide their own nonce to bypass this.

Of course, ephemeral keys should be used whenever possible.

@jedisct1 jedisct1 closed this Apr 6, 2019

@spinda

This comment has been minimized.

Copy link
Contributor Author

spinda commented Apr 6, 2019

Thanks for the explanation. So the server's tx session key generated by hydro_kx_n_* can be used to send messages back to the client, as long as it's used with libhydrogen's secretbox construction, thanks to nonce randomization. However, unlike with a NOISE_NK-based protocol, if the server private key is leaked then past sessions can be decrypted. Do I have that right now?

@jedisct1

This comment has been minimized.

Copy link
Owner

jedisct1 commented Apr 6, 2019

Correct. You lose forward secrecy, which is impossible to achieve with a single RTT.

However, even non-ephemeral server static keys are not designed to be used forever. The current best practices go completely against this.

Let's Encrypt certificates, for example, can only be used for 90 days. Protocols such as DNSCrypt require certificates to be only valid for up to 24 hours.

These certificates are signed with a long-term key, and can be rotated frequently. This can have performance tradeoffs, but clients can fetch and verify new public keys as needed.

If a server secret key is leaked, only the very recent session keys can be recovered, so you still get some forward secrecy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.