Skip to content

aead: Proposal for revision to API surface for trait AeadInPlace #1672

@Cel-Service

Description

@Cel-Service

Migrated from PR #1663 for discussion.

@tarcieri -- following on from where we left off: I've been ruminating on the issue since my last message yesterday.

As @newpavlov proposed, I've been working on modifying the algorithm crates so that we can revise the API surface of AeadInPlace (or whatever we might rename or split it out to) to be implemented in terms of methods which consume InOutBuf.


Fully separating authentication tag placement from the algorithm itself

As you already know, currently, the AeadInPlace API is generally implemented in terms of self.encrypt_in_place_detached and self.decrypt_in_place_detached. If we want to change this so that the API is implemented in terms of methods consuming InOutBuf, then we need to make some surrounding changes to make that work.

Implementing in-place, tag-position-agnostic decryption in terms of a call to self.decrypt_inout_detached (or whatever we call it) would be very simple: the API could be revised to fn decrypt_in_place<'a>(&self, nonce: &Nonce<Self>, ad: &[u8], buffer: &'a mut [u8]) -> Result<&'a mut [u8], Error>, very similar to plain fn decrypt.

The provided implementation would split the provided buffer, converting part of it into an InOutBuf<'_, '_, u8> and part of it into an &Tag<Self> in order to pass it to self.decrypt_inout_detached; it would then return the subslice containing just the decrypted plaintext to the caller (thereby retaining all of the convenience that current API consumers enjoy).

The trouble is, in order to implement its counterpart, encrypt_in_place, in terms of encrypt_inout_detached specifically for tag-prepending AEADs, something needs to give:

  • If we were to just convert a subslice of the buffer into an InOutBuf (as above), the caller would need to put their plaintext at an algorithm-dependent offset -- this would be VERY surprising and strange breaking behavior, so it's not an option.

  • If we want to sand down the above rough edge but keep the concept, InOutBuf would need to support an offset (so that .get_in() would return just the plaintext region, and .get_out() would return just the ciphertext region).

  • If we just throw our hands up and accept the status quo, then the aes-siv crate (and any implementation of AeadInPlace which prepends its authentication tag) needs to implement BOTH an in-place, authentication-tag-packing-aware encryption method (one which has to read plaintext from the start and write ciphertext at an offset) AND in-out, authentication-tag-packing-unaware encryption method (one which simply reads plaintext from the in-buffer and write ciphertext to the out-buffer at the same position).

Honestly, I'm personally of the mind that the last option might be the most sensible one in terms of time spent, but I haven't yet dug deep into the aes-siv crate to gauge how much work would be required and whether I'm confident enough to implement the necessary changes optimally. That said, I think that adjusting InOutBuf to suit this use case might end up being the more flexible, elegant, and above all maintainable option.

What do you think? Am I overlooking something?

Simplifying wrappers

On a lesser note, as I was mentioning last night in the above PR, I'd very much like to see the specialist buffer types entirely abstracted away from callers in wrapper methods where possible.

As above, whatever we end up doing to encrypt_in_place, it would be ideal if we could also have an in-place encryption method that foregoes specialist types in exchange for slightly more complex semantics (needing to provide at least Algorithm::TagSize::to_usize() additional bytes of unused space).

Once we've resolved the conundrum in the section above, so long as adding a separate method is acceptable, it should be a fairly straightforward implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    aeadAuthenticated Encryption with Associated Data (AEAD) crate

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions