crypto_bigint/uint/
div_limb.rs

1//! Implementation of constant-time division via reciprocal precomputation, as described in
2//! "Improved Division by Invariant Integers" by Niels Möller and Torbjorn Granlund
3//! (DOI: 10.1109/TC.2010.143, <https://gmplib.org/~tege/division-paper.pdf>).
4use subtle::{Choice, ConditionallySelectable};
5
6use crate::{
7    primitives::{addhilo, mulhilo},
8    ConstChoice, Limb, NonZero, Uint, WideWord, Word,
9};
10
11/// Calculates the reciprocal of the given 32-bit divisor with the highmost bit set.
12#[cfg(target_pointer_width = "32")]
13pub const fn reciprocal(d: Word) -> Word {
14    debug_assert!(d >= (1 << (Word::BITS - 1)));
15
16    let d0 = d & 1;
17    let d10 = d >> 22;
18    let d21 = (d >> 11) + 1;
19    let d31 = (d >> 1) + d0;
20    let v0 = short_div((1 << 24) - (1 << 14) + (1 << 9), 24, d10, 10);
21    let (hi, _lo) = mulhilo(v0 * v0, d21);
22    let v1 = (v0 << 4) - hi - 1;
23
24    // Checks that the expression for `e` can be simplified in the way we did below.
25    debug_assert!(mulhilo(v1, d31).0 == (1 << 16) - 1);
26    let e = Word::MAX - v1.wrapping_mul(d31) + 1 + (v1 >> 1) * d0;
27
28    let (hi, _lo) = mulhilo(v1, e);
29    // Note: the paper does not mention a wrapping add here,
30    // but the 64-bit version has it at this stage, and the function panics without it
31    // when calculating a reciprocal for `Word::MAX`.
32    let v2 = (v1 << 15).wrapping_add(hi >> 1);
33
34    // The paper has `(v2 + 1) * d / 2^32` (there's another 2^32, but it's accounted for later).
35    // If `v2 == 2^32-1` this should give `d`, but we can't achieve this in our wrapping arithmetic.
36    // Hence the `ct_select()`.
37    let x = v2.wrapping_add(1);
38    let (hi, _lo) = mulhilo(x, d);
39    let hi = ConstChoice::from_u32_nonzero(x).select_word(d, hi);
40
41    v2.wrapping_sub(hi).wrapping_sub(d)
42}
43
44/// Calculates the reciprocal of the given 64-bit divisor with the highmost bit set.
45#[cfg(target_pointer_width = "64")]
46pub const fn reciprocal(d: Word) -> Word {
47    debug_assert!(d >= (1 << (Word::BITS - 1)));
48
49    let d0 = d & 1;
50    let d9 = d >> 55;
51    let d40 = (d >> 24) + 1;
52    let d63 = (d >> 1) + d0;
53    let v0 = short_div((1 << 19) - 3 * (1 << 8), 19, d9 as u32, 9) as u64;
54    let v1 = (v0 << 11) - ((v0 * v0 * d40) >> 40) - 1;
55    let v2 = (v1 << 13) + ((v1 * ((1 << 60) - v1 * d40)) >> 47);
56
57    // Checks that the expression for `e` can be simplified in the way we did below.
58    debug_assert!(mulhilo(v2, d63).0 == (1 << 32) - 1);
59    let e = Word::MAX - v2.wrapping_mul(d63) + 1 + (v2 >> 1) * d0;
60
61    let (hi, _lo) = mulhilo(v2, e);
62    let v3 = (v2 << 31).wrapping_add(hi >> 1);
63
64    // The paper has `(v3 + 1) * d / 2^64` (there's another 2^64, but it's accounted for later).
65    // If `v3 == 2^64-1` this should give `d`, but we can't achieve this in our wrapping arithmetic.
66    // Hence the `ct_select()`.
67    let x = v3.wrapping_add(1);
68    let (hi, _lo) = mulhilo(x, d);
69    let hi = ConstChoice::from_word_nonzero(x).select_word(d, hi);
70
71    v3.wrapping_sub(hi).wrapping_sub(d)
72}
73
74/// Returns `u32::MAX` if `a < b` and `0` otherwise.
75#[inline]
76const fn lt(a: u32, b: u32) -> u32 {
77    // TODO: Move to using ConstChoice::le
78    let bit = (((!a) & b) | (((!a) | b) & (a.wrapping_sub(b)))) >> (u32::BITS - 1);
79    bit.wrapping_neg()
80}
81
82/// Returns `a` if `c == 0` and `b` if `c == u32::MAX`.
83#[inline(always)]
84const fn select(a: u32, b: u32, c: u32) -> u32 {
85    // TODO: Move to using ConstChoice::select
86    a ^ (c & (a ^ b))
87}
88
89/// Calculates `dividend / divisor`, given `dividend` and `divisor`
90/// along with their maximum bitsizes.
91#[inline(always)]
92const fn short_div(dividend: u32, dividend_bits: u32, divisor: u32, divisor_bits: u32) -> u32 {
93    // TODO: this may be sped up even more using the fact that `dividend` is a known constant.
94
95    // In the paper this is a table lookup, but since we want it to be constant-time,
96    // we have to access all the elements of the table, which is quite large.
97    // So this shift-and-subtract approach is actually faster.
98
99    // Passing `dividend_bits` and `divisor_bits` because calling `.leading_zeros()`
100    // causes a significant slowdown, and we know those values anyway.
101
102    let mut dividend = dividend;
103    let mut divisor = divisor << (dividend_bits - divisor_bits);
104    let mut quotient: u32 = 0;
105    let mut i = dividend_bits - divisor_bits + 1;
106
107    while i > 0 {
108        i -= 1;
109        let bit = lt(dividend, divisor);
110        dividend = select(dividend.wrapping_sub(divisor), dividend, bit);
111        divisor >>= 1;
112        let inv_bit = !bit;
113        quotient |= (inv_bit >> (u32::BITS - 1)) << i;
114    }
115
116    quotient
117}
118
119/// Calculate the quotient and the remainder of the division of a wide word
120/// (supplied as high and low words) by `d`, with a precalculated reciprocal `v`.
121#[inline(always)]
122pub(crate) const fn div2by1(u1: Word, u0: Word, reciprocal: &Reciprocal) -> (Word, Word) {
123    let d = reciprocal.divisor_normalized;
124
125    debug_assert!(d >= (1 << (Word::BITS - 1)));
126    debug_assert!(u1 < d);
127
128    let (q1, q0) = mulhilo(reciprocal.reciprocal, u1);
129    let (q1, q0) = addhilo(q1, q0, u1, u0);
130    let q1 = q1.wrapping_add(1);
131    let r = u0.wrapping_sub(q1.wrapping_mul(d));
132
133    let r_gt_q0 = ConstChoice::from_word_lt(q0, r);
134    let q1 = r_gt_q0.select_word(q1, q1.wrapping_sub(1));
135    let r = r_gt_q0.select_word(r, r.wrapping_add(d));
136
137    // If this was a normal `if`, we wouldn't need wrapping ops, because there would be no overflow.
138    // But since we calculate both results either way, we have to wrap.
139    // Added an assert to still check the lack of overflow in debug mode.
140    debug_assert!(r < d || q1 < Word::MAX);
141    let r_ge_d = ConstChoice::from_word_le(d, r);
142    let q1 = r_ge_d.select_word(q1, q1.wrapping_add(1));
143    let r = r_ge_d.select_word(r, r.wrapping_sub(d));
144
145    (q1, r)
146}
147
148/// Given two long integers `u = (..., u0, u1, u2)` and `v = (..., v0, v1)`
149/// (where `u2` and `v1` are the most significant limbs), where `floor(u / v) <= Limb::MAX`,
150/// calculates `q` such that `q - 1 <= floor(u / v) <= q`.
151/// In place of `v1` takes its reciprocal, and assumes that `v` was already pre-shifted
152/// so that v1 has its most significant bit set (that is, the reciprocal's `shift` is 0).
153#[inline(always)]
154pub(crate) const fn div3by2(
155    u2: Word,
156    u1: Word,
157    u0: Word,
158    v1_reciprocal: &Reciprocal,
159    v0: Word,
160) -> Word {
161    debug_assert!(v1_reciprocal.shift == 0);
162    debug_assert!(u2 <= v1_reciprocal.divisor_normalized);
163
164    // This method corresponds to Algorithm Q:
165    // https://janmr.com/blog/2014/04/basic-multiple-precision-long-division/
166
167    let q_maxed = ConstChoice::from_word_eq(u2, v1_reciprocal.divisor_normalized);
168    let (mut quo, rem) = div2by1(q_maxed.select_word(u2, 0), u1, v1_reciprocal);
169    // When the leading dividend word equals the leading divisor word, cap the quotient
170    // at Word::MAX and set the remainder to the sum of the top dividend words.
171    quo = q_maxed.select_word(quo, Word::MAX);
172    let mut rem = q_maxed.select_wide_word(rem as WideWord, (u2 as WideWord) + (u1 as WideWord));
173
174    let mut i = 0;
175    while i < 2 {
176        let qy = (quo as WideWord) * (v0 as WideWord);
177        let rx = (rem << Word::BITS) | (u0 as WideWord);
178        // If r < b and q*y[-2] > r*x[-1], then set q = q - 1 and r = r + v1
179        let done = ConstChoice::from_word_nonzero((rem >> Word::BITS) as Word)
180            .or(ConstChoice::from_wide_word_le(qy, rx));
181        quo = done.select_word(quo.wrapping_sub(1), quo);
182        rem = done.select_wide_word(rem + (v1_reciprocal.divisor_normalized as WideWord), rem);
183        i += 1;
184    }
185
186    quo
187}
188
189/// A pre-calculated reciprocal for division by a single limb.
190#[derive(Copy, Clone, Debug, PartialEq, Eq)]
191pub struct Reciprocal {
192    divisor_normalized: Word,
193    shift: u32,
194    reciprocal: Word,
195}
196
197impl Reciprocal {
198    /// Pre-calculates a reciprocal for a known divisor,
199    /// to be used in the single-limb division later.
200    pub const fn new(divisor: NonZero<Limb>) -> Self {
201        let divisor = divisor.0;
202
203        // Assuming this is constant-time for primitive types.
204        let shift = divisor.0.leading_zeros();
205
206        // Will not panic since divisor is non-zero
207        let divisor_normalized = divisor.0 << shift;
208
209        Self {
210            divisor_normalized,
211            shift,
212            reciprocal: reciprocal(divisor_normalized),
213        }
214    }
215
216    /// Returns a default instance of this object.
217    /// It is a self-consistent `Reciprocal` that will not cause panics in functions that take it.
218    ///
219    /// NOTE: intended for using it as a placeholder during compile-time array generation,
220    /// don't rely on the contents.
221    pub const fn default() -> Self {
222        Self {
223            divisor_normalized: Word::MAX,
224            shift: 0,
225            // The result of calling `reciprocal(Word::MAX)`
226            // This holds both for 32- and 64-bit versions.
227            reciprocal: 1,
228        }
229    }
230
231    #[cfg(feature = "alloc")]
232    pub(crate) const fn divisor(&self) -> NonZero<Limb> {
233        NonZero(Limb(self.divisor_normalized >> self.shift))
234    }
235
236    /// Get the shift value
237    pub const fn shift(&self) -> u32 {
238        self.shift
239    }
240}
241
242impl ConditionallySelectable for Reciprocal {
243    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
244        Self {
245            divisor_normalized: Word::conditional_select(
246                &a.divisor_normalized,
247                &b.divisor_normalized,
248                choice,
249            ),
250            shift: u32::conditional_select(&a.shift, &b.shift, choice),
251            reciprocal: Word::conditional_select(&a.reciprocal, &b.reciprocal, choice),
252        }
253    }
254}
255
256// `CtOption.map()` needs this; for some reason it doesn't use the value it already has
257// for the `None` branch.
258impl Default for Reciprocal {
259    fn default() -> Self {
260        Self::default()
261    }
262}
263
264/// Divides `u` by the divisor encoded in the `reciprocal`, and returns
265/// the quotient and the remainder.
266#[inline(always)]
267pub(crate) const fn div_rem_limb_with_reciprocal<const L: usize>(
268    u: &Uint<L>,
269    reciprocal: &Reciprocal,
270) -> (Uint<L>, Limb) {
271    let (u_shifted, u_hi) = u.shl_limb(reciprocal.shift);
272    let mut r = u_hi.0;
273    let mut q = [Limb::ZERO; L];
274
275    let mut j = L;
276    while j > 0 {
277        j -= 1;
278        let (qj, rj) = div2by1(r, u_shifted.as_limbs()[j].0, reciprocal);
279        q[j] = Limb(qj);
280        r = rj;
281    }
282    (Uint::<L>::new(q), Limb(r >> reciprocal.shift))
283}
284
285/// Divides `u` by the divisor encoded in the `reciprocal`, and returns the remainder.
286#[inline(always)]
287pub(crate) const fn rem_limb_with_reciprocal<const L: usize>(
288    u: &Uint<L>,
289    reciprocal: &Reciprocal,
290) -> Limb {
291    let (u_shifted, u_hi) = u.shl_limb(reciprocal.shift);
292    let mut r = u_hi.0;
293
294    let mut j = L;
295    while j > 0 {
296        j -= 1;
297        let (_, rj) = div2by1(r, u_shifted.as_limbs()[j].0, reciprocal);
298        r = rj;
299    }
300    Limb(r >> reciprocal.shift)
301}
302
303/// Divides the wide `u` by the divisor encoded in the `reciprocal`, and returns the remainder.
304#[inline(always)]
305pub(crate) const fn rem_limb_with_reciprocal_wide<const L: usize>(
306    lo_hi: (&Uint<L>, &Uint<L>),
307    reciprocal: &Reciprocal,
308) -> Limb {
309    let (lo_shifted, carry) = lo_hi.0.shl_limb(reciprocal.shift);
310    let (mut hi_shifted, xhi) = lo_hi.1.shl_limb(reciprocal.shift);
311    hi_shifted.limbs[0].0 |= carry.0;
312    let mut r = xhi.0;
313
314    let mut j = L;
315    while j > 0 {
316        j -= 1;
317        let (_, rj) = div2by1(r, hi_shifted.as_limbs()[j].0, reciprocal);
318        r = rj;
319    }
320    j = L;
321    while j > 0 {
322        j -= 1;
323        let (_, rj) = div2by1(r, lo_shifted.as_limbs()[j].0, reciprocal);
324        r = rj;
325    }
326    Limb(r >> reciprocal.shift)
327}
328
329/// Computes `(a * b) % d`.
330#[inline(always)]
331pub(crate) const fn mul_rem(a: Limb, b: Limb, d: NonZero<Limb>) -> Limb {
332    let rec = Reciprocal::new(d);
333    let (hi, lo) = mulhilo(a.0, b.0);
334    rem_limb_with_reciprocal(&Uint::from_words([lo, hi]), &rec)
335}
336
337#[cfg(test)]
338mod tests {
339    use super::{div2by1, Reciprocal};
340    use crate::{Limb, NonZero, Word};
341    #[test]
342    fn div2by1_overflow() {
343        // A regression test for a situation when in div2by1() an operation (`q1 + 1`)
344        // that is protected from overflowing by a condition in the original paper (`r >= d`)
345        // still overflows because we're calculating the results for both branches.
346        let r = Reciprocal::new(NonZero::new(Limb(Word::MAX - 1)).unwrap());
347        assert_eq!(
348            div2by1(Word::MAX - 2, Word::MAX - 63, &r),
349            (Word::MAX, Word::MAX - 65)
350        );
351    }
352}