Coverage Report

Created: 2025-12-31 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/crypto-bigint-0.5.5/src/uint/cmp.rs
Line
Count
Source
1
//! [`Uint`] comparisons.
2
//!
3
//! By default these are all constant-time and use the `subtle` crate.
4
5
use super::Uint;
6
use crate::{CtChoice, Limb};
7
use core::cmp::Ordering;
8
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
9
10
impl<const LIMBS: usize> Uint<LIMBS> {
11
    /// Return `b` if `c` is truthy, otherwise return `a`.
12
    #[inline]
13
0
    pub(crate) const fn ct_select(a: &Self, b: &Self, c: CtChoice) -> Self {
14
0
        let mut limbs = [Limb::ZERO; LIMBS];
15
16
0
        let mut i = 0;
17
0
        while i < LIMBS {
18
0
            limbs[i] = Limb::ct_select(a.limbs[i], b.limbs[i], c);
19
0
            i += 1;
20
0
        }
21
22
0
        Uint { limbs }
23
0
    }
24
25
    #[inline]
26
0
    pub(crate) const fn ct_swap(a: &Self, b: &Self, c: CtChoice) -> (Self, Self) {
27
0
        let new_a = Self::ct_select(a, b, c);
28
0
        let new_b = Self::ct_select(b, a, c);
29
30
0
        (new_a, new_b)
31
0
    }
32
33
    /// Returns the truthy value if `self`!=0 or the falsy value otherwise.
34
    #[inline]
35
0
    pub(crate) const fn ct_is_nonzero(&self) -> CtChoice {
36
0
        let mut b = 0;
37
0
        let mut i = 0;
38
0
        while i < LIMBS {
39
0
            b |= self.limbs[i].0;
40
0
            i += 1;
41
0
        }
42
0
        Limb(b).ct_is_nonzero()
43
0
    }
44
45
    /// Returns the truthy value if `self` is odd or the falsy value otherwise.
46
0
    pub(crate) const fn ct_is_odd(&self) -> CtChoice {
47
0
        CtChoice::from_lsb(self.limbs[0].0 & 1)
48
0
    }
49
50
    /// Returns the truthy value if `self == rhs` or the falsy value otherwise.
51
    #[inline]
52
59.3k
    pub(crate) const fn ct_eq(lhs: &Self, rhs: &Self) -> CtChoice {
53
59.3k
        let mut acc = 0;
54
59.3k
        let mut i = 0;
55
56
296k
        while i < LIMBS {
57
237k
            acc |= lhs.limbs[i].0 ^ rhs.limbs[i].0;
58
237k
            i += 1;
59
237k
        }
60
61
        // acc == 0 if and only if self == rhs
62
59.3k
        Limb(acc).ct_is_nonzero().not()
63
59.3k
    }
<crypto_bigint::uint::Uint<4>>::ct_eq
Line
Count
Source
52
59.3k
    pub(crate) const fn ct_eq(lhs: &Self, rhs: &Self) -> CtChoice {
53
59.3k
        let mut acc = 0;
54
59.3k
        let mut i = 0;
55
56
296k
        while i < LIMBS {
57
237k
            acc |= lhs.limbs[i].0 ^ rhs.limbs[i].0;
58
237k
            i += 1;
59
237k
        }
60
61
        // acc == 0 if and only if self == rhs
62
59.3k
        Limb(acc).ct_is_nonzero().not()
63
59.3k
    }
Unexecuted instantiation: <crypto_bigint::uint::Uint<_>>::ct_eq
64
65
    /// Returns the truthy value if `self <= rhs` and the falsy value otherwise.
66
    #[inline]
67
48.2k
    pub(crate) const fn ct_lt(lhs: &Self, rhs: &Self) -> CtChoice {
68
        // We could use the same approach as in Limb::ct_lt(),
69
        // but since we have to use Uint::wrapping_sub(), which calls `sbb()`,
70
        // there are no savings compared to just calling `sbb()` directly.
71
48.2k
        let (_res, borrow) = lhs.sbb(rhs, Limb::ZERO);
72
48.2k
        CtChoice::from_mask(borrow.0)
73
48.2k
    }
<crypto_bigint::uint::Uint<4>>::ct_lt
Line
Count
Source
67
48.2k
    pub(crate) const fn ct_lt(lhs: &Self, rhs: &Self) -> CtChoice {
68
        // We could use the same approach as in Limb::ct_lt(),
69
        // but since we have to use Uint::wrapping_sub(), which calls `sbb()`,
70
        // there are no savings compared to just calling `sbb()` directly.
71
48.2k
        let (_res, borrow) = lhs.sbb(rhs, Limb::ZERO);
72
48.2k
        CtChoice::from_mask(borrow.0)
73
48.2k
    }
Unexecuted instantiation: <crypto_bigint::uint::Uint<_>>::ct_lt
74
75
    /// Returns the truthy value if `self >= rhs` and the falsy value otherwise.
76
    #[inline]
77
0
    pub(crate) const fn ct_gt(lhs: &Self, rhs: &Self) -> CtChoice {
78
0
        let (_res, borrow) = rhs.sbb(lhs, Limb::ZERO);
79
0
        CtChoice::from_mask(borrow.0)
80
0
    }
Unexecuted instantiation: <crypto_bigint::uint::Uint<4>>::ct_gt
Unexecuted instantiation: <crypto_bigint::uint::Uint<_>>::ct_gt
81
82
    /// Returns the ordering between `self` and `rhs` as an i8.
83
    /// Values correspond to the Ordering enum:
84
    ///   -1 is Less
85
    ///   0 is Equal
86
    ///   1 is Greater
87
    #[inline]
88
0
    pub(crate) const fn ct_cmp(lhs: &Self, rhs: &Self) -> i8 {
89
0
        let mut i = 0;
90
0
        let mut borrow = Limb::ZERO;
91
0
        let mut diff = Limb::ZERO;
92
93
0
        while i < LIMBS {
94
0
            let (w, b) = rhs.limbs[i].sbb(lhs.limbs[i], borrow);
95
0
            diff = diff.bitor(w);
96
0
            borrow = b;
97
0
            i += 1;
98
0
        }
99
0
        let sgn = ((borrow.0 & 2) as i8) - 1;
100
0
        (diff.ct_is_nonzero().to_u8() as i8) * sgn
101
0
    }
Unexecuted instantiation: <crypto_bigint::uint::Uint<4>>::ct_cmp
Unexecuted instantiation: <crypto_bigint::uint::Uint<_>>::ct_cmp
102
103
    /// Returns the Ordering between `self` and `rhs` in variable time.
104
0
    pub const fn cmp_vartime(&self, rhs: &Self) -> Ordering {
105
0
        let mut i = LIMBS - 1;
106
        loop {
107
0
            let (val, borrow) = self.limbs[i].sbb(rhs.limbs[i], Limb::ZERO);
108
0
            if val.0 != 0 {
109
0
                return if borrow.0 != 0 {
110
0
                    Ordering::Less
111
                } else {
112
0
                    Ordering::Greater
113
                };
114
0
            }
115
0
            if i == 0 {
116
0
                return Ordering::Equal;
117
0
            }
118
0
            i -= 1;
119
        }
120
0
    }
121
}
122
123
impl<const LIMBS: usize> ConstantTimeEq for Uint<LIMBS> {
124
    #[inline]
125
59.3k
    fn ct_eq(&self, other: &Self) -> Choice {
126
59.3k
        Uint::ct_eq(self, other).into()
127
59.3k
    }
<crypto_bigint::uint::Uint<4> as subtle::ConstantTimeEq>::ct_eq
Line
Count
Source
125
59.3k
    fn ct_eq(&self, other: &Self) -> Choice {
126
59.3k
        Uint::ct_eq(self, other).into()
127
59.3k
    }
Unexecuted instantiation: <crypto_bigint::uint::Uint<_> as subtle::ConstantTimeEq>::ct_eq
128
}
129
130
impl<const LIMBS: usize> ConstantTimeGreater for Uint<LIMBS> {
131
    #[inline]
132
0
    fn ct_gt(&self, other: &Self) -> Choice {
133
0
        Uint::ct_gt(self, other).into()
134
0
    }
Unexecuted instantiation: <crypto_bigint::uint::Uint<4> as subtle::ConstantTimeGreater>::ct_gt
Unexecuted instantiation: <crypto_bigint::uint::Uint<_> as subtle::ConstantTimeGreater>::ct_gt
135
}
136
137
impl<const LIMBS: usize> ConstantTimeLess for Uint<LIMBS> {
138
    #[inline]
139
48.2k
    fn ct_lt(&self, other: &Self) -> Choice {
140
48.2k
        Uint::ct_lt(self, other).into()
141
48.2k
    }
<crypto_bigint::uint::Uint<4> as subtle::ConstantTimeLess>::ct_lt
Line
Count
Source
139
48.2k
    fn ct_lt(&self, other: &Self) -> Choice {
140
48.2k
        Uint::ct_lt(self, other).into()
141
48.2k
    }
Unexecuted instantiation: <crypto_bigint::uint::Uint<_> as subtle::ConstantTimeLess>::ct_lt
142
}
143
144
impl<const LIMBS: usize> Eq for Uint<LIMBS> {}
145
146
impl<const LIMBS: usize> Ord for Uint<LIMBS> {
147
0
    fn cmp(&self, other: &Self) -> Ordering {
148
0
        let c = Self::ct_cmp(self, other);
149
0
        match c {
150
0
            -1 => Ordering::Less,
151
0
            0 => Ordering::Equal,
152
0
            _ => Ordering::Greater,
153
        }
154
0
    }
Unexecuted instantiation: <crypto_bigint::uint::Uint<4> as core::cmp::Ord>::cmp
Unexecuted instantiation: <crypto_bigint::uint::Uint<_> as core::cmp::Ord>::cmp
155
}
156
157
impl<const LIMBS: usize> PartialOrd for Uint<LIMBS> {
158
0
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
159
0
        Some(self.cmp(other))
160
0
    }
