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}