Skip to content

Commit

Permalink
Use Integer and Monty instead of Uint
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Dec 28, 2023
1 parent 45152ad commit e97198a
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 251 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ categories = ["cryptography", "no-std"]
rust-version = "1.73"

[dependencies]
crypto-bigint = { version = "0.6.0-pre.5", default-features = false, features = ["rand_core"] }
crypto-bigint = { version = "0.6.0-pre.5", default-features = false, features = ["alloc", "rand_core"] }
rand_core = { version = "0.6.4", default-features = false }
openssl = { version = "0.10.39", optional = true, features = ["vendored"] }
rug = { version = "1.18", default-features = false, features = ["integer"], optional = true }
Expand Down
44 changes: 22 additions & 22 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ fn make_rng() -> ChaCha8Rng {
ChaCha8Rng::from_seed(*b"01234567890123456789012345678901")
}

fn make_sieve<const L: usize>(rng: &mut impl CryptoRngCore) -> Sieve<L> {
let start = random_odd_uint::<L>(rng, Uint::<L>::BITS);
fn make_sieve<const L: usize>(rng: &mut impl CryptoRngCore) -> Sieve<Uint<L>> {
let start = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS);
Sieve::new(&start, Uint::<L>::BITS, false)
}

Expand All @@ -36,13 +36,13 @@ fn bench_sieve(c: &mut Criterion) {
let mut group = c.benchmark_group("Sieve");

group.bench_function("(U128) random start", |b| {
b.iter(|| random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128))
b.iter(|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 128))
});

group.bench_function("(U128) creation", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128),
|start| Sieve::new(&start, 128, false),
|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 128),
|start| Sieve::new(start.as_ref(), 128, false),
BatchSize::SmallInput,
)
});
Expand All @@ -57,13 +57,13 @@ fn bench_sieve(c: &mut Criterion) {
});

group.bench_function("(U1024) random start", |b| {
b.iter(|| random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024))
b.iter(|| random_odd_uint::<Uint<{ nlimbs!(1024) }>>(&mut OsRng, 1024))
});

group.bench_function("(U1024) creation", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024),
|start| Sieve::new(&start, 1024, false),
|| random_odd_uint::<Uint<{ nlimbs!(1024) }>>(&mut OsRng, 1024),
|start| Sieve::new(start.as_ref(), 1024, false),
BatchSize::SmallInput,
)
});
Expand All @@ -84,7 +84,7 @@ fn bench_miller_rabin(c: &mut Criterion) {

group.bench_function("(U128) creation", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128),
|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 128),
MillerRabin::new,
BatchSize::SmallInput,
)
Expand All @@ -100,7 +100,7 @@ fn bench_miller_rabin(c: &mut Criterion) {

group.bench_function("(U1024) creation", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024),
|| random_odd_uint::<Uint<{ nlimbs!(1024) }>>(&mut OsRng, 1024),
MillerRabin::new,
BatchSize::SmallInput,
)
Expand Down Expand Up @@ -193,39 +193,39 @@ fn bench_presets(c: &mut Criterion) {

group.bench_function("(U128) Prime test", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128),
|num| is_prime_with_rng(&mut OsRng, &num),
|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 128),
|num| is_prime_with_rng(&mut OsRng, num.as_ref()),
BatchSize::SmallInput,
)
});

group.bench_function("(U128) Safe prime test", |b| {
b.iter_batched(
|| random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128),
|num| is_safe_prime_with_rng(&mut OsRng, &num),
|| random_odd_uint::<Uint<{ nlimbs!(128) }>>(&mut OsRng, 128),
|num| is_safe_prime_with_rng(&mut OsRng, num.as_ref()),
BatchSize::SmallInput,
)
});

let mut rng = make_rng();
group.bench_function("(U128) Random prime", |b| {
b.iter(|| generate_prime_with_rng::<{ nlimbs!(128) }>(&mut rng, None))
b.iter(|| generate_prime_with_rng::<Uint<{ nlimbs!(128) }>>(&mut rng, 128))
});

let mut rng = make_rng();
group.bench_function("(U1024) Random prime", |b| {
b.iter(|| generate_prime_with_rng::<{ nlimbs!(1024) }>(&mut rng, None))
b.iter(|| generate_prime_with_rng::<Uint<{ nlimbs!(1024) }>>(&mut rng, 1024))
});

let mut rng = make_rng();
group.bench_function("(U128) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<{ nlimbs!(128) }>(&mut rng, None))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(128) }>>(&mut rng, 128))
});

group.sample_size(20);
let mut rng = make_rng();
group.bench_function("(U1024) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<{ nlimbs!(1024) }>(&mut rng, None))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(1024) }>>(&mut rng, 1024))
});

