Skip to content

Commit 14297e2

Browse files
authored
ctutils: add CtGt and CtLt (#1264)
Adds traits for computing greater than/less than in constant time. Uses overflowing subtraction as the implementation strategy for unsigned integers. Also adds impls for `cmp::Ordering`.
1 parent b5d010e commit 14297e2

File tree

4 files changed

+117
-1
lines changed

4 files changed

+117
-1
lines changed

ctutils/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,4 @@ mod traits;
7070

7171
pub use choice::Choice;
7272
pub use ct_option::CtOption;
73-
pub use traits::{ct_eq::CtEq, ct_select::CtSelect};
73+
pub use traits::{ct_eq::CtEq, ct_gt::CtGt, ct_lt::CtLt, ct_select::CtSelect};

ctutils/src/traits.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44
//! on in the same module.
55
66
pub(crate) mod ct_eq;
7+
pub(crate) mod ct_gt;
8+
pub(crate) mod ct_lt;
79
pub(crate) mod ct_select;

ctutils/src/traits/ct_gt.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use crate::Choice;
2+
use core::cmp;
3+
4+
/// Constant time greater than operation.
5+
pub trait CtGt {
6+
/// Compute whether `self > other` in constant time.
7+
fn ct_gt(&self, other: &Self) -> Choice;
8+
}
9+
10+
// Impl `CtGt` using overflowing subtraction
11+
macro_rules! impl_unsigned_ct_gt {
12+
( $($uint:ty),+ ) => {
13+
$(
14+
impl CtGt for $uint {
15+
#[inline]
16+
fn ct_gt(&self, other: &Self) -> Choice {
17+
let (_, overflow) = other.overflowing_sub(*self);
18+
Choice::new(overflow.into())
19+
}
20+
}
21+
)+
22+
};
23+
}
24+
25+
impl_unsigned_ct_gt!(u8, u16, u32, u64, u128);
26+
27+
impl CtGt for cmp::Ordering {
28+
#[inline]
29+
fn ct_gt(&self, other: &Self) -> Choice {
30+
// No impl of `CtGt` for `i8`, so use `u8`
31+
let a = (*self as i8) + 1;
32+
let b = (*other as i8) + 1;
33+
(a as u8).ct_gt(&(b as u8))
34+
}
35+
}
36+
37+
#[cfg(test)]
38+
mod tests {
39+
use super::CtGt;
40+
use core::cmp::Ordering;
41+
42+
#[test]
43+
fn ct_gt() {
44+
let a = 42u64;
45+
let b = 43u64;
46+
assert!(!a.ct_gt(&a).to_bool());
47+
assert!(!a.ct_gt(&b).to_bool());
48+
assert!(b.ct_gt(&a).to_bool());
49+
}
50+
51+
#[test]
52+
fn ordering() {
53+
assert!(!Ordering::Equal.ct_gt(&Ordering::Equal).to_bool());
54+
assert!(!Ordering::Less.ct_gt(&Ordering::Greater).to_bool());
55+
assert!(Ordering::Greater.ct_gt(&Ordering::Less).to_bool());
56+
}
57+
}

ctutils/src/traits/ct_lt.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use crate::Choice;
2+
use core::cmp;
3+
4+
/// Constant time less than operation.
5+
pub trait CtLt {
6+
/// Compute whether `self < other` in constant time.
7+
fn ct_lt(&self, other: &Self) -> Choice;
8+
}
9+
10+
// Impl `CtLt` using overflowing subtraction
11+
macro_rules! impl_unsigned_ct_lt {
12+
( $($uint:ty),+ ) => {
13+
$(
14+
impl CtLt for $uint {
15+
#[inline]
16+
fn ct_lt(&self, other: &Self) -> Choice {
17+
let (_, overflow) = self.overflowing_sub(*other);
18+
Choice::new(overflow.into())
19+
}
20+
}
21+
)+
22+
};
23+
}
24+
25+
impl_unsigned_ct_lt!(u8, u16, u32, u64, u128);
26+
27+
impl CtLt for cmp::Ordering {
28+
#[inline]
29+
fn ct_lt(&self, other: &Self) -> Choice {
30+
// No impl of `CtLt` for `i8`, so use `u8`
31+
let a = (*self as i8) + 1;
32+
let b = (*other as i8) + 1;
33+
(a as u8).ct_lt(&(b as u8))
34+
}
35+
}
36+
37+
#[cfg(test)]
38+
mod tests {
39+
use super::CtLt;
40+
use core::cmp::Ordering;
41+
42+
#[test]
43+
fn ct_lt() {
44+
let a = 42u64;
45+
let b = 43u64;
46+
assert!(!a.ct_lt(&a).to_bool());
47+
assert!(a.ct_lt(&b).to_bool());
48+
assert!(!b.ct_lt(&a).to_bool());
49+
}
50+
51+
#[test]
52+
fn ordering() {
53+
assert!(!Ordering::Equal.ct_lt(&Ordering::Equal).to_bool());
54+
assert!(Ordering::Less.ct_lt(&Ordering::Greater).to_bool());
55+
assert!(!Ordering::Greater.ct_lt(&Ordering::Less).to_bool());
56+
}
57+
}

0 commit comments

Comments
 (0)