Bad API design for aes-gcm

Hi everyone.

Has anyone here worked with aes-gcm? As far as I understand, when using encrypt_in_place / decrypt_in_place, I’m required to implement the Buffer trait only because the library can’t tell in advance whether there’s enough space for the authentication tag during encryption.

Isn’t that just bad design? It forces me to inject a buffer pool into the object implementing Buffer, just to avoid unnecessary allocations - instead of simply allowing me to pass a &mut [u8] with enough capacity upfront.

&mut [u8] implements Buffer: Buffer in buffer - Rust. Or am I missing something?

In fact, encrypt_in_place / decrypt_in_place requires an aead::Buffer, which in turn requires implementing the method:

fn extend_from_slice(&mut self, other: &[u8]) -> Result<()>

specifically (if I understand correctly), to handle the case when the original buffer does not have enough space to append the authentication tag.

Therefore, if I don’t want to use Vec<u8>, but instead a buffer pool of Box<[u8]> (preallocated by powers of two), I’m forced to inject this pool into the object implementing aead::Buffer, otherwise I won’t satisfy the trait contract - instead of simply being able to pass a &mut [u8] directly.

aead::Buffer is already implemented for Vec. It is not exactly what you want but if you preallocate the vectors with Vec::with_capacity you almost get the same thing of a Box<[u8]>.

1 Like

Ah, I see. I copied the link for the trait from another crate. It's interesting that it has a method to extend the buffer but also a method to truncate it.

You could try as fogzot suggested to provide a Vec with a fixed capacity and see it it needs to reallocate.

1 Like

Note that Buffer is implemented for ArrayVec, so you can use that it you don't want to allocate.

I also imagine that implementing Buffer for some type whose storage is backed by a &mut [u8] is possible. The reason it can't just be implemented for a &mut [u8] is that it needs to track how much data was written, and &mut [u8] can't do that.

2 Likes

Yes, I see that it can be done with Vec, but intuitively, it feels a bit off. It’s strange that the library tries to grow the buffer on its own instead of returning an error and letting me handle it explicitly.

I see that if I provide a vector with enough capacity for the authentication tag, the library won’t try to grow it (at least that’s what it looks like). But what bothers me is the API design itself.

Why does a &mut [u8] even need to track how many bytes were written? You can just maintain a separate counter and return it as Result<usize, ...>, so I can trim the buffer myself to the correct size.

People are going to forget to do that and then they leak data.

2 Likes