It's the same idea as in "forward secrecy": once the key material is discarded, it's gone, and no future compromise can bring it back. Being able to say "after this point in time these values don't exist anymore" is a powerful cryptographic primitive.
I want to be able to have an ephemeral secret, and then trust the code to do its very best to get rid of it when it's no longer needed. That doesn't mean just leave it rotting on the heap and promising it doesn't get accesed again, it means burning it to make sure. That's the underpinning of any possible proof of forward security.
Sure, you say, I want a helper process? Fine idea: compartmentation. Now that helper process needs secure zeroisation. And since I want it to be secure, surely I want to write that process in Rust. See where I'm going here?
Whether 'safe' code I trust within my environment can read it again is totally irrelevant, if the machine later gets rooted or booted, nonstopped or whatever.
Zeroing out memory after use is mitigation against security flaws like heart bleed. It's not a security feature in it self. Although you are right that the secure process is probably just the better solution anyway.
It's sort of like the arguments for DRM. Of course pirates will always break it, but we can still mitigate it in a number of ways.
Maybe zeroing destructors make sense as defense-in-depth, but I don't see how they can fix a Heartbleed-style exploit in Rust. In code where the buffer is freed and its destructor runs, Rust's memory safety guarantees already prevent it from being accessed after free. In vulnerable code that just uses the same buffer twice, the destructor never has a chance to run so its behavior doesn't matter.
The real Heartbleed vulnerability (CVE-2014-0160 in OpenSSL) involved reading into uninitialized memory in a newly-allocated buffer, which safe Rust code already prevents [2].
The point is Rust already provides safety guarantees. If you don't trust the runtime, then why would you trust the built-in zero'ing? I get the "defense in depth" argument, but it feels a bit like doing this:
{
int a = secret; // Get secret.
assert(a == secret); // Check "a" is actually that.
a = 0; // Ensure "a" is zero'd on exit.
assert(a == 0); // Just because.
}
And yes, I get that you can build this into the language so it's not quite as ridiculous - you actually wipe tainted stack, for example.But the point is: the runtime has an ABI and a machine model. Information is allowed to leak across function boundaries, beacuse it doesn't matter. Without using the "unsafe" keyword, there are no methods of getting around the machine model and dipping into the underlying actual machine.
Even if you don't have a "safe" language and runtime, it's still of limited value. It protects against threats involving data or control flow corruption after key usage, and where there isn't sufficient control of the program to perturb the secret-consuming functions. That's more of an annoyance than prevention. On the other hand, it gave the programmer a false sense that it was properly wiping secrets.
We actually have an interesting project in rust where someone is writing a syntax extension to take rust like code and generate assembly [0]. It's probably unsafe to use right now but if sufficiently well implemented it could be the foundation of a lot of interesting cryptography work.
There are many ways to dig out stale memory if you're running at sufficient privilege, for example. Direct cache introspection, for example, or bypass. Zero-izing alone is not sufficiently strong to mitigate the threats people imagine it works against.
If you are running in an environment where you don't trust code running in the same compartment/sandbox/process, then it's futile to zero out memory. The caller could have prepared things such that the memset doesn't work, if the key material went somewhere else.
If you ever find yourself thinking you need to do this, what you instead need is a helper process who's only purpose is to do primitive operations with sensitive key material.
Particularly as Rust is already a "safe" language - it doesn't even make sense to zero memory which by definition another piece of code can't access. Unless there's declared "unsafe" code lying around, but you wouldn't put that in the same process, would you? At which point, what are you even protecting against? If an in-process threat is that advanced, then you're not achieving anything.