Skip to content

cmov: add examples defining correct usage #868

@brxken128

Description

@brxken128

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions