I am writing an app where users can communicate between devices with end to end encryption. For this I use the libsodium encryption library. The asymmetric encryption function, crypto_box(...) requires a nonce as one of the arguments.
I am a bit confused about how to handle nonces. Does every message to one person need to be encrypted using different nonces? This does not seem right since I would have to store the used nonces on a server with public access where an attacker could just use one of the used nonces again.
Is it enough that all messages sent from A to B have different nonces or can the nonce use to send a message from A to B not be used to send from C to B?
Can someone please explain this to me.
The nonce doesn't have to be confidential, but it should be used with just one invocation of crypto_box_easy() for a particular pair of public and secret keys. One easy way to generate a nonce is to use randombytes_buf() , considering the size of the nonces the risk of any random collisions is negligible.
Each stream requires a key and a nonce . The key forms the shared secret and should only be known to trusted parties. The nonce is not secret and is stored or sent along with the ciphertext. The purpose of the nonce is to make a random stream unique to protect gainst re-use attacks.
A unique nonce is required for every message sent using a given shared secret key. The nonce doesn't have to be secret; a simple counter is totally fine; changing a single bit in the nonce is going to make the ciphertext look totally different even if the same message is sent twice.
What's a shared secret? It a key calculated from (A's secret key, B's public key) or (A's public key, B's secret key). A and B perform a different calculation, based on what they have, but end up with the same shared secret.
The shared secrets used in crypto_box
are 256-bit long. Which is huge. You can safely consider that shared secrets are going to be unique for each "conversation".
So, (A, B), (A, C) and (C, B) can safely exchange messages using the same nonces. But if A sends a message to B using a given nonce, B cannot send a message to A using the same nonce. Nonces have to be unique for everything exchanged during a conversation between A and B.
So, a simple counter can be fine. Have A pick even numbers, leave odd numbers to B, increment the nonce by 2 after every message sent and you're good to go.
But the cipher used in the crypto_box
construction actually has a really huge nonce. 192 bits.
Which means that if you ignore everything I wrote and just pick a random nonce every time you send a message, the probability to get a collision is so small that you can rest assured that it will never ever happen in practice.
Some stream ciphers included in Sodium (AES128-CTR, ChaCha20, Salsa20) have a shorter nonce, and require a counter to avoid collisions. This is why they are in the "advanced" section of the documentation.
But with crypto_box
and crypto_secretbox
, just pick a random nonce every time (randombytes_buf(nonce, sizeof nonce)
) and you will be safe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With