Skip to main content

crypto_bigint/uint/boxed/
shl.rs

1//! [`BoxedUint`] bitwise left shift operations.
2
3use crate::{BoxedUint, Choice, CtOption, Limb, Shl, ShlAssign, ShlVartime, WrappingShl};
4
5impl BoxedUint {
6    /// Computes `self << shift`.
7    ///
8    /// # Panics
9    /// - if `shift >= self.bits_precision()`.
10    #[must_use]
11    #[track_caller]
12    pub fn shl(&self, shift: u32) -> BoxedUint {
13        let mut result = self.clone();
14        result.shl_assign(shift);
15        result
16    }
17
18    /// Computes `self <<= shift`.
19    ///
20    /// # Panics
21    /// - if `shift >= self.bits_precision()`.
22    pub fn shl_assign(&mut self, shift: u32) {
23        self.as_mut_uint_ref().shl_assign(shift);
24    }
25
26    /// Computes `self << shift`.
27    ///
28    /// Returns `self` and a truthy `Choice` if `shift >= self.bits_precision()`,
29    /// or the result and a falsy `Choice` otherwise.
30    #[must_use]
31    pub fn overflowing_shl(&self, shift: u32) -> CtOption<Self> {
32        let mut result = self.clone();
33        let overflow = result.as_mut_uint_ref().overflowing_shl_assign(shift);
34        CtOption::new(result, overflow.not())
35    }
36
37    /// Computes `self << shift` in variable-time.
38    ///
39    /// Returns `None` if `shift >= self.bits_precision()`, otherwise the shifted result.
40    #[must_use]
41    pub fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self> {
42        if shift < self.bits_precision() {
43            Some(self.unbounded_shl_vartime(shift))
44        } else {
45            None
46        }
47    }
48
49    /// Computes `self <<= shift`.
50    ///
51    /// Returns a truthy `Choice` if `shift >= self.bits_precision()` or a falsy `Choice` otherwise.
52    #[must_use]
53    pub fn overflowing_shl_assign(&mut self, shift: u32) -> Choice {
54        self.as_mut_uint_ref().overflowing_shl_assign(shift)
55    }
56
57    /// Computes `self <<= shift` in variable-time.
58    ///
59    /// If `shift >= self.bits_precision()`, shifts `self` in place and returns `false`.
60    /// Otherwise returns `true` and leaves `self` unmodified.
61    #[must_use]
62    pub fn overflowing_shl_assign_vartime(&mut self, shift: u32) -> bool {
63        if shift < self.bits_precision() {
64            self.as_mut_uint_ref().unbounded_shl_assign_vartime(shift);
65            false
66        } else {
67            true
68        }
69    }
70
71    /// Computes `self << shift` in a panic-free manner, producing zero in the case of overflow.
72    #[must_use]
73    pub fn unbounded_shl(&self, shift: u32) -> Self {
74        let mut result = self.clone();
75        result.unbounded_shl_assign(shift);
76        result
77    }
78
79    /// Computes `self <<= shift` in a panic-free manner, producing zero in the case of overflow.
80    pub fn unbounded_shl_assign(&mut self, shift: u32) {
81        self.as_mut_uint_ref().unbounded_shl_assign(shift);
82    }
83
84    /// Computes `self << shift` in variable-time in a panic-free manner, producing zero
85    /// in the case of overflow.
86    #[must_use]
87    pub fn unbounded_shl_vartime(&self, shift: u32) -> Self {
88        let mut result = Self::zero_with_precision(self.bits_precision());
89        self.as_uint_ref()
90            .unbounded_shl_vartime(shift, result.as_mut_uint_ref());
91        result
92    }
93
94    /// Computes `self <<= shift` in variable-time in a panic-free manner, producing zero
95    /// in the case of overflow.
96    pub fn unbounded_shl_assign_vartime(&mut self, shift: u32) {
97        self.as_mut_uint_ref().unbounded_shl_assign_vartime(shift);
98    }
99
100    /// Computes `self << shift` in a panic-free manner, masking off bits of `shift` which would cause the shift to
101    /// exceed the type's width.
102    #[must_use]
103    pub fn wrapping_shl(&self, shift: u32) -> Self {
104        let mut result = self.clone();
105        result.wrapping_shl_assign(shift);
106        result
107    }
108
109    /// Computes `self <<= shift` in a panic-free manner, masking off bits of `shift` which would cause the shift to
110    /// exceed the type's width.
111    pub fn wrapping_shl_assign(&mut self, shift: u32) {
112        self.as_mut_uint_ref().wrapping_shl_assign(shift);
113    }
114
115    /// Computes `self << shift` in variable-time in a panic-free manner, masking off bits of `shift` which would cause
116    /// the shift to exceed the type's width.
117    #[must_use]
118    #[allow(clippy::integer_division_remainder_used, reason = "vartime")]
119    pub fn wrapping_shl_vartime(&self, shift: u32) -> Self {
120        self.unbounded_shl_vartime(shift % self.bits_precision())
121    }
122
123    /// Computes `self <<= shift` in variable-time in a panic-free manner, masking
124    /// off bits of `shift` which would cause the shift to exceed the type's width.
125    pub fn wrapping_shl_assign_vartime(&mut self, shift: u32) {
126        self.as_mut_uint_ref().wrapping_shl_assign_vartime(shift);
127    }
128
129    /// Computes `self << shift`.
130    /// Returns `None` if `shift >= self.bits_precision()`.
131    ///
132    /// NOTE: this operation is variable time with respect to `shift` *ONLY*.
133    ///
134    /// When used with a fixed `shift`, this function is constant-time with respect to `self`.
135    #[inline(always)]
136    #[must_use]
137    pub fn shl_vartime(&self, shift: u32) -> Option<Self> {
138        self.overflowing_shl_vartime(shift)
139    }
140
141    /// Computes `self << 1` in constant-time.
142    pub(crate) fn shl1(&self) -> (Self, Limb) {
143        let mut ret = self.clone();
144        let carry = ret.as_mut_uint_ref().shl1_assign();
145        (ret, carry)
146    }
147}
148
149macro_rules! impl_shl {
150    ($($shift:ty),+) => {
151        $(
152            impl Shl<$shift> for BoxedUint {
153                type Output = BoxedUint;
154
155                #[inline]
156                fn shl(self, shift: $shift) -> BoxedUint {
157                    <&Self>::shl(&self, shift)
158                }
159            }
160
161            impl Shl<$shift> for &BoxedUint {
162                type Output = BoxedUint;
163
164                #[inline]
165                fn shl(self, shift: $shift) -> BoxedUint {
166                    BoxedUint::shl(self, u32::try_from(shift).expect("invalid shift"))
167                }
168            }
169
170            impl ShlAssign<$shift> for BoxedUint {
171                fn shl_assign(&mut self, shift: $shift) {
172                    BoxedUint::shl_assign(self, u32::try_from(shift).expect("invalid shift"))
173                }
174            }
175        )+
176    };
177}
178
179impl_shl!(i32, u32, usize);
180
181impl WrappingShl for BoxedUint {
182    fn wrapping_shl(&self, shift: u32) -> BoxedUint {
183        self.wrapping_shl(shift)
184    }
185}
186
187impl ShlVartime for BoxedUint {
188    fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self> {
189        self.overflowing_shl_vartime(shift)
190    }
191
192    fn unbounded_shl_vartime(&self, shift: u32) -> Self {
193        self.unbounded_shl_vartime(shift)
194    }
195
196    fn wrapping_shl_vartime(&self, shift: u32) -> Self {
197        self.wrapping_shl_vartime(shift)
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::BoxedUint;
204    use crate::{ShlVartime, WrappingShl};
205
206    #[test]
207    fn shl() {
208        let one = BoxedUint::one_with_precision(128);
209
210        assert_eq!(BoxedUint::from(2u8), &one << 1);
211        assert_eq!(BoxedUint::from(4u8), &one << 2);
212        assert_eq!(
213            BoxedUint::from(0x80000000000000000u128),
214            one.shl_vartime(67).unwrap()
215        );
216    }
217
218    #[test]
219    fn shl1_assign() {
220        let n = BoxedUint::from(0x3c442b21f19185fe433f0a65af902b8fu128);
221        let n_shl1 = BoxedUint::from(0x78885643e3230bfc867e14cb5f20571eu128);
222        assert_eq!(n.shl1().0, n_shl1);
223    }
224
225    #[test]
226    fn shl_vartime() {
227        let one = BoxedUint::one_with_precision(128);
228
229        assert_eq!(BoxedUint::from(2u8), one.shl_vartime(1).unwrap());
230        assert_eq!(BoxedUint::from(4u8), one.shl_vartime(2).unwrap());
231        assert_eq!(
232            BoxedUint::from(0x80000000000000000u128),
233            one.shl_vartime(67).unwrap()
234        );
235    }
236
237    #[test]
238    fn overflowing_shl() {
239        assert_eq!(
240            BoxedUint::one().overflowing_shl(2).into_option(),
241            Some(BoxedUint::from(4u8))
242        );
243        assert_eq!(BoxedUint::max(192).overflowing_shl(192).into_option(), None);
244        assert_eq!(
245            ShlVartime::overflowing_shl_vartime(&BoxedUint::one(), 2),
246            Some(BoxedUint::from(4u8))
247        );
248        assert_eq!(
249            ShlVartime::overflowing_shl_vartime(&BoxedUint::max(192), 192),
250            None
251        );
252        let mut boxed = BoxedUint::one();
253        assert!(!boxed.overflowing_shl_assign_vartime(2));
254        assert_eq!(boxed, BoxedUint::from(4u8));
255        let mut boxed = BoxedUint::max(192);
256        assert!(boxed.overflowing_shl_assign_vartime(192));
257    }
258
259    #[test]
260    fn unbounded_shl() {
261        assert_eq!(BoxedUint::one().unbounded_shl(2), BoxedUint::from(4u8));
262        assert_eq!(BoxedUint::max(192).unbounded_shl(192), BoxedUint::zero());
263        assert_eq!(
264            ShlVartime::unbounded_shl_vartime(&BoxedUint::one(), 2),
265            BoxedUint::from(4u8)
266        );
267        assert_eq!(
268            ShlVartime::unbounded_shl_vartime(&BoxedUint::max(192), 192),
269            BoxedUint::zero()
270        );
271    }
272
273    #[test]
274    fn wrapping_shl() {
275        assert_eq!(
276            WrappingShl::wrapping_shl(&BoxedUint::one(), 2),
277            BoxedUint::from(4u8)
278        );
279        assert_eq!(
280            WrappingShl::wrapping_shl(&BoxedUint::one_with_precision(192), 192),
281            BoxedUint::one()
282        );
283        assert_eq!(
284            ShlVartime::wrapping_shl_vartime(&BoxedUint::one(), 2),
285            BoxedUint::from(4u8)
286        );
287        assert_eq!(
288            ShlVartime::wrapping_shl_vartime(&BoxedUint::one_with_precision(192), 192),
289            BoxedUint::one()
290        );
291    }
292}