-
Notifications
You must be signed in to change notification settings - Fork 56
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
Boxed uintlike coverage #436
Changes from all commits
13ca4be
f8ecad3
ef2c7bb
24792c3
97f2111
83e065e
1d100c1
9666ad5
d10f9fd
4aedbec
4db5f76
8c839eb
ce00645
a4f8125
1e6cb45
89171b5
233b9e5
97209b3
abbfd1f
49d72c5
d2d0e0f
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 |
---|---|---|
|
@@ -9,6 +9,7 @@ mod pow; | |
mod sub; | ||
|
||
use super::{ | ||
div_by_2, | ||
reduction::{montgomery_reduction_boxed, montgomery_reduction_boxed_mut}, | ||
Retrieve, | ||
}; | ||
|
@@ -242,6 +243,18 @@ impl BoxedMontyForm { | |
debug_assert!(self.montgomery_form < self.params.modulus); | ||
self.montgomery_form.clone() | ||
} | ||
|
||
/// Performs the modular division by 2, that is for given `x` returns `y` | ||
/// such that `y * 2 = x mod p`. This means: | ||
/// - if `x` is even, returns `x / 2`, | ||
/// - if `x` is odd, returns `(x + p) / 2` | ||
/// (since the modulus `p` in Montgomery form is always odd, this divides entirely). | ||
pub fn div_by_2(&self) -> Self { | ||
Self { | ||
montgomery_form: div_by_2::boxed::div_by_2(&self.montgomery_form, &self.params.modulus), | ||
params: self.params.clone(), // TODO: avoid clone? | ||
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. There would be no need for clone if it was an inplace method, but if it creates a new number, I think cloning is unavoidable. 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. When |
||
} | ||
} | ||
} | ||
|
||
impl Retrieve for BoxedMontyForm { | ||
|
@@ -263,7 +276,7 @@ fn convert_to_montgomery(integer: &mut BoxedUint, params: &BoxedMontyParams) { | |
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::{BoxedMontyParams, BoxedUint}; | ||
use super::{BoxedMontyForm, BoxedMontyParams, BoxedUint}; | ||
|
||
#[test] | ||
fn new_params_with_invalid_modulus() { | ||
|
@@ -280,4 +293,15 @@ mod tests { | |
fn new_params_with_valid_modulus() { | ||
BoxedMontyParams::new(BoxedUint::from(3u8)).unwrap(); | ||
} | ||
|
||
#[test] | ||
fn div_by_2() { | ||
let params = BoxedMontyParams::new(BoxedUint::from(9u8)).unwrap(); | ||
let zero = BoxedMontyForm::zero(params.clone()); | ||
let one = BoxedMontyForm::one(params.clone()); | ||
let two = one.add(&one); | ||
|
||
assert_eq!(zero.div_by_2(), zero); | ||
assert_eq!(one.div_by_2().mul(&two), one); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,3 +28,23 @@ pub(crate) fn div_by_2<const LIMBS: usize>(a: &Uint<LIMBS>, modulus: &Uint<LIMBS | |
|
||
Uint::<LIMBS>::select(&if_even, &if_odd, is_odd) | ||
} | ||
|
||
#[cfg(feature = "alloc")] | ||
pub(crate) mod boxed { | ||
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. I think putting different variants of this function in modules is a bit of an overcomplication, perhaps just different suffixes would suffice. |
||
use crate::{BoxedUint, ConstantTimeSelect}; | ||
|
||
pub(crate) fn div_by_2(a: &BoxedUint, modulus: &BoxedUint) -> BoxedUint { | ||
debug_assert_eq!(a.bits_precision(), modulus.bits_precision()); | ||
|
||
let (mut half, is_odd) = a.shr1_with_carry(); | ||
let half_modulus = modulus.shr1(); | ||
|
||
let if_odd = half | ||
.wrapping_add(&half_modulus) | ||
.wrapping_add(&BoxedUint::one_with_precision(a.bits_precision())); | ||
|
||
half.ct_assign(&if_odd, is_odd); | ||
|
||
half | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -10,6 +10,7 @@ mod bits; | |||||||||||||||||||||||||||||
mod cmp; | ||||||||||||||||||||||||||||||
mod ct; | ||||||||||||||||||||||||||||||
mod div; | ||||||||||||||||||||||||||||||
mod div_limb; | ||||||||||||||||||||||||||||||
pub(crate) mod encoding; | ||||||||||||||||||||||||||||||
mod from; | ||||||||||||||||||||||||||||||
mod inv_mod; | ||||||||||||||||||||||||||||||
|
@@ -19,6 +20,7 @@ mod neg; | |||||||||||||||||||||||||||||
mod neg_mod; | ||||||||||||||||||||||||||||||
mod shl; | ||||||||||||||||||||||||||||||
mod shr; | ||||||||||||||||||||||||||||||
mod sqrt; | ||||||||||||||||||||||||||||||
mod sub; | ||||||||||||||||||||||||||||||
mod sub_mod; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
@@ -92,6 +94,17 @@ impl BoxedUint { | |||||||||||||||||||||||||||||
.fold(Choice::from(1), |acc, limb| acc & limb.is_zero()) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
/// Returns the truthy value if `self`!=0 or the falsy value otherwise. | ||||||||||||||||||||||||||||||
pub(crate) fn is_nonzero(&self) -> Choice { | ||||||||||||||||||||||||||||||
let mut b = 0; | ||||||||||||||||||||||||||||||
let mut i = 0; | ||||||||||||||||||||||||||||||
while i < self.limbs.len() { | ||||||||||||||||||||||||||||||
b |= self.limbs[i].0; | ||||||||||||||||||||||||||||||
i += 1; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
Limb(b).is_nonzero().into() | ||||||||||||||||||||||||||||||
Comment on lines
+99
to
+105
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.
Suggested change
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. How about just 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. I mean, we can do that, but other instances can be replaced with |
||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
/// Is this [`BoxedUint`] equal to one? | ||||||||||||||||||||||||||||||
pub fn is_one(&self) -> Choice { | ||||||||||||||||||||||||||||||
let mut iter = self.limbs.iter(); | ||||||||||||||||||||||||||||||
|
@@ -245,6 +258,13 @@ impl BoxedUint { | |||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
impl NonZero<BoxedUint> { | ||||||||||||||||||||||||||||||
/// TODO: this is not really "const", but I need a way to return (value, choice) since | ||||||||||||||||||||||||||||||
/// BoxedUint is not [`ConditionallySelectable`] so `CtChoice::map` and such does not work | ||||||||||||||||||||||||||||||
pub fn const_new(n: BoxedUint) -> (Self, Choice) { | ||||||||||||||||||||||||||||||
let nonzero = n.is_nonzero(); | ||||||||||||||||||||||||||||||
(Self(n), nonzero) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
Comment on lines
+261
to
+266
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. I factored these into methods on e.g. in this case You can use |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
/// Widen this type's precision to the given number of bits. | ||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||
/// See [`BoxedUint::widen`] for more information, including panic conditions. | ||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
//! Bit manipulation functions. | ||
|
||
use crate::{BoxedUint, Limb, Zero}; | ||
use crate::{BoxedUint, ConstChoice, Limb, Zero}; | ||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; | ||
|
||
impl BoxedUint { | ||
|
@@ -24,6 +24,23 @@ impl BoxedUint { | |
Limb::BITS * n - leading_zeros | ||
} | ||
|
||
/// `floor(log2(self.bits_precision()))`. | ||
pub(crate) fn log2_bits(&self) -> u32 { | ||
u32::BITS - self.bits_precision().leading_zeros() - 1 | ||
} | ||
|
||
/// Returns `true` if the bit at position `index` is set, `false` otherwise. | ||
/// | ||
/// # Remarks | ||
/// This operation is variable time with respect to `index` only. | ||
pub fn bit_vartime(&self, index: u32) -> bool { | ||
if index >= self.bits_precision() { | ||
false | ||
} else { | ||
(self.limbs[(index / Limb::BITS) as usize].0 >> (index % Limb::BITS)) & 1 == 1 | ||
} | ||
} | ||
|
||
/// Calculate the number of bits needed to represent this number in variable-time with respect | ||
/// to `self`. | ||
pub fn bits_vartime(&self) -> u32 { | ||
|
@@ -55,6 +72,45 @@ impl BoxedUint { | |
count | ||
} | ||
|
||
/// Calculate the number of trailing ones in the binary representation of this number. | ||
pub fn trailing_ones(&self) -> u32 { | ||
let limbs = self.as_limbs(); | ||
|
||
let mut count = 0; | ||
let mut i = 0; | ||
let mut nonmax_limb_not_encountered = ConstChoice::TRUE; | ||
while i < limbs.len() { | ||
let l = limbs[i]; | ||
let z = l.trailing_ones(); | ||
count += nonmax_limb_not_encountered.if_true_u32(z); | ||
nonmax_limb_not_encountered = | ||
nonmax_limb_not_encountered.and(ConstChoice::from_word_eq(l.0, Limb::MAX.0)); | ||
i += 1; | ||
} | ||
|
||
count | ||
} | ||
|
||
/// Calculate the number of trailing ones in the binary representation of this number, | ||
/// variable time in `self`. | ||
pub fn trailing_ones_vartime(&self) -> u32 { | ||
let limbs = self.as_limbs(); | ||
|
||
let mut count = 0; | ||
let mut i = 0; | ||
while i < limbs.len() { | ||
let l = limbs[i]; | ||
let z = l.trailing_ones(); | ||
count += z; | ||
if z != Limb::BITS { | ||
break; | ||
} | ||
i += 1; | ||
} | ||
|
||
count | ||
} | ||
|
||
/// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`. | ||
pub(crate) fn set_bit(&mut self, index: u32, bit_value: Choice) { | ||
let limb_num = (index / Limb::BITS) as usize; | ||
|
@@ -84,7 +140,7 @@ mod tests { | |
fn uint_with_bits_at(positions: &[u32]) -> BoxedUint { | ||
let mut result = BoxedUint::zero_with_precision(256); | ||
for &pos in positions { | ||
result |= BoxedUint::one_with_precision(256).shl_vartime(pos).unwrap(); | ||
result |= BoxedUint::one_with_precision(256).overflowing_shl(pos).0; | ||
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. Any specific reason for the change here? 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. Another instance of me being confused. Will restore back to its original state. |
||
} | ||
result | ||
} | ||
|
@@ -101,6 +157,54 @@ mod tests { | |
assert_eq!(87, n2.bits()); | ||
} | ||
|
||
#[test] | ||
fn bit_vartime() { | ||
let u = uint_with_bits_at(&[16, 48, 112, 127, 255]); | ||
assert!(!u.bit_vartime(0)); | ||
assert!(!u.bit_vartime(1)); | ||
assert!(u.bit_vartime(16)); | ||
assert!(u.bit_vartime(127)); | ||
assert!(u.bit_vartime(255)); | ||
assert!(!u.bit_vartime(256)); | ||
assert!(!u.bit_vartime(260)); | ||
} | ||
|
||
#[test] | ||
fn trailing_ones() { | ||
let u = !uint_with_bits_at(&[16, 79, 150]); | ||
assert_eq!(u.trailing_ones(), 16); | ||
|
||
let u = !uint_with_bits_at(&[79, 150]); | ||
assert_eq!(u.trailing_ones(), 79); | ||
|
||
let u = !uint_with_bits_at(&[150, 207]); | ||
assert_eq!(u.trailing_ones(), 150); | ||
|
||
let u = !uint_with_bits_at(&[0, 150, 207]); | ||
assert_eq!(u.trailing_ones(), 0); | ||
|
||
let u = !BoxedUint::zero_with_precision(256); | ||
assert_eq!(u.trailing_ones(), 256); | ||
} | ||
|
||
#[test] | ||
fn trailing_ones_vartime() { | ||
let u = !uint_with_bits_at(&[16, 79, 150]); | ||
assert_eq!(u.trailing_ones_vartime(), 16); | ||
|
||
let u = !uint_with_bits_at(&[79, 150]); | ||
assert_eq!(u.trailing_ones_vartime(), 79); | ||
|
||
let u = !uint_with_bits_at(&[150, 207]); | ||
assert_eq!(u.trailing_ones_vartime(), 150); | ||
|
||
let u = !uint_with_bits_at(&[0, 150, 207]); | ||
assert_eq!(u.trailing_ones_vartime(), 0); | ||
|
||
let u = !BoxedUint::zero_with_precision(256); | ||
assert_eq!(u.trailing_ones_vartime(), 256); | ||
} | ||
|
||
#[test] | ||
fn set_bit() { | ||
let mut u = uint_with_bits_at(&[16, 79, 150]); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,28 @@ use subtle::{ | |
Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess, | ||
}; | ||
|
||
impl BoxedUint { | ||
/// Returns the Ordering between `self` and `rhs` in variable time. | ||
pub fn cmp_vartime(&self, rhs: &Self) -> Ordering { | ||
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. I have a feeling that using comparisons between limbs instead of subtraction may be faster. Put a TODO perhaps? |
||
debug_assert_eq!(self.limbs.len(), rhs.limbs.len()); | ||
let mut i = self.limbs.len() - 1; | ||
loop { | ||
let (val, borrow) = self.limbs[i].sbb(rhs.limbs[i], Limb::ZERO); | ||
if val.0 != 0 { | ||
return if borrow.0 != 0 { | ||
Ordering::Less | ||
} else { | ||
Ordering::Greater | ||
}; | ||
} | ||
if i == 0 { | ||
return Ordering::Equal; | ||
} | ||
i -= 1; | ||
} | ||
} | ||
} | ||
|
||
impl ConstantTimeEq for BoxedUint { | ||
#[inline] | ||
fn ct_eq(&self, other: &Self) -> Choice { | ||
|
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.
Why the change here? And if you're benchmarking
overflowing_shl
now, the string label should be changed too.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.
Yeah sorry I think I got really confused between the various
shl
/shr
functions.