Skip to main content

cmpa/
montgomery.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2023, 2026 SUSE LLC
3// Author: Nicolai Stange <nstange@suse.de>
4
5//! Implementation of Montgomery multiplication and related primitives.
6//!
7//! Montgomery multiplication, as introduced in Peter L. Montgomery, "Modular
8//! Multiplication Without Trial Division", Mathematics of Computation, vol. 44,
9//! no. 170, pp. 519-521, 1985, computes modular products without any trial
10//! divisions, for factors given in [Montgomery form](#montgomery-form). This
11//! makes it the building block of choice for algorithms involving many modular
12//! multiplications with the same modulus, like modular exponentiation.
13//!
14//! # Montgomery form
15//!
16//! For a given odd modulus *N*, the Montgomery form of an integer *a* is
17//! *a · R mod N*, where *R* is the [Montgomery
18//! radix](ct_montgomery_radix_shift_len), a power of two greater than *N*. For
19//! the purpose of this implementation, *R* is set to
20//! *2<sup>8 · `ct_montgomery_radix_shift_len(N.len())`</sup>*, where
21//! `ct_montgomery_radix_shift_len()` rounds *N*'s [length in units of
22//! bytes](MpUIntCommon::len) up to the next multiple of the [`LimbType`] size.
23//! That is, *R* is two to the power of the number of bits in the [`LimbType`]
24//! limbs spanned by *N*.
25//!
26//! The following primitives are provided for converting to and from the
27//! Montgomery form:
28//! - [`ct_to_montgomery_form_direct_mp()`] converts an integer to Montgomery
29//!   form directly, by means of a division.
30//! - [`ct_to_montgomery_form_mp()`] converts an integer to Montgomery form by a
31//!   Montgomery multiplication with the precomputed *R<sup>2</sup> mod N*, c.f.
32//!   [`ct_montgomery_radix2_mod_n_mp()`]. This is more efficient for converting
33//!   multiple integers for the same modulus.
34//! - [`ct_montgomery_redc_mp()`] converts an integer in Montgomery form back,
35//!   by means of a Montgomery reduction.
36
37use super::add::ct_sub_cond_mp_mp;
38use super::cmp::{ct_geq_mp_mp, ct_lt_mp_mp};
39use super::div::{
40    CtModLshiftedMpMpError, CtModPow2MpError, CtMpDivisor, CtMpDivisorError, ct_mod_lshifted_mp_mp,
41    ct_mod_pow2_mp,
42};
43use super::limb::{
44    LIMB_BITS, LimbChoice, LimbType, ct_add_l_l, ct_eq_l_l, ct_inv_mod_l, ct_lsb_mask_l,
45    ct_mul_add_l_l_l_c,
46};
47use super::limbs::{
48    MpMutNativeEndianUIntLimbsSlice, MpMutUInt, MpMutUIntSlice, MpUIntCommon,
49    ct_mp_limbs_align_len, ct_mp_nlimbs,
50};
51
52/// Determine the Montgomery radix's exponent in units of bytes.
53///
54/// For a modulus *N*, the Montgomery radix *R*, c.f. the section on the
55/// [Montgomery form](self#montgomery-form), is chosen as
56/// *2<sup>8 · `ct_montgomery_radix_shift_len(N.len())`</sup>* in this
57/// implementation. That is, return the modulus' [length in units of
58/// bytes](MpUIntCommon::len), rounded up to the next multiple of the
59/// [`LimbType`] size, i.e. the length of the [`LimbType`] limbs spanned by the
60/// modulus.
61///
62/// Runs in constant time.
63///
64/// # Arguments
65///
66/// * `n_len` - The modulus' [length in units of bytes](MpUIntCommon::len).
67pub fn ct_montgomery_radix_shift_len(n_len: usize) -> usize {
68    ct_mp_limbs_align_len(n_len)
69}
70
71/// Determine the Montgomery radix's exponent in units of limbs.
72///
73/// For a modulus *N*, the [Montgomery radix](ct_montgomery_radix_shift_len) is
74/// *R = 2<sup>[`LimbType::BITS`] · `ct_montgomery_radix_shift_mp_nlimbs(N.len())`</sup>*.
75/// That is, return the number of [`LimbType`] limbs spanned by the modulus. See
76/// [`ct_montgomery_radix_shift_len()`] for the exponent in units of bytes.
77///
78/// Runs in constant time.
79///
80/// # Arguments
81///
82/// * `n_len` - The modulus' [length in units of bytes](MpUIntCommon::len).
83fn ct_montgomery_radix_shift_mp_nlimbs(n_len: usize) -> usize {
84    ct_mp_nlimbs(n_len)
85}
86
87/// Error returned by [`ct_montgomery_neg_n0_inv_mod_l_mp()`].
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub enum CtMontgomeryNegN0InvModLMpError {
90    /// The modulus `n` is empty or even.
91    InvalidModulus,
92}
93
94/// Compute the negated inverse of a modulus' least significant limb.
95///
96/// Returns *-`n`<sub>0</sub><sup>-1</sup> mod 2<sup>[`LimbType::BITS`]</sup>*,
97/// where *`n`<sub>0</sub>* denotes `n`'s least significant limb. That is, the
98/// returned value's product with *`n`<sub>0</sub>* equals *-1* modulo the
99/// limb base. This value is needed by the Montgomery reduction and
100/// multiplication, c.f. [`ct_montgomery_redc_mp()`],
101/// [`ct_montgomery_mul_mod_mp_mp()`] and [`ct_to_montgomery_form_mp()`], and
102/// is to be computed once per modulus.
103///
104/// Runs in constant time.
105///
106/// # Arguments
107///
108/// * `n` - The modulus. Must be odd.
109///
110/// # Errors
111///
112/// Returns [`CtMontgomeryNegN0InvModLMpError::InvalidModulus`] if `n` is empty
113/// or even.
114pub fn ct_montgomery_neg_n0_inv_mod_l_mp<NT: MpUIntCommon>(
115    n: &NT,
116) -> Result<LimbType, CtMontgomeryNegN0InvModLMpError> {
117    if n.is_empty() {
118        return Err(CtMontgomeryNegN0InvModLMpError::InvalidModulus);
119    }
120    let n0 = n.load_l(0);
121    if ct_eq_l_l(n0 & 1, 0).unwrap() != 0 {
122        return Err(CtMontgomeryNegN0InvModLMpError::InvalidModulus);
123    }
124    let n0_inv_mod_l = ct_inv_mod_l(n0);
125    Ok((!n0_inv_mod_l).wrapping_add(1))
126}
127
128#[test]
129fn test_ct_montgomery_neg_n0_inv_mod_l_mp() {
130    use super::limb::LIMB_BYTES;
131    use super::limbs::{MpMutBigEndianUIntByteSlice, MpMutUIntSlicePriv as _};
132
133    for n0 in 0 as LimbType..128 {
134        let n0 = 2 * n0 + 1;
135        for j in 0..2048 {
136            const MERSENNE_PRIME_13: LimbType = 8191 as LimbType;
137            let v = MERSENNE_PRIME_13.wrapping_mul((511 as LimbType).wrapping_mul(j));
138            let v = v << 8;
139            let n0 = n0.wrapping_add(v);
140
141            let mut n0_buf: [u8; LIMB_BYTES] = [0; LIMB_BYTES];
142            let mut n = MpMutBigEndianUIntByteSlice::from_slice(n0_buf.as_mut_slice()).unwrap();
143            n.store_l(0, n0);
144            let neg_n0_inv = ct_montgomery_neg_n0_inv_mod_l_mp(&n).unwrap();
145            assert_eq!(n0.wrapping_mul(neg_n0_inv), !0);
146        }
147    }
148}
149
150/// Incremental, limb-wise Montgomery reduction step.
151///
152/// Carries out a single step of Montgomery reduction by
153/// *2<sup>e</sup>*, *e* ≤ [`LimbType::BITS`], on a multiprecision integer *t*
154/// modulo an odd *n*: compute *(t + m · n) / 2<sup>e</sup>*, with
155/// *m = t · (-n<sup>-1</sup>) mod 2<sup>e</sup>* chosen such that the division
156/// is exact. The result is congruent to *t · 2<sup>-e</sup>* modulo *n*. A full
157/// Montgomery reduction, c.f. [`ct_montgomery_redc_mp()`], consists of as many
158/// such steps with *e* = [`LimbType::BITS`] as there are limbs in the
159/// modulus.
160///
161/// The limbs of *t* and *n* are to be fed in from least to most significant,
162/// one at a time, and the result limbs are returned likewise. This allows for
163/// interleaving the reduction with other computations producing *t*'s limbs,
164/// like the multiplication in [`ct_montgomery_mul_mod_mp_mp()`]:
165/// - [`start()`](Self::start) consumes *t*'s and *n*'s least significant limbs.
166/// - Each subsequent [`update()`](Self::update) consumes the respective next
167///   limbs of *t* and *n* and returns the result's next limb.
168/// - [`finish()`](Self::finish) consumes the limb above the last one fed to
169///   [`update()`](Self::update), if any, and returns the result's most
170///   significant limb along with a carry.
171///
172/// Each step runs in constant time.
173pub(super) struct CtMontgomeryRedcKernel {
174    /// The right shift distance for moving a reduced limb's bits above
175    /// position *e* to the bottom of a result limb, i.e. *e* modulo
176    /// [`LimbType::BITS`].
177    redc_pow2_rshift: u32,
178    /// Mask of the *e* least significant bits.
179    redc_pow2_mask: LimbType,
180    /// The multiplier
181    /// *m = t<sub>0</sub> · (-n<sub>0</sub><sup>-1</sup>) mod 2<sup>e</sup>*,
182    /// where *t<sub>0</sub>* and *n<sub>0</sub>* denote *t*'s and *n*'s least
183    /// significant limbs.
184    m: LimbType,
185    /// The bits of the most recently reduced limb above position *e*, already
186    /// shifted to the bottom. They make up the lower part of the result limb
187    /// returned next.
188    last_redced_val: LimbType,
189    /// The carry of the running computation of *t + m · n*.
190    carry: LimbType,
191}
192
193impl CtMontgomeryRedcKernel {
194    /// Extract a reduced limb's bits above position *e*, shifted to the bottom.
195    ///
196    /// They make up the lower part of a result limb.
197    ///
198    /// # Arguments
199    ///
200    /// * `redc_pow2_rshift` - c.f.
201    ///   [`redc_pow2_rshift`](Self::redc_pow2_rshift).
202    /// * `redc_pow2_mask` - c.f. [`redc_pow2_mask`](Self::redc_pow2_mask).
203    /// * `val` - The reduced limb.
204    const fn redc_rshift_lo(
205        redc_pow2_rshift: u32,
206        redc_pow2_mask: LimbType,
207        val: LimbType,
208    ) -> LimbType {
209        // There are two possible cases where redc_pow2_rshift == 0:
210        // a.) redc_pow2_exp == 0. In this case !redc_pow2_mask == !0.
211        // b.) redc_pow2_exp == LIMB_BITS. In this case !redc_pow2_mask == !!0 == 0.
212        (val & !redc_pow2_mask) >> redc_pow2_rshift
213    }
214
215    /// Extract a reduced limb's *e* least significant bits, shifted to the top.
216    ///
217    /// They make up the upper part of the preceding result limb.
218    ///
219    /// # Arguments
220    ///
221    /// * `redc_pow2_rshift` - c.f.
222    ///   [`redc_pow2_rshift`](Self::redc_pow2_rshift).
223    /// * `redc_pow2_mask` - c.f. [`redc_pow2_mask`](Self::redc_pow2_mask).
224    /// * `val` - The reduced limb.
225    const fn redc_lshift_hi(
226        redc_pow2_rshift: u32,
227        redc_pow2_mask: LimbType,
228        val: LimbType,
229    ) -> LimbType {
230        // There are two possible cases where redc_pow2_rshift == 0:
231        // a.) redc_pow2_exp == 0. In this case redc_pow2_mask == 0 as well.
232        // b.) redc_pow2_exp == LIMB_BITS. In this case redc_pow2_mask == !0.
233        (val & redc_pow2_mask) << ((LIMB_BITS - redc_pow2_rshift) % LIMB_BITS)
234    }
235
236    /// Start a reduction step.
237    ///
238    /// Compute the multiplier *m* and consume *t*'s and *n*'s least
239    /// significant limbs.
240    ///
241    /// # Arguments
242    ///
243    /// * `redc_pow2_exp` - The exponent *e* of the power of two to reduce by.
244    ///   Must be at most [`LimbType::BITS`].
245    /// * `t0_val` - *t*'s least significant limb.
246    /// * `n0_val` - *n*'s least significant limb. Must be odd.
247    /// * `neg_n0_inv_mod_l` - The negated inverse of `n0_val` modulo
248    ///   *2<sup>[`LimbType::BITS`]</sup>*, as computed by
249    ///   [`ct_montgomery_neg_n0_inv_mod_l_mp()`].
250    pub fn start(
251        redc_pow2_exp: u32,
252        t0_val: LimbType,
253        n0_val: LimbType,
254        neg_n0_inv_mod_l: LimbType,
255    ) -> Self {
256        debug_assert!(redc_pow2_exp <= LIMB_BITS);
257
258        // Calculate the shift distance for right shifting a redced limb into its final
259        // position. Be careful to keep it < LIMB_BITS for not running into
260        // undefined behaviour with the shift.  See the comments in
261        // redc_rshift_lo()/redc_rshift_hi() for the mask<->rshift interaction.
262        let redc_pow2_rshift = redc_pow2_exp % LIMB_BITS;
263        let redc_pow2_mask = ct_lsb_mask_l(redc_pow2_exp);
264
265        // For any i >= j, if n' == -n^{-1} mod 2^i, then n' mod 2^j == -n^{-1} mod 2^j.
266        let neg_n0_inv_mod_l = neg_n0_inv_mod_l & redc_pow2_mask;
267        debug_assert_eq!(
268            neg_n0_inv_mod_l.wrapping_mul(n0_val) & redc_pow2_mask,
269            redc_pow2_mask
270        );
271
272        let m = t0_val.wrapping_mul(neg_n0_inv_mod_l) & redc_pow2_mask;
273
274        let (carry, redced_t0_val) = ct_mul_add_l_l_l_c(t0_val, m, n0_val, 0);
275        debug_assert_eq!(redced_t0_val & redc_pow2_mask, 0);
276        // If redc_pow2_exp < LIMB_BITS, the upper bits of the reduced zeroth limb
277        // will become the lower bits of the resulting zeroth limb.
278        let last_redced_val = Self::redc_rshift_lo(redc_pow2_rshift, redc_pow2_mask, redced_t0_val);
279
280        Self {
281            redc_pow2_rshift,
282            redc_pow2_mask,
283            m,
284            last_redced_val,
285            carry,
286        }
287    }
288
289    /// Feed the next pair of limbs and return the next result limb.
290    ///
291    /// The `j`'th invocation, counting from zero, consumes *t*'s and *n*'s
292    /// limbs at index `j + 1` and returns the result's limb at index `j`.
293    ///
294    /// # Arguments
295    ///
296    /// * `t_val` - *t*'s next limb.
297    /// * `n_val` - *n*'s next limb, or zero if *n* has no more limbs.
298    pub fn update(&mut self, t_val: LimbType, n_val: LimbType) -> LimbType {
299        let redced_t_val;
300        (self.carry, redced_t_val) = ct_mul_add_l_l_l_c(t_val, self.m, n_val, self.carry);
301
302        // If redc_pow2_exp < LIMB_BITS, the lower bits of the reduced current limb
303        // correspond to the upper bits of the returned result limb.
304        let result_val = self.last_redced_val
305            | Self::redc_lshift_hi(self.redc_pow2_rshift, self.redc_pow2_mask, redced_t_val);
306        // If redc_pow2_exp < LIMB_BITS, the upper bits of the reduced current limb
307        // will become the lower bits of the subsequently returned result limb.
308        self.last_redced_val =
309            Self::redc_rshift_lo(self.redc_pow2_rshift, self.redc_pow2_mask, redced_t_val);
310
311        result_val
312    }
313
314    /// Finish the reduction step.
315    ///
316    /// Consume *t*'s last limb, i.e. the one above the last one fed to
317    /// [`update()`](Self::update), and return a pair of the carry beyond the
318    /// result's most significant limb and that limb itself.
319    ///
320    /// # Arguments
321    ///
322    /// * `t_val` - *t*'s last limb, which, for a full Montgomery reduction,
323    ///   is usually the carry returned from the preceding reduction step.
324    pub fn finish(self, t_val: LimbType) -> (LimbType, LimbType) {
325        let (carry_hi, redced_t_val) = ct_add_l_l(t_val, self.carry);
326        let carry_hi = Self::redc_lshift_hi(self.redc_pow2_rshift, self.redc_pow2_mask, carry_hi);
327        let carry_lo =
328            Self::redc_rshift_lo(self.redc_pow2_rshift, self.redc_pow2_mask, redced_t_val);
329        (
330            carry_hi | carry_lo,
331            self.last_redced_val
332                | Self::redc_lshift_hi(self.redc_pow2_rshift, self.redc_pow2_mask, redced_t_val),
333        )
334    }
335}
336
337/// Error returned by [`ct_montgomery_redc_mp()`].
338#[derive(Clone, Copy, Debug, PartialEq, Eq)]
339pub enum CtMontgomeryRedcMpError {
340    /// The modulus `n` is empty or even.
341    InvalidModulus,
342    /// The modulus' [length in units of bytes](MpUIntCommon::len) doesn't [fit
343    /// into](MpUIntCommon::len_is_compatible_with) `t`'s.
344    InsufficientResultSpace,
345    /// `t`'s [length in units of bytes](MpUIntCommon::len) doesn't [fit
346    /// into](MpUIntCommon::len_is_compatible_with) twice the modulus'.
347    InputValueOutOfRange,
348}
349
350/// Montgomery reduction, converting from Montgomery form.
351///
352/// The multiprecision integers may be of different representations.
353///
354/// Replace `t` by *`t` · R<sup>-1</sup> mod `n`*, where *R* is the [Montgomery
355/// radix](ct_montgomery_radix_shift_len), c.f. the section on the [Montgomery
356/// form](self#montgomery-form). The result is less than `n`. In particular, for
357/// `t` in [Montgomery form](self#montgomery-form), i.e. *`t` = a · R mod `n`*
358/// for some *a*, this converts back to *a mod `n`*. Likewise, applied to the
359/// product of two integers in [Montgomery form](self#montgomery-form), it
360/// yields their product in [Montgomery form](self#montgomery-form).
361///
362/// Execution time depends only on the multiprecision integers' [lengths in
363/// units of bytes](MpUIntCommon::len) and is otherwise constant as far as
364/// branching is concerned. The computation cost is O(m · n), where m is `t`'s
365/// and n is `n`'s [length](MpUIntCommon::len). See
366/// [`ct_to_montgomery_form_mp()`] and [`ct_to_montgomery_form_direct_mp()`] for
367/// converting to [Montgomery form](self#montgomery-form).
368///
369/// # Arguments
370///
371/// * `t` - The multiprecision integer to reduce. Its value must be less than
372///   *`n` · R*, which holds e.g. for the product of two integers less than `n`.
373///   Its [length in units of bytes](MpUIntCommon::len) must [fit
374///   into](MpUIntCommon::len_is_compatible_with) twice `n`'s. It will be
375///   overwritten by the result.
376/// * `n` - The modulus. Must be odd. Its [length in units of
377///   bytes](MpUIntCommon::len) must [fit
378///   into](MpUIntCommon::len_is_compatible_with) `t`'s.
379/// * `neg_n0_inv_mod_l` - The negated inverse of `n`'s least significant limb
380///   modulo *2<sup>[`LimbType::BITS`]</sup>*, as computed by
381///   [`ct_montgomery_neg_n0_inv_mod_l_mp()`].
382///
383/// # Errors
384///
385/// - Returns [`CtMontgomeryRedcMpError::InvalidModulus`] if `n` is empty or
386///   even.
387/// - Returns [`CtMontgomeryRedcMpError::InsufficientResultSpace`] if `n`'s
388///   length doesn't fit into `t`'s.
389/// - Returns [`CtMontgomeryRedcMpError::InputValueOutOfRange`] if `t`'s length
390///   doesn't fit into twice `n`'s.
391///
392/// Nothing gets modified in any of these cases.
393pub fn ct_montgomery_redc_mp<TT: MpMutUInt, NT: MpUIntCommon>(
394    t: &mut TT,
395    n: &NT,
396    neg_n0_inv_mod_l: LimbType,
397) -> Result<(), CtMontgomeryRedcMpError> {
398    if n.is_empty() {
399        return Err(CtMontgomeryRedcMpError::InvalidModulus);
400    }
401    let n0_val = n.load_l(0);
402    if ct_eq_l_l(n0_val & 1, 0).unwrap() != 0 {
403        return Err(CtMontgomeryRedcMpError::InvalidModulus);
404    }
405    if !n.len_is_compatible_with(t.len()) {
406        return Err(CtMontgomeryRedcMpError::InsufficientResultSpace);
407    }
408    if !t.len_is_compatible_with(2 * n.len()) {
409        return Err(CtMontgomeryRedcMpError::InputValueOutOfRange);
410    }
411    let t_nlimbs = t.nlimbs();
412    let n_nlimbs = n.nlimbs();
413    debug_assert!(n0_val.wrapping_mul(neg_n0_inv_mod_l) == !0);
414
415    let mut reduced_t_carry = 0;
416    // t's high limb might be a partial one, do not update directly in the course of
417    // reducing in order to avoid overflowing it. Use a shadow instead.
418    let mut t_high_shadow = t.load_l(t_nlimbs - 1);
419    for _i in 0..ct_montgomery_radix_shift_mp_nlimbs(n.len()) {
420        let mut redc_kernel =
421            CtMontgomeryRedcKernel::start(LIMB_BITS, t.load_l(0), n0_val, neg_n0_inv_mod_l);
422        let mut j = 0;
423        while j + 2 < n_nlimbs {
424            debug_assert!(j < t_nlimbs - 1);
425            t.store_l_full(
426                j,
427                redc_kernel.update(t.load_l_full(j + 1), n.load_l_full(j + 1)),
428            );
429            j += 1;
430        }
431
432        if n_nlimbs != 1 {
433            debug_assert_eq!(j + 2, n_nlimbs);
434            // Do not read the potentially partial, stale high limb directly from t, use the
435            // t_high_shadow shadow instead.
436            let t_val = if j + 2 != t_nlimbs {
437                t.load_l_full(j + 1)
438            } else {
439                t_high_shadow
440            };
441            t.store_l_full(j, redc_kernel.update(t_val, n.load_l(j + 1)));
442            j += 1;
443        }
444
445        while j + 2 < t_nlimbs {
446            t.store_l_full(j, redc_kernel.update(t.load_l_full(j + 1), 0));
447            j += 1;
448        }
449        if j + 1 == t_nlimbs - 1 {
450            t.store_l_full(j, redc_kernel.update(t_high_shadow, 0));
451            j += 1;
452        }
453        debug_assert_eq!(j, t_nlimbs - 1);
454
455        (reduced_t_carry, t_high_shadow) = redc_kernel.finish(reduced_t_carry);
456        debug_assert!(reduced_t_carry <= 1);
457    }
458
459    // Now apply the high limb shadow back.
460    let t_high_shadow_mask = t.partial_high_mask();
461    let t_high_shadow_shift = t.partial_high_shift();
462    assert!(t_high_shadow_shift == 0 || reduced_t_carry == 0);
463    reduced_t_carry |= (t_high_shadow & !t_high_shadow_mask) >> t_high_shadow_shift;
464    t_high_shadow &= t_high_shadow_mask;
465    t.store_l(t_nlimbs - 1, t_high_shadow);
466
467    ct_sub_cond_mp_mp(t, n, LimbChoice::from(reduced_t_carry) | ct_geq_mp_mp(t, n));
468    debug_assert!(ct_geq_mp_mp(t, n).unwrap() == 0);
469    Ok(())
470}
471
472#[cfg(test)]
473fn test_ct_montgomery_redc_mp<TT: MpMutUIntSlice, NT: MpMutUIntSlice>() {
474    use super::div::ct_mod_mp_mp;
475    use super::limb::LIMB_BYTES;
476
477    for i in 0..64 {
478        const MERSENNE_PRIME_13: LimbType = 8191 as LimbType;
479        let n_high = MERSENNE_PRIME_13.wrapping_mul((16385 as LimbType).wrapping_mul(i));
480        for j in 0..64 {
481            const MERSENNE_PRIME_17: LimbType = 131071 as LimbType;
482            let n_low = MERSENNE_PRIME_17.wrapping_mul((1023 as LimbType).wrapping_mul(j));
483            // Force n_low odd.
484            let n_low = n_low | 1;
485            let mut n = tst_mk_mp_backing_vec!(NT, 2 * LIMB_BYTES);
486            let mut n = NT::from_slice(n.as_mut_slice()).unwrap();
487            n.store_l(0, n_low);
488            n.store_l(1, n_high);
489            let neg_n0_inv = ct_montgomery_neg_n0_inv_mod_l_mp(&n).unwrap();
490
491            for k in 0..8 {
492                let t_high = MERSENNE_PRIME_17.wrapping_mul((8191 as LimbType).wrapping_mul(k));
493                for l in 0..8 {
494                    let t_low =
495                        MERSENNE_PRIME_13.wrapping_mul((131087 as LimbType).wrapping_mul(l));
496
497                    let mut t = tst_mk_mp_backing_vec!(TT, 2 * LIMB_BYTES);
498                    let mut t = TT::from_slice(t.as_mut_slice()).unwrap();
499                    t.store_l(0, t_low);
500                    t.store_l(1, t_high);
501
502                    // All montgomery operations are defined mod n, compute t mod n
503                    ct_mod_mp_mp(None, &mut t, &CtMpDivisor::new(&n, None).unwrap());
504                    let t_low = t.load_l(0);
505                    let t_high = t.load_l(1);
506
507                    // To Montgomery form: t * R mod n
508                    ct_to_montgomery_form_direct_mp(&mut t, &n).unwrap();
509
510                    // And back to normal: (t * R mod n) / R mod n
511                    ct_montgomery_redc_mp(&mut t, &n, neg_n0_inv).unwrap();
512                    assert_eq!(t.load_l(0), t_low);
513                    assert_eq!(t.load_l(1), t_high);
514                }
515            }
516        }
517    }
518}
519
520#[test]
521fn test_ct_montgomery_redc_be_be() {
522    use super::limbs::MpMutBigEndianUIntByteSlice;
523    test_ct_montgomery_redc_mp::<MpMutBigEndianUIntByteSlice, MpMutBigEndianUIntByteSlice>()
524}
525
526#[test]
527fn test_ct_montgomery_redc_le_le() {
528    use super::limbs::MpMutLittleEndianUIntByteSlice;
529    test_ct_montgomery_redc_mp::<MpMutLittleEndianUIntByteSlice, MpMutLittleEndianUIntByteSlice>()
530}
531
532#[test]
533fn test_ct_montgomery_redc_ne_ne() {
534    use super::limbs::MpMutNativeEndianUIntLimbsSlice;
535    test_ct_montgomery_redc_mp::<MpMutNativeEndianUIntLimbsSlice, MpMutNativeEndianUIntLimbsSlice>()
536}
537
538/// Error returned by [`ct_montgomery_mul_mod_mp_mp()`].
539#[derive(Clone, Copy, Debug, PartialEq, Eq)]
540pub enum CtMontgomeryMulModMpMpError {
541    /// The modulus `n` is empty or even.
542    InvalidModulus,
543    /// The modulus' [length in units of bytes](MpUIntCommon::len) doesn't [fit
544    /// into](MpUIntCommon::len_is_compatible_with) `result`'s.
545    InsufficientResultSpace,
546    /// `op0`'s or `op1`'s [length in units of bytes](MpUIntCommon::len) doesn't
547    /// [fit into](MpUIntCommon::len_is_compatible_with) the modulus'.
548    InconsistentOperandLengths,
549}
550
551/// Montgomery multiplication.
552///
553/// The multiprecision integers may be of different representations.
554///
555/// Compute *`op0` · `op1` · R<sup>-1</sup> mod `n`*, where *R* is the
556/// [Montgomery radix](ct_montgomery_radix_shift_len), and store it in `result`,
557/// with any of `result`'s bytes above `n`'s length cleared. The result is less
558/// than `n`. For factors in [Montgomery form](self#montgomery-form), i.e.
559/// *`op0` = a · R mod `n`* and *`op1` = b · R mod `n`*, the result is the
560/// product's [Montgomery form](self#montgomery-form), *a · b · R mod `n`*.
561///
562/// The multiplication and the Montgomery reduction are fused, following the
563/// "Finely Integrated Operand Scanning (FIOS)" method from Ç. K. Koç, T. Acar
564/// and B. S. Kaliski, "Analyzing and Comparing Montgomery Multiplication
565/// Algorithms", IEEE Micro, 16(3):26-33, June 1996.
566///
567/// Execution time depends only on the multiprecision integers' [lengths in
568/// units of bytes](MpUIntCommon::len) and is otherwise constant as far as
569/// branching is concerned. The computation cost is O(n<sup>2</sup>), where n
570/// is `n`'s [length](MpUIntCommon::len). See [`ct_to_montgomery_form_mp()`]
571/// and [`ct_to_montgomery_form_direct_mp()`] for converting the factors to,
572/// and [`ct_montgomery_redc_mp()`] for converting the result back from
573/// [Montgomery form](self#montgomery-form).
574///
575/// # Arguments
576///
577/// * `result` - The output for the result. Its [length in units of
578///   bytes](MpUIntCommon::len) must be such that `n`'s [fits
579///   into](MpUIntCommon::len_is_compatible_with) it.
580/// * `op0` - The first factor, usually in [Montgomery
581///   form](self#montgomery-form). Must be less than `n`. Its [length in units
582///   of bytes](MpUIntCommon::len) must [fit
583///   into](MpUIntCommon::len_is_compatible_with) `n`'s.
584/// * `op1` - The second factor, usually in [Montgomery
585///   form](self#montgomery-form). Must be less than `n`. Its [length in units
586///   of bytes](MpUIntCommon::len) must [fit
587///   into](MpUIntCommon::len_is_compatible_with) `n`'s.
588/// * `n` - The modulus. Must be odd.
589/// * `neg_n0_inv_mod_l` - The negated inverse of `n`'s least significant limb
590///   modulo *2<sup>[`LimbType::BITS`]</sup>*, as computed by
591///   [`ct_montgomery_neg_n0_inv_mod_l_mp()`].
592///
593/// # Errors
594///
595/// - Returns [`CtMontgomeryMulModMpMpError::InvalidModulus`] if `n` is empty
596///   or even.
597/// - Returns [`CtMontgomeryMulModMpMpError::InsufficientResultSpace`] if `n`'s
598///   length doesn't fit into `result`'s.
599/// - Returns [`CtMontgomeryMulModMpMpError::InconsistentOperandLengths`] if
600///   `op0`'s or `op1`'s length doesn't fit into `n`'s.
601///
602/// Nothing gets modified in any of these cases.
603pub fn ct_montgomery_mul_mod_mp_mp<
604    RT: MpMutUIntSlice,
605    T0: MpUIntCommon,
606    T1: MpUIntCommon,
607    NT: MpUIntCommon,
608>(
609    result: &mut RT,
610    op0: &T0,
611    op1: &T1,
612    n: &NT,
613    neg_n0_inv_mod_l: LimbType,
614) -> Result<(), CtMontgomeryMulModMpMpError> {
615    // This is an implementation of the "Finely Integrated Operand Scanning (FIOS)
616    // Method" approach to fused multiplication and Montgomery reduction, as
617    // described in "Analyzing and Comparing Montgomery Multiplication
618    // Algorithm", IEEE Micro, 16(3):26-33, June 1996.
619    if n.is_empty() {
620        return Err(CtMontgomeryMulModMpMpError::InvalidModulus);
621    }
622    let n0_val = n.load_l(0);
623    if ct_eq_l_l(n0_val & 1, 0).unwrap() != 0 {
624        return Err(CtMontgomeryMulModMpMpError::InvalidModulus);
625    }
626    if !n.len_is_compatible_with(result.len()) {
627        return Err(CtMontgomeryMulModMpMpError::InsufficientResultSpace);
628    }
629    debug_assert!(n.nlimbs() <= result.nlimbs());
630    if !op0.len_is_compatible_with(n.len()) || !op1.len_is_compatible_with(n.len()) {
631        return Err(CtMontgomeryMulModMpMpError::InconsistentOperandLengths);
632    }
633    debug_assert!(op0.nlimbs() <= n.nlimbs());
634    debug_assert!(ct_lt_mp_mp(op0, n).unwrap() != 0);
635    debug_assert!(op1.nlimbs() <= n.nlimbs());
636    debug_assert!(ct_lt_mp_mp(op1, n).unwrap() != 0);
637
638    let op0_nlimbs = op0.nlimbs();
639    let op1_nlimbs = op1.nlimbs();
640    let n_nlimbs = n.nlimbs();
641    debug_assert!(n0_val.wrapping_mul(neg_n0_inv_mod_l) == !0);
642
643    result.clear_bytes_above(0);
644    let mut result = result.shrink_to(n.len());
645    debug_assert_eq!(result.nlimbs(), n.nlimbs());
646    let mut result_carry = 0;
647    // result's high limb might be a partial one, do not update directly in the
648    // course of reducing in order to avoid overflowing it. Use a shadow
649    // instead.
650    let mut result_high_shadow = 0;
651    for i in 0..op0_nlimbs {
652        debug_assert!(result_carry <= 1); // Loop invariant.
653        let op0_val = op0.load_l(i);
654
655        // Do not read the potentially partial, stale high limb directly from result,
656        // use the result_high_shadow shadow instead.
657        let result_val = if n_nlimbs != 1 {
658            result.load_l_full(0)
659        } else {
660            result_high_shadow
661        };
662        let op1_val = op1.load_l(0);
663        let (mut op0_op1_add_carry, result_val) =
664            ct_mul_add_l_l_l_c(result_val, op0_val, op1_val, 0);
665
666        let mut redc_kernel =
667            CtMontgomeryRedcKernel::start(LIMB_BITS, result_val, n0_val, neg_n0_inv_mod_l);
668
669        let mut j = 0;
670        while j + 1 < op1_nlimbs {
671            let op1_val = op1.load_l(j + 1);
672
673            // Do not read the potentially partial, stale high limb directly from result,
674            // use the result_high_shadow shadow instead.
675            let mut result_val = if j + 1 != n_nlimbs - 1 {
676                result.load_l_full(j + 1)
677            } else {
678                result_high_shadow
679            };
680
681            (op0_op1_add_carry, result_val) =
682                ct_mul_add_l_l_l_c(result_val, op0_val, op1_val, op0_op1_add_carry);
683
684            let n_val = n.load_l(j + 1);
685            let result_val = redc_kernel.update(result_val, n_val);
686            result.store_l_full(j, result_val);
687            j += 1;
688        }
689        debug_assert_eq!(j + 1, op1_nlimbs);
690
691        // If op1_nlimbs < n_nlimbs, handle the rest by propagating the multiplication
692        // carry and continue redcing.
693        while j + 1 < n_nlimbs {
694            // Do not read the potentially partial, stale high limb directly from result,
695            // use the result_high_shadow shadow instead.
696            let mut result_val = if j + 1 != n_nlimbs - 1 {
697                result.load_l_full(j + 1)
698            } else {
699                result_high_shadow
700            };
701
702            (op0_op1_add_carry, result_val) = ct_add_l_l(result_val, op0_op1_add_carry);
703
704            let n_val = n.load_l(j + 1);
705            let result_val = redc_kernel.update(result_val, n_val);
706            result.store_l_full(j, result_val);
707            j += 1;
708        }
709        debug_assert_eq!(j + 1, n_nlimbs);
710
711        let mut result_val;
712        debug_assert!(result_carry <= 1);
713        (result_carry, result_val) = ct_add_l_l(result_carry, op0_op1_add_carry);
714        debug_assert!(result_carry <= 1);
715        debug_assert!(result_carry == 0 || result_val == 0);
716
717        (result_carry, result_val) = redc_kernel.finish(result_val);
718        debug_assert!(result_carry <= 1);
719        result_high_shadow = result_val;
720    }
721
722    // If op0_nlimbs < the montgomery radix shift distance, handle the rest by
723    // REDCing it.
724    for _i in op0_nlimbs..ct_montgomery_radix_shift_mp_nlimbs(n.len()) {
725        // Do not read the potentially partial, stale high limb directly from result,
726        // use the result_high_shadow shadow instead.
727        let result_val = if n_nlimbs != 1 {
728            result.load_l_full(0)
729        } else {
730            result_high_shadow
731        };
732
733        let mut redc_kernel =
734            CtMontgomeryRedcKernel::start(LIMB_BITS, result_val, n0_val, neg_n0_inv_mod_l);
735
736        let mut j = 0;
737        while j + 1 < n_nlimbs {
738            // Do not read the potentially partial, stale high limb directly from result,
739            // use the result_high_shadow shadow instead.
740            let result_val = if j + 1 != n_nlimbs - 1 {
741                result.load_l_full(j + 1)
742            } else {
743                result_high_shadow
744            };
745
746            let n_val = n.load_l(j + 1);
747            let result_val = redc_kernel.update(result_val, n_val);
748            result.store_l_full(j, result_val);
749            j += 1;
750        }
751        debug_assert_eq!(j + 1, n_nlimbs);
752
753        (result_carry, result_high_shadow) = redc_kernel.finish(result_carry);
754        debug_assert!(result_carry <= 1);
755    }
756
757    // Now apply the high limb shadow back.
758    debug_assert!(result.nlimbs() == n.nlimbs());
759    let result_high_shadow_mask = n.partial_high_mask();
760    let result_high_shadow_shift = n.partial_high_shift();
761    assert!(result_high_shadow_shift == 0 || result_carry == 0);
762    result_carry |= (result_high_shadow & !result_high_shadow_mask) >> result_high_shadow_shift;
763    result_high_shadow &= result_high_shadow_mask;
764    result.store_l(n_nlimbs - 1, result_high_shadow);
765
766    let result_geq_n = LimbChoice::from(result_carry) | ct_geq_mp_mp(&result, n);
767    ct_sub_cond_mp_mp(&mut result, n, result_geq_n);
768    debug_assert!(ct_geq_mp_mp(&result, n).unwrap() == 0);
769    Ok(())
770}
771
772#[cfg(test)]
773fn test_ct_montgomery_mul_mod_mp_mp<
774    RT: MpMutUIntSlice,
775    T0: MpMutUIntSlice,
776    T1: MpMutUIntSlice,
777    NT: MpMutUIntSlice,
778>() {
779    use super::div::ct_mod_mp_mp;
780    use super::limb::LIMB_BYTES;
781    use super::mul::ct_mul_trunc_mp_mp;
782
783    for i in 0..16 {
784        const MERSENNE_PRIME_13: LimbType = 8191 as LimbType;
785        let n_high = MERSENNE_PRIME_13.wrapping_mul((65543 as LimbType).wrapping_mul(i));
786        for j in 0..16 {
787            const MERSENNE_PRIME_17: LimbType = 131071 as LimbType;
788            let n_low = MERSENNE_PRIME_17.wrapping_mul((4095 as LimbType).wrapping_mul(j));
789            // Force n_low odd.
790            let n_low = n_low | 1;
791            let mut n_buf = tst_mk_mp_backing_vec!(NT, 2 * LIMB_BYTES);
792            let mut n = NT::from_slice(n_buf.as_mut_slice()).unwrap();
793            n.store_l(0, n_low);
794            n.store_l(1, n_high);
795            drop(n);
796            let n_lengths = if !RT::SUPPORTS_UNALIGNED_BUFFER_LENGTHS
797                || !T0::SUPPORTS_UNALIGNED_BUFFER_LENGTHS
798                || !T1::SUPPORTS_UNALIGNED_BUFFER_LENGTHS
799                || !NT::SUPPORTS_UNALIGNED_BUFFER_LENGTHS
800            {
801                [LIMB_BYTES, 2 * LIMB_BYTES]
802            } else {
803                [2 * LIMB_BYTES - 1, 2 * LIMB_BYTES]
804            };
805            for n_len in n_lengths {
806                let mut n_buf = n_buf.clone();
807                let mut n = NT::from_slice(n_buf.as_mut_slice()).unwrap();
808                n.clear_bytes_above(n_len);
809                let n = n.shrink_to(n_len);
810                let neg_n0_inv = ct_montgomery_neg_n0_inv_mod_l_mp(&n).unwrap();
811
812                // r_mod_n = 2^(2 * LIMB_BITS) % n.
813                let mut r_mod_n = tst_mk_mp_backing_vec!(RT, 3 * LIMB_BYTES);
814                let mut r_mod_n = RT::from_slice(r_mod_n.as_mut_slice()).unwrap();
815                r_mod_n.store_l_full(ct_montgomery_radix_shift_mp_nlimbs(n_len), 1);
816                ct_mod_mp_mp(None, &mut r_mod_n, &CtMpDivisor::new(&n, None).unwrap());
817                let r_mod_n = r_mod_n.shrink_to(n.len());
818
819                for k in 0..4 {
820                    let a_high =
821                        MERSENNE_PRIME_17.wrapping_mul((16383 as LimbType).wrapping_mul(k));
822                    for l in 0..4 {
823                        let a_low =
824                            MERSENNE_PRIME_13.wrapping_mul((262175 as LimbType).wrapping_mul(l));
825                        let mut a_buf = tst_mk_mp_backing_vec!(T0, 2 * LIMB_BYTES);
826                        let mut a = T0::from_slice(a_buf.as_mut_slice()).unwrap();
827                        a.store_l(0, a_low);
828                        a.store_l(1, a_high);
829                        // All montgomery operations are defined mod n, compute a mod n
830                        ct_mod_mp_mp(None, &mut a, &CtMpDivisor::new(&n, None).unwrap());
831                        drop(a);
832                        for s in 0..4 {
833                            let b_high = MERSENNE_PRIME_13
834                                .wrapping_mul((262175 as LimbType).wrapping_mul(s));
835                            for t in 0..4 {
836                                const MERSENNE_PRIME_19: LimbType = 524287 as LimbType;
837                                let b_low = MERSENNE_PRIME_19
838                                    .wrapping_mul((4095 as LimbType).wrapping_mul(t));
839                                let mut b_buf = tst_mk_mp_backing_vec!(T1, 2 * LIMB_BYTES);
840                                let mut b = T1::from_slice(b_buf.as_mut_slice()).unwrap();
841                                b.store_l(0, b_low);
842                                b.store_l(1, b_high);
843                                // All montgomery operations are defined mod n, compute b mod n
844                                ct_mod_mp_mp(None, &mut b, &CtMpDivisor::new(&n, None).unwrap());
845                                drop(b);
846
847                                for op_len in [0, LIMB_BYTES, n_len] {
848                                    let mut a_buf = a_buf.clone();
849                                    let mut a = T0::from_slice(a_buf.as_mut_slice()).unwrap();
850                                    a.clear_bytes_above(op_len);
851                                    let a = a.shrink_to(op_len);
852                                    let mut b_buf = b_buf.clone();
853                                    let mut b = T1::from_slice(b_buf.as_mut_slice()).unwrap();
854                                    b.clear_bytes_above(op_len);
855                                    let b = b.shrink_to(op_len);
856
857                                    let mut _result = tst_mk_mp_backing_vec!(RT, 4 * LIMB_BYTES);
858                                    let mut result =
859                                        RT::from_slice(_result.as_mut_slice()).unwrap();
860                                    let mut mg_mul_result = result.shrink_to(n_len);
861                                    ct_montgomery_mul_mod_mp_mp(
862                                        &mut mg_mul_result,
863                                        &a,
864                                        &b,
865                                        &n,
866                                        neg_n0_inv,
867                                    )
868                                    .unwrap();
869                                    drop(mg_mul_result);
870
871                                    // For testing against the expected result computed using the
872                                    // "conventional" methods only, multiply by r_mod_n -- this
873                                    // avoids having to multiply
874                                    // the conventional product by r^-1 mod n, which is
875                                    // not known without implementing Euklid's algorithm.
876                                    ct_mul_trunc_mp_mp(&mut result, n.len(), &r_mod_n);
877                                    ct_mod_mp_mp(
878                                        None,
879                                        &mut result,
880                                        &CtMpDivisor::new(&n, None).unwrap(),
881                                    );
882                                    drop(result);
883
884                                    let mut _expected = tst_mk_mp_backing_vec!(RT, 4 * LIMB_BYTES);
885                                    let mut expected =
886                                        RT::from_slice(_expected.as_mut_slice()).unwrap();
887                                    expected.copy_from(&a);
888                                    ct_mul_trunc_mp_mp(&mut expected, op_len, &b);
889                                    ct_mod_mp_mp(
890                                        None,
891                                        &mut expected,
892                                        &CtMpDivisor::new(&n, None).unwrap(),
893                                    );
894                                    drop(expected);
895
896                                    assert_eq!(_result, _expected);
897                                }
898                            }
899                        }
900                    }
901                }
902            }
903        }
904    }
905}
906
907#[test]
908fn test_ct_montgomery_mul_mod_be_be_be_be() {
909    use super::limbs::MpMutBigEndianUIntByteSlice;
910    test_ct_montgomery_mul_mod_mp_mp::<
911        MpMutBigEndianUIntByteSlice,
912        MpMutBigEndianUIntByteSlice,
913        MpMutBigEndianUIntByteSlice,
914        MpMutBigEndianUIntByteSlice,
915    >()
916}
917
918#[test]
919fn test_ct_montgomery_mul_mod_le_le_le_le() {
920    use super::limbs::MpMutLittleEndianUIntByteSlice;
921    test_ct_montgomery_mul_mod_mp_mp::<
922        MpMutLittleEndianUIntByteSlice,
923        MpMutLittleEndianUIntByteSlice,
924        MpMutLittleEndianUIntByteSlice,
925        MpMutLittleEndianUIntByteSlice,
926    >()
927}
928
929#[test]
930fn test_ct_montgomery_mul_mod_ne_ne_ne_ne() {
931    use super::limbs::MpMutNativeEndianUIntLimbsSlice;
932    test_ct_montgomery_mul_mod_mp_mp::<
933        MpMutNativeEndianUIntLimbsSlice,
934        MpMutNativeEndianUIntLimbsSlice,
935        MpMutNativeEndianUIntLimbsSlice,
936        MpMutNativeEndianUIntLimbsSlice,
937    >()
938}
939
940/// Error returned by [`ct_to_montgomery_form_direct_mp()`] and
941/// [`ct_montgomery_radix2_mod_n_mp()`].
942#[derive(Clone, Copy, Debug, PartialEq, Eq)]
943pub enum CtMontgomeryTransformationError {
944    /// The modulus `n` is even, including the case of an empty one.
945    InvalidModulus,
946    /// The modulus' [length in units of bytes](MpUIntCommon::len) doesn't [fit
947    /// into](MpUIntCommon::len_is_compatible_with) the result's.
948    InsufficientResultSpace,
949}
950
951/// Convert a multiprecision integer to Montgomery form by division.
952///
953/// The multiprecision integers may be of different representations.
954///
955/// Replace `t` by its [Montgomery form](self#montgomery-form),
956/// *`t` · R mod `n`*, where *R* is the [Montgomery
957/// radix](ct_montgomery_radix_shift_len). The result is less than `n`. `t`'s
958/// value isn't restricted, in particular it may exceed `n`.
959///
960/// The conversion is carried out directly, by means of a division. See
961/// [`ct_to_montgomery_form_mp()`] for a variant based on Montgomery
962/// multiplication with a precomputed *R<sup>2</sup> mod `n`*, which is more
963/// efficient when converting multiple integers for the same modulus, and
964/// [`ct_montgomery_redc_mp()`] for converting back.
965///
966/// Execution time depends only on the multiprecision integers' [lengths in
967/// units of bytes](MpUIntCommon::len) as well as on `n`'s length without any
968/// leading zero bytes, c.f. [`CtMpDivisor`], and is otherwise constant as far
969/// as branching is concerned. The computation cost is O((m + n) · n), where m
970/// is `t`'s and n is `n`'s [length](MpUIntCommon::len).
971///
972/// # Arguments
973///
974/// * `t` - The multiprecision integer to convert. It will be overwritten by the
975///   result.
976/// * `n` - The modulus. Must be odd. Its [length in units of
977///   bytes](MpUIntCommon::len) must [fit
978///   into](MpUIntCommon::len_is_compatible_with) `t`'s.
979///
980/// # Errors
981///
982/// - Returns [`CtMontgomeryTransformationError::InvalidModulus`] if `n` is
983///   even.
984/// - Returns [`CtMontgomeryTransformationError::InsufficientResultSpace`] if
985///   `n`'s length doesn't fit into `t`'s.
986///
987/// Nothing gets modified in either case.
988pub fn ct_to_montgomery_form_direct_mp<TT: MpMutUInt, NT: MpUIntCommon>(
989    t: &mut TT,
990    n: &NT,
991) -> Result<(), CtMontgomeryTransformationError> {
992    if n.test_bit(0).unwrap() == 0 {
993        return Err(CtMontgomeryTransformationError::InvalidModulus);
994    }
995    if !n.len_is_compatible_with(t.len()) {
996        return Err(CtMontgomeryTransformationError::InsufficientResultSpace);
997    }
998    debug_assert!(t.nlimbs() >= n.nlimbs());
999    let radix_shift_len = ct_montgomery_radix_shift_len(n.len());
1000    let n = CtMpDivisor::new(n, None).map_err(|e| match e {
1001        CtMpDivisorError::DivisorIsZero => {
1002            // n had been checked for being odd above, so should be unreachable, but play
1003            // safe.
1004            debug_assert!(false);
1005            CtMontgomeryTransformationError::InvalidModulus
1006        }
1007    })?;
1008    ct_mod_lshifted_mp_mp(t, t.len(), radix_shift_len, &n).map_err(|e| match e {
1009        CtModLshiftedMpMpError::InsufficientRemainderSpace => {
1010            // The result space had been checked at function entry already, but play safe.
1011            debug_assert!(false);
1012            CtMontgomeryTransformationError::InsufficientResultSpace
1013        }
1014    })?;
1015    Ok(())
1016}
1017
1018/// Compute the square of the Montgomery radix modulo a multiprecision integer.
1019///
1020/// The multiprecision integers may be of different representations.
1021///
1022/// Compute *R<sup>2</sup> mod `n`*, where *R* is the [Montgomery
1023/// radix](ct_montgomery_radix_shift_len), and store it in `radix2_mod_n_out`,
1024/// with any of its bytes above `n`'s length cleared. The result is less than
1025/// `n`. It's to be computed once per modulus and serves as input to
1026/// [`ct_to_montgomery_form_mp()`] for converting integers to [Montgomery
1027/// form](self#montgomery-form).
1028///
1029/// Execution time depends only on the multiprecision integers' [lengths in
1030/// units of bytes](MpUIntCommon::len) as well as on `n`'s length without any
1031/// leading zero bytes, c.f. [`CtMpDivisor`], and is otherwise constant as far
1032/// as branching is concerned. The computation cost is O(n<sup>2</sup>), where
1033/// n is `n`'s [length](MpUIntCommon::len).
1034///
1035/// # Arguments
1036///
1037/// * `radix2_mod_n_out` - The output for the result. Its [length in units of
1038///   bytes](MpUIntCommon::len) must be such that `n`'s [fits
1039///   into](MpUIntCommon::len_is_compatible_with) it.
1040/// * `n` - The modulus. Must be odd.
1041///
1042/// # Errors
1043///
1044/// - Returns [`CtMontgomeryTransformationError::InvalidModulus`] if `n` is
1045///   even.
1046/// - Returns [`CtMontgomeryTransformationError::InsufficientResultSpace`] if
1047///   `n`'s length doesn't fit into `radix2_mod_n_out`'s.
1048///
1049/// Nothing gets modified in either case.
1050pub fn ct_montgomery_radix2_mod_n_mp<RX2T: MpMutUIntSlice, NT: MpUIntCommon>(
1051    radix2_mod_n_out: &mut RX2T,
1052    n: &NT,
1053) -> Result<(), CtMontgomeryTransformationError> {
1054    if n.test_bit(0).unwrap() == 0 {
1055        return Err(CtMontgomeryTransformationError::InvalidModulus);
1056    }
1057    if !n.len_is_compatible_with(radix2_mod_n_out.len()) {
1058        return Err(CtMontgomeryTransformationError::InsufficientResultSpace);
1059    }
1060
1061    radix2_mod_n_out.clear_bytes_above(n.len());
1062    let mut radix2_mod_n_out = radix2_mod_n_out.shrink_to(n.len());
1063    debug_assert_eq!(radix2_mod_n_out.nlimbs(), n.nlimbs());
1064
1065    let radix_shift_len = ct_montgomery_radix_shift_len(n.len());
1066    let n = CtMpDivisor::new(n, None).map_err(|e| match e {
1067        CtMpDivisorError::DivisorIsZero => {
1068            // n had been checked for being odd above, so should be unreachable, but play
1069            // safe.
1070            debug_assert!(false);
1071            CtMontgomeryTransformationError::InvalidModulus
1072        }
1073    })?;
1074    ct_mod_pow2_mp::<_, _>(2 * 8 * radix_shift_len, &mut radix2_mod_n_out, &n).map_err(
1075        |e| match e {
1076            CtModPow2MpError::InsufficientRemainderSpace => {
1077                // The result space had been checked at function entry already, but play safe.
1078                debug_assert!(false);
1079                CtMontgomeryTransformationError::InsufficientResultSpace
1080            }
1081        },
1082    )?;
1083    Ok(())
1084}
1085
1086/// Error returned by [`ct_to_montgomery_form_mp()`].
1087#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1088pub enum CtToMontgomeryFormMpError {
1089    /// The modulus `n` is empty or even.
1090    InvalidModulus,
1091    /// The modulus' [length in units of bytes](MpUIntCommon::len) doesn't [fit
1092    /// into](MpUIntCommon::len_is_compatible_with) the result's.
1093    InsufficientResultSpace,
1094    /// `t`'s [length in units of bytes](MpUIntCommon::len) doesn't [fit
1095    /// into](MpUIntCommon::len_is_compatible_with) the modulus'.
1096    InconsistentOperandLengths,
1097    /// `radix2_mod_n`'s [length in units of bytes](MpUIntCommon::len) doesn't
1098    /// [fit into](MpUIntCommon::len_is_compatible_with) the modulus'.
1099    InconsistentRadix2ModNLenth,
1100}
1101
1102/// Convert a multiprecision integer to Montgomery form by Montgomery
1103/// multiplication.
1104///
1105/// The multiprecision integers may be of different representations.
1106///
1107/// Store the [Montgomery form](self#montgomery-form) of `t`, *`t` · R mod `n`*,
1108/// where *R* is the [Montgomery radix](ct_montgomery_radix_shift_len), in
1109/// `result`. The result is less than `n`. The conversion is carried out by a
1110/// Montgomery multiplication of `t` with the precomputed
1111/// *R<sup>2</sup> mod `n`*, c.f. [`ct_montgomery_radix2_mod_n_mp()`], which is
1112/// more efficient than [`ct_to_montgomery_form_direct_mp()`] when converting
1113/// multiple integers for the same modulus. See [`ct_montgomery_redc_mp()`] for
1114/// converting back.
1115///
1116/// Execution time depends only on the multiprecision integers' [lengths in
1117/// units of bytes](MpUIntCommon::len) and is otherwise constant as far as
1118/// branching is concerned. The computation cost is O(n<sup>2</sup>), where n is
1119/// `n`'s [length](MpUIntCommon::len).
1120///
1121/// # Arguments
1122///
1123/// * `result` - The output for the result. Its [length in units of
1124///   bytes](MpUIntCommon::len) must be such that `n`'s [fits
1125///   into](MpUIntCommon::len_is_compatible_with) it.
1126/// * `t` - The multiprecision integer to convert. Must be less than `n`. Its
1127///   [length in units of bytes](MpUIntCommon::len) must [fit
1128///   into](MpUIntCommon::len_is_compatible_with) `n`'s.
1129/// * `n` - The modulus. Must be odd.
1130/// * `neg_n0_inv_mod_l` - The negated inverse of `n`'s least significant limb
1131///   modulo *2<sup>[`LimbType::BITS`]</sup>*, as computed by
1132///   [`ct_montgomery_neg_n0_inv_mod_l_mp()`].
1133/// * `radix2_mod_n` - *R<sup>2</sup> mod `n`*, as computed by
1134///   [`ct_montgomery_radix2_mod_n_mp()`]. Its [length in units of
1135///   bytes](MpUIntCommon::len) must [fit
1136///   into](MpUIntCommon::len_is_compatible_with) `n`'s.
1137///
1138/// # Errors
1139///
1140/// - Returns [`CtToMontgomeryFormMpError::InvalidModulus`] if `n` is empty or
1141///   even.
1142/// - Returns [`CtToMontgomeryFormMpError::InsufficientResultSpace`] if `n`'s
1143///   length doesn't fit into `result`'s.
1144/// - Returns [`CtToMontgomeryFormMpError::InconsistentOperandLengths`] if `t`'s
1145///   length doesn't fit into `n`'s.
1146/// - Returns [`CtToMontgomeryFormMpError::InconsistentRadix2ModNLenth`] if
1147///   `radix2_mod_n`'s length doesn't fit into `n`'s.
1148///
1149/// Nothing gets modified in any of these cases.
1150pub fn ct_to_montgomery_form_mp<
1151    RT: MpMutUIntSlice,
1152    TT: MpUIntCommon,
1153    NT: MpUIntCommon,
1154    RX2T: MpUIntCommon,
1155>(
1156    result: &mut RT,
1157    t: &TT,
1158    n: &NT,
1159    neg_n0_inv_mod_l: LimbType,
1160    radix2_mod_n: &RX2T,
1161) -> Result<(), CtToMontgomeryFormMpError> {
1162    // The Montgomery multiplication will do all error checking needed. However, to
1163    // disambiguate which of the two factors has a length inconsistent with n,
1164    // if any, check that here.
1165    if !t.len_is_compatible_with(n.len()) {
1166        return Err(CtToMontgomeryFormMpError::InconsistentOperandLengths);
1167    }
1168    if !radix2_mod_n.len_is_compatible_with(n.len()) {
1169        return Err(CtToMontgomeryFormMpError::InconsistentRadix2ModNLenth);
1170    }
1171    debug_assert!(ct_lt_mp_mp(t, n).unwrap() != 0);
1172    debug_assert!(ct_lt_mp_mp(radix2_mod_n, n).unwrap() != 0);
1173
1174    // All input arguments have been validated above, just unwrap().
1175    ct_montgomery_mul_mod_mp_mp(result, t, radix2_mod_n, n, neg_n0_inv_mod_l).map_err(
1176        |e| match e {
1177            CtMontgomeryMulModMpMpError::InsufficientResultSpace => {
1178                CtToMontgomeryFormMpError::InsufficientResultSpace
1179            }
1180            CtMontgomeryMulModMpMpError::InvalidModulus => {
1181                CtToMontgomeryFormMpError::InvalidModulus
1182            }
1183            CtMontgomeryMulModMpMpError::InconsistentOperandLengths => {
1184                // The multiplication's factors have been validated above, but play safe.
1185                CtToMontgomeryFormMpError::InconsistentOperandLengths
1186            }
1187        },
1188    )?;
1189
1190    Ok(())
1191}
1192
1193#[cfg(test)]
1194fn test_ct_to_montgomery_form_mp<TT: MpMutUIntSlice, NT: MpMutUIntSlice, RX2T: MpMutUIntSlice>() {
1195    use super::cmp::ct_eq_mp_mp;
1196    use super::div::ct_mod_mp_mp;
1197    use super::limb::LIMB_BYTES;
1198
1199    for i in 0..16 {
1200        const MERSENNE_PRIME_13: LimbType = 8191 as LimbType;
1201        let n_high = MERSENNE_PRIME_13.wrapping_mul((65543 as LimbType).wrapping_mul(i));
1202        for j in 0..16 {
1203            const MERSENNE_PRIME_17: LimbType = 131071 as LimbType;
1204            let n_low = MERSENNE_PRIME_17.wrapping_mul((4095 as LimbType).wrapping_mul(j));
1205            // Force n_low odd.
1206            let n_low = n_low | 1;
1207            let mut n_buf = tst_mk_mp_backing_vec!(NT, 2 * LIMB_BYTES);
1208            let mut n = NT::from_slice(n_buf.as_mut_slice()).unwrap();
1209            n.store_l(0, n_low);
1210            n.store_l(1, n_high);
1211            drop(n);
1212            let n_lengths = if !NT::SUPPORTS_UNALIGNED_BUFFER_LENGTHS {
1213                [LIMB_BYTES, 2 * LIMB_BYTES]
1214            } else {
1215                [2 * LIMB_BYTES - 1, 2 * LIMB_BYTES]
1216            };
1217
1218            for n_len in n_lengths {
1219                let mut n_buf = n_buf.clone();
1220                let mut n = NT::from_slice(n_buf.as_mut_slice()).unwrap();
1221                n.clear_bytes_above(n_len);
1222                let n = n.shrink_to(n_len);
1223                let neg_n0_inv = ct_montgomery_neg_n0_inv_mod_l_mp(&n).unwrap();
1224
1225                let mut radix2_mod_n = tst_mk_mp_backing_vec!(RX2T, n_len);
1226                let mut radix2_mod_n = RX2T::from_slice(radix2_mod_n.as_mut_slice()).unwrap();
1227                ct_montgomery_radix2_mod_n_mp(&mut radix2_mod_n, &n).unwrap();
1228
1229                for k in 0..4 {
1230                    let a_high =
1231                        MERSENNE_PRIME_17.wrapping_mul((16383 as LimbType).wrapping_mul(k));
1232                    for l in 0..4 {
1233                        let a_low =
1234                            MERSENNE_PRIME_13.wrapping_mul((262175 as LimbType).wrapping_mul(l));
1235                        let mut a = tst_mk_mp_backing_vec!(TT, 2 * LIMB_BYTES);
1236                        let mut a = TT::from_slice(a.as_mut_slice()).unwrap();
1237                        a.store_l(0, a_low);
1238                        a.store_l(1, a_high);
1239                        // All montgomery operations are defined mod n, compute a mod n
1240                        ct_mod_mp_mp(None, &mut a, &CtMpDivisor::new(&n, None).unwrap());
1241                        let mut a = a.shrink_to(n_len);
1242
1243                        let mut result = tst_mk_mp_backing_vec!(TT, n_len);
1244                        let mut result = TT::from_slice(result.as_mut_slice()).unwrap();
1245                        ct_to_montgomery_form_mp(&mut result, &a, &n, neg_n0_inv, &radix2_mod_n)
1246                            .unwrap();
1247
1248                        ct_to_montgomery_form_direct_mp(&mut a, &n).unwrap();
1249                        assert_eq!(ct_eq_mp_mp(&result, &a).unwrap(), 1);
1250                    }
1251                }
1252            }
1253        }
1254    }
1255}
1256
1257#[test]
1258fn test_ct_to_montgomery_form_be_be_be() {
1259    use super::limbs::MpMutBigEndianUIntByteSlice;
1260    test_ct_to_montgomery_form_mp::<
1261        MpMutBigEndianUIntByteSlice,
1262        MpMutBigEndianUIntByteSlice,
1263        MpMutBigEndianUIntByteSlice,
1264    >()
1265}
1266
1267#[test]
1268fn test_ct_to_montgomery_form_le_le_le() {
1269    use super::limbs::MpMutLittleEndianUIntByteSlice;
1270    test_ct_to_montgomery_form_mp::<
1271        MpMutLittleEndianUIntByteSlice,
1272        MpMutLittleEndianUIntByteSlice,
1273        MpMutLittleEndianUIntByteSlice,
1274    >()
1275}
1276
1277#[test]
1278fn test_ct_to_montgomery_form_ne_ne_ne() {
1279    use super::limbs::MpMutNativeEndianUIntLimbsSlice;
1280    test_ct_to_montgomery_form_mp::<
1281        MpMutNativeEndianUIntLimbsSlice,
1282        MpMutNativeEndianUIntLimbsSlice,
1283        MpMutNativeEndianUIntLimbsSlice,
1284    >()
1285}
1286
1287/// Montgomery exponentiation kernel.
1288///
1289/// Compute the [Montgomery form](self#montgomery-form) of `op0`'s value raised
1290/// to the power of `exponent` modulo `n`, for `op0` given in [Montgomery
1291/// form](self#montgomery-form), and store it in `result`. `result` must be
1292/// initialized to the [Montgomery form](self#montgomery-form) of one, i.e. to
1293/// *R mod `n`*, with *R* being the [Montgomery
1294/// radix](ct_montgomery_radix_shift_len).
1295///
1296/// Only the `exponent_nbits` least significant bits of `exponent` are
1297/// considered, with `exponent_nbits` capped to `exponent`'s bit length. The
1298/// computation follows the left-to-right binary method, with the
1299/// multiplication by `op0` carried out for every bit and undone by a
1300/// conditional copy for zero bits, so that the execution time doesn't depend
1301/// on `exponent`'s value.
1302///
1303/// Execution time depends only on the multiprecision integers' [lengths in
1304/// units of bytes](MpUIntCommon::len) and on `exponent_nbits`, and is otherwise
1305/// constant as far as branching is concerned. The computation cost is
1306/// O(e · n<sup>2</sup>), where e is `exponent_nbits` and n is `n`'s
1307/// [length](MpUIntCommon::len). The arguments are validated by the callers,
1308/// [`ct_montogmery_exp_mod_odd_mp_mp()`] and [`ct_exp_mod_odd_mp_mp()`].
1309///
1310/// # Arguments
1311///
1312/// * `result` - The output for the result. Must be initialized to the
1313///   [Montgomery form](self#montgomery-form) of one, *R mod `n`*, and span
1314///   exactly as many limbs as `n`.
1315/// * `op0` - The base, in [Montgomery form](self#montgomery-form). Must be less
1316///   than `n`. Its [length in units of bytes](MpUIntCommon::len) must [fit
1317///   into](MpUIntCommon::len_is_compatible_with) `n`'s.
1318/// * `n` - The modulus. Must be odd.
1319/// * `neg_n0_inv_mod_l` - The negated inverse of `n`'s least significant limb
1320///   modulo *2<sup>[`LimbType::BITS`]</sup>*, as computed by
1321///   [`ct_montgomery_neg_n0_inv_mod_l_mp()`].
1322/// * `exponent` - The exponent.
1323/// * `exponent_nbits` - The number of `exponent`'s least significant bits to
1324///   consider.
1325/// * `scratch` - Scratch buffer. Must have at least as many [`LimbType`]
1326///   elements as `n` spans limbs, c.f.
1327///   [`nlimbs_for_len()`](crate::MpNativeEndianUIntLimbsSlice::nlimbs_for_len).
1328fn _ct_montogmery_exp_mod_mp_mp<
1329    RT: MpMutUIntSlice,
1330    T0: MpUIntCommon,
1331    NT: MpUIntCommon,
1332    ET: MpUIntCommon,
1333>(
1334    result: &mut RT,
1335    op0: &T0,
1336    n: &NT,
1337    neg_n0_inv_mod_l: LimbType,
1338    exponent: &ET,
1339    exponent_nbits: usize,
1340    scratch: &mut [LimbType],
1341) {
1342    debug_assert_eq!(result.nlimbs(), n.nlimbs());
1343
1344    let n_nlimbs = MpMutNativeEndianUIntLimbsSlice::nlimbs_for_len(n.len());
1345    debug_assert!(scratch.len() >= n_nlimbs);
1346    let mut scratch = MpMutNativeEndianUIntLimbsSlice::from_limbs(scratch);
1347    scratch.clear_bytes_above(n.len());
1348    let mut scratch = scratch.shrink_to(n.len());
1349
1350    let exponent_nbits = exponent_nbits.min(8 * exponent.len());
1351    for i in 0..exponent_nbits {
1352        // Input arguments have been validated/setup by callers, just unwrap() the
1353        // result.
1354        ct_montgomery_mul_mod_mp_mp(&mut scratch, result, result, n, neg_n0_inv_mod_l).unwrap();
1355        ct_montgomery_mul_mod_mp_mp(result, &scratch, op0, n, neg_n0_inv_mod_l).unwrap();
1356        // If the current exponent bit is zero, "undo" the latter multiplication.
1357        result.copy_from_cond(&scratch, !exponent.test_bit(exponent_nbits - i - 1));
1358    }
1359}
1360
1361/// Error returned by [`ct_montogmery_exp_mod_odd_mp_mp()`].
1362#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1363pub enum CtMontgomeryExpModOddMpMpError {
1364    /// The modulus `n` is even, including the case of an empty one.
1365    InvalidModulus,
1366    /// The modulus' [length in units of bytes](MpUIntCommon::len) doesn't [fit
1367    /// into](MpUIntCommon::len_is_compatible_with) `result`'s.
1368    InsufficientResultSpace,
1369    /// `scratch` has fewer [`LimbType`] elements than the modulus spans limbs.
1370    InsufficientScratchSpace,
1371    /// `op0`'s [length in units of bytes](MpUIntCommon::len) doesn't [fit
1372    /// into](MpUIntCommon::len_is_compatible_with) the modulus'.
1373    InconsistentOperandLengths,
1374    /// `radix_mod_n`'s [length in units of bytes](MpUIntCommon::len) doesn't
1375    /// [fit into](MpUIntCommon::len_is_compatible_with) the modulus'.
1376    InconsistendRadixModNLengh,
1377}
1378
1379/// Modular exponentiation in Montgomery form.
1380///
1381/// The multiprecision integers may be of different representations.
1382///
1383/// Compute the [Montgomery form](self#montgomery-form) of `op0`'s value raised
1384/// to the power of `exponent` modulo an odd `n`, for `op0` given in [Montgomery
1385/// form](self#montgomery-form), and store it in `result`, with any of
1386/// `result`'s bytes above `n`'s length cleared. That is, for
1387/// *`op0` = a · R mod `n`*, with *R* being the [Montgomery
1388/// radix](ct_montgomery_radix_shift_len), compute
1389/// *a<sup>`exponent`</sup> · R mod `n`*. The result is less than `n`. Only the
1390/// `exponent_nbits` least significant bits of `exponent` are considered, which
1391/// allows for not revealing the exponent's actual bit length.
1392///
1393/// The exponentiation follows the left-to-right binary method with Montgomery
1394/// multiplications, c.f. [`ct_montgomery_mul_mod_mp_mp()`], carrying out a
1395/// multiplication by `op0` for every exponent bit, so that the execution time
1396/// doesn't depend on `exponent`'s value. See [`ct_exp_mod_odd_mp_mp()`] for a
1397/// variant operating on, and returning, integers not in [Montgomery
1398/// form](self#montgomery-form).
1399///
1400/// Execution time depends only on the multiprecision integers' [lengths in
1401/// units of bytes](MpUIntCommon::len), on `scratch`'s length and on
1402/// `exponent_nbits`, capped to `exponent`'s bit length, and is otherwise
1403/// constant as far as branching is concerned. The computation cost is O(e ·
1404/// n<sup>2</sup>), where e is `exponent_nbits` and n is `n`'s
1405/// [length](MpUIntCommon::len).
1406///
1407/// # Arguments
1408///
1409/// * `result` - The output for the result. Its [length in units of
1410///   bytes](MpUIntCommon::len) must be such that `n`'s [fits
1411///   into](MpUIntCommon::len_is_compatible_with) it.
1412/// * `op0` - The base, in [Montgomery form](self#montgomery-form). Must be less
1413///   than `n`. Its [length in units of bytes](MpUIntCommon::len) must [fit
1414///   into](MpUIntCommon::len_is_compatible_with) `n`'s.
1415/// * `n` - The modulus. Must be odd.
1416/// * `neg_n0_inv_mod_l` - The negated inverse of `n`'s least significant limb
1417///   modulo *2<sup>[`LimbType::BITS`]</sup>*, as computed by
1418///   [`ct_montgomery_neg_n0_inv_mod_l_mp()`].
1419/// * `radix_mod_n` - The [Montgomery form](self#montgomery-form) of one, i.e.
1420///   *R mod `n`*, as obtained e.g. by Montgomery reducing the result of
1421///   [`ct_montgomery_radix2_mod_n_mp()`] with [`ct_montgomery_redc_mp()`]. Its
1422///   [length in units of bytes](MpUIntCommon::len) must [fit
1423///   into](MpUIntCommon::len_is_compatible_with) `n`'s.
1424/// * `exponent` - The exponent. Its [length in units of
1425///   bytes](MpUIntCommon::len) isn't restricted.
1426/// * `exponent_nbits` - The number of `exponent`'s least significant bits to
1427///   consider. Values exceeding `exponent`'s bit length are capped to the
1428///   latter.
1429/// * `scratch` - Scratch buffer. Must have at least as many [`LimbType`]
1430///   elements as `n` spans limbs, c.f.
1431///   [`nlimbs_for_len()`](crate::MpNativeEndianUIntLimbsSlice::nlimbs_for_len).
1432///
1433/// # Errors
1434///
1435/// - Returns [`CtMontgomeryExpModOddMpMpError::InvalidModulus`] if `n` is
1436///   even.
1437/// - Returns [`CtMontgomeryExpModOddMpMpError::InsufficientResultSpace`] if
1438///   `n`'s length doesn't fit into `result`'s.
1439/// - Returns [`CtMontgomeryExpModOddMpMpError::InsufficientScratchSpace`] if
1440///   `scratch` is too short.
1441/// - Returns [`CtMontgomeryExpModOddMpMpError::InconsistentOperandLengths`] if
1442///   `op0`'s length doesn't fit into `n`'s.
1443/// - Returns [`CtMontgomeryExpModOddMpMpError::InconsistendRadixModNLengh`] if
1444///   `radix_mod_n`'s length doesn't fit into `n`'s.
1445///
1446/// Nothing gets modified in any of these cases.
1447#[allow(clippy::too_many_arguments)]
1448pub fn ct_montogmery_exp_mod_odd_mp_mp<
1449    RT: MpMutUIntSlice,
1450    T0: MpUIntCommon,
1451    NT: MpUIntCommon,
1452    RXT: MpUIntCommon,
1453    ET: MpUIntCommon,
1454>(
1455    result: &mut RT,
1456    op0: &T0,
1457    n: &NT,
1458    neg_n0_inv_mod_l: LimbType,
1459    radix_mod_n: &RXT,
1460    exponent: &ET,
1461    exponent_nbits: usize,
1462    scratch: &mut [LimbType],
1463) -> Result<(), CtMontgomeryExpModOddMpMpError> {
1464    if n.test_bit(0).unwrap() == 0 {
1465        return Err(CtMontgomeryExpModOddMpMpError::InvalidModulus);
1466    }
1467    if !n.len_is_compatible_with(result.len()) {
1468        return Err(CtMontgomeryExpModOddMpMpError::InsufficientResultSpace);
1469    }
1470    if scratch.len() < MpMutNativeEndianUIntLimbsSlice::nlimbs_for_len(n.len()) {
1471        return Err(CtMontgomeryExpModOddMpMpError::InsufficientScratchSpace);
1472    }
1473    if !op0.len_is_compatible_with(n.len()) {
1474        return Err(CtMontgomeryExpModOddMpMpError::InconsistentOperandLengths);
1475    }
1476    debug_assert!(ct_lt_mp_mp(op0, n).unwrap() != 0);
1477    if !radix_mod_n.len_is_compatible_with(n.len()) {
1478        return Err(CtMontgomeryExpModOddMpMpError::InconsistendRadixModNLengh);
1479    }
1480    debug_assert!(ct_lt_mp_mp(radix_mod_n, n).unwrap() != 0);
1481
1482    // Initialize the result with a one in Montgomery form.
1483    result.clear_bytes_above(n.len());
1484    let mut result = result.shrink_to(n.len());
1485    debug_assert_eq!(result.nlimbs(), n.nlimbs());
1486    result.copy_from(radix_mod_n);
1487
1488    _ct_montogmery_exp_mod_mp_mp(
1489        &mut result,
1490        op0,
1491        n,
1492        neg_n0_inv_mod_l,
1493        exponent,
1494        exponent_nbits,
1495        scratch,
1496    );
1497    Ok(())
1498}
1499
1500/// Error returned by [`ct_exp_mod_odd_mp_mp()`].
1501#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1502pub enum CtExpModOddMpMpError {
1503    /// The modulus `n` is empty or even.
1504    InvalidModulus,
1505    /// The modulus' [length in units of bytes](MpUIntCommon::len) doesn't [fit
1506    /// into](MpUIntCommon::len_is_compatible_with) `result`'s.
1507    InsufficientResultSpace,
1508    /// `scratch` has fewer [`LimbType`] elements than the modulus spans limbs.
1509    InsufficientScratchSpace,
1510    /// `op0`'s and the modulus' [lengths in units of bytes](MpUIntCommon::len)
1511    /// don't [fit into](MpUIntCommon::len_is_compatible_with) each other
1512    /// mutually.
1513    InconsistentOperandLengths,
1514}
1515
1516/// Modular exponentiation with an odd modulus.
1517///
1518/// The multiprecision integers may be of different representations.
1519///
1520/// Compute *`op0`<sup>`exponent`</sup> mod `n`* for an odd `n` and store it in
1521/// `result`, with any of `result`'s bytes above `n`'s length cleared. The
1522/// result is less than `n`. Only the `exponent_nbits` least significant bits of
1523/// `exponent` are considered, which allows for not revealing the exponent's
1524/// actual bit length.
1525///
1526/// The exponentiation is carried out by means of Montgomery multiplications:
1527/// `op0` gets converted to [Montgomery form](self#montgomery-form), c.f.
1528/// [`ct_to_montgomery_form_mp()`], and the result back from it, c.f.
1529/// [`ct_montgomery_redc_mp()`]. To save a buffer, `op0` gets overwritten by its
1530/// [Montgomery form](self#montgomery-form) in the course. See
1531/// [`ct_montogmery_exp_mod_odd_mp_mp()`] for a variant operating on, and
1532/// returning, integers in [Montgomery form](self#montgomery-form), which avoids
1533/// the conversions, e.g. for multiple exponentiations with the same modulus.
1534///
1535/// Execution time depends only on the multiprecision integers' [lengths in
1536/// units of bytes](MpUIntCommon::len), on `scratch`'s length, on
1537/// `exponent_nbits`, capped to `exponent`'s bit length, as well as on `n`'s
1538/// length without any leading zero bytes, c.f. [`CtMpDivisor`], and is
1539/// otherwise constant as far as branching is concerned. In particular, it
1540/// doesn't depend on `exponent`'s value. The computation cost is
1541/// O((e + 1) · n<sup>2</sup>), where e is `exponent_nbits` and n is `n`'s
1542/// [length](MpUIntCommon::len).
1543///
1544/// # Arguments
1545///
1546/// * `result` - The output for the result. Its [length in units of
1547///   bytes](MpUIntCommon::len) must be such that `n`'s [fits
1548///   into](MpUIntCommon::len_is_compatible_with) it.
1549/// * `op0` - The base. Must be less than `n`. Its [length in units of
1550///   bytes](MpUIntCommon::len) and `n`'s must [fit
1551///   into](MpUIntCommon::len_is_compatible_with) each other mutually. It will
1552///   be overwritten by its [Montgomery form](self#montgomery-form).
1553/// * `n` - The modulus. Must be odd.
1554/// * `exponent` - The exponent. Its [length in units of
1555///   bytes](MpUIntCommon::len) isn't restricted.
1556/// * `exponent_nbits` - The number of `exponent`'s least significant bits to
1557///   consider. Values exceeding `exponent`'s bit length are capped to the
1558///   latter.
1559/// * `scratch` - Scratch buffer. Must have at least as many [`LimbType`]
1560///   elements as `n` spans limbs, c.f.
1561///   [`nlimbs_for_len()`](crate::MpNativeEndianUIntLimbsSlice::nlimbs_for_len).
1562///
1563/// # Errors
1564///
1565/// - Returns [`CtExpModOddMpMpError::InvalidModulus`] if `n` is empty or even.
1566/// - Returns [`CtExpModOddMpMpError::InsufficientResultSpace`] if `n`'s length
1567///   doesn't fit into `result`'s.
1568/// - Returns [`CtExpModOddMpMpError::InsufficientScratchSpace`] if `scratch`
1569///   is too short.
1570/// - Returns [`CtExpModOddMpMpError::InconsistentOperandLengths`] if `op0`'s
1571///   and `n`'s lengths don't fit into each other mutually.
1572///
1573/// Nothing gets modified in any of these cases.
1574pub fn ct_exp_mod_odd_mp_mp<
1575    RT: MpMutUIntSlice,
1576    T0: MpMutUInt,
1577    NT: MpUIntCommon,
1578    ET: MpUIntCommon,
1579>(
1580    result: &mut RT,
1581    op0: &mut T0,
1582    n: &NT,
1583    exponent: &ET,
1584    exponent_nbits: usize,
1585    scratch: &mut [LimbType],
1586) -> Result<(), CtExpModOddMpMpError> {
1587    if !n.len_is_compatible_with(result.len()) {
1588        return Err(CtExpModOddMpMpError::InsufficientResultSpace);
1589    }
1590    if !op0.len_is_compatible_with(n.len()) {
1591        return Err(CtExpModOddMpMpError::InconsistentOperandLengths);
1592    }
1593    debug_assert!(ct_lt_mp_mp(op0, n).unwrap() != 0);
1594    if !n.len_is_compatible_with(op0.len()) {
1595        // op0 will get transformed in-place into Montgomery form. So the
1596        // backing byte slice must be large enough.
1597        return Err(CtExpModOddMpMpError::InconsistentOperandLengths);
1598    }
1599    debug_assert_eq!(op0.nlimbs(), n.nlimbs());
1600    if scratch.len() < MpMutNativeEndianUIntLimbsSlice::nlimbs_for_len(n.len()) {
1601        return Err(CtExpModOddMpMpError::InsufficientScratchSpace);
1602    }
1603
1604    // This checks the modulus for validity as a side-effect.
1605    let neg_n0_inv_mod_l = ct_montgomery_neg_n0_inv_mod_l_mp(n).map_err(|e| match e {
1606        CtMontgomeryNegN0InvModLMpError::InvalidModulus => CtExpModOddMpMpError::InvalidModulus,
1607    })?;
1608
1609    // Shrink result[] to the length of n. It will be used to temporarily hold the
1610    // radix^2 mod n and ct_to_montgomery_form_mp() below would complain if its
1611    // length is unexpectedly large.
1612    result.clear_bytes_above(n.len());
1613    let mut result = result.shrink_to(n.len());
1614    debug_assert_eq!(result.nlimbs(), n.nlimbs());
1615
1616    // The radix squared mod n gets into result[], it will be reduced
1617    // later on to a one in Montgomery form.
1618    ct_montgomery_radix2_mod_n_mp(&mut result, n).unwrap();
1619
1620    // Transform op0 into Montgomery form, the function argument will get
1621    // overwritten to save an extra scratch buffer.
1622    let mut mg_op0 = MpMutNativeEndianUIntLimbsSlice::from_limbs(scratch);
1623    mg_op0.clear_bytes_above(n.len());
1624    let mut mg_op0 = mg_op0.shrink_to(n.len());
1625    ct_to_montgomery_form_mp(&mut mg_op0, op0, n, neg_n0_inv_mod_l, &result).unwrap();
1626    op0.copy_from(&mg_op0);
1627
1628    // Reduce the radix squared mod n in result[] to the radix mod n,
1629    // i.e. to a one in Montgomery form.
1630    ct_montgomery_redc_mp(&mut result, n, neg_n0_inv_mod_l).unwrap();
1631
1632    // Do the Montgomery exponentiation.
1633    _ct_montogmery_exp_mod_mp_mp(
1634        &mut result,
1635        op0,
1636        n,
1637        neg_n0_inv_mod_l,
1638        exponent,
1639        exponent_nbits,
1640        scratch,
1641    );
1642
1643    // And transform the result back from Montgomery form.
1644    ct_montgomery_redc_mp(&mut result, n, neg_n0_inv_mod_l).unwrap();
1645    Ok(())
1646}
1647
1648#[cfg(test)]
1649fn test_ct_exp_mod_odd_mp_mp<
1650    RT: MpMutUIntSlice,
1651    T0: MpMutUIntSlice,
1652    NT: MpMutUIntSlice,
1653    ET: MpMutUIntSlice,
1654>() {
1655    extern crate alloc;
1656    use super::limb::LIMB_BYTES;
1657    use super::mul::ct_mul_trunc_mp_l;
1658    use super::shift::ct_lshift_mp;
1659    use alloc::vec;
1660
1661    fn test_one<RT: MpMutUIntSlice, T0: MpMutUIntSlice, NT: MpUIntCommon, ET: MpUIntCommon>(
1662        op0: &T0,
1663        n: &NT,
1664        exponent: &ET,
1665    ) {
1666        use super::cmp::ct_eq_mp_mp;
1667        use super::div::ct_mod_mp_mp;
1668        use super::mul::{ct_mul_trunc_mp_mp, ct_square_trunc_mp};
1669
1670        let n_len = n.len();
1671
1672        let mut op0_mod_n = tst_mk_mp_backing_vec!(T0, n_len.max(op0.len()));
1673        let mut op0_mod_n = T0::from_slice(&mut op0_mod_n).unwrap();
1674        op0_mod_n.copy_from(op0);
1675        ct_mod_mp_mp(None, &mut op0_mod_n, &CtMpDivisor::new(n, None).unwrap());
1676
1677        let mut op0_scratch = tst_mk_mp_backing_vec!(T0, n_len);
1678        let mut op0_scratch = T0::from_slice(&mut op0_scratch).unwrap();
1679        op0_scratch.copy_from(&op0_mod_n);
1680        let mut result = tst_mk_mp_backing_vec!(RT, n_len);
1681        let mut result = RT::from_slice(&mut result).unwrap();
1682        let mut scratch =
1683            vec![0 as LimbType; MpMutNativeEndianUIntLimbsSlice::nlimbs_for_len(n_len)];
1684        ct_exp_mod_odd_mp_mp(
1685            &mut result,
1686            &mut op0_scratch,
1687            n,
1688            exponent,
1689            8 * exponent.len(),
1690            &mut scratch,
1691        )
1692        .unwrap();
1693
1694        // Compute the expected value using repeated multiplications/squarings and
1695        // modular reductions.
1696        let mut expected = tst_mk_mp_backing_vec!(RT, 2 * n_len);
1697        let mut expected = RT::from_slice(&mut expected).unwrap();
1698        expected.clear_bytes_above(0);
1699        expected.store_l(0, 1);
1700        for i in 0..8 * exponent.len() {
1701            ct_square_trunc_mp(&mut expected, n_len);
1702            ct_mod_mp_mp(None, &mut expected, &CtMpDivisor::new(n, None).unwrap());
1703            if exponent.test_bit(8 * exponent.len() - i - 1).unwrap() != 0 {
1704                ct_mul_trunc_mp_mp(&mut expected, n_len, &op0_mod_n);
1705                ct_mod_mp_mp(None, &mut expected, &CtMpDivisor::new(n, None).unwrap());
1706            }
1707        }
1708        assert_ne!(ct_eq_mp_mp(&result, &expected).unwrap(), 0);
1709    }
1710
1711    let exponent_len = LIMB_BYTES + 1;
1712    let mut e0_buf = tst_mk_mp_backing_vec!(ET, exponent_len);
1713    let mut e1_buf = tst_mk_mp_backing_vec!(ET, exponent_len);
1714    let mut e1 = ET::from_slice(&mut e1_buf).unwrap();
1715    e1.store_l(0, 1);
1716    drop(e1);
1717    let mut e2_buf = tst_mk_mp_backing_vec!(ET, exponent_len);
1718    let mut e2 = ET::from_slice(&mut e2_buf).unwrap();
1719    e2.store_l(0, 2);
1720    drop(e2);
1721    let mut ef_buf = tst_mk_mp_backing_vec!(ET, exponent_len);
1722    ef_buf.fill(0xefu8.into());
1723
1724    for n_len in [1, LIMB_BYTES + 1, 2 * LIMB_BYTES - 1, 3 * LIMB_BYTES] {
1725        let mut n = tst_mk_mp_backing_vec!(NT, n_len);
1726        let mut n = NT::from_slice(&mut n).unwrap();
1727        n.store_l(0, 1);
1728        while n.load_l((n_len - 1) / LIMB_BYTES) >> (8 * ((n_len - 1) % LIMB_BYTES)) == 0 {
1729            ct_mul_trunc_mp_l(&mut n, n_len, 251);
1730        }
1731
1732        for op0_len in 1..n_len {
1733            let mut op0 = tst_mk_mp_backing_vec!(T0, n_len);
1734            let mut op0 = T0::from_slice(&mut op0).unwrap();
1735            op0.store_l(0, 1);
1736            for _ in 0..op0_len {
1737                ct_mul_trunc_mp_l(&mut op0, n_len, 241);
1738            }
1739            ct_lshift_mp(&mut op0, 8 * (n_len - op0_len));
1740            for e_buf in [&mut e0_buf, &mut e1_buf, &mut e2_buf, &mut ef_buf] {
1741                let e = ET::from_slice(e_buf).unwrap();
1742                test_one::<RT, _, _, _>(&op0, &n, &e);
1743            }
1744        }
1745    }
1746}
1747
1748#[test]
1749fn test_ct_exp_mod_odd_be_be_be_be() {
1750    use super::limbs::MpMutBigEndianUIntByteSlice;
1751    test_ct_exp_mod_odd_mp_mp::<
1752        MpMutBigEndianUIntByteSlice,
1753        MpMutBigEndianUIntByteSlice,
1754        MpMutBigEndianUIntByteSlice,
1755        MpMutBigEndianUIntByteSlice,
1756    >()
1757}
1758
1759#[test]
1760fn test_ct_exp_mod_odd_le_le_le_le() {
1761    use super::limbs::MpMutLittleEndianUIntByteSlice;
1762    test_ct_exp_mod_odd_mp_mp::<
1763        MpMutLittleEndianUIntByteSlice,
1764        MpMutLittleEndianUIntByteSlice,
1765        MpMutLittleEndianUIntByteSlice,
1766        MpMutLittleEndianUIntByteSlice,
1767    >()
1768}
1769
1770#[test]
1771fn test_ct_exp_mod_odd_ne_ne_ne_ne() {
1772    use super::limbs::MpMutNativeEndianUIntLimbsSlice;
1773    test_ct_exp_mod_odd_mp_mp::<
1774        MpMutNativeEndianUIntLimbsSlice,
1775        MpMutNativeEndianUIntLimbsSlice,
1776        MpMutNativeEndianUIntLimbsSlice,
1777        MpMutNativeEndianUIntLimbsSlice,
1778    >()
1779}