subtle
subtle copied to clipboard
The documentation should show an example of typical correct usage
While it might seem obvious to people familiar with this crate, I am actually confused how to use this crate after just landing on it looking for a constant comparison crate.
What I want to do is:
if password_in_request != password_in_the_config {
bail!("Wrong password");
}
for usual reasons.
I open https://docs.rs/subtle/ , I can see ConstantTimeEq and it is clear I should use ct_eq. But then ... why is it returning Choice? Am I supposed to call .into() on it? It seems so... ? But why can't ct_eq return bool right away by doing that .into() under the hood? Unclear.
So, it seems to me that the documentation can be improved in two ways:
- Please add an idiomatic example on a front page of a correct usage. It will take one short paragraph and give an immediate answer to developers that are just looking for a solution.
- Please somewhere early in the documentation of
Choiceexplain why: Why does it exist, why can'tct_eqjust returnbool.
(Disclaimer: I never contributed to the crate, I merely use it and I want to help)
Why does [Choice] exist, why can't
ct_eqjust returnbool.
When using bool, the compiler can sometimes find optimizations for it and make your operations/branch conditions not constant-time. subtle works by using Choice is a wrapper struct around a fake bool (Choice is just a u8 that can be 1 or 0) that tries to make sure the compiler won't optimize it back into a bool. Choice implements bit operations etc the same way a bool does.
Why can't
ct_eqreturnboolright away by doing that.into()under the hood? Unclear.
You don't always want to convert back into bool directly. You might still need to do extra constant-time operations after calling ct_eq().
For example, say you're checking both a username and a password: (username_in_request.ct_ne(&username_in_the_config)) | (password_in_request.ct_ne(&password_in_the_config)). You're doing two constant-time equality checks (one for the username, one for the password), and they both return Choice, but the OR "|" also needs to be constant time, so you have to do Choice | Choice, which gives you yet another Choice. Only after you're done doing all that, you then convert back that last Choice into a bool.
Am I supposed to call .into() on it?
In your case, I think yes you can call .into() to convert back into bool after you're done doing your constant-time checks.
Assuming your passwords are say byte arrays &[u8], which implement ConstantTimeEq, something like this should work (Warning, I didn't test it):
if bool::from(password_in_request.ct_ne(&password_in_the_config)) {
bail!("Wrong password");
}
However, after you convert into a bool, you stop being in "constant-time land", so be careful, because you may also need to use Choice elsewhere in your code, in which case converting back to bool can make things not constant-time anymore.
Please add an idiomatic example on a front page of a correct usage. It will take one short paragraph and give an immediate answer to developers that are just looking for a solution.
I think it would be a good idea to have examples and better explanations. Maybe I'll try making a PR for it. OTOH it's easy to use that crate incorrectly, and simply throwing an example that everyone will copy-paste without understanding what the crate does will probably cause problems.
Thanks. BTW. From the top of my head: if password_in_request.ct_ne(&password_in_the_config).into() { works just fine.
Yeah, you can write .into(). I just wrote bool::from as a personal preference because it makes it more explicit that you're using a bool instead of Choice.