Skip to content

Commit 3e9fa33

Browse files
authored
crypto-bigint: impl ConstantTimeGreater/ConstantTimeLess for UInt (#459)
Also adds `Ord` and `PartialOrd` impls that use the subtle-based comparisons internally.
1 parent 9ccdfc6 commit 3e9fa33

File tree

2 files changed

+129
-22
lines changed

2 files changed

+129
-22
lines changed

crypto-bigint/src/uint.rs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
mod macros;
77

88
mod add;
9+
mod ct;
910
mod decode;
1011
mod from;
1112
mod mul;
@@ -15,8 +16,8 @@ mod sub;
1516
mod array;
1617

1718
use crate::{Concat, Limb, NumBits, NumBytes, Split};
18-
use core::fmt;
19-
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
19+
use core::{cmp::Ordering, fmt};
20+
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
2021

2122
/// Big unsigned integer.
2223
///
@@ -61,30 +62,30 @@ impl<const LIMBS: usize> AsRef<[Limb]> for UInt<LIMBS> {
6162
}
6263
}
6364

64-
impl<const LIMBS: usize> ConditionallySelectable for UInt<LIMBS> {
65-
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
66-
let mut limbs = [0; LIMBS];
67-
68-
for i in 0..LIMBS {
69-
limbs[i] = Limb::conditional_select(&a.limbs[0], &b.limbs[0], choice);
70-
}
71-
72-
Self { limbs }
65+
impl<const LIMBS: usize> Default for UInt<LIMBS> {
66+
fn default() -> Self {
67+
Self::ZERO
7368
}
7469
}
7570

76-
impl<const LIMBS: usize> ConstantTimeEq for UInt<LIMBS> {
77-
fn ct_eq(&self, other: &Self) -> Choice {
78-
self.limbs
79-
.iter()
80-
.zip(other.limbs.iter())
81-
.fold(Choice::from(1), |acc, (a, b)| acc & a.ct_eq(b))
71+
impl<const LIMBS: usize> Eq for UInt<LIMBS> {}
72+
73+
impl<const LIMBS: usize> Ord for UInt<LIMBS> {
74+
fn cmp(&self, other: &Self) -> Ordering {
75+
if self.ct_lt(other).into() {
76+
Ordering::Less
77+
} else if self.ct_gt(other).into() {
78+
Ordering::Greater
79+
} else {
80+
debug_assert!(bool::from(self.ct_eq(other)));
81+
Ordering::Equal
82+
}
8283
}
8384
}
8485

85-
impl<const LIMBS: usize> Default for UInt<LIMBS> {
86-
fn default() -> Self {
87-
Self::ZERO
86+
impl<const LIMBS: usize> PartialOrd for UInt<LIMBS> {
87+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
88+
Some(self.cmp(other))
8889
}
8990
}
9091

@@ -94,8 +95,6 @@ impl<const LIMBS: usize> PartialEq for UInt<LIMBS> {
9495
}
9596
}
9697

97-
impl<const LIMBS: usize> Eq for UInt<LIMBS> {}
98-
9998
impl<const LIMBS: usize> fmt::Display for UInt<LIMBS> {
10099
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101100
fmt::UpperHex::fmt(self, f)

crypto-bigint/src/uint/ct.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//! `subtle`-based constant time operations on [`UInt`].
2+
3+
use super::UInt;
4+
use crate::Limb;
5+
use subtle::{
6+
Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess,
7+
};
8+
9+
impl<const LIMBS: usize> ConditionallySelectable for UInt<LIMBS> {
10+
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
11+
let mut limbs = [0; LIMBS];
12+
13+
for i in 0..LIMBS {
14+
limbs[i] = Limb::conditional_select(&a.limbs[0], &b.limbs[0], choice);
15+
}
16+
17+
Self { limbs }
18+
}
19+
}
20+
21+
impl<const LIMBS: usize> ConstantTimeEq for UInt<LIMBS> {
22+
fn ct_eq(&self, other: &Self) -> Choice {
23+
self.limbs
24+
.iter()
25+
.zip(other.limbs.iter())
26+
.fold(Choice::from(1), |acc, (a, b)| acc & a.ct_eq(b))
27+
}
28+
}
29+
30+
impl<const LIMBS: usize> ConstantTimeGreater for UInt<LIMBS> {
31+
fn ct_gt(&self, other: &Self) -> Choice {
32+
let underflow = other.sbb(self, 0).1;
33+
!underflow.ct_eq(&0)
34+
}
35+
}
36+
37+
impl<const LIMBS: usize> ConstantTimeLess for UInt<LIMBS> {
38+
fn ct_lt(&self, other: &Self) -> Choice {
39+
let underflow = self.sbb(other, 0).1;
40+
!underflow.ct_eq(&0)
41+
}
42+
}
43+
44+
#[cfg(test)]
45+
mod tests {
46+
use crate::U128;
47+
use subtle::{
48+
Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess,
49+
};
50+
51+
#[test]
52+
fn conditional_select() {
53+
let a = U128::ZERO;
54+
let b = U128::MAX;
55+
56+
assert_eq!(U128::conditional_select(&a, &b, Choice::from(0)), a);
57+
assert_eq!(U128::conditional_select(&a, &b, Choice::from(1)), b);
58+
}
59+
60+
#[test]
61+
fn ct_eq() {
62+
let a = U128::ZERO;
63+
let b = U128::MAX;
64+
65+
assert!(bool::from(a.ct_eq(&a)));
66+
assert!(!bool::from(a.ct_eq(&b)));
67+
assert!(!bool::from(b.ct_eq(&a)));
68+
assert!(bool::from(b.ct_eq(&b)));
69+
}
70+
71+
#[test]
72+
fn ct_gt() {
73+
let a = U128::ZERO;
74+
let b = U128::ONE;
75+
let c = U128::MAX;
76+
77+
assert!(bool::from(b.ct_gt(&a)));
78+
assert!(bool::from(c.ct_gt(&a)));
79+
assert!(bool::from(c.ct_gt(&b)));
80+
81+
assert!(!bool::from(a.ct_gt(&a)));
82+
assert!(!bool::from(b.ct_gt(&b)));
83+
assert!(!bool::from(c.ct_gt(&c)));
84+
85+
assert!(!bool::from(a.ct_gt(&b)));
86+
assert!(!bool::from(a.ct_gt(&c)));
87+
assert!(!bool::from(b.ct_gt(&c)));
88+
}
89+
90+
#[test]
91+
fn ct_lt() {
92+
let a = U128::ZERO;
93+
let b = U128::ONE;
94+
let c = U128::MAX;
95+
96+
assert!(bool::from(a.ct_lt(&b)));
97+
assert!(bool::from(a.ct_lt(&c)));
98+
assert!(bool::from(b.ct_lt(&c)));
99+
100+
assert!(!bool::from(a.ct_lt(&a)));
101+
assert!(!bool::from(b.ct_lt(&b)));
102+
assert!(!bool::from(c.ct_lt(&c)));
103+
104+
assert!(!bool::from(b.ct_lt(&a)));
105+
assert!(!bool::from(c.ct_lt(&a)));
106+
assert!(!bool::from(c.ct_lt(&b)));
107+
}
108+
}

0 commit comments

Comments
 (0)