Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 9 additions & 21 deletions src/modular/bingcd/gcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ impl<const LIMBS: usize> Odd<Uint<LIMBS>> {
#[inline(always)]
pub(crate) const fn optimized_bingcd(&self, rhs: &Uint<LIMBS>) -> Self {
self.optimized_bingcd_::<{ U64::BITS }, { U64::LIMBS }, { U128::LIMBS }>(rhs, U64::BITS - 1)
.0
}

/// Computes `gcd(self, rhs)`, leveraging the optimized Binary GCD algorithm.
Expand Down Expand Up @@ -154,9 +153,8 @@ impl<const LIMBS: usize> Odd<Uint<LIMBS>> {
&self,
rhs: &Uint<LIMBS>,
batch_max: u32,
) -> (Self, Word) {
) -> Self {
let (mut a, mut b) = (*self.as_ref(), *rhs);
let mut jacobi_neg = 0;

let mut iterations = Self::MINIMAL_BINGCD_ITERATIONS;
while iterations > 0 {
Expand All @@ -169,11 +167,10 @@ impl<const LIMBS: usize> Odd<Uint<LIMBS>> {
let b_ = b.compact::<K, LIMBS_2K>(n);

// Compute the batch update matrix from a_ and b_.
let (.., matrix, j_neg) = a_
let (.., matrix) = a_
.to_odd()
.expect_copied("a_ is always odd")
.partial_binxgcd::<LIMBS_K>(&b_, batch, Choice::FALSE);
jacobi_neg ^= j_neg;

// Update `a` and `b` using the update matrix.
// Safe to use vartime: the number of doublings is the same as the batch size.
Expand All @@ -182,11 +179,8 @@ impl<const LIMBS: usize> Odd<Uint<LIMBS>> {
(b, _) = updated_b.dropped_abs_sign();
}

(
a.to_odd()
.expect_copied("gcd of an odd value is always odd"),
jacobi_neg,
)
a.to_odd()
.expect_copied("gcd of an odd value is always odd")
}

/// Variable time equivalent of [`Self::optimized_bingcd`].
Expand All @@ -196,7 +190,6 @@ impl<const LIMBS: usize> Odd<Uint<LIMBS>> {
rhs,
U64::BITS - 1,
)
.0
}

/// Variable time equivalent of [`Self::optimized_bingcd_`].
Expand All @@ -209,9 +202,8 @@ impl<const LIMBS: usize> Odd<Uint<LIMBS>> {
&self,
rhs: &Uint<LIMBS>,
batch_max: u32,
) -> (Self, Word) {
) -> Self {
let (mut a, mut b) = (*self.as_ref(), *rhs);
let mut jacobi_neg = 0;

while !b.is_zero_vartime() {
// Construct a_ and b_ as the summary of a and b, respectively.
Expand All @@ -221,7 +213,7 @@ impl<const LIMBS: usize> Odd<Uint<LIMBS>> {

if n <= Uint::<LIMBS_2K>::BITS {
loop {
jacobi_neg ^= bingcd_step(&mut b_, &mut a_).2;
bingcd_step(&mut b_, &mut a_);
if b_.is_zero_vartime() {
break;
}
Expand All @@ -231,23 +223,19 @@ impl<const LIMBS: usize> Odd<Uint<LIMBS>> {
}

// Compute the batch update matrix from a_ and b_.
let (.., matrix, j_neg) = a_
let (.., matrix) = a_
.to_odd()
.expect_copied("a_ is always odd")
.partial_binxgcd::<LIMBS_K>(&b_, batch_max, Choice::FALSE);
jacobi_neg ^= j_neg;

// Update `a` and `b` using the update matrix.
let (updated_a, updated_b) = matrix.extended_apply_to_vartime((a, b));
(a, _) = updated_a.dropped_abs_sign();
(b, _) = updated_b.dropped_abs_sign();
}

(
a.to_odd()
.expect_copied("gcd of an odd value with something else is always odd"),
jacobi_neg,
)
a.to_odd()
.expect_copied("gcd of an odd value with something else is always odd")
}
}

Expand Down
29 changes: 12 additions & 17 deletions src/modular/bingcd/xgcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use super::gcd::bingcd_step;
use crate::modular::bingcd::matrix::{DividedIntMatrix, DividedPatternMatrix, PatternMatrix, Unit};
use crate::primitives::u32_max;
use crate::{Choice, Int, Limb, NonZeroUint, Odd, OddUint, U64, U128, Uint, Word};
use crate::{Choice, Int, Limb, NonZeroUint, Odd, OddUint, U64, U128, Uint};

/// Binary XGCD update step.
///
Expand Down Expand Up @@ -34,8 +34,8 @@ const fn binxgcd_step<const LIMBS: usize, const MATRIX_LIMBS: usize>(
b: &mut Uint<LIMBS>,
matrix: &mut DividedPatternMatrix<MATRIX_LIMBS>,
halt_at_zero: Choice,
) -> Word {
let (a_odd, swap, j) = bingcd_step(a, b);
) {
let (a_odd, swap, _) = bingcd_step(a, b);

// swap if a odd and a < b
matrix.conditional_swap_rows(swap);
Expand All @@ -45,8 +45,6 @@ const fn binxgcd_step<const LIMBS: usize, const MATRIX_LIMBS: usize>(

// Double the bottom row of the matrix when a was ≠ 0 and when not halting.
matrix.conditional_double_bottom_row(a.is_nonzero().or(halt_at_zero.not()));

j
}

/// Container for the raw output of the Binary XGCD algorithm.
Expand Down Expand Up @@ -209,7 +207,7 @@ impl<const LIMBS: usize> OddUint<LIMBS> {
/// Ref: Pornin, Optimized Binary GCD for Modular Inversion, Algorithm 1.
/// <https://eprint.iacr.org/2020/972.pdf>.
pub(crate) const fn classic_binxgcd(&self, rhs: &Self) -> DividedPatternXgcdOutput<LIMBS> {
let (gcd, _, matrix, _) =
let (gcd, _, matrix) =
self.partial_binxgcd::<LIMBS>(rhs.as_ref(), Self::MIN_BINXGCD_ITERATIONS, Choice::TRUE);
DividedPatternXgcdOutput { gcd, matrix }
}
Expand Down Expand Up @@ -278,7 +276,7 @@ impl<const LIMBS: usize> OddUint<LIMBS> {
Choice::from_u32_le(b_bits, K - 1).or(Choice::from_u32_eq(n, 2 * K));

// Compute the K-1 iteration update matrix from a_ and b_
let (.., update_matrix, _) = compact_a
let (.., update_matrix) = compact_a
.to_odd()
.expect_copied("a is always odd")
.partial_binxgcd::<LIMBS_K>(&compact_b, K - 1, b_eq_compact_b);
Expand Down Expand Up @@ -322,7 +320,7 @@ impl<const LIMBS: usize> OddUint<LIMBS> {
rhs: &Uint<LIMBS>,
iterations: u32,
halt_at_zero: Choice,
) -> (Self, Uint<LIMBS>, DividedPatternMatrix<UPDATE_LIMBS>, Word) {
) -> (Self, Uint<LIMBS>, DividedPatternMatrix<UPDATE_LIMBS>) {
let (mut a, mut b) = (*self.as_ref(), *rhs);
// This matrix corresponds with (f0, g0, f1, g1) in the paper.
let mut matrix = DividedPatternMatrix::UNIT;
Expand All @@ -334,12 +332,10 @@ impl<const LIMBS: usize> OddUint<LIMBS> {
Uint::swap(&mut a, &mut b);
matrix.swap_rows();

let mut jacobi_neg = 0;
let mut i = 0;

while i < iterations {
jacobi_neg ^=
binxgcd_step::<LIMBS, UPDATE_LIMBS>(&mut a, &mut b, &mut matrix, halt_at_zero);
binxgcd_step::<LIMBS, UPDATE_LIMBS>(&mut a, &mut b, &mut matrix, halt_at_zero);
i += 1;
}

Expand All @@ -348,7 +344,7 @@ impl<const LIMBS: usize> OddUint<LIMBS> {
matrix.swap_rows();

let a = a.to_odd().expect_copied("a is always odd");
(a, b, matrix, jacobi_neg)
(a, b, matrix)
}
}

Expand Down Expand Up @@ -514,7 +510,7 @@ mod tests {

#[test]
fn test_partial_binxgcd() {
let (.., matrix, _) = A.partial_binxgcd::<{ U64::LIMBS }>(&B, 5, Choice::TRUE);
let (.., matrix) = A.partial_binxgcd::<{ U64::LIMBS }>(&B, 5, Choice::TRUE);
assert_eq!(matrix.k, 5);
assert_eq!(
matrix,
Expand All @@ -527,8 +523,7 @@ mod tests {
let target_a = U64::from_be_hex("1CB3FB3FA1218FDB").to_odd().unwrap();
let target_b = U64::from_be_hex("0EA028AF0F8966B6");

let (new_a, new_b, matrix, _) =
A.partial_binxgcd::<{ U64::LIMBS }>(&B, 5, Choice::TRUE);
let (new_a, new_b, matrix) = A.partial_binxgcd::<{ U64::LIMBS }>(&B, 5, Choice::TRUE);

assert_eq!(new_a, target_a);
assert_eq!(new_b, target_b);
Expand All @@ -547,7 +542,7 @@ mod tests {

#[test]
fn test_partial_binxgcd_halts() {
let (gcd, _, matrix, _) =
let (gcd, _, matrix) =
SMALL_A.partial_binxgcd::<{ U64::LIMBS }>(&SMALL_B, 60, Choice::TRUE);
assert_eq!(matrix.k, 35);
assert_eq!(matrix.k_upper_bound, 60);
Expand All @@ -556,7 +551,7 @@ mod tests {

#[test]
fn test_partial_binxgcd_does_not_halt() {
let (gcd, .., matrix, _) =
let (gcd, .., matrix) =
SMALL_A.partial_binxgcd::<{ U64::LIMBS }>(&SMALL_B, 60, Choice::FALSE);
assert_eq!(matrix.k, 60);
assert_eq!(matrix.k_upper_bound, 60);
Expand Down
35 changes: 18 additions & 17 deletions src/uint/mod_symbol.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Support for computing modular symbols.

use crate::{JacobiSymbol, Odd, U64, U128, Uint};
use crate::{JacobiSymbol, Odd, Uint};

impl<const LIMBS: usize> Uint<LIMBS> {
/// Compute the Jacobi symbol `(self|rhs)`.
Expand All @@ -19,14 +19,8 @@ impl<const LIMBS: usize> Uint<LIMBS> {
return a.jacobi_symbol(rhs);
}

let (gcd, jacobi_neg) = if LIMBS < 4 {
rhs.classic_bingcd_(&self.resize())
} else {
rhs.optimized_bingcd_::<{ U64::BITS }, { U64::LIMBS }, { U128::LIMBS }>(
&self.resize(),
U64::BITS - 2,
)
};
let (gcd, jacobi_neg) = rhs.classic_bingcd_(&self.resize());

// The sign of the Jacobi symbol is represented by jacobi_neg. We select 0 as the
// symbol when the GCD is not one, otherwise 1 or -1.
let jacobi = (jacobi_neg as i8 * -2 + 1) as i64;
Expand Down Expand Up @@ -87,14 +81,7 @@ impl<const LIMBS: usize> Uint<LIMBS> {
}
}

let (gcd, jacobi_neg) = if LIMBS < 4 {
rhs.classic_bingcd_vartime_(&self.resize())
} else {
rhs.optimized_bingcd_vartime_::<{ U64::BITS }, { U64::LIMBS }, { U128::LIMBS }>(
&self.resize(),
U64::BITS - 2,
)
};
let (gcd, jacobi_neg) = rhs.classic_bingcd_vartime_(&self.resize());
JacobiSymbol::from_i8(if gcd.as_ref().cmp_vartime(&Uint::ONE).is_eq() {
jacobi_neg as i8 * -2 + 1
} else {
Expand Down Expand Up @@ -200,4 +187,18 @@ mod tests {
assert_eq!(res, res_vartime_small);
assert_eq!(res, res_vartime_large);
}

#[test]
// test from issue #1295 - variations in only the middle bits can trip up optimized binary GCD method
fn jacobi_edge() {
use crate::{Odd, U256};

assert_eq!(
U256::from_be_hex("0000000000000002108DEAFCB180F023912BEED0186CEEAD593A8507B7DA4E9B")
.jacobi_symbol(&Odd::<U256>::from_be_hex(
"0000000000000002108DEAFCB180F023912BEED0186CEEED593A8507B7DA4E9B",
)),
JacobiSymbol::MinusOne
);
}
}
Loading