LoupVaillant / Monocypher

An easy to use, easy to deploy crypto library
https://monocypher.org
Other
614 stars 81 forks source link

reliability of crypto_wipe #109

Closed monolifed closed 6 years ago

monolifed commented 6 years ago

I noticed that libsodium uses a few different strategies for different compilers and platforms to make sure that memory is actually zeroed/wiped. Is it left to the users in Monocypher? It would be nice if at least it let users to use their own memory zero/wipe function as an option. e.g. SecureZeroMemory on windows

LoupVaillant commented 6 years ago

Monocypher provides the crypto_wipe() function for this, and uses it to wipe every single intermediate buffer the user cannot wipe themselves (temporary arrays for instance). It also wipes contexts when the user finishes with the relevant crypto_*_final() function.

Moreover, the manual is currently littered with injunction to use crypto_wipe() whenever relevant (basically every single code example shows how it might be done). Finally, there's a recommendation in the introduction to lock memory regions, with the name of the relevant functions on the most popular systems (look for "forward secrecy").

So users aren't exactly left to their own devices. There are however a couple limitations: crypto_wipe() is based on the volatile keyword, which the language specifies to mark memory regions as "side effects", meaning that reading and writing through a volatile pointer is interpreted as being a side effect. In practice, this is pretty reliable, but I've read rumours about discussions among compiler writers that they might just be able to ignore this keyword altogether for temporary buffers on the stacks. (I have a couple reasons why this might be the case, but I'd be speculating.) Then there's no portable way in C nor C++ to erase processor registers. Because of this, Monocypher doesn't even try to wipe scalar temporaries, which are assume to live in registers most of the time; the manual likewise doesn't tell users to wipe their own scalars (most secrets are in buffers anyway).


We could conceivably be even more secure, for instance by writing non-portable versions of crypto_wipe() (one for each supported platform), just to be extra secure, as well as a tiny bit faster. But we won't, because platform specific stuff is out of scope. I want something stable, that requires minimal maintenance, if at all.

We could have users take advantage of system specific functionality themselves, but then they would have to worry about platform specific stuff. They have enough on their plate already, and this extra cognitive load makes them more likely to make mistakes, thus reducing the very security we sought to enhance.

I also surmise that other security considerations dwarf this issue. Suspend to disk for instance is the most impossible to address. If the user suspends their computer in the middle of a crypto operation, your key will end up on the disk, and there's nothing an app developer can do about it. And if some buffer does not get wiped (like, volatile didn't work for some reason), it will likely be forever inaccessible as soon as the process ends (assuming the OS does its job zeroing out memory it gives to the processes). And by the way, Monocypher has internal buffers. Those rely on volatile, and that's it. Even if the user find even more reliable ways to erase their own buffers, there is no way to do the same with Monocypher's internal buffers, short of modifying the source code itself.


tl;dr: I'm pretty satisfied with the status quo, and don't think we can improve it meaningfully. You can close this if you agree, or respond if you disagree—I'm not ruling out any mistake on my end, it has happened before.

monolifed commented 6 years ago

platform specific stuff is out of scope

I guessed so. I think you can use a macro that uses users custom wipe function or the internal one if a custom function is not provided.
Anyway, thanks for the explanation. It was mostly a question.