Skip to content

Commit e7d3b16

Browse files
authored
Some simplifications of vartime division (#646)
1 parent 8520fdb commit e7d3b16

File tree

3 files changed

+224
-190
lines changed

3 files changed

+224
-190
lines changed

src/uint/boxed/div.rs

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,42 @@ impl RemLimb for BoxedUint {
382382
}
383383
}
384384

385+
/// Computes `limbs << shift` inplace, where `0 <= shift < Limb::BITS`, returning the carry.
386+
fn shl_limb_vartime(limbs: &mut [Limb], shift: u32) -> Limb {
387+
if shift == 0 {
388+
return Limb::ZERO;
389+
}
390+
391+
let lshift = shift;
392+
let rshift = Limb::BITS - shift;
393+
let limbs_num = limbs.len();
394+
395+
let carry = limbs[limbs_num - 1] >> rshift;
396+
for i in (1..limbs_num).rev() {
397+
limbs[i] = (limbs[i] << lshift) | (limbs[i - 1] >> rshift);
398+
}
399+
limbs[0] <<= lshift;
400+
401+
carry
402+
}
403+
404+
/// Computes `limbs >> shift` inplace, where `0 <= shift < Limb::BITS`.
405+
fn shr_limb_vartime(limbs: &mut [Limb], shift: u32) {
406+
if shift == 0 {
407+
return;
408+
}
409+
410+
let lshift = Limb::BITS - shift;
411+
let rshift = shift;
412+
413+
let limbs_num = limbs.len();
414+
415+
for i in 0..limbs_num - 1 {
416+
limbs[i] = (limbs[i] >> rshift) | (limbs[i + 1] << lshift);
417+
}
418+
limbs[limbs_num - 1] >>= rshift;
419+
}
420+
385421
/// Computes `x` / `y`, returning the quotient in `x` and the remainder in `y`.
386422
///
387423
/// This function operates in variable-time. It will panic if the divisor is zero
@@ -408,51 +444,44 @@ pub(crate) fn div_rem_vartime_in_place(x: &mut [Limb], y: &mut [Limb]) {
408444
}
409445

410446
let lshift = y[yc - 1].leading_zeros();
411-
let rshift = if lshift == 0 { 0 } else { Limb::BITS - lshift };
412-
let mut x_hi = Limb::ZERO;
413-
let mut carry;
414-
415-
if lshift != 0 {
416-
// Shift divisor such that it has no leading zeros
417-
// This means that div2by1 requires no extra shifts, and ensures that the high word >= b/2
418-
carry = Limb::ZERO;
419-
for i in 0..yc {
420-
(y[i], carry) = (Limb((y[i].0 << lshift) | carry.0), Limb(y[i].0 >> rshift));
421-
}
422447

423-
// Shift the dividend to match
424-
carry = Limb::ZERO;
425-
for i in 0..xc {
426-
(x[i], carry) = (Limb((x[i].0 << lshift) | carry.0), Limb(x[i].0 >> rshift));
427-
}
428-
x_hi = carry;
429-
}
448+
// Shift divisor such that it has no leading zeros
449+
// This means that div2by1 requires no extra shifts, and ensures that the high word >= b/2
450+
shl_limb_vartime(y, lshift);
451+
452+
// Shift the dividend to match
453+
let mut x_hi = shl_limb_vartime(x, lshift);
430454

431455
let reciprocal = Reciprocal::new(y[yc - 1].to_nz().expect("zero divisor"));
432456

433457
for xi in (yc - 1..xc).rev() {
434458
// Divide high dividend words by the high divisor word to estimate the quotient word
435-
let (mut quo, _) = div3by2(x_hi.0, x[xi].0, x[xi - 1].0, &reciprocal, y[yc - 2].0);
459+
let mut quo = div3by2(x_hi.0, x[xi].0, x[xi - 1].0, &reciprocal, y[yc - 2].0);
436460

437461
// Subtract q*divisor from the dividend
438-
carry = Limb::ZERO;
439-
let mut borrow = Limb::ZERO;
440-
let mut tmp;
441-
for i in 0..yc {
442-
(tmp, carry) = Limb::ZERO.mac(y[i], Limb(quo), carry);
443-
(x[xi + i + 1 - yc], borrow) = x[xi + i + 1 - yc].sbb(tmp, borrow);
444-
}
445-
(_, borrow) = x_hi.sbb(carry, borrow);
462+
let borrow = {
463+
let mut carry = Limb::ZERO;
464+
let mut borrow = Limb::ZERO;
465+
let mut tmp;
466+
for i in 0..yc {
467+
(tmp, carry) = Limb::ZERO.mac(y[i], Limb(quo), carry);
468+
(x[xi + i + 1 - yc], borrow) = x[xi + i + 1 - yc].sbb(tmp, borrow);
469+
}
470+
(_, borrow) = x_hi.sbb(carry, borrow);
471+
borrow
472+
};
446473

447474
// If the subtraction borrowed, then decrement q and add back the divisor
448475
// The probability of this being needed is very low, about 2/(Limb::MAX+1)
449-
let ct_borrow = ConstChoice::from_word_mask(borrow.0);
450-
carry = Limb::ZERO;
451-
for i in 0..yc {
452-
(x[xi + i + 1 - yc], carry) =
453-
x[xi + i + 1 - yc].adc(Limb::select(Limb::ZERO, y[i], ct_borrow), carry);
454-
}
455-
quo = ct_borrow.select_word(quo, quo.saturating_sub(1));
476+
quo = {
477+
let ct_borrow = ConstChoice::from_word_mask(borrow.0);
478+
let mut carry = Limb::ZERO;
479+
for i in 0..yc {
480+
(x[xi + i + 1 - yc], carry) =
481+
x[xi + i + 1 - yc].adc(Limb::select(Limb::ZERO, y[i], ct_borrow), carry);
482+
}
483+
ct_borrow.select_word(quo, quo.wrapping_sub(1))
484+
};
456485

457486
// Store the quotient within dividend and set x_hi to the current highest word
458487
x_hi = x[xi];
@@ -464,12 +493,7 @@ pub(crate) fn div_rem_vartime_in_place(x: &mut [Limb], y: &mut [Limb]) {
464493
y[yc - 1] = x_hi;
465494

466495
// Unshift the remainder from the earlier adjustment
467-
if lshift != 0 {
468-
carry = Limb::ZERO;
469-
for i in (0..yc).rev() {
470-
(y[i], carry) = (Limb((y[i].0 >> lshift) | carry.0), Limb(y[i].0 << rshift));
471-
}
472-
}
496+
shr_limb_vartime(y, lshift);
473497

474498
// Shift the quotient to the low limbs within dividend
475499
// let x_size = xc - yc + 1;

0 commit comments

Comments
 (0)