-
Notifications
You must be signed in to change notification settings - Fork 55
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
Problem with mont form inversion #606
Conversation
@dvdplm it looks like this test is making use of internals / private fields. Can you write it entirely in terms of the public API? Otherwise I can't tell if you're miscomputing constants. It would also be good to generate reference values with e.g. |
You mean to instantiate the I think having known good values would be very good, and I had a look at the Sage docs but couldn't find much info on how to do modular maths with it. I'll try again though. |
Done. |
@dvdplm as an alternative to |
@tarcieri I added a test using |
Add tests for random uints and their inverses for both v06 and v05
@tarcieri @fjarri I think I have found another clue that might help us narrow down what is going wrong here. The problem seems to lie in the modulus, in the size of the modulus. In the latest commit (e0da58b) I have added more tests to illustrate how moduli of sizes Not sure what the next steps should be but I wanted to leave as much information as possible here. |
fn v06_inversion_random_uints_u2048_modulus() { | ||
let mut rng = ChaChaRng::from_seed(RANDOM_SEED); | ||
let modulus = U2048::from_be_hex(concat!( | ||
"1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
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.
Changing this to 1effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
makes the test pass. For this issue it doesn't seem to matter what any other bytes are.
let mut rng = ChaChaRng::from_seed(RANDOM_SEED); | ||
let modulus = U4096::from_be_hex(concat!( | ||
// 0x07… fails (bin 00000111) | ||
"06ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
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.
For U4096
the "failure value" is different.
fn v06_inversion_random_uints_u1024_modulus() { | ||
let mut rng = ChaChaRng::from_seed(RANDOM_SEED); | ||
let modulus = U1024::from_be_hex(concat!( | ||
"1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
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.
U1024
seems to be able to cope with any modulus.
@dvdplm that's a good data point to know re: specific sized integers being impacted My best guess would be it's a bug in the Lines 12 to 19 in 2b81eae
That's used here when writing impls of the crypto-bigint/src/uint/macros.rs Line 11 in 2b81eae
|
I was able to produce a test failure using the existing test suite by modifying the proptests for modular inversion in I then made those same tests pass by modifying macro_rules! bernstein_yang_nlimbs {
($bits:expr) => {
(($bits / 64) + (($bits / 64) * 2).div_ceil(64) + 2)
};
} That probably isn't the best way to fix this specific problem, but I believe it does demonstrate that there are currently too few 62-bit limbs for a given sized integer, leading to truncation and thus the miscomputed results. @dvdplm I haven't tested your specific vectors, but perhaps you could try that locally and see if it corrects the issue? |
@@ -38,6 +38,7 @@ num-modular = { version = "0.6", features = ["num-bigint", "num-integer", "num-t | |||
proptest = "1" | |||
rand_core = { version = "0.6", features = ["std"] } | |||
rand_chacha = "0.3" | |||
crypto_bigint_05 = {package = "crypto-bigint", version = "0.5.5"} |
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.
Would you mind removing this dependency and the tests that depend on it, leaving only tests against the current codebase with fixed vectors?
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 mean, this PR is just a throw-away thing to tinker with a tricky bug and this was convenient for that purpose. If there's anything we salvage here long term it's perhaps the test vectors, but perhaps we can put those in your other PR?
I'm also a little confused why the current calculation for the number of limbs is insufficient, but I'll have to go back through the paper and find where it's defined. FWIW, it currently computes 34 limbs for a 2048-bit integer, where 34 * 62 = 2108 bits |
Aha, so the issue is for a given
We currently compute 34 limbs for a 2048-bit integer:
That's 4-bits too small. |
The previous calculation of the number of unsaturated 62-bit limbs needed to represent an integer of a given size was incorrect, leading to miscomputed results as seen in #606. This commit switches to a much simpler calculation based on `div_ceiling(62)`, and also adds a const assertion that the computed number of limbs is sufficient to hold a `$bits`-sized integer.
Awesome sleuthing! I was thiiiiiis close, but you got there first! :) |
Yep, will do first thing in the morning. |
Changing the |
The previous calculation of the number of unsaturated 62-bit limbs needed to represent an integer of a given size was incorrect, leading to miscomputed results as seen in #606. This commit switches to a much simpler calculation based on `div_ceiling(62)`, and also adds a const assertion that the computed number of limbs is sufficient to hold a `$bits`-sized integer.
I have checked that #610 indeed fixes all test failures on this branch. 🎉 To check upstream it would be really good to have a new pre-release of |
Yes, I can cut another release soon |
Released in v0.6.0-rc.0 |
The previous calculation of the number of unsaturated 62-bit limbs needed to represent an integer of a given size was incorrect, leading to miscomputed results as seen in RustCrypto#606. This commit switches to a much simpler calculation based on `div_ceiling(62)`, and also adds a const assertion that the computed number of limbs is sufficient to hold a `$bits`-sized integer.
Going to close this out, but I'd still be curious in getting some of these tests in if you'd like to resubmit just those |
Test to illustrate a potential problem with the Bernstein&Yang code to
invert numbers in Montgomery form. The specific parameters shown here
come from intermittent test failures of homomorphic_mul in the
Synedrion signining library (i.e. parameters are generated with an
unseeded CSPRNG).
Run the test with
cargo t inversion_v06_vs_v055
.For convenience, after the test follows a commented out version of the
same that passes with
crypto-bigint
v0.5.5.To compare with v0.5.5 do the following:
git checkout v0.5.5
cargo update -p proc-macro2
to work around a recent compilerincompatibility
runtime_mod.rs
cargo t inversion_v06v_v055