Skip to main content

crypto_bigint/
checked.rs

1//! Checked arithmetic.
2
3use crate::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Choice, CtEq, CtOption, CtSelect};
4use core::ops::{Add, Div, Mul, Sub};
5
6#[cfg(feature = "serde")]
7use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9/// Provides intentionally-checked arithmetic on `T`.
10///
11/// Internally this leverages the [`CtOption`] type from the [`ctutils`] crate in order to
12/// handle overflows.
13#[derive(Copy, Clone, Debug)]
14pub struct Checked<T>(pub CtOption<T>);
15
16impl<T> Checked<T> {
17    /// Create a new checked arithmetic wrapper for the given value.
18    pub const fn new(val: T) -> Self {
19        Self(CtOption::some(val))
20    }
21
22    /// Create a new checked arithmetic wrapper for the given value.
23    #[cfg(test)]
24    pub(crate) fn none() -> Self
25    where
26        T: Default,
27    {
28        Self(CtOption::none())
29    }
30}
31
32impl<T> Add<Self> for Checked<T>
33where
34    T: CheckedAdd + CtSelect + Default,
35{
36    type Output = Checked<T>;
37
38    #[inline]
39    fn add(self, rhs: Self) -> Self::Output {
40        Checked(
41            self.0
42                .as_ref()
43                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_add(rhs))),
44        )
45    }
46}
47
48impl<T> Add<&Self> for Checked<T>
49where
50    T: CheckedAdd + CtSelect + Default,
51{
52    type Output = Checked<T>;
53
54    #[inline]
55    fn add(self, rhs: &Self) -> Self::Output {
56        Checked(
57            self.0
58                .as_ref()
59                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_add(rhs))),
60        )
61    }
62}
63
64impl<T> Add<Checked<T>> for &Checked<T>
65where
66    T: CheckedAdd + CtSelect + Default,
67{
68    type Output = Checked<T>;
69
70    #[inline]
71    fn add(self, rhs: Checked<T>) -> Self::Output {
72        Checked(
73            self.0
74                .as_ref()
75                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_add(rhs))),
76        )
77    }
78}
79
80impl<T> Add<&Checked<T>> for &Checked<T>
81where
82    T: CheckedAdd + CtSelect + Default,
83{
84    type Output = Checked<T>;
85
86    #[inline]
87    fn add(self, rhs: &Checked<T>) -> Self::Output {
88        Checked(
89            self.0
90                .as_ref()
91                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_add(rhs))),
92        )
93    }
94}
95
96impl<T> Sub<Self> for Checked<T>
97where
98    T: CheckedSub + CtSelect + Default,
99{
100    type Output = Checked<T>;
101
102    #[inline]
103    fn sub(self, rhs: Self) -> Self::Output {
104        Checked(
105            self.0
106                .as_ref()
107                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_sub(rhs))),
108        )
109    }
110}
111
112impl<T> Sub<&Self> for Checked<T>
113where
114    T: CheckedSub + CtSelect + Default,
115{
116    type Output = Checked<T>;
117
118    #[inline]
119    fn sub(self, rhs: &Self) -> Self::Output {
120        Checked(
121            self.0
122                .as_ref()
123                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_sub(rhs))),
124        )
125    }
126}
127
128impl<T> Sub<Checked<T>> for &Checked<T>
129where
130    T: CheckedSub + CtSelect + Default,
131{
132    type Output = Checked<T>;
133
134    #[inline]
135    fn sub(self, rhs: Checked<T>) -> Self::Output {
136        Checked(
137            self.0
138                .as_ref()
139                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_sub(rhs))),
140        )
141    }
142}
143
144impl<T> Sub<&Checked<T>> for &Checked<T>
145where
146    T: CheckedSub + CtSelect + Default,
147{
148    type Output = Checked<T>;
149
150    #[inline]
151    fn sub(self, rhs: &Checked<T>) -> Self::Output {
152        Checked(
153            self.0
154                .as_ref()
155                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_sub(rhs))),
156        )
157    }
158}
159
160impl<T> Mul<Self> for Checked<T>
161where
162    T: CheckedMul + CtSelect + Default,
163{
164    type Output = Checked<T>;
165
166    #[inline]
167    fn mul(self, rhs: Self) -> Self::Output {
168        Checked(
169            self.0
170                .as_ref()
171                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_mul(rhs))),
172        )
173    }
174}
175
176impl<T> Mul<&Self> for Checked<T>
177where
178    T: CheckedMul + CtSelect + Default,
179{
180    type Output = Checked<T>;
181
182    #[inline]
183    fn mul(self, rhs: &Self) -> Self::Output {
184        Checked(
185            self.0
186                .as_ref()
187                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_mul(rhs))),
188        )
189    }
190}
191
192impl<T> Mul<Checked<T>> for &Checked<T>
193where
194    T: CheckedMul + CtSelect + Default,
195{
196    type Output = Checked<T>;
197
198    #[inline]
199    fn mul(self, rhs: Checked<T>) -> Self::Output {
200        Checked(
201            self.0
202                .as_ref()
203                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_mul(rhs))),
204        )
205    }
206}
207
208impl<T> Mul<&Checked<T>> for &Checked<T>
209where
210    T: CheckedMul + CtSelect + Default,
211{
212    type Output = Checked<T>;
213
214    #[inline]
215    fn mul(self, rhs: &Checked<T>) -> Self::Output {
216        Checked(
217            self.0
218                .as_ref()
219                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_mul(rhs))),
220        )
221    }
222}
223
224impl<T> Div<Self> for Checked<T>
225where
226    T: CheckedDiv + CtSelect + Default,
227{
228    type Output = Checked<T>;
229
230    #[inline]
231    fn div(self, rhs: Self) -> Self::Output {
232        Checked(
233            self.0
234                .as_ref()
235                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_div(rhs))),
236        )
237    }
238}
239
240impl<T> Div<&Self> for Checked<T>
241where
242    T: CheckedDiv + CtSelect + Default,
243{
244    type Output = Checked<T>;
245
246    #[inline]
247    fn div(self, rhs: &Self) -> Self::Output {
248        Checked(
249            self.0
250                .as_ref()
251                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_div(rhs))),
252        )
253    }
254}
255
256impl<T> Div<Checked<T>> for &Checked<T>
257where
258    T: CheckedDiv + CtSelect + Default,
259{
260    type Output = Checked<T>;
261
262    #[inline]
263    fn div(self, rhs: Checked<T>) -> Self::Output {
264        Checked(
265            self.0
266                .as_ref()
267                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_div(rhs))),
268        )
269    }
270}
271
272impl<T> Div<&Checked<T>> for &Checked<T>
273where
274    T: CheckedDiv + CtSelect + Default,
275{
276    type Output = Checked<T>;
277
278    #[inline]
279    fn div(self, rhs: &Checked<T>) -> Self::Output {
280        Checked(
281            self.0
282                .as_ref()
283                .and_then(|lhs| rhs.0.as_ref().and_then(|rhs| lhs.checked_div(rhs))),
284        )
285    }
286}
287
288impl<T> CtEq for Checked<T>
289where
290    T: CtEq,
291{
292    #[inline]
293    fn ct_eq(&self, other: &Self) -> Choice {
294        self.0.ct_eq(&other.0)
295    }
296}
297
298impl<T> CtSelect for Checked<T>
299where
300    T: CtSelect,
301{
302    #[inline]
303    fn ct_select(&self, other: &Self, choice: Choice) -> Self {
304        Self(self.0.ct_select(&other.0, choice))
305    }
306}
307
308impl<T> Default for Checked<T>
309where
310    T: Default,
311{
312    fn default() -> Self {
313        Self::new(T::default())
314    }
315}
316
317impl<T> From<Checked<T>> for CtOption<T> {
318    fn from(checked: Checked<T>) -> CtOption<T> {
319        checked.0
320    }
321}
322
323impl<T> From<CtOption<T>> for Checked<T> {
324    fn from(ct_option: CtOption<T>) -> Checked<T> {
325        Checked(ct_option)
326    }
327}
328
329impl<T> From<Checked<T>> for Option<T> {
330    fn from(checked: Checked<T>) -> Option<T> {
331        checked.0.into()
332    }
333}
334
335#[cfg(feature = "serde")]
336impl<'de, T: Default + Deserialize<'de>> Deserialize<'de> for Checked<T> {
337    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
338    where
339        D: Deserializer<'de>,
340    {
341        let value = Option::<T>::deserialize(deserializer)?;
342        let choice = Choice::from_u8_lsb(value.is_some().into());
343        Ok(Self(CtOption::new(value.unwrap_or_default(), choice)))
344    }
345}
346
347#[cfg(feature = "serde")]
348impl<T: Copy + Serialize> Serialize for Checked<T> {
349    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
350    where
351        S: Serializer,
352    {
353        self.0.into_option().serialize(serializer)
354    }
355}
356
357#[cfg(feature = "subtle")]
358impl<T> subtle::ConditionallySelectable for Checked<T>
359where
360    T: Copy,
361    Self: CtSelect,
362{
363    #[inline]
364    fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
365        a.ct_select(b, choice.into())
366    }
367}
368
369#[cfg(feature = "subtle")]
370impl<T> subtle::ConstantTimeEq for Checked<T>
371where
372    Self: CtEq,
373{
374    #[inline]
375    fn ct_eq(&self, rhs: &Self) -> subtle::Choice {
376        CtEq::ct_eq(self, rhs).into()
377    }
378}
379
380#[cfg(test)]
381mod tests {
382    use super::Checked;
383    use crate::{
384        CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Choice, CtEq, CtOption, CtSelect, Limb,
385    };
386
387    // FIXME Could use itertools combinations or an equivalent iterator
388    const INPUTS: &[(Limb, Limb)] = &[
389        (Limb::ZERO, Limb::ZERO),
390        (Limb::ZERO, Limb::ONE),
391        (Limb::ZERO, Limb::MAX),
392        (Limb::ONE, Limb::ZERO),
393        (Limb::ONE, Limb::ONE),
394        (Limb::ONE, Limb::MAX),
395        (Limb::MAX, Limb::ZERO),
396        (Limb::MAX, Limb::ONE),
397        (Limb::MAX, Limb::MAX),
398    ];
399
400    #[test]
401    fn checked_new() {
402        assert_eq!(
403            Checked::<Limb>::default().0.into_option(),
404            Some(Limb::default())
405        );
406        for (a, _b) in INPUTS {
407            assert_eq!(Checked::<Limb>::new(*a).0.into_option(), Some(*a));
408        }
409    }
410
411    #[test]
412    fn checked_eq_select() {
413        let pairs: &[(Checked<Limb>, Checked<Limb>, bool)] = &[
414            (Checked::none(), Checked::none(), true),
415            (Checked::none(), Checked::new(Limb::ZERO), false),
416            (Checked::new(Limb::ZERO), Checked::none(), false),
417            (Checked::new(Limb::ZERO), Checked::new(Limb::ZERO), true),
418            (Checked::new(Limb::ZERO), Checked::new(Limb::ONE), false),
419        ];
420
421        for (a, b, eq) in pairs {
422            assert_eq!(a.ct_eq(b).to_bool(), *eq);
423            assert!(a.ct_select(b, Choice::FALSE).ct_eq(a).to_bool());
424            assert!(a.ct_select(b, Choice::TRUE).ct_eq(b).to_bool());
425            #[cfg(feature = "subtle")]
426            assert_eq!(bool::from(subtle::ConstantTimeEq::ct_eq(a, b)), *eq);
427            #[cfg(feature = "subtle")]
428            assert!(
429                subtle::ConditionallySelectable::conditional_select(
430                    a,
431                    b,
432                    subtle::Choice::from(0u8)
433                )
434                .ct_eq(a)
435                .to_bool()
436            );
437            #[cfg(feature = "subtle")]
438            assert!(
439                subtle::ConditionallySelectable::conditional_select(
440                    a,
441                    b,
442                    subtle::Choice::from(1u8)
443                )
444                .ct_eq(b)
445                .to_bool()
446            );
447        }
448    }
449
450    #[test]
451    fn checked_from() {
452        assert_eq!(Checked::<Limb>(CtOption::none()).0.into_option(), None);
453        assert_eq!(
454            Checked::<Limb>::default().0.into_option(),
455            Checked::<Limb>::from(CtOption::<Limb>::some(Limb::default()))
456                .0
457                .into_option(),
458        );
459        assert_eq!(
460            Checked::<Limb>::default().0.into_option(),
461            Into::<Option<Limb>>::into(Checked::<Limb>::default())
462        );
463        assert_eq!(
464            Checked::<Limb>::default().0.into_option(),
465            Into::<CtOption<Limb>>::into(Checked::<Limb>::default()).into_option()
466        );
467    }
468
469    #[allow(clippy::op_ref)]
470    #[test]
471    fn checked_add() {
472        for (a, b) in INPUTS {
473            let expect = a.checked_add(b).into_option();
474            assert_eq!(
475                (Checked::new(*a) + Checked::new(*b)).0.into_option(),
476                expect
477            );
478            assert_eq!(
479                (&Checked::new(*a) + Checked::new(*b)).0.into_option(),
480                expect
481            );
482            assert_eq!(
483                (Checked::new(*a) + &Checked::new(*b)).0.into_option(),
484                expect
485            );
486            assert_eq!(
487                (&Checked::new(*a) + &Checked::new(*b)).0.into_option(),
488                expect
489            );
490        }
491    }
492
493    #[allow(clippy::op_ref)]
494    #[test]
495    fn checked_sub() {
496        for (a, b) in INPUTS {
497            let expect = a.checked_sub(b).into_option();
498            assert_eq!(
499                (Checked::new(*a) - Checked::new(*b)).0.into_option(),
500                expect
501            );
502            assert_eq!(
503                (&Checked::new(*a) - Checked::new(*b)).0.into_option(),
504                expect
505            );
506            assert_eq!(
507                (Checked::new(*a) - &Checked::new(*b)).0.into_option(),
508                expect
509            );
510            assert_eq!(
511                (&Checked::new(*a) - &Checked::new(*b)).0.into_option(),
512                expect
513            );
514        }
515    }
516
517    #[allow(clippy::op_ref)]
518    #[test]
519    fn checked_mul() {
520        for (a, b) in INPUTS {
521            let expect = a.checked_mul(b).into_option();
522            assert_eq!(
523                (Checked::new(*a) * Checked::new(*b)).0.into_option(),
524                expect
525            );
526            assert_eq!(
527                (&Checked::new(*a) * Checked::new(*b)).0.into_option(),
528                expect
529            );
530            assert_eq!(
531                (Checked::new(*a) * &Checked::new(*b)).0.into_option(),
532                expect
533            );
534            assert_eq!(
535                (&Checked::new(*a) * &Checked::new(*b)).0.into_option(),
536                expect
537            );
538        }
539    }
540
541    #[allow(clippy::op_ref)]
542    #[test]
543    fn checked_div() {
544        for (a, b) in INPUTS {
545            let expect = a.checked_div(b).into_option();
546            assert_eq!(
547                (Checked::new(*a) / Checked::new(*b)).0.into_option(),
548                expect
549            );
550            assert_eq!(
551                (&Checked::new(*a) / Checked::new(*b)).0.into_option(),
552                expect
553            );
554            assert_eq!(
555                (Checked::new(*a) / &Checked::new(*b)).0.into_option(),
556                expect
557            );
558            assert_eq!(
559                (&Checked::new(*a) / &Checked::new(*b)).0.into_option(),
560                expect
561            );
562        }
563    }
564}