Skip to content

Commit 747f0ca

Browse files
authored
Add Odd newtype (#487)
Adds a newtype similar to `NonZero` which identifies odd integers at the type level. This can be used for infallible conversions for moduli, i.e. reusing a `*MontParams` modulus as a Bernstein-Yang modulus infallibly.
1 parent c6aceaa commit 747f0ca

39 files changed

+352
-248
lines changed

benches/boxed_monty.rs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use criterion::{
44
};
55
use crypto_bigint::{
66
modular::{BoxedMontyForm, BoxedMontyParams},
7-
BoxedUint, NonZero, RandomMod,
7+
BoxedUint, NonZero, Odd, RandomMod,
88
};
99
use num_bigint::BigUint;
1010
use rand_core::OsRng;
@@ -17,10 +17,7 @@ fn to_biguint(uint: &BoxedUint) -> BigUint {
1717
}
1818

1919
fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
20-
let params = BoxedMontyParams::new(
21-
BoxedUint::random(&mut OsRng, UINT_BITS) | BoxedUint::one_with_precision(UINT_BITS),
22-
)
23-
.unwrap();
20+
let params = BoxedMontyParams::new(Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS));
2421

2522
group.bench_function("invert, U256", |b| {
2623
b.iter_batched(
@@ -60,8 +57,8 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
6057
)
6158
});
6259

