/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 | | } |