-
Notifications
You must be signed in to change notification settings - Fork 322
Support 5 mod 8 case in SqrtPrecomputation #968
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?
Changes from all commits
8918f1d
bebc3d8
20f03bd
087aad3
d0fa65c
b665879
0137ed3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,6 +93,37 @@ pub trait MontConfig<const N: usize>: 'static + Sync + Send + Sized { | |
} | ||
}; | ||
|
||
/// (MODULUS + 3) / 8 when MODULUS % 8 == 5. Used for square root precomputations. | ||
#[doc(hidden)] | ||
const MODULUS_PLUS_THREE_DIV_EIGHT: Option<BigInt<N>> = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very interesting! I too think it would be helpful to reference a source—perhaps from Hacker's Delight, a university lecture, or an ePrint paper—for further context. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is just a partial case of Tonneli-Shanks algorithm https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @serhii023, thanks for the link—this adds some context. And BTW in general, optimizations are great and appreciated. That being said, a reasonable concern here is that proof by example is not generally sufficient for a cryptographic library (which has a higher standard than general purpose software). What are your thoughts? Would you be interested in writing a proof for the cases you've referenced? TBH, maybe a cool side-project in Lean etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is a proof: Consider than partial case where Consider our case
Above proof is not a proof by example, but a general proof for all primes of form |
||
match Self::MODULUS.mod_8() == 5 { | ||
true => { | ||
let (modulus_plus_three, carry) = Self::MODULUS.const_add_with_carry(&BigInt!("3")); | ||
let mut result = modulus_plus_three.divide_by_2_round_down(); | ||
// Since modulus_plus_one is even, dividing by 2 results in a MSB of 0. | ||
// Thus we can set MSB to `carry` to get the correct result of (MODULUS + 1) // 2: | ||
result.0[N - 1] |= (carry as u64) << 63; | ||
result = result.divide_by_2_round_down(); | ||
|
||
Some(result.divide_by_2_round_down()) | ||
}, | ||
false => None, | ||
} | ||
}; | ||
|
||
/// (MODULUS - 1) / 4 when MODULUS % 8 == 5. Used for square root precomputations. | ||
#[doc(hidden)] | ||
const MODULUS_MINUS_ONE_DIV_FOUR: Option<BigInt<N>> = { | ||
match Self::MODULUS.mod_8() == 5 { | ||
true => { | ||
let (modulus_plus_three, _) = Self::MODULUS.const_sub_with_borrow(&BigInt::one()); | ||
let result = modulus_plus_three.divide_by_2_round_down(); | ||
Some(result.divide_by_2_round_down()) | ||
}, | ||
false => None, | ||
} | ||
}; | ||
|
||
/// Sets `a = a + b`. | ||
#[inline(always)] | ||
fn add_assign(a: &mut Fp<MontBackend<Self, N>, N>, b: &Fp<MontBackend<Self, N>, N>) { | ||
|
@@ -548,12 +579,27 @@ pub const fn sqrt_precomputation<const N: usize, T: MontConfig<N>>( | |
}), | ||
None => None, | ||
}, | ||
_ => Some(SqrtPrecomputation::TonelliShanks { | ||
two_adicity: <MontBackend<T, N>>::TWO_ADICITY, | ||
quadratic_nonresidue_to_trace: T::TWO_ADIC_ROOT_OF_UNITY, | ||
trace_of_modulus_minus_one_div_two: | ||
&<Fp<MontBackend<T, N>, N>>::TRACE_MINUS_ONE_DIV_TWO.0, | ||
}), | ||
_ => match T::MODULUS.mod_8() { | ||
5 => match ( | ||
T::MODULUS_PLUS_THREE_DIV_EIGHT.as_ref(), | ||
T::MODULUS_MINUS_ONE_DIV_FOUR.as_ref(), | ||
) { | ||
( | ||
Some(BigInt(modulus_plus_three_div_eight)), | ||
Some(BigInt(modulus_minus_one_div_four)), | ||
) => Some(SqrtPrecomputation::Case5Mod8 { | ||
modulus_plus_three_div_eight, | ||
modulus_minus_one_div_four, | ||
}), | ||
_ => None, | ||
}, | ||
_ => Some(SqrtPrecomputation::TonelliShanks { | ||
two_adicity: <MontBackend<T, N>>::TWO_ADICITY, | ||
quadratic_nonresidue_to_trace: T::TWO_ADIC_ROOT_OF_UNITY, | ||
trace_of_modulus_minus_one_div_two: | ||
&<Fp<MontBackend<T, N>, N>>::TRACE_MINUS_ONE_DIV_TWO.0, | ||
}), | ||
}, | ||
} | ||
} | ||
|
||
|
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.
So, this and the above
mod_4
generalize to any modulo power two and could be rewritten in a general way correct?https://www.geeksforgeeks.org/compute-modulus-division-by-a-power-of-2-number
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.
Exactly, the value a % (2^d) is equal to a & (2^d - 1) and this evaluation is more efficient, however I wrote implementation similar to the existing code for the sake of simplicity.