1use crate::ct;
7use crate::error::Result;
8use crate::montgomery::{MontgomeryContext, from_mont, mont_mul, to_mont};
9use crate::types::BigIntCore;
10
11#[cfg(feature = "alloc")]
12use crate::types::BigInt;
13
14#[cfg(feature = "alloc")]
18pub fn mod_pow(ctx: &MontgomeryContext, base: &BigInt, exponent: &BigInt) -> Result<BigInt> {
19 if exponent.is_zero() {
21 let one = BigInt::from_u64(1, ctx.modulus().max_limbs());
23 return Ok(one);
24 }
25
26 if base.is_zero() {
27 return Ok(BigInt::from_u64(0, ctx.modulus().max_limbs()));
29 }
30
31 let x_mont = to_mont(ctx, base)?;
33
34 let one = BigInt::from_u64(1, ctx.modulus().max_limbs());
36 let r_mont = to_mont(ctx, &one)?;
37
38 let mut r0 = r_mont;
40 let mut r1 = x_mont;
41
42 let exp_limbs = exponent.limbs();
44 let mut bit_pos = 0;
45
46 let mut msb_found = false;
48 for i in (0..exp_limbs.len()).rev() {
49 if exp_limbs[i] != 0 {
50 bit_pos = (i * 64) + (63 - exp_limbs[i].leading_zeros() as usize);
51 msb_found = true;
52 break;
53 }
54 }
55
56 if !msb_found {
57 let one = BigInt::from_u64(1, ctx.modulus().max_limbs());
59 return Ok(one);
60 }
61
62 for i in (0..=bit_pos).rev() {
64 let limb_idx = i / 64;
65 let bit_idx = i % 64;
66 let bit = if limb_idx < exp_limbs.len() {
67 (exp_limbs[limb_idx] >> bit_idx) & 1
68 } else {
69 0
70 };
71
72 swap_bigint(bit, &mut r0, &mut r1);
74
75 r1 = mont_mul(ctx, &r0, &r1)?;
77 r0 = mont_mul(ctx, &r0, &r0)?;
78
79 swap_bigint(bit, &mut r0, &mut r1);
81 }
82
83 from_mont(ctx, &r0)
85}
86
87fn swap_bigint(choice: u64, a: &mut BigInt, b: &mut BigInt) {
89 let a_limbs = a.limbs_mut();
90 let b_limbs = b.limbs_mut();
91 let len = a_limbs.len().min(b_limbs.len());
92
93 for i in 0..len {
94 ct::ct_swap(choice, &mut a_limbs[i], &mut b_limbs[i]);
95 }
96
97 let a_sign = a.sign();
99 let b_sign = b.sign();
100 let new_a_sign = ct::ct_select(choice, b_sign as u64, a_sign as u64) != 0;
101 let new_b_sign = ct::ct_select(choice, a_sign as u64, b_sign as u64) != 0;
102 a.set_sign(new_a_sign);
103 b.set_sign(new_b_sign);
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn test_mod_pow_exponent_zero() {
112 let modulus = BigInt::from_u64(97, 10);
113 let ctx = MontgomeryContext::new(modulus).unwrap();
114 let base = BigInt::from_u64(5, 10);
115 let exponent = BigInt::from_u64(0, 10);
116 let result = mod_pow(&ctx, &base, &exponent).unwrap();
117 assert_eq!(result.limbs()[0], 1);
118 }
119}