161
}
162
163
impl<const LIMBS: usize> PartialEq for Uint<LIMBS> {
164
0
    fn eq(&self, other: &Self) -> bool {
165
0
        self.ct_eq(other).into()
166
0
    }
167
}
168
169
#[cfg(test)]
170
mod tests {
171
    use crate::{Integer, Zero, U128};
172
    use core::cmp::Ordering;
173
    use subtle::{ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
174
175
    #[test]
176
    fn is_zero() {
177
        assert!(bool::from(U128::ZERO.is_zero()));
178
        assert!(!bool::from(U128::ONE.is_zero()));
179
        assert!(!bool::from(U128::MAX.is_zero()));
180
    }
181
182
    #[test]
183
    fn is_odd() {
184
        assert!(!bool::from(U128::ZERO.is_odd()));
185
        assert!(bool::from(U128::ONE.is_odd()));
186
        assert!(bool::from(U128::MAX.is_odd()));
187
    }
188
189
    #[test]
190
    fn ct_eq() {
191
        let a = U128::ZERO;
192
        let b = U128::MAX;
193
194
        assert!(bool::from(a.ct_eq(&a)));
195
        assert!(!bool::from(a.ct_eq(&b)));
196
        assert!(!bool::from(b.ct_eq(&a)));
197
        assert!(bool::from(b.ct_eq(&b)));
198
    }
199
200
    #[test]
201
    fn ct_gt() {
202
        let a = U128::ZERO;
203
        let b = U128::ONE;
204
        let c = U128::MAX;
205
206
        assert!(bool::from(b.ct_gt(&a)));
207
        assert!(bool::from(c.ct_gt(&a)));
208
        assert!(bool::from(c.ct_gt(&b)));
209
210
        assert!(!bool::from(a.ct_gt(&a)));
211
        assert!(!bool::from(b.ct_gt(&b)));
212
        assert!(!bool::from(c.ct_gt(&c)));
213
214
        assert!(!bool::from(a.ct_gt(&b)));
215
        assert!(!bool::from(a.ct_gt(&c)));
216
        assert!(!bool::from(b.ct_gt(&c)));
217
    }
218
219
    #[test]
220
    fn ct_lt() {
221
        let a = U128::ZERO;
222
        let b = U128::ONE;
223
        let c = U128::MAX;
224
225
        assert!(bool::from(a.ct_lt(&b)));
226
        assert!(bool::from(a.ct_lt(&c)));
227
        assert!(bool::from(b.ct_lt(&c)));
228
229
        assert!(!bool::from(a.ct_lt(&a)));
230
        assert!(!bool::from(b.ct_lt(&b)));
231
        assert!(!bool::from(c.ct_lt(&c)));
232
233
        assert!(!bool::from(b.ct_lt(&a)));
234
        assert!(!bool::from(c.ct_lt(&a)));
235
        assert!(!bool::from(c.ct_lt(&b)));
236
    }
237
238
    #[test]
239
    fn cmp() {
240
        let a = U128::ZERO;
241
        let b = U128::ONE;
242
        let c = U128::MAX;
243
244
        assert_eq!(a.cmp(&b), Ordering::Less);
245
        assert_eq!(a.cmp(&c), Ordering::Less);
246
        assert_eq!(b.cmp(&c), Ordering::Less);
247
248
        assert_eq!(a.cmp(&a), Ordering::Equal);
249
        assert_eq!(b.cmp(&b), Ordering::Equal);
250
        assert_eq!(c.cmp(&c), Ordering::Equal);
251
252
        assert_eq!(b.cmp(&a), Ordering::Greater);
253
        assert_eq!(c.cmp(&a), Ordering::Greater);
254
        assert_eq!(c.cmp(&b), Ordering::Greater);
255
    }
256
257
    #[test]
258
    fn cmp_vartime() {
259
        let a = U128::ZERO;
260
        let b = U128::ONE;
261
        let c = U128::MAX;
262
263
        assert_eq!(a.cmp_vartime(&b), Ordering::Less);
264
        assert_eq!(a.cmp_vartime(&c), Ordering::Less);
265
        assert_eq!(b.cmp_vartime(&c), Ordering::Less);
266
267
        assert_eq!(a.cmp_vartime(&a), Ordering::Equal);
268
        assert_eq!(b.cmp_vartime(&b), Ordering::Equal);
269
        assert_eq!(c.cmp_vartime(&c), Ordering::Equal);
270
271
        assert_eq!(b.cmp_vartime(&a), Ordering::Greater);
272
        assert_eq!(c.cmp_vartime(&a), Ordering::Greater);
273
        assert_eq!(c.cmp_vartime(&b), Ordering::Greater);
274
    }
275
}