crypto_bigint/uint/boxed/
shl.rs1use crate::{
4 BoxedUint, ConstChoice, ConstantTimeSelect, Limb, ShlVartime, Word, WrappingShl, Zero,
5};
6use core::ops::{Shl, ShlAssign};
7use subtle::{Choice, ConstantTimeLess, CtOption};
8
9impl BoxedUint {
10 pub fn shl(&self, shift: u32) -> BoxedUint {
14 let (result, overflow) = self.overflowing_shl(shift);
15 assert!(!bool::from(overflow), "attempt to shift left with overflow");
16 result
17 }
18
19 pub fn shl_assign(&mut self, shift: u32) {
23 let overflow = self.overflowing_shl_assign(shift);
24 assert!(!bool::from(overflow), "attempt to shift left with overflow");
25 }
26
27 pub fn overflowing_shl(&self, shift: u32) -> (Self, Choice) {
32 let mut result = self.clone();
33 let overflow = result.overflowing_shl_assign(shift);
34 (result, overflow)
35 }
36
37 pub fn overflowing_shl_assign(&mut self, shift: u32) -> Choice {
41 let shift_bits = u32::BITS - (self.bits_precision() - 1).leading_zeros();
44 let overflow = !shift.ct_lt(&self.bits_precision());
45 let shift = shift % self.bits_precision();
46 let mut temp = self.clone();
47
48 for i in 0..shift_bits {
49 let bit = Choice::from(((shift >> i) & 1) as u8);
50 temp.set_zero();
51 self.shl_vartime_into(&mut temp, 1 << i)
53 .expect("shift within range");
54 self.ct_assign(&temp, bit);
55 }
56
57 #[cfg(feature = "zeroize")]
58 zeroize::Zeroize::zeroize(&mut temp);
59
60 self.conditional_set_zero(overflow);
61 overflow
62 }
63
64 pub fn wrapping_shl(&self, shift: u32) -> Self {
67 self.overflowing_shl(shift).0
68 }
69
70 pub fn wrapping_shl_vartime(&self, shift: u32) -> Self {
73 let mut result = Self::zero_with_precision(self.bits_precision());
74 let _ = self.shl_vartime_into(&mut result, shift);
75 result
76 }
77
78 #[inline(always)]
87 fn shl_vartime_into(&self, dest: &mut Self, shift: u32) -> Option<()> {
88 if shift >= self.bits_precision() {
89 return None;
90 }
91
92 let nlimbs = self.nlimbs();
93 let shift_num = (shift / Limb::BITS) as usize;
94 let rem = shift % Limb::BITS;
95
96 for i in shift_num..nlimbs {
97 dest.limbs[i] = self.limbs[i - shift_num];
98 }
99
100 if rem == 0 {
101 return Some(());
102 }
103
104 let mut carry = Limb::ZERO;
105
106 for i in shift_num..nlimbs {
107 let shifted = dest.limbs[i].shl(rem);
108 let new_carry = dest.limbs[i].shr(Limb::BITS - rem);
109 dest.limbs[i] = shifted.bitor(carry);
110 carry = new_carry;
111 }
112
113 Some(())
114 }
115
116 #[inline(always)]
123 pub fn shl_vartime(&self, shift: u32) -> Option<Self> {
124 let mut result = Self::zero_with_precision(self.bits_precision());
125 let success = self.shl_vartime_into(&mut result, shift);
126 success.map(|_| result)
127 }
128
129 pub(crate) fn overflowing_shl1(&self) -> (Self, Limb) {
131 let mut ret = self.clone();
132 let carry = ret.shl1_assign();
133 (ret, carry)
134 }
135
136 pub(crate) fn shl1_assign(&mut self) -> Limb {
138 let mut carry = self.limbs[0] >> Limb::HI_BIT;
139 self.limbs[0].shl_assign(1);
140 for i in 1..self.limbs.len() {
141 (self.limbs[i], carry) = ((self.limbs[i] << 1) | carry, self.limbs[i] >> Limb::HI_BIT);
142 }
143 carry
144 }
145
146 pub(crate) fn shl_limb(&self, shift: u32) -> (Self, Limb) {
149 let mut limbs = vec![Limb::ZERO; self.limbs.len()];
150
151 let nz = ConstChoice::from_u32_nonzero(shift);
152 let lshift = shift;
153 let rshift = nz.if_true_u32(Limb::BITS - shift);
154 let carry = nz.if_true_word(
155 self.limbs[self.limbs.len() - 1]
156 .0
157 .wrapping_shr(Word::BITS - shift),
158 );
159
160 limbs[0] = Limb(self.limbs[0].0 << lshift);
161 let mut i = 1;
162 while i < self.limbs.len() {
163 let mut limb = self.limbs[i].0 << lshift;
164 let hi = self.limbs[i - 1].0 >> rshift;
165 limb |= nz.if_true_word(hi);
166 limbs[i] = Limb(limb);
167 i += 1
168 }
169
170 (
171 BoxedUint {
172 limbs: limbs.into(),
173 },
174 Limb(carry),
175 )
176 }
177}
178
179macro_rules! impl_shl {
180 ($($shift:ty),+) => {
181 $(
182 impl Shl<$shift> for BoxedUint {
183 type Output = BoxedUint;
184
185 #[inline]
186 fn shl(self, shift: $shift) -> BoxedUint {
187 <&Self>::shl(&self, shift)
188 }
189 }
190
191 impl Shl<$shift> for &BoxedUint {
192 type Output = BoxedUint;
193
194 #[inline]
195 fn shl(self, shift: $shift) -> BoxedUint {
196 BoxedUint::shl(self, u32::try_from(shift).expect("invalid shift"))
197 }
198 }
199
200 impl ShlAssign<$shift> for BoxedUint {
201 fn shl_assign(&mut self, shift: $shift) {
202 BoxedUint::shl_assign(self, u32::try_from(shift).expect("invalid shift"))
203 }
204 }
205 )+
206 };
207}
208
209impl_shl!(i32, u32, usize);
210
211impl WrappingShl for BoxedUint {
212 fn wrapping_shl(&self, shift: u32) -> BoxedUint {
213 self.wrapping_shl(shift)
214 }
215}
216
217impl ShlVartime for BoxedUint {
218 fn overflowing_shl_vartime(&self, shift: u32) -> CtOption<Self> {
219 let (result, overflow) = self.overflowing_shl(shift);
220 CtOption::new(result, !overflow)
221 }
222 fn wrapping_shl_vartime(&self, shift: u32) -> Self {
223 self.wrapping_shl(shift)
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::BoxedUint;
230
231 #[test]
232 fn shl() {
233 let one = BoxedUint::one_with_precision(128);
234
235 assert_eq!(BoxedUint::from(2u8), &one << 1);
236 assert_eq!(BoxedUint::from(4u8), &one << 2);
237 assert_eq!(
238 BoxedUint::from(0x80000000000000000u128),
239 one.shl_vartime(67).unwrap()
240 );
241 }
242
243 #[test]
244 fn shl1_assign() {
245 let mut n = BoxedUint::from(0x3c442b21f19185fe433f0a65af902b8fu128);
246 let n_shl1 = BoxedUint::from(0x78885643e3230bfc867e14cb5f20571eu128);
247 n.shl1_assign();
248 assert_eq!(n, n_shl1);
249 }
250
251 #[test]
252 fn shl_vartime() {
253 let one = BoxedUint::one_with_precision(128);
254
255 assert_eq!(BoxedUint::from(2u8), one.shl_vartime(1).unwrap());
256 assert_eq!(BoxedUint::from(4u8), one.shl_vartime(2).unwrap());
257 assert_eq!(
258 BoxedUint::from(0x80000000000000000u128),
259 one.shl_vartime(67).unwrap()
260 );
261 }
262}