Skip to content

Commit f9ef5aa

Browse files
authored
Expand integer trait (#489)
See also: #436 and entropyxyz/crypto-primes#37
1 parent 871b645 commit f9ef5aa

File tree

14 files changed

+668
-8
lines changed

14 files changed

+668
-8
lines changed

src/const_choice.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use subtle::{Choice, CtOption};
22

3-
use crate::{modular::BernsteinYangInverter, NonZero, Odd, Uint, Word};
3+
use crate::{modular::BernsteinYangInverter, Limb, NonZero, Odd, Uint, Word};
44

55
/// A boolean value returned by constant-time `const fn`s.
66
// TODO: should be replaced by `subtle::Choice` or `CtOption`
@@ -319,6 +319,20 @@ impl<const LIMBS: usize> ConstCtOption<Odd<Uint<LIMBS>>> {
319319
}
320320
}
321321

322+
impl ConstCtOption<NonZero<Limb>> {
323+
/// Returns the contained value, consuming the `self` value.
324+
///
325+
/// # Panics
326+
///
327+
/// Panics if the value is none with a custom panic message provided by
328+
/// `msg`.
329+
#[inline]
330+
pub const fn expect(self, msg: &str) -> NonZero<Limb> {
331+
assert!(self.is_some.is_true_vartime(), "{}", msg);
332+
self.value
333+
}
334+
}
335+
322336
impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize>
323337
ConstCtOption<BernsteinYangInverter<SAT_LIMBS, UNSAT_LIMBS>>
324338
{

src/modular/boxed_monty_form.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//! Implements `BoxedMontyForm`s, supporting modular arithmetic with a modulus whose size and value
21
//! is chosen at runtime.
32
43
mod add;
@@ -9,6 +8,7 @@ mod pow;
98
mod sub;
109

1110
use super::{
11+
div_by_2,
1212
reduction::{montgomery_reduction_boxed, montgomery_reduction_boxed_mut},
1313
Retrieve,
1414
};
@@ -219,6 +219,18 @@ impl BoxedMontyForm {
219219
debug_assert!(self.montgomery_form < self.params.modulus);
220220
self.montgomery_form.clone()
221221
}
222+
223+
/// Performs the modular division by 2, that is for given `x` returns `y`
224+
/// such that `y * 2 = x mod p`. This means:
225+
/// - if `x` is even, returns `x / 2`,
226+
/// - if `x` is odd, returns `(x + p) / 2`
227+
/// (since the modulus `p` in Montgomery form is always odd, this divides entirely).
228+
pub fn div_by_2(&self) -> Self {
229+
Self {
230+
montgomery_form: div_by_2::div_by_2_boxed(&self.montgomery_form, &self.params.modulus),
231+
params: self.params.clone(),
232+
}
233+
}
222234
}
223235

