-
Notifications
You must be signed in to change notification settings - Fork 270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Zeroize #553
Comments
Moving to rust-secp. I'm surprised that no such issue already exists. The TL;DR is that these zeroing crates (there are a couple, IIRC the other popular one is called
Having said this, I would like to support this somehow, even if the library doesn't have first-class support for it. I don't know either whether this is feasible. |
I believe this is a lost cause. The compiler may copy secret bytes to unexpected locations as a result of optimizations and there's no way to prevent that. Also the only thing this protects against is leakage due to UB. Rust has much less UB than C, so the value is questionable and the significant effort required to actually make this possible would be better spent at preventing (the remaining) UB in the first place. |
@sanket1729 but the immediately following section says that copies and moves undermine the crate and that you need to use |
@apoelstra thanks for pointing that out. I am with @Kixunil now, this is a very hard problem to solve and our resources are better invested elsewhere. |
subtle is about being constant time, not about zeroing memory. zeroize is about traits to zeroize memory. Accordingly, I don't believe this is fruitless regarding functionality. I also don't believe implementing Not to say that magically gives you time or makes it a priority. I just wanted to to highlight the benefits/impacts over it. |
@kayabaNerve thanks for explaining, that sounds at least doable (even though I still find value questionable). |
Yeah, memory access will always be a question of "If someone already has that much access, isn't it moot?" My preference is to minimize copies, and do what I can, even if sure, the second I perform multiplication, the underlying lib probably makes a few copies (hopefully on the stack, at least). The stack ones will hopefully get cleared out soon, and I can know my long lived ones aren't just available for the next program which calls |
They already aren't. The kernel zeroizes allocated pages. It'd be a huge vulnerability if it didn't. |
Some systems do not offer that behavior, per malloc's memory being undefined (justifying calloc), nor do I believe Linux is complete in that behavior. Beyond apparently being configurable when compiling the kernel, both Linux and Windows seem to only zero pages before handing off to a distinct process. While I did say, "next program" (which may still be feasible on some embedded systems), there are other concerns where a process both manages a secret key and exposes some level of memory buffer access to users (though Rust thankfully prevents most of these). |
That's something completely different. The process gets zeroed memory from the kernel but it doesn't zero it's own which means that if you allocate, write something and then deallocate the next allocation may obtain the pointer containing what you just wrote (or may not).
Rust protecting against this is exactly my point. It's no longer that big issue (as an interesting data point, according to a recent report, Google found no vulnerabilities in their Rust code so far). If we focus our time on getting |
One could argue the opposite and Copy makes it unergonomical to prevent copies that will not be erased. In any case, the functions on SecretKey that take
If my type was already a Same for
Even if one can't have 100% guarantees with Rust, one can go a long way with using @apoelstra would it be possible to change negate(), mul_tweak() etc. to work on |
It doesn't because you can't prevent them. Every move is a This is a lost cause, just make your software secure by using other techniques.
That's an illusion since compiler can decide to optimize-out the
We already had such API and it led to horrible code where a meaning of a variable suddenly changed without changing it's name. At best I would be sympathethic to take |
I am aware there is no 100% guarantee, but https://docs.rs/zeroize/latest/zeroize/ contains a good summary. Stack values are problematic, heap values less so (when not re-allocating). Even if there is no guarantee, it is better to try to avoid copies than not to try or even do the opposite: making it hard not to leave copies, which
If it returns |
I don't see such claim in the linked doc and it makes no sense why it should be the case. It's just a piece of memory either way and it has no effect on how the compiler generates the code that accesses it.
At the cost of worse readability. There are no solutions, just tradeoffs.
It does if you instantly wrap it in some zeroize thing or a
I've already explained this. |
I suggest adding Afaik I suppose I could still try do manually erase all the copies I have some control over, but that is difficult to get right. What is your general advise here when using |
I'd be fine adding a I tend to be more optimistic than Kix that
In short, yes. |
Correct.
If you really want a box this should be more efficient but it's relying on optimizer doing RVO which is currently not guaranteed even though likely. (There is a proposal to guarantee RVO which would help you a lot.) let boxed = Box::new_uninit();
let boxed = Box::write(boxed, construct_secret());
let secret = Zeroize::new(boxed); This needs nightly but the API was approved for stabilization. However given how much effort you and other people put into arguing about zeroization and making the crate I find it really weird that impl<'a, T: Zeroize> Zeroizing<StackBox<'a, T>> {
fn in_place(place: &'a mut MaybeUninit<T>, value: T) -> Self { /* ... */ }
} It'd be far better if you added that API to the library than ask every crate that might hold secrets to change its. If you find RVO to be unreliable that may be something for Rust/LLVM devs to work on but even then we should rather support an API that allows one to express the operation more cleanly. Something like: fn negate_in_place<'a>(&self, place: &mut 'a MaybeUninit<Self>) -> &'a mut Self { /* ... */ }
First you need to realize that zeroizing in Rust has completely different cost/benefit than in C++ where it was probably popularized. Both because Rust is memory-safe (lower chance of bugs in the first place, thus lower benefit) and because Rust doesn't have move constructors (more difficult to implement - higher cost). So I'd say consider investing in other areas - e.g. proving your If you still want to do it, try relying on RVO as I demonstrated and check the assembly. |
I don't know how feasible it is, but as there are a lot of secrets here, it would make sense.
The text was updated successfully, but these errors were encountered: