-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Correct documentation of integer_division_remainder_used #15661
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
base: master
Are you sure you want to change the base?
Conversation
|
r? @Alexendoo rustbot has assigned @Alexendoo. Use |
|
Notes for the reviewer:
|
|
@tarcieri you opened the issue that encouraged this lint to exist :3 can I ask you for feedback on this? Like... well, basically on the changes proposed:
|
While an implementation of a Barrett Reduction won't necessarily be constant-time, it's certainly possible to implement a Barrett Reduction in constant-time, which is an extremely common technique in cryptography, and such implementations run in a fixed number of CPU cycles. See Algorithm 6) Barrett Reduction modulo p from this paper: You can read about our constant-time implementation of a Barrett Reduction for the P-256 scalar field here: https://reports.zksecurity.xyz/reports/near-p256/#analysis-of-the-bound-of-barrett-reduction And generally it's covered for cryptographic applications in HAC. See section 14.3.3: https://cacr.uwaterloo.ca/hac/about/chap14.pdf
...if you're talking about CPUs from the '80s like 80286 that predate barrel shifters, sure. These days even a Cortex-M0 has a barrel shifter. Are there any targets Rust actually supports that have non-constant-time bit shift operations? FWIW, on the @RustCrypto crates we do include a caveat about non-constant-time multipliers on e.g. PPC32 targets that short circuit on zero. I am not aware of any other specific cases of CPUs that are tier 1-3 rustc targets where operations like addition, multiplication, and bitwise operations including shifts are not constant-time, though looking over the table of Rust Tier 3 targets, I guess there's a |
|
@tarcieri nice! I think I'll be adding a lot of those references to the lint in my next push :3 But first.. I have more questions! If you'd have me ^^
Well, you know in cryptography, so you're more aware than I am about the importance of these nuances (and how misinformation has been used in the past to lower cryptographic defenses at the implementor's level)
I don't want the constant-timeness of something be guaranteed by happenstance. The compiler and the language don't guarantee it, so it's currently a sort of implicit guarantee? But not even that... it's more happenstance than anything else. That's why I still hesitate. Maybe the lint should just say all the caveats and not recommend any particular operation 100%
The original example worked because it was replacing a division by 4 with a shift by 2. But: Given those two things, I wonder how would a more general example look like. One for divisions other than by 2, 4, 8, etc. Or maybe one does not divide by numbers other than 2^n in current cryptography, and I shouldn't worry about it here. |
Short of implementing the algorithm in architecture-specific assembly you can't, or rather mechanisms for ensuring such optimizations won't be applied are nascent. However, the use of division is highly problematic. This lint was motivated by a real-world example, KyberSlash, where a similar vulnerability was seen across multiple implementations in many languages impacted by many compiler versions (including the @RustCrypto The specific mitigation to use is going to be highly algorithm-dependent (the Barrett reduction example comes from Kyberslash FWIW), so perhaps the clippy lint itself shouldn't be prescriptive about the solution, but rather focus on the problem. Seems you were on the same page with that here:
As for this:
Actually cryptography deals quite a bit with dividing by things which aren't powers of two: many asymmetric cryptographic algorithms are built around modular arithmetic where the modulus is something like a very large prime number. The general strategy is to replace the division by algorithms that can be expressed in terms of additions/subtractions, multiplications, and bitwise operations, without data-dependent branches, pointer calculations, or table lookups. A Barrett reduction is one example. Several others are covered in that HAC chapter I linked earlier. Another general approach is to turn division into multiplication using e.g. a(n X)GCD algorithm and/or modular inversions. |
b34c7d3 to
8a611e1
Compare
Nice! I thought so. Since I've been out of the loop in state-of-the-art of asymmetric cryptography for a good while, I had to ask. Modular arithmetic is so cool, man. Okay, I think I'm content with how this has ended up. @tarcieri I've pushed my latest changes. Can I bother you one more time, and ask you to proof-read the new documentation? |
1. Barrett Reduction isn't constant-time, so that was eliminated. 2. The "use instead" example didn't solve the timing attack, so it was eliminated as well. 3. Added some context and recommendations for the user. 4. Added a timestamp, hoping that whoever reads this in the future can make a more informed decision in case the docs become obsolete.
|
@Alexendoo lemme know if I should change anything |
| /// If within its capabilities, the compiler will [optimize away any constant-time implementation](https://eprint.iacr.org/2025/435) | ||
| /// of an algorithm. This can and does lead to unexpected non-constant execution times outside of | ||
| /// the user's agency. | ||
| /// | ||
| /// The solution to achieve constant-time operation is often to either implement | ||
| /// a constant-time algorithm in assembly code, or to delegate such operations to a well-known, | ||
| /// audited and / or certified cryptographic library. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be offering advice on how to implement constant time algorithms, pointing out that it needs to be replaced with one is enough for the purpose of this lint IMO
Mentioning KyberSlash as a specific example is nice though, I'd say drop the Final Notes section and move that into the Why restrict this? paragraph
changelog: [
integer_division_remainder_used]: corrected documentation errors