group.finish();
Expand All @@ -235,19 +235,19 @@ fn bench_presets(c: &mut Criterion) {

let mut rng = make_rng();
group.bench_function("(U128) Random safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<{ nlimbs!(128) }>(&mut rng, None))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(128) }>>(&mut rng, 128))
});

// The performance should scale with the prime size, not with the Uint size.
// So we should strive for this test's result to be as close as possible
// to that of the previous one and as far away as possible from the next one.
group.bench_function("(U256) Random 128 bit safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<{ nlimbs!(256) }>(&mut rng, Some(128)))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(256) }>>(&mut rng, 128))
});

// The upper bound for the previous test.
group.bench_function("(U256) Random 256 bit safe prime", |b| {
b.iter(|| generate_safe_prime_with_rng::<{ nlimbs!(256) }>(&mut rng, None))
b.iter(|| generate_safe_prime_with_rng::<Uint<{ nlimbs!(256) }>>(&mut rng, 256))
});

group.finish();
Expand All @@ -258,7 +258,7 @@ fn bench_gmp(c: &mut Criterion) {
let mut group = c.benchmark_group("GMP");

fn random<const L: usize>(rng: &mut impl CryptoRngCore) -> Integer {
let num = random_odd_uint::<L>(rng, Uint::<L>::BITS);
let num = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS).get();
Integer::from_digits(num.as_words(), Order::Lsf)
}

Expand Down
8 changes: 4 additions & 4 deletions src/hazmat/gcd.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use crypto_bigint::{Limb, NonZero, Uint, Word};
use crypto_bigint::{Integer, Limb, NonZero, Word};