224236
impl Retrieve for BoxedMontyForm {
@@ -258,3 +270,26 @@ fn convert_to_montgomery(integer: &mut BoxedUint, params: &BoxedMontyParams) {
258270
#[cfg(feature = "zeroize")]
259271
product.zeroize();
260272
}
273+
274+
#[cfg(test)]
275+
mod tests {
276+
use super::{BoxedMontyForm, BoxedMontyParams, BoxedUint, Odd};
277+
278+
#[test]
279+
fn new_params_with_valid_modulus() {
280+
let modulus = Odd::new(BoxedUint::from(3u8)).unwrap();
281+
BoxedMontyParams::new(modulus);
282+
}
283+
284+
#[test]
285+
fn div_by_2() {
286+
let modulus = Odd::new(BoxedUint::from(9u8)).unwrap();
287+
let params = BoxedMontyParams::new(modulus);
288+
let zero = BoxedMontyForm::zero(params.clone());
289+
let one = BoxedMontyForm::one(params.clone());
290+
let two = one.add(&one);
291+
292+
assert_eq!(zero.div_by_2(), zero);
293+
assert_eq!(one.div_by_2().mul(&two), one);
294+
}
295+
}

src/modular/div_by_2.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use crate::Uint;
2+
#[cfg(feature = "alloc")]
3+
use crate::{BoxedUint, ConstantTimeSelect};
24

35
pub(crate) fn div_by_2<const LIMBS: usize>(a: &Uint<LIMBS>, modulus: &Uint<LIMBS>) -> Uint<LIMBS> {
46
// We are looking for such `x` that `x * 2 = y mod modulus`,
@@ -28,3 +30,19 @@ pub(crate) fn div_by_2<const LIMBS: usize>(a: &Uint<LIMBS>, modulus: &Uint<LIMBS
2830

2931
Uint::<LIMBS>::select(&if_even, &if_odd, is_odd)
3032
}
33+
34+
#[cfg(feature = "alloc")]
35+
pub(crate) fn div_by_2_boxed(a: &BoxedUint, modulus: &BoxedUint) -> BoxedUint {
36+
debug_assert_eq!(a.bits_precision(), modulus.bits_precision());
37+
38+
let (mut half, is_odd) = a.shr1_with_carry();
39+
let half_modulus = modulus.shr1();
40+
41+
let if_odd = half
42+
.wrapping_add(&half_modulus)
43+
.wrapping_add(&BoxedUint::one_with_precision(a.bits_precision()));
44+
45+
half.ct_assign(&if_odd, is_odd);
46+
47+
half
48+
}

src/odd.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,27 @@ impl Odd<BoxedUint> {
143143
Odd(ret)
144144
}
145145
}
146+
147+
#[cfg(test)]
148+
mod tests {
149+
#[cfg(feature = "alloc")]
150+
use super::BoxedUint;
151+
use super::{Odd, Uint};
152+
153+
#[test]
154+
fn not_odd_numbers() {
155+
let zero = Odd::new(Uint::<4>::ZERO);
156+
assert!(bool::from(zero.is_none()));
157+
let two = Odd::new(Uint::<4>::from(2u8));
158+
assert!(bool::from(two.is_none()));
159+
}
160+
161+
#[cfg(feature = "alloc")]
162+
#[test]
163+
fn not_odd_numbers_boxed() {
164+
let zero = Odd::new(BoxedUint::zero());
165+
assert!(bool::from(zero.is_none()));
166+
let two = Odd::new(BoxedUint::from(2u8));
167+
assert!(bool::from(two.is_none()));
168+
}
169+
}

src/uint/boxed.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod bits;
1010
mod cmp;
1111
mod ct;
1212
mod div;
13+
mod div_limb;
1314
pub(crate) mod encoding;
1415
mod from;
1516
mod inv_mod;
@@ -19,6 +20,7 @@ mod neg;
1920
mod neg_mod;
2021
mod shl;
2122
mod shr;
23+
mod sqrt;
2224
mod sub;
2325
mod sub_mod;
2426

@@ -92,6 +94,14 @@ impl BoxedUint {
9294
.fold(Choice::from(1), |acc, limb| acc & limb.is_zero())
9395
}
9496

97+
/// Is this [`BoxedUint`] not equal to zero?
98+
pub fn is_nonzero(&self) -> Choice {
99+
// TODO: why not just !self.is_zero()?
100+
self.limbs
101+
.iter()
102+
.fold(Choice::from(0), |acc, limb| acc | limb.is_nonzero().into())
103+
}
104+
95105
/// Is this [`BoxedUint`] equal to one?
96106
pub fn is_one(&self) -> Choice {
97107
let mut iter = self.limbs.iter();

src/uint/boxed/bits.rs

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Bit manipulation functions.
22
3-
use crate::{BoxedUint, Limb, Zero};
3+
use crate::{BoxedUint, ConstChoice, Limb, Zero};
44
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
55

66
impl BoxedUint {
@@ -24,6 +24,11 @@ impl BoxedUint {
2424
Limb::BITS * n - leading_zeros
2525
}
2626

27+
/// `floor(log2(self.bits_precision()))`.
28+
pub(crate) fn log2_bits(&self) -> u32 {
29+
u32::BITS - self.bits_precision().leading_zeros() - 1
30+
}
31+
2732
/// Calculate the number of bits needed to represent this number in variable-time with respect
2833
/// to `self`.
2934
pub fn bits_vartime(&self) -> u32 {
@@ -36,6 +41,18 @@ impl BoxedUint {
3641
Limb::BITS * (i as u32 + 1) - limb.leading_zeros()
3742
}
3843

44+
/// Returns `true` if the bit at position `index` is set, `false` otherwise.
45+
///
46+
/// # Remarks
47+
/// This operation is variable time with respect to `index` only.
48+
pub fn bit_vartime(&self, index: u32) -> bool {
49+
if index >= self.bits_precision() {
50+
false
51+
} else {
52+
(self.limbs[(index / Limb::BITS) as usize].0 >> (index % Limb::BITS)) & 1 == 1
53+
}
54+
}
55+
3956
/// Get the precision of this [`BoxedUint`] in bits.
4057
pub fn bits_precision(&self) -> u32 {
4158
self.limbs.len() as u32 * Limb::BITS
@@ -55,6 +72,45 @@ impl BoxedUint {
5572
count
5673
}
5774

75+
/// Calculate the number of trailing ones in the binary representation of this number.
76+
pub fn trailing_ones(&self) -> u32 {
77+
let limbs = self.as_limbs();
78+
79+
let mut count = 0;
80+
let mut i = 0;
81+
let mut nonmax_limb_not_encountered = ConstChoice::TRUE;
82+
while i < limbs.len() {
83+
let l = limbs[i];
84+
let z = l.trailing_ones();
85+
count += nonmax_limb_not_encountered.if_true_u32(z);
86+
nonmax_limb_not_encountered =
87+
nonmax_limb_not_encountered.and(ConstChoice::from_word_eq(l.0, Limb::MAX.0));
88+
i += 1;
89+
}
90+
91+
count
92+
}
93+
94+
/// Calculate the number of trailing ones in the binary representation of this number,
95+
/// variable time in `self`.
96+
pub fn trailing_ones_vartime(&self) -> u32 {
97+
let limbs = self.as_limbs();
98+
99+
let mut count = 0;
100+
let mut i = 0;
101+
while i < limbs.len() {
102+
let l = limbs[i];
103+
let z = l.trailing_ones();
104+
count += z;
105+
if z != Limb::BITS {
106+
break;
107+
}
108+
i += 1;
109+
}
110+
111+
count
112+
}
113+
58114
/// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`.
59115
pub(crate) fn set_bit(&mut self, index: u32, bit_value: Choice) {
60116
let limb_num = (index / Limb::BITS) as usize;
@@ -89,6 +145,18 @@ mod tests {
89145
result
90146
}
91147

148+
#[test]
149+
fn bit_vartime() {
150+
let u = uint_with_bits_at(&[16, 48, 112, 127, 255]);
151+
assert!(!u.bit_vartime(0));
152+
assert!(!u.bit_vartime(1));
153+
assert!(u.bit_vartime(16));
154+
assert!(u.bit_vartime(127));
155+
assert!(u.bit_vartime(255));
156+
assert!(!u.bit_vartime(256));
157+
assert!(!u.bit_vartime(260));
158+
}
159+
92160
#[test]
93161
fn bits() {
94162
assert_eq!(0, BoxedUint::zero().bits());
@@ -119,4 +187,40 @@ mod tests {
119187
u.set_bit(150, Choice::from(0));
120188
assert_eq!(u, uint_with_bits_at(&[16, 79]));
121189
}
190+
191+
#[test]
192+
fn trailing_ones() {
193+
let u = !uint_with_bits_at(&[16, 79, 150]);
194+
assert_eq!(u.trailing_ones(), 16);
195+
196+
let u = !uint_with_bits_at(&[79, 150]);
197+
assert_eq!(u.trailing_ones(), 79);
198+
199+
let u = !uint_with_bits_at(&[150, 207]);
200+
assert_eq!(u.trailing_ones(), 150);
201+
202+
let u = !uint_with_bits_at(&[0, 150, 207]);
203+
assert_eq!(u.trailing_ones(), 0);
204+
205+
let u = !BoxedUint::zero_with_precision(256);
206+
assert_eq!(u.trailing_ones(), 256);
207+
}
208+
209+
#[test]
210+
fn trailing_ones_vartime() {
211+
let u = !uint_with_bits_at(&[16, 79, 150]);
212+
assert_eq!(u.trailing_ones_vartime(), 16);
213+
214+
let u = !uint_with_bits_at(&[79, 150]);
215+
assert_eq!(u.trailing_ones_vartime(), 79);
216+
217+
let u = !uint_with_bits_at(&[150, 207]);
218+
assert_eq!(u.trailing_ones_vartime(), 150);
219+
220+
let u = !uint_with_bits_at(&[0, 150, 207]);
221+
assert_eq!(u.trailing_ones_vartime(), 0);
222+
223+
let u = !BoxedUint::zero_with_precision(256);
224+
assert_eq!(u.trailing_ones_vartime(), 256);
225+
}
122226
}

src/uint/boxed/cmp.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,30 @@ use subtle::{
1010
Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess,
1111
};
1212

13+
impl BoxedUint {
14+
/// Returns the Ordering between `self` and `rhs` in variable time.
15+
pub fn cmp_vartime(&self, rhs: &Self) -> Ordering {
16+
debug_assert_eq!(self.limbs.len(), rhs.limbs.len());
17+
let mut i = self.limbs.len() - 1;
18+
loop {
19+
// TODO: investigate if directly comparing limbs is faster than performing a
20+
// subtraction between limbs
21+
let (val, borrow) = self.limbs[i].sbb(rhs.limbs[i], Limb::ZERO);
22+
if val.0 != 0 {
23+
return if borrow.0 != 0 {
24+
Ordering::Less
25+
} else {
26+
Ordering::Greater
27+
};
28+
}
29+
if i == 0 {
30+
return Ordering::Equal;
31+
}
32+
i -= 1;
33+
}
34+
}
35+
}
36+
1337
impl ConstantTimeEq for BoxedUint {
1438
#[inline]
1539
fn ct_eq(&self, other: &Self) -> Choice {

src/uint/boxed/div.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
//! [`BoxedUint`] division operations.
22
3-
use crate::{BoxedUint, CheckedDiv, ConstantTimeSelect, Limb, NonZero, Wrapping};
3+
use crate::{
4+
uint::boxed, BoxedUint, CheckedDiv, ConstantTimeSelect, Limb, NonZero, Reciprocal, Wrapping,
5+
};
46
use core::ops::{Div, DivAssign, Rem, RemAssign};
57
use subtle::{Choice, ConstantTimeEq, ConstantTimeLess, CtOption};
68

79
impl BoxedUint {
10+
/// Computes `self` / `rhs` using a pre-made reciprocal,
11+
/// returns the quotient (q) and remainder (r).
12+
pub fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb) {
13+
boxed::div_limb::div_rem_limb_with_reciprocal(self, reciprocal)
14+
}
15+
16+
/// Computes `self` / `rhs`, returns the quotient (q) and remainder (r).
17+
pub fn div_rem_limb(&self, rhs: NonZero<Limb>) -> (Self, Limb) {
18+
boxed::div_limb::div_rem_limb_with_reciprocal(self, &Reciprocal::new(rhs))
19+
}
20+
821
/// Computes self / rhs, returns the quotient, remainder.
922
pub fn div_rem(&self, rhs: &NonZero<Self>) -> (Self, Self) {
1023
// Since `rhs` is nonzero, this should always hold.
@@ -61,6 +74,14 @@ impl BoxedUint {
6174
self.div_rem(rhs).0
6275
}
6376

77+
/// Wrapped division is just normal division i.e. `self` / `rhs`
78+
///
79+
/// There’s no way wrapping could ever happen.
80+
/// This function exists, so that all operations are accounted for in the wrapping operations
81+
pub fn wrapping_div_vartime(&self, rhs: &NonZero<Self>) -> Self {
82+
self.div_rem_vartime(rhs).0
83+
}
84+
6485
/// Perform checked division, returning a [`CtOption`] which `is_some`
6586
/// only if the rhs != 0
6687
pub fn checked_div(&self, rhs: &Self) -> CtOption<Self> {

0 commit comments

Comments
 (0)