aws / aws-lc-rs

aws-lc-rs is a cryptographic library using AWS-LC for its cryptographic operations. The library strives to be API-compatible with the popular Rust library named ring.
Other
311 stars 50 forks source link

::aead::{SealingKey, OpeningKey} how to prepend nonce to packet? #570

Open jac-cbi opened 3 weeks ago

jac-cbi commented 3 weeks ago

Problem:

I'm attempting to use aws_lc_rs::aead to encrypt and decrypt packets. The AEAD in-place should work fine. However, I need to include the nonce in the header (aad'd as well).

I'm currently using Counter64 to get the ball rolling, but I see no way to extract the nonce used for encryption so that I might add it to the header. And, afaict, it doesn't add it itself.

How is this intended to be done? The Ring API + AWS enhancements(?) really constrains the use of the Nonce, and for good reason. I must be missing something, how are users of this crate passing the Nonce to the receiver for decryption to succeed?

Note: that embedded systems are in scope, so I'm avoiding use of the allocator anywhere I know exactly how long buffers need to be (header_len + data_len + tag_len and so forth)

Note2: RandomizedNonceKey is not suitable for my use case because it doesn't allow me to set the first nonce and then increment the nonce. My design relies heavily on two devices to arrive at the same shared secret, then deriving from that everything necessary to perform encryption and decryption. this includes the nonces.

Solution:

Ideally, I'd like .seal_in_place_separate_tag() to return (tag, nonce) so that I can append and prepend as needed. However, that sucks from an API to support perspective...

What am I missing?

EDIT: Please @ me, I'm not on GitHub regularly

skmcgrail commented 2 weeks ago

@jac-cbi as requested.

Your use case makes sense, and I understand the pain points you are experiencing with the API. If I am understanding your use case correctly, you want to know the nonce that will be used in the call to encryption so that you can include that nonce in the header you will be including with the ciphertext, and you are using that header as the AAD that will be used in computing the tag. Is that correct?

This is currently awkward with the API if you are trying to limit yourself to just the SealingKey and OpeningKey types and associated methods. Both those operate on the assumption that both parties know the starting state of the NonceSequence that is required to perform encrypt and decrypt operations in a sequential and lockstep order.

Likely these operations aren't happening in lock-step on both sides, so on the decryptor side you could parse the Nonce out of the header received from the encryptor, and have a custom NonceSequence implementation that accepts the parsed Nonce value, and only accepts one call to advance which would return that parsed Nonce. This is awkward and wouldn't really gain you anything over just using LessSafeKey::open_in_place which allows you to provide the Nonce as input from the value you've parsed from the header.

Now looking at the encryptor side there is some challenges. We could add an API that returns the Nonce that was retrieved from the NonceSequence and used in the encryption operation, but that doesn't solve your use case of wanting to include that Nonce in the AAD for the encryption operation. Yes, given a Counter32 and knowing the starting state you could technically make assumptions about what the next value would be for subsequent calls, but that seems fairly fragile.

I don't think it makes sense for our library to have a notion of the header, it's format, whether Nonce is or is not a field in that header, whether the whole header is included in the AAD or not. I think the best course of action is likely to use a combiantion of Counter32Builder / Counter32 and LessSafeKey::seal_in_place or the other available seal operations to know the Nonce upfront, and be able to explicitly specify that usage of that Nonce value upfront. It does require explicit management of the Nonce by your application, and ensuring that the Nonce is not reused, but I don't really see a good way around that without us adding additional APIs and likely being too restrictive on the format expectations.

Unless there is a misunderstanding in your requirement "need to include the nonce in the header (aad'd as well)", if the Nonce itself doesn't need to be included in the AAD in your use case, then we are open to adding a function variant that returns the Nonce that was used from the provided sequence.