Skip to main content

crypto_bigint/uint/boxed/
shr.rs

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