crypto_bigint/uint/boxed/
add_mod.rs1use crate::{AddMod, BoxedUint, Limb, NonZero};
4
5impl BoxedUint {
6 #[must_use]
10 pub fn add_mod(&self, rhs: &Self, p: &NonZero<Self>) -> Self {
11 let mut result = self.clone();
12 result.add_mod_assign(rhs, p);
13 result
14 }
15
16 pub fn add_mod_assign(&mut self, rhs: &Self, p: &NonZero<Self>) {
20 debug_assert_eq!(self.bits_precision(), p.bits_precision());
21 debug_assert_eq!(rhs.bits_precision(), p.bits_precision());
22 debug_assert!(*self < p.as_ref());
23 debug_assert!(rhs < p.as_ref());
24
25 let carry = self.carrying_add_assign(rhs, Limb::ZERO);
26 self.sub_assign_mod_with_carry(carry, p, p);
27 }
28
29 #[must_use]
33 pub fn double_mod(&self, p: &NonZero<Self>) -> Self {
34 let (mut w, carry) = self.shl1();
35 w.sub_assign_mod_with_carry(carry, p, p);
36 w
37 }
38
39 #[must_use]
44 pub fn add_mod_special(&self, rhs: &Self, c: Limb) -> Self {
45 let (mut out, carry) = self.carrying_add(rhs, c);
47
48 let l = carry.wrapping_sub(Limb::ONE) & c;
52 out.wrapping_sub_assign(l);
53 out
54 }
55}
56
57impl AddMod for BoxedUint {
58 type Output = Self;
59
60 fn add_mod(&self, rhs: &Self, p: &NonZero<Self>) -> Self {
61 self.add_mod(rhs, p)
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::BoxedUint;
68 use hex_literal::hex;
69
70 #[cfg(feature = "rand_core")]
71 use crate::{Limb, NonZero, Random, RandomMod};
72 #[cfg(feature = "rand_core")]
73 use rand_core::SeedableRng;
74
75 #[test]
78 fn add_mod_nist_p256() {
79 let a = BoxedUint::from_be_slice(
80 &hex!("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"),
81 256,
82 )
83 .unwrap();
84 let b = BoxedUint::from_be_slice(
85 &hex!("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"),
86 256,
87 )
88 .unwrap();
89 let n = BoxedUint::from_be_slice(
90 &hex!("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"),
91 256,
92 )
93 .unwrap()
94 .to_nz()
95 .unwrap();
96
97 let actual = a.add_mod(&b, &n);
98 let expected = BoxedUint::from_be_slice(
99 &hex!("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"),
100 256,
101 )
102 .unwrap();
103
104 assert_eq!(expected, actual);
105 }
106
107 #[test]
108 fn double_mod_expected() {
109 let a = BoxedUint::from_be_hex(
110 "44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56",
111 256,
112 )
113 .unwrap();
114 let n = BoxedUint::from_be_hex(
115 "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
116 256,
117 )
118 .unwrap()
119 .to_nz()
120 .unwrap();
121
122 assert_eq!(a.add_mod(&a, &n), a.double_mod(&n));
123 }
124
125 #[test]
126 #[cfg(feature = "rand_core")]
127 fn add_mod_special() {
128 let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
129 let moduli = [
130 NonZero::<Limb>::random_from_rng(&mut rng),
131 NonZero::<Limb>::random_from_rng(&mut rng),
132 ];
133 let sizes = if cfg!(miri) {
134 &[1, 2, 3][..]
135 } else {
136 &[1, 2, 3, 4, 8, 16][..]
137 };
138
139 for size in sizes.iter().copied() {
140 let bits = size * Limb::BITS;
141
142 for special in &moduli {
143 let zero = BoxedUint::zero_with_precision(bits);
144 let one = BoxedUint::one_with_precision(bits);
145 let p = NonZero::new(zero.wrapping_sub(special.get())).unwrap();
146 let minus_one = p.wrapping_sub(Limb::ONE);
147
148 let base_cases = [
149 (&zero, &zero, &zero),
150 (&one, &zero, &one),
151 (&zero, &one, &one),
152 (&minus_one, &one, &zero),
153 (&one, &minus_one, &zero),
154 ];
155
156 for (a, b, c) in base_cases {
157 let x = a.add_mod_special(b, *special.as_ref());
158 assert_eq!(&x, c, "{} - {} mod {} = {} != {}", a, b, p, x, c);
159 }
160
161 for _i in 0..100 {
162 let a = BoxedUint::random_mod_vartime(&mut rng, &p);
163 let b = BoxedUint::random_mod_vartime(&mut rng, &p);
164
165 let c = a.add_mod_special(&b, *special.as_ref());
166 assert!(c < *p, "not reduced: {} >= {} ", c, p);
167
168 let expected = a.add_mod(&b, &p);
169 assert_eq!(c, expected, "incorrect result");
170 }
171 }
172 }
173 }
174}