RustCrypto / AEADs

Authenticated Encryption with Associated Data Algorithms: high-level encryption ciphers
726 stars 154 forks source link

Implement `Zeroize` #65

Open vlad9486 opened 4 years ago

vlad9486 commented 4 years ago

The type ChaCha20Poly1305 should implement Zeroize. Also, I do not sure if AesGcm should implement Zeroize too. It will allow the user to implement Zeroize for types containing them.

tarcieri commented 4 years ago

Just a quick note on this: will revisit it soon. It's a bit tricky as both of those ciphers have SIMD backends, and right now there are no Zeroize impls for the SIMD register types, so doing this properly needs support for zeroizing SIMD types in zeroize first.

kurnevsky commented 3 years ago

Looks like NewAead::new should take key by value, otherwise it won't be zeroed properly.

tarcieri commented 3 years ago

Other way around: taking it by value can make a copy via a move, making the old version impossible to zero out.

kurnevsky commented 3 years ago

Yeah, looks like you are right. So this means currently there is no way to use aead ciphers safely if you don't zero memory manually...

tarcieri commented 3 years ago

FWIW, aead 1.0 took the key by value, however pretty much everything that key would get passed to next (e.g. block cipher or stream cipher) performs some sort of key expansion / round key setup / state initialization which accepts the key by reference.

It felt much cleaner to accept a key by reference.

No matter what, the caller needs to deal with the key somehow. In some of the other crates (notably elliptic-curve) there are key types which handle loading keys from on disk (from e.g. PKCS#8) as well as zeroization. But note this is still all best effort and unless pinned or stored on the heap it's possible for copies of these keys to be made unintentionally right now.

kurnevsky commented 3 years ago

I actually had in mind a case when we need to create aead from rng, with no need to store the key anywhere except memory. The only way to do it now is to generate a new key, wrap it to aead and then zero key buffer. Not sure though how common this case is, or if it indeed requires memory zeroing, but still it feels like there should be an easy way to wrap a newly generated key. Probably key fields in aeads can be made public so we can just create it as a struct?

kurnevsky commented 3 years ago

On the second thought if a key is stored in a stack then it'll likely be moved, and drop won't help in this case. So probably we shouldn't care that much about stack, and if we do we can use something like https://docs.rs/clear_on_drop/0.2.4/clear_on_drop/fn.clear_stack_on_return.html

tarcieri commented 3 years ago

Probably key fields in aeads can be made public so we can just create it as a struct?

That would still be a move which would copy the key to within the struct. Also as I mentioned earlier, often the key is transformed in some way prior to use, e.g. expanded into per-round keys as in AES.

Since the NewAead API takes it by reference, it won't be moved, so you can create value on the stack and use it to initialize the AEAD, then zero it out with zeroize().

kurnevsky commented 3 years ago

That would still be a move which would copy the key to within the struct.

Actually it won't, because this struct is a simple wrapper. The only case I can imagine of is when key is stored in a Box or similar and being moved to a stack. But again, this can happen with aead struct itself and Drop trait won't help here.

I also checked a case when we create a random buffer and pass it to a constructor by reference where we store it - rustc/llvm can optimize it to not to copy the buffer. Here what I wrote: https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gist=81662cc073f5ea50483d9e08f99826ae - rustc generates the same assembly when we pass parameter by value or by reference. So it means we don't need to zero key manually in such case. But still it can be moved later, if we store it in a heap, for example.

Update: no, rust can't optimize it and copies memory :( https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gist=8fbba37c53741ec4ec1484bf8d6631ab

kurnevsky commented 3 years ago

Here found a blogpost on this: https://benma.github.io/2020/10/16/rust-zeroize-move.html And since aead stores data in stack it applicable to it as well.

tarcieri commented 3 years ago

Actually it won't, because this struct is a simple wrapper.

It depends. The struct could be stored in a Box. With a non-opaque struct, It could also be initialized with values returned from another function, which would definitely incur a copy until placement-by-return lands.

This is why using references provides better guarantees. You know any data passed by reference won't be copied.