63-
let m = BoxedUint::random(&mut OsRng, UINT_BITS) | BoxedUint::one_with_precision(UINT_BITS);
64-
let params = BoxedMontyParams::new(m).unwrap();
60+
let m = Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS);
61+
let params = BoxedMontyParams::new(m);
6562
group.bench_function("modpow, BoxedUint^BoxedUint", |b| {
6663
b.iter_batched(
6764
|| {
@@ -96,24 +93,21 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
9693
fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
9794
group.bench_function("BoxedMontyParams::new", |b| {
9895
b.iter_batched(
99-
|| BoxedUint::random(&mut OsRng, UINT_BITS) | BoxedUint::one_with_precision(UINT_BITS),
96+
|| Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS),
10097
|modulus| black_box(BoxedMontyParams::new(modulus)),
10198
BatchSize::SmallInput,
10299
)
103100
});
104101

105102
group.bench_function("BoxedMontyParams::new_vartime", |b| {
106103
b.iter_batched(
107-
|| BoxedUint::random(&mut OsRng, UINT_BITS) | BoxedUint::one_with_precision(UINT_BITS),
104+
|| Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS),
108105
|modulus| black_box(BoxedMontyParams::new_vartime(modulus)),
109106
BatchSize::SmallInput,
110107
)
111108
});
112109

113-
let params = BoxedMontyParams::new(
114-
BoxedUint::random(&mut OsRng, UINT_BITS) | BoxedUint::one_with_precision(UINT_BITS),
115-
)
116-
.unwrap();
110+
let params = BoxedMontyParams::new(Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS));
117111
group.bench_function("BoxedMontyForm::new", |b| {
118112
b.iter_batched(
119113
|| BoxedUint::random(&mut OsRng, UINT_BITS),
@@ -122,10 +116,7 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
122116
)
123117
});
124118

125-
let params = BoxedMontyParams::new(
126-
BoxedUint::random(&mut OsRng, UINT_BITS) | BoxedUint::one_with_precision(UINT_BITS),
127-
)
128-
.unwrap();
119+
let params = BoxedMontyParams::new(Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS));
129120
group.bench_function("BoxedMontyForm::retrieve", |b| {
130121
b.iter_batched(
131122
|| BoxedMontyForm::new(BoxedUint::random(&mut OsRng, UINT_BITS), params.clone()),

benches/monty.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use criterion::{
44
};
55
use crypto_bigint::{
66
modular::{MontyForm, MontyParams},
7-
Invert, Inverter, PrecomputeInverter, Random, U256,
7+
Invert, Inverter, Odd, PrecomputeInverter, Random, U256,
88
};
99
use rand_core::OsRng;
1010

@@ -14,22 +14,22 @@ use crypto_bigint::MultiExponentiate;
1414
fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
1515
group.bench_function("MontyParams creation", |b| {
1616
b.iter_batched(
17-
|| U256::random(&mut OsRng) | U256::ONE,
18-
|modulus| black_box(MontyParams::new(&modulus)),
17+
|| Odd::<U256>::random(&mut OsRng),
18+
|modulus| black_box(MontyParams::new(modulus)),
1919
BatchSize::SmallInput,
2020
)
2121
});
2222

23-
let params = MontyParams::new(&(U256::random(&mut OsRng) | U256::ONE)).unwrap();
23+
let params = MontyParams::new(Odd::<U256>::random(&mut OsRng));
2424
group.bench_function("MontyForm creation", |b| {
2525
b.iter_batched(
26-
|| U256::random(&mut OsRng),
26+
|| Odd::<U256>::random(&mut OsRng),
2727
|x| black_box(MontyForm::new(&x, params)),
2828
BatchSize::SmallInput,
2929
)
3030
});
3131

32-
let params = MontyParams::new(&(U256::random(&mut OsRng) | U256::ONE)).unwrap();
32+
let params = MontyParams::new(Odd::<U256>::random(&mut OsRng));
3333
group.bench_function("MontyForm retrieve", |b| {
3434
b.iter_batched(
3535
|| MontyForm::new(&U256::random(&mut OsRng), params),
@@ -40,7 +40,7 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
4040
}
4141

4242
fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
43-
let params = MontyParams::new(&(U256::random(&mut OsRng) | U256::ONE)).unwrap();
43+
let params = MontyParams::new(Odd::<U256>::random(&mut OsRng));
4444

4545
group.bench_function("invert, U256", |b| {
4646
b.iter_batched(

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, Uint, Word};
3+
use crate::{modular::BernsteinYangInverter, 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`
@@ -305,6 +305,20 @@ impl<const LIMBS: usize> ConstCtOption<NonZero<Uint<LIMBS>>> {
305305
}
306306
}
307307

308+
impl<const LIMBS: usize> ConstCtOption<Odd<Uint<LIMBS>>> {
309+
/// Returns the contained value, consuming the `self` value.
310+
///
311+
/// # Panics
312+
///
313+
/// Panics if the value is none with a custom panic message provided by
314+
/// `msg`.
315+
#[inline]
316+
pub const fn expect(self, msg: &str) -> Odd<Uint<LIMBS>> {
317+
assert!(self.is_some.is_true_vartime(), "{}", msg);
318+
self.value
319+
}
320+
}
321+
308322
impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize>
309323
ConstCtOption<BernsteinYangInverter<SAT_LIMBS, UNSAT_LIMBS>>
310324
{

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ mod checked;
170170
mod const_choice;
171171
mod limb;
172172
mod non_zero;
173+
mod odd;
173174
mod primitives;
174175
mod traits;
175176
mod uint;
@@ -180,6 +181,7 @@ pub use crate::{
180181
const_choice::{ConstChoice, ConstCtOption},
181182
limb::{Limb, WideWord, Word},
182183
non_zero::NonZero,
184+
odd::Odd,
183185
traits::*,
184186
uint::div_limb::Reciprocal,
185187
uint::*,

src/limb/bit_and.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Limb bit and operations.
22
33
use super::Limb;
4-
use core::ops::BitAnd;
4+
use core::ops::{BitAnd, BitAndAssign};
55

66
impl Limb {
77
/// Calculates `a & b`.
@@ -19,3 +19,15 @@ impl BitAnd for Limb {
1919
self.bitand(rhs)
2020
}
2121
}
22+
23+
impl BitAndAssign for Limb {
24+
fn bitand_assign(&mut self, rhs: Self) {
25+
self.0 &= rhs.0;
26+
}
27+
}
28+
29+
impl BitAndAssign<&Limb> for Limb {
30+
fn bitand_assign(&mut self, rhs: &Limb) {
31+
self.0 &= rhs.0;
32+
}
33+
}

src/limb/bit_xor.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Limb bit xor operations.
22
33
use super::Limb;
4-
use core::ops::BitXor;
4+
use core::ops::{BitXor, BitXorAssign};
55

66
impl Limb {
77
/// Calculates `a ^ b`.
@@ -18,3 +18,9 @@ impl BitXor for Limb {
1818
self.bitxor(rhs)
1919
}
2020
}
21+
22+
impl BitXorAssign for Limb {
23+
fn bitxor_assign(&mut self, rhs: Self) {
24+
self.0 ^= rhs.0;
25+
}
26+
}

src/modular/add.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use crate::Uint;
1+
use crate::{Odd, Uint};
22

33
pub(crate) const fn add_montgomery_form<const LIMBS: usize>(
44
a: &Uint<LIMBS>,
55
b: &Uint<LIMBS>,
6-
modulus: &Uint<LIMBS>,
6+
modulus: &Odd<Uint<LIMBS>>,
77
) -> Uint<LIMBS> {
8-
a.add_mod(b, modulus)
8+
a.add_mod(b, &modulus.0)
99
}

src/modular/bernstein_yang.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#[macro_use]
1414
mod macros;
1515

16-
use crate::{ConstChoice, ConstCtOption, Inverter, Limb, Uint, Word};
16+
use crate::{ConstChoice, ConstCtOption, Inverter, Limb, Odd, Uint, Word};
1717
use subtle::CtOption;
1818

1919
/// Modular multiplicative inverter based on the Bernstein-Yang method.
@@ -61,14 +61,12 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize>
6161
/// Creates the inverter for specified modulus and adjusting parameter.
6262
///
6363
/// Modulus must be odd. Returns `None` if it is not.
64-
pub const fn new(modulus: &Uint<SAT_LIMBS>, adjuster: &Uint<SAT_LIMBS>) -> ConstCtOption<Self> {
65-
let ret = Self {
66-
modulus: Int62L::from_uint(modulus),
64+
pub const fn new(modulus: &Odd<Uint<SAT_LIMBS>>, adjuster: &Uint<SAT_LIMBS>) -> Self {
65+
Self {
66+
modulus: Int62L::from_uint(&modulus.0),
6767
adjuster: Int62L::from_uint(adjuster),
68-
inverse: inv_mod2_62(modulus.as_words()),
69-
};
70-
71-
ConstCtOption::new(ret, modulus.is_odd())
68+
inverse: inv_mod2_62(modulus.0.as_words()),
69+
}
7270
}
7371

7472
/// Returns either the adjusted modular multiplicative inverse for the argument or `None`

0 commit comments

Comments
 (0)