-
Notifications
You must be signed in to change notification settings - Fork 155
Description
While trying to make my Rust equality checks constant-time, I noticed that there's no examples on how to use the cmov crate. I understand the ASM behind it, and the core principles, but I'm unsure on how things should be structured beyond that.
For example, take these two implementations on comparing [T] ([u8] in this case):
Using cmov on the inner type:
impl ConstantTimeEq for u8 {
fn ct_eq(&self, rhs: &Self) -> bool {
let mut x = 0u8;
x.cmovnz(1u8, u8::from(self == rhs));
x != 0u8
}
}
impl<T: ConstantTimeEq> ConstantTimeEq for [T] {
fn ct_eq(&self, rhs: &Self) -> bool {
// short-circuit if lengths don't match
if self.len().ct_ne(&rhs.len()) {
return false;
}
let mut x = 1u8;
self.iter()
.zip(rhs.iter())
.for_each(|(a, b)| x.cmovz(0u8, u8::from(a.ct_eq(b))));
x != 0u8
}
}Using PartialEq on the inner type:
impl<T: PartialEq> ConstantTimeEq for [T] {
fn ct_eq(&self, rhs: &Self) -> bool {
// short-circuit if lengths don't match
if self.len().ct_ne(&rhs.len()) {
return false;
}
let mut x = 1u8;
self.iter()
.zip(rhs.iter())
.for_each(|(a, b)| x.cmovz(0u8, u8::from(a == b)));
x != 0u8
}
}Is the first one unnecessarily complex, or is it the correct usage? It's what I'm currently testing/using for my (draft) implementation.
Out of curiosity, I tested both with dudect_bencher. The inner-type-cmov approach sat at t ≈ +1.2, while the PartialEq implementation ranged anywhere from t ≈ +1.6 to t = +3.67626. Sometimes the t value would be negative (for both impls), however I couldn't find the definition of that anywhere.
I'd be happy to open a PR that adds some examples afterwards too!