crypto_bigint/
checked.rs

1//! Checked arithmetic.
2
3use crate::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub};
4use core::ops::{Add, Div, Mul, Sub};
5use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
6
7#[cfg(feature = "serde")]
8use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10/// Provides intentionally-checked arithmetic on `T`.
11///
12/// Internally this leverages the [`CtOption`] type from the [`subtle`] crate
13/// in order to handle overflows.
14#[derive(Copy, Clone, Debug)]
15pub struct Checked<T>(pub CtOption<T>);
16
17impl<T> Checked<T> {
18    /// Create a new checked arithmetic wrapper for the given value.
19    pub fn new(val: T) -> Self {
20        Self(CtOption::new(val, Choice::from(1)))
21    }
22}
23
24impl<T> Add<Self> for Checked<T>
25where
26    T: CheckedAdd + ConditionallySelectable + Default,
27{
28    type Output = Checked<T>;
29
30    #[inline]
31    fn add(self, rhs: Self) -> Self::Output {
32        Checked(
33            self.0
34                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_add(&rhs))),
35        )
36    }
37}
38
39impl<T> Add<&Self> for Checked<T>
40where
41    T: CheckedAdd + ConditionallySelectable + Default,
42{
43    type Output = Checked<T>;
44
45    #[inline]
46    fn add(self, rhs: &Self) -> Self::Output {
47        Checked(
48            self.0
49                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_add(&rhs))),
50        )
51    }
52}
53
54impl<T> Add<Checked<T>> for &Checked<T>
55where
56    T: CheckedAdd + ConditionallySelectable + Default,
57{
58    type Output = Checked<T>;
59
60    #[inline]
61    fn add(self, rhs: Checked<T>) -> Self::Output {
62        Checked(
63            self.0
64                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_add(&rhs))),
65        )
66    }
67}
68
69impl<T> Add<&Checked<T>> for &Checked<T>
70where
71    T: CheckedAdd + ConditionallySelectable + Default,
72{
73    type Output = Checked<T>;
74
75    #[inline]
76    fn add(self, rhs: &Checked<T>) -> Self::Output {
77        Checked(
78            self.0
79                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_add(&rhs))),
80        )
81    }
82}
83
84impl<T> Sub<Self> for Checked<T>
85where
86    T: CheckedSub + ConditionallySelectable + Default,
87{
88    type Output = Checked<T>;
89
90    #[inline]
91    fn sub(self, rhs: Self) -> Self::Output {
92        Checked(
93            self.0
94                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_sub(&rhs))),
95        )
96    }
97}
98
99impl<T> Sub<&Self> for Checked<T>
100where
101    T: CheckedSub + ConditionallySelectable + Default,
102{
103    type Output = Checked<T>;
104
105    #[inline]
106    fn sub(self, rhs: &Self) -> Self::Output {
107        Checked(
108            self.0
109                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_sub(&rhs))),
110        )
111    }
112}
113
114impl<T> Sub<Checked<T>> for &Checked<T>
115where
116    T: CheckedSub + ConditionallySelectable + Default,
117{
118    type Output = Checked<T>;
119
120    #[inline]
121    fn sub(self, rhs: Checked<T>) -> Self::Output {
122        Checked(
123            self.0
124                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_sub(&rhs))),
125        )
126    }
127}
128
129impl<T> Sub<&Checked<T>> for &Checked<T>
130where
131    T: CheckedSub + ConditionallySelectable + Default,
132{
133    type Output = Checked<T>;
134
135    #[inline]
136    fn sub(self, rhs: &Checked<T>) -> Self::Output {
137        Checked(
138            self.0
139                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_sub(&rhs))),
140        )
141    }
142}
143
144impl<T> Mul<Self> for Checked<T>
145where
146    T: CheckedMul + ConditionallySelectable + Default,
147{
148    type Output = Checked<T>;
149
150    #[inline]
151    fn mul(self, rhs: Self) -> Self::Output {
152        Checked(
153            self.0
154                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_mul(&rhs))),
155        )
156    }
157}
158
159impl<T> Mul<&Self> for Checked<T>
160where
161    T: CheckedMul + ConditionallySelectable + Default,
162{
163    type Output = Checked<T>;
164
165    #[inline]
166    fn mul(self, rhs: &Self) -> Self::Output {
167        Checked(
168            self.0
169                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_mul(&rhs))),
170        )
171    }
172}
173
174impl<T> Mul<Checked<T>> for &Checked<T>
175where
176    T: CheckedMul + ConditionallySelectable + Default,
177{
178    type Output = Checked<T>;
179
180    #[inline]
181    fn mul(self, rhs: Checked<T>) -> Self::Output {
182        Checked(
183            self.0
184                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_mul(&rhs))),
185        )
186    }
187}
188
189impl<T> Mul<&Checked<T>> for &Checked<T>
190where
191    T: CheckedMul + ConditionallySelectable + Default,
192{
193    type Output = Checked<T>;
194
195    #[inline]
196    fn mul(self, rhs: &Checked<T>) -> Self::Output {
197        Checked(
198            self.0
199                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_mul(&rhs))),
200        )
201    }
202}
203
204impl<T> Div<Self> for Checked<T>
205where
206    T: CheckedDiv + ConditionallySelectable + Default,
207{
208    type Output = Checked<T>;
209
210    #[inline]
211    fn div(self, rhs: Self) -> Self::Output {
212        Checked(
213            self.0
214                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_div(&rhs))),
215        )
216    }
217}
218
219impl<T> Div<&Self> for Checked<T>
220where
221    T: CheckedDiv + ConditionallySelectable + Default,
222{
223    type Output = Checked<T>;
224
225    #[inline]
226    fn div(self, rhs: &Self) -> Self::Output {
227        Checked(
228            self.0
229                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_div(&rhs))),
230        )
231    }
232}
233
234impl<T> Div<Checked<T>> for &Checked<T>
235where
236    T: CheckedDiv + ConditionallySelectable + Default,
237{
238    type Output = Checked<T>;
239
240    #[inline]
241    fn div(self, rhs: Checked<T>) -> Self::Output {
242        Checked(
243            self.0
244                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_div(&rhs))),
245        )
246    }
247}
248
249impl<T> Div<&Checked<T>> for &Checked<T>
250where
251    T: CheckedDiv + ConditionallySelectable + Default,
252{
253    type Output = Checked<T>;
254
255    #[inline]
256    fn div(self, rhs: &Checked<T>) -> Self::Output {
257        Checked(
258            self.0
259                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_div(&rhs))),
260        )
261    }
262}
263
264impl<T: ConditionallySelectable> ConditionallySelectable for Checked<T> {
265    #[inline]
266    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
267        Self(CtOption::conditional_select(&a.0, &b.0, choice))
268    }
269}
270
271impl<T: ConstantTimeEq> ConstantTimeEq for Checked<T> {
272    #[inline]
273    fn ct_eq(&self, rhs: &Self) -> Choice {
274        self.0.ct_eq(&rhs.0)
275    }
276}
277
278impl<T> Default for Checked<T>
279where
280    T: Default,
281{
282    fn default() -> Self {
283        Self::new(T::default())
284    }
285}
286
287impl<T> From<Checked<T>> for CtOption<T> {
288    fn from(checked: Checked<T>) -> CtOption<T> {
289        checked.0
290    }
291}
292
293impl<T> From<CtOption<T>> for Checked<T> {
294    fn from(ct_option: CtOption<T>) -> Checked<T> {
295        Checked(ct_option)
296    }
297}
298
299impl<T> From<Checked<T>> for Option<T> {
300    fn from(checked: Checked<T>) -> Option<T> {
301        checked.0.into()
302    }
303}
304
305#[cfg(feature = "serde")]
306impl<'de, T: Default + Deserialize<'de>> Deserialize<'de> for Checked<T> {
307    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
308    where
309        D: Deserializer<'de>,
310    {
311        let value = Option::<T>::deserialize(deserializer)?;
312        let choice = Choice::from(value.is_some() as u8);
313        Ok(Self(CtOption::new(value.unwrap_or_default(), choice)))
314    }
315}
316
317#[cfg(feature = "serde")]
318impl<T: Copy + Serialize> Serialize for Checked<T> {
319    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
320    where
321        S: Serializer,
322    {
323        Option::<T>::from(self.0).serialize(serializer)
324    }
325}
326
327#[cfg(all(test, feature = "serde"))]
328#[allow(clippy::unwrap_used)]
329mod tests {
330
331    use crate::{Checked, U64};
332    use subtle::{Choice, ConstantTimeEq, CtOption};
333
334    #[test]
335    fn serde() {
336        let test = Checked::new(U64::from_u64(0x0011223344556677));
337
338        let serialized = bincode::serialize(&test).unwrap();
339        let deserialized: Checked<U64> = bincode::deserialize(&serialized).unwrap();
340
341        assert!(bool::from(test.ct_eq(&deserialized)));
342
343        let test = Checked::new(U64::ZERO) - Checked::new(U64::ONE);
344        assert!(bool::from(
345            test.ct_eq(&Checked(CtOption::new(U64::ZERO, Choice::from(0))))
346        ));
347
348        let serialized = bincode::serialize(&test).unwrap();
349        let deserialized: Checked<U64> = bincode::deserialize(&serialized).unwrap();
350
351        assert!(bool::from(test.ct_eq(&deserialized)));
352    }
353
354    #[test]
355    fn serde_owned() {
356        let test = Checked::new(U64::from_u64(0x0011223344556677));
357
358        let serialized = bincode::serialize(&test).unwrap();
359        let deserialized: Checked<U64> = bincode::deserialize_from(serialized.as_slice()).unwrap();
360
361        assert!(bool::from(test.ct_eq(&deserialized)));
362
363        let test = Checked::new(U64::ZERO) - Checked::new(U64::ONE);
364        assert!(bool::from(
365            test.ct_eq(&Checked(CtOption::new(U64::ZERO, Choice::from(0))))
366        ));
367
368        let serialized = bincode::serialize(&test).unwrap();
369        let deserialized: Checked<U64> = bincode::deserialize_from(serialized.as_slice()).unwrap();
370
371        assert!(bool::from(test.ct_eq(&deserialized)));
372    }
373}