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

Example for aead streaming encryption #270

Open
TrueBrain opened this issue Jan 28, 2024 · 2 comments
Open

Example for aead streaming encryption #270

TrueBrain opened this issue Jan 28, 2024 · 2 comments

Comments

@TrueBrain
Copy link

For OpenTTD we are trying to figure out how we want to do encryption between server and client, and monocypher came out on top because of its small footprint and easy to use API. However, internally we have a small debate how to do things, as that goes with encryption.

In the documentation you make use of "incremental encryption" with some suggestion here and there about "stream", and this makes things a bit unclear to us. And this is mostly because we don't really understand any of this, so I thought it would be easier to just ask, instead of us trying something that might be insecure :D

First of all, can we use crypto_aead_write for streaming encryption? As in, we have a small packet between 10 and 1200 bytes, which we send every once in a while.
Run it through crypto_aead_write, send the mac and cipher_text to the other side, and repeat? The nonce is already sent in the handshake, and from how I read it, we only need to send that once?

Second, as I read it, the key is rotated on every crypto_aead_write action, and as such, we do not need to add anything to prevent replay-attacks, like rotating the nonce every packet or something?

Ideally, if it is not all that much to ask, what would really help us is an example of a streaming implementation. Much like the Encrypt one message with the incremental interface example, but with more than one packet. With two packets alone would be of great help, just to understand what we do need to send, and what we don't.

Any help would be greatly appreciated :) Tnx!

@LoupVaillant
Copy link
Owner

Hi,

Your understanding of the streaming API is mostly correct. One thing you might have overlooked is the crypto_aead_init_*() functions of which we have 3 variants: djb, x, and ietf. You need to chose one of those to initialise the context… and chose your nonce. More on that at the end.


First of all, can we use crypto_aead_write for streaming encryption? As in, we have a small packet between 10 and 1200 bytes, which we send every once in a while.

Yes you can, and I explicitly thought of your use case when I designed this API.

Run it through crypto_aead_write, send the mac and cipher_text to the other side, and repeat?

Yep.

Second, as I read it, the key is rotated on every crypto_aead_write action, and as such, we do not need to add anything to prevent replay-attacks, like rotating the nonce every packet or something?

Correct. Once you've initialised the context, crypto_aead_write() will take care of everything, you don't need to call anything else, and you don't need to rotate the nonce. Just encrypt, send the cyphertext and mac, done.

Ideally, if it is not all that much to ask, what would really help us is an example of a streaming implementation.

I'll work on this. In the mean time:

// Handshake takes care of key,
// You may need to chose a nonce.
uint8_t key        [32];
uint8_t nonce      [24];
arc4random_buf(key,   32);
arc4random_buf(nonce, 24);

// Init streaming context.
// Note the use of `crypto_aead_init_x()` to support random nonces.
crypto_aead_ctx ctx;
crypto_aead_init_x(&ctx, key, nonce);

// Streaming loop (one message at a time)
while (plaintext_available()) {
    // Get buffers
    uint8_t *text = plaintext_text();
    size_t size = plaintext_size();
    uint8_t mac[16];

    // Encrypt
    // Note how I overwrite the plaintext
    crypto_aead_write(&ctx, text, mac, NULL, 0, text, size);

    // Send
    send(socket, cipher_text, text_size, flags);
    send(socket, mac, 16, flags);
}

// Wipe sensitive info
crypto_wipe(key, 32);
crypto_wipe(&ctx, sizeof(ctx));

The nonce is already sent in the handshake, and from how I read it, we only need to send that once?

That would depend on your handshake:

  • If your handshake guarantees that you'll be using random symmetric keys every time (one example would be the Noise handshakes), then you can just use a fixed nonce, you don't even need to send it.
  • If your handshake repeats the same keys, you do need to change the nonce. I would recommend using random 24-byte nonces, and the crypto_aead_init_x() to handle it. (You could use a counter, but then you must make sure never to reset it before the key itself changes, that's more error prone).
  • If you have a handshake you trust that gives you the nonce, yes, you can just use that nonce. In this case, just chose the crypto_aead_init_*() function that has a long enough nonce, and pad the rest of the nonce with zeroes if you need to.

Hope this clears things up. I'll keep this issue open until I clarify the documentation.

@TrueBrain
Copy link
Author

Thank you so much! Exactly the answers we were looking for :)

And thank you for the example, this is lovely!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants