crypto_bigint/limb/
cmp.rs

1//! Limb comparisons
2
3use crate::{ConstChoice, Limb};
4use core::cmp::Ordering;
5use subtle::{
6    Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess,
7};
8
9impl Limb {
10    /// Is this limb an odd number?
11    #[inline]
12    pub fn is_odd(&self) -> Choice {
13        Choice::from(self.0 as u8 & 1)
14    }
15
16    /// Perform a comparison of the inner value in variable-time.
17    ///
18    /// Note that the [`PartialOrd`] and [`Ord`] impls wrap constant-time
19    /// comparisons using the `subtle` crate.
20    pub fn cmp_vartime(&self, other: &Self) -> Ordering {
21        self.0.cmp(&other.0)
22    }
23
24    /// Performs an equality check in variable-time.
25    pub const fn eq_vartime(&self, other: &Self) -> bool {
26        self.0 == other.0
27    }
28
29    /// Return `b` if `c` is truthy, otherwise return `a`.
30    #[inline]
31    pub(crate) const fn select(a: Self, b: Self, c: ConstChoice) -> Self {
32        Self(c.select_word(a.0, b.0))
33    }
34
35    /// Returns the truthy value if `self != 0` and the falsy value otherwise.
36    #[inline]
37    pub(crate) const fn is_nonzero(&self) -> ConstChoice {
38        ConstChoice::from_word_nonzero(self.0)
39    }
40}
41
42impl ConstantTimeEq for Limb {
43    #[inline]
44    fn ct_eq(&self, other: &Self) -> Choice {
45        self.0.ct_eq(&other.0)
46    }
47
48    #[inline]
49    fn ct_ne(&self, other: &Self) -> Choice {
50        self.0.ct_ne(&other.0)
51    }
52}
53
54impl ConstantTimeGreater for Limb {
55    #[inline]
56    fn ct_gt(&self, other: &Self) -> Choice {
57        ConstChoice::from_word_gt(self.0, other.0).into()
58    }
59}
60
61impl ConstantTimeLess for Limb {
62    #[inline]
63    fn ct_lt(&self, other: &Self) -> Choice {
64        ConstChoice::from_word_lt(self.0, other.0).into()
65    }
66}
67
68impl Eq for Limb {}
69
70impl Ord for Limb {
71    fn cmp(&self, other: &Self) -> Ordering {
72        let mut ret = Ordering::Less;
73        ret.conditional_assign(&Ordering::Equal, self.ct_eq(other));
74        ret.conditional_assign(&Ordering::Greater, self.ct_gt(other));
75        debug_assert_eq!(ret == Ordering::Less, bool::from(self.ct_lt(other)));
76        ret
77    }
78}
79
80impl PartialOrd for Limb {
81    #[inline]
82    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
83        Some(self.cmp(other))
84    }
85}
86
87impl PartialEq for Limb {
88    #[inline]
89    fn eq(&self, other: &Self) -> bool {
90        self.ct_eq(other).into()
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use crate::{Limb, Zero};
97    use core::cmp::Ordering;
98    use subtle::{ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
99
100    #[test]
101    fn is_zero() {
102        assert!(bool::from(Limb::ZERO.is_zero()));
103        assert!(!bool::from(Limb::ONE.is_zero()));
104        assert!(!bool::from(Limb::MAX.is_zero()));
105    }
106
107    #[test]
108    fn is_odd() {
109        assert!(!bool::from(Limb::ZERO.is_odd()));
110        assert!(bool::from(Limb::ONE.is_odd()));
111        assert!(bool::from(Limb::MAX.is_odd()));
112    }
113
114    #[test]
115    fn ct_eq() {
116        let a = Limb::ZERO;
117        let b = Limb::MAX;
118
119        assert!(bool::from(a.ct_eq(&a)));
120        assert!(!bool::from(a.ct_eq(&b)));
121        assert!(!bool::from(b.ct_eq(&a)));
122        assert!(bool::from(b.ct_eq(&b)));
123    }
124
125    #[test]
126    fn ct_gt() {
127        let a = Limb::ZERO;
128        let b = Limb::ONE;
129        let c = Limb::MAX;
130
131        assert!(bool::from(b.ct_gt(&a)));
132        assert!(bool::from(c.ct_gt(&a)));
133        assert!(bool::from(c.ct_gt(&b)));
134
135        assert!(!bool::from(a.ct_gt(&a)));
136        assert!(!bool::from(b.ct_gt(&b)));
137        assert!(!bool::from(c.ct_gt(&c)));
138
139        assert!(!bool::from(a.ct_gt(&b)));
140        assert!(!bool::from(a.ct_gt(&c)));
141        assert!(!bool::from(b.ct_gt(&c)));
142    }
143
144    #[test]
145    fn ct_lt() {
146        let a = Limb::ZERO;
147        let b = Limb::ONE;
148        let c = Limb::MAX;
149
150        assert!(bool::from(a.ct_lt(&b)));
151        assert!(bool::from(a.ct_lt(&c)));
152        assert!(bool::from(b.ct_lt(&c)));
153
154        assert!(!bool::from(a.ct_lt(&a)));
155        assert!(!bool::from(b.ct_lt(&b)));
156        assert!(!bool::from(c.ct_lt(&c)));
157
158        assert!(!bool::from(b.ct_lt(&a)));
159        assert!(!bool::from(c.ct_lt(&a)));
160        assert!(!bool::from(c.ct_lt(&b)));
161    }
162
163    #[test]
164    fn cmp() {
165        assert_eq!(Limb::ZERO.cmp(&Limb::ONE), Ordering::Less);
166        assert_eq!(Limb::ONE.cmp(&Limb::ONE), Ordering::Equal);
167        assert_eq!(Limb::MAX.cmp(&Limb::ONE), Ordering::Greater);
168    }
169}