/// Calculates the greatest common divisor of `n` and `m`.
/// By definition, `gcd(0, m) == m`.
/// `n` must be non-zero.
pub(crate) fn gcd_vartime<const L: usize>(n: &Uint<L>, m: Word) -> Word {
pub(crate) fn gcd_vartime<T: Integer>(n: &T, m: Word) -> Word {
// This is an internal function, and it will never be called with `m = 0`.
// Allowing `m = 0` would require us to have the return type of `Uint<L>`
// (since `gcd(n, 0) = n`).
debug_assert!(m != 0);

// This we can check since it doesn't affect the return type,
// even though `n` will not be 0 either in the application.
if n == &Uint::<L>::ZERO {
if n.is_zero().into() {
return m;
}

Expand All @@ -23,7 +23,7 @@ pub(crate) fn gcd_vartime<const L: usize>(n: &Uint<L>, m: Word) -> Word {
} else {
// In this branch `n` is `Word::BITS` bits or shorter,
// so we can safely take the first limb.
let n = n.as_words()[0];
let n = n.as_ref()[0].0;
if n > m {
(n, m)
} else {
Expand Down
71 changes: 32 additions & 39 deletions src/hazmat/jacobi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Jacobi symbol calculation.
use crypto_bigint::{Limb, NonZero, Odd, Uint, Word};
use crypto_bigint::{Integer, Limb, NonZero, Odd, Word};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum JacobiSymbol {
Expand All @@ -20,37 +20,13 @@ impl core::ops::Neg for JacobiSymbol {
}
}

// A helper trait to generalize some functions over Word and Uint.
trait SmallMod {
fn mod8(&self) -> Word;
fn mod4(&self) -> Word;
}

impl SmallMod for Word {
fn mod8(&self) -> Word {
self & 7
}
fn mod4(&self) -> Word {
self & 3
}
}

impl<const L: usize> SmallMod for Uint<L> {
fn mod8(&self) -> Word {
self.as_limbs()[0].0 & 7
}
fn mod4(&self) -> Word {
self.as_limbs()[0].0 & 3
}
}

/// Transforms `(a/p)` -> `(r/p)` for odd `p`, where the resulting `r` is odd, and `a = r * 2^s`.
/// Takes a Jacobi symbol value, and returns `r` and the new Jacobi symbol,
/// negated if the transformation changes parity.
///
/// Note that the returned `r` is odd.
fn reduce_numerator<V: SmallMod>(j: JacobiSymbol, a: Word, p: &V) -> (JacobiSymbol, Word) {
let p_mod_8 = p.mod8();
fn apply_reduce_numerator(j: JacobiSymbol, a: Word, p: Word) -> (JacobiSymbol, Word) {
let p_mod_8 = p & 7;
let s = a.trailing_zeros();
let j = if (s & 1) == 1 && (p_mod_8 == 3 || p_mod_8 == 5) {
-j
Expand All @@ -60,38 +36,55 @@ fn reduce_numerator<V: SmallMod>(j: JacobiSymbol, a: Word, p: &V) -> (JacobiSymb
(j, a >> s)
}

fn reduce_numerator_long<T: Integer>(j: JacobiSymbol, a: Word, p: &T) -> (JacobiSymbol, Word) {
apply_reduce_numerator(j, a, p.as_ref()[0].0)
}

fn reduce_numerator_short(j: JacobiSymbol, a: Word, p: Word) -> (JacobiSymbol, Word) {
apply_reduce_numerator(j, a, p)
}

/// Transforms `(a/p)` -> `(p/a)` for odd and coprime `a` and `p`.
/// Takes a Jacobi symbol value, and returns the swapped pair and the new Jacobi symbol,
/// negated if the transformation changes parity.
fn swap<T: SmallMod, V: SmallMod>(j: JacobiSymbol, a: T, p: V) -> (JacobiSymbol, V, T) {
let j = if a.mod4() == 1 || p.mod4() == 1 {
fn apply_swap(j: JacobiSymbol, a: Word, p: Word) -> JacobiSymbol {
if a & 3 == 1 || p & 3 == 1 {
j
} else {
-j
};
}
}

fn swap_long<T: Integer>(j: JacobiSymbol, a: Word, p: &Odd<T>) -> (JacobiSymbol, &Odd<T>, Word) {
let j = apply_swap(j, a, p.as_ref().as_ref()[0].0);
(j, p, a)
}

fn swap_short(j: JacobiSymbol, a: Word, p: Word) -> (JacobiSymbol, Word, Word) {
let j = apply_swap(j, a, p);
(j, p, a)
}

/// Returns the Jacobi symbol `(a/p)` given an odd `p`.
pub(crate) fn jacobi_symbol_vartime<const L: usize>(
pub(crate) fn jacobi_symbol_vartime<T: Integer>(
abs_a: Word,
a_is_negative: bool,
p_long: &Odd<Uint<L>>,
p_long: &Odd<T>,
) -> JacobiSymbol {
let result = JacobiSymbol::One; // Keep track of all the sign flips here.

// Deal with a negative `a` first:
// (-a/n) = (-1/n) * (a/n)
// = (-1)^((n-1)/2) * (a/n)
// = (-1 if n = 3 mod 4 else 1) * (a/n)
let result = if a_is_negative && p_long.mod4() == 3 {
let result = if a_is_negative && p_long.as_ref().as_ref()[0].0 & 3 == 3 {
-result
} else {
result
};

// A degenerate case.
if abs_a == 1 || p_long.as_ref() == &Uint::<L>::ONE {
if abs_a == 1 || p_long.as_ref() == &T::one() {
return result;
}

Expand All @@ -100,14 +93,14 @@ pub(crate) fn jacobi_symbol_vartime<const L: usize>(
// Normalize input: at the end we want `a < p`, `p` odd, and both fitting into a `Word`.
let (result, a, p): (JacobiSymbol, Word, Word) = if p_long.bits_vartime() <= Limb::BITS {
let a = a_limb.0;
let p = p_long.as_limbs()[0].0;
let p = p_long.as_ref().as_ref()[0].0;
(result, a % p, p)
} else {
let (result, a) = reduce_numerator(result, a_limb.0, p_long.as_ref());
let (result, a) = reduce_numerator_long(result, a_limb.0, p_long.as_ref());
if a == 1 {
return result;
}
let (result, a_long, p) = swap(result, a, p_long.get());
let (result, a_long, p) = swap_long(result, a, p_long);
// Can unwrap here, since `p` is swapped with `a`,
// and `a` would be odd after `reduce_numerator()`.
let a =
Expand All @@ -127,7 +120,7 @@ pub(crate) fn jacobi_symbol_vartime<const L: usize>(
// At this point `p` is odd (either coming from outside of the `loop`,
// or from the previous iteration, where a previously reduced `a`
// was swapped into its place), so we can call this.
(result, a) = reduce_numerator(result, a, &p);
(result, a) = reduce_numerator_short(result, a, p);

if a == 1 {
return result;
Expand All @@ -138,7 +131,7 @@ pub(crate) fn jacobi_symbol_vartime<const L: usize>(
// Note that technically `swap()` only returns a valid `result` if `a` and `p` are coprime.
// But if they are not, we will return `Zero` eventually,
// which is not affected by any sign changes.
(result, a, p) = swap(result, a, p);
(result, a, p) = swap_short(result, a, p);

a %= p;
}
Expand Down
Loading

0 comments on commit e97198a

Please sign in to comment.