From 7f58e6a519f443f692ac2b1bbc20144cac4cfc59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louis=20Dupr=C3=A9=20Bertoni?= Date: Wed, 10 Jan 2024 11:42:46 +0200 Subject: [PATCH] Implement Ord and PartialOrd for Hash This removes the dependencies on constant_time_eq and replaces it with subtle. --- Cargo.toml | 4 ++-- src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 701121376..80ed360ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ neon = [] # --no-default-features, the only way to use the SIMD implementations in this # crate is to enable the corresponding instruction sets statically for the # entire build, with e.g. RUSTFLAGS="-C target-cpu=native". -std = [] +std = ["subtle/std"] # The `rayon` feature (disabled by default, but enabled for docs.rs) adds the # `update_rayon` and (in combination with `mmap` below) `update_mmap_rayon` @@ -94,12 +94,12 @@ features = ["mmap", "rayon", "serde", "zeroize"] [dependencies] arrayref = "0.3.5" arrayvec = { version = "0.7.4", default-features = false } -constant_time_eq = "0.3.0" cfg-if = "1.0.0" digest = { version = "0.10.1", features = [ "mac" ], optional = true } memmap2 = { version = "0.9", optional = true } rayon = { version = "1.2.1", optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } +subtle = { version = "2.5.0", default-features = false, features = ["i128"] } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"], optional = true } [dev-dependencies] diff --git a/src/lib.rs b/src/lib.rs index d661cb2db..e05e7d3bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,9 +135,10 @@ mod join; use arrayref::{array_mut_ref, array_ref}; use arrayvec::{ArrayString, ArrayVec}; -use core::cmp; use core::fmt; +use core::{cmp, cmp::Ordering}; use platform::{Platform, MAX_SIMD_DEGREE, MAX_SIMD_DEGREE_OR_2}; +use subtle::{ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess}; /// The number of bytes in a [`Hash`](struct.Hash.html), 32. pub const OUT_LEN: usize = 32; @@ -327,7 +328,7 @@ impl PartialEq for Hash { #[cfg(miri)] return constant_time_eq_miri(&self.0, &other.0); #[cfg(not(miri))] - constant_time_eq::constant_time_eq_32(&self.0, &other.0) + self.0.ct_eq(&other.0).into() } } @@ -338,7 +339,7 @@ impl PartialEq<[u8; OUT_LEN]> for Hash { #[cfg(miri)] return constant_time_eq_miri(&self.0, other); #[cfg(not(miri))] - constant_time_eq::constant_time_eq_32(&self.0, other) + self.0.ct_eq(other).into() } } @@ -349,12 +350,51 @@ impl PartialEq<[u8]> for Hash { #[cfg(miri)] return constant_time_eq_miri(&self.0, other); #[cfg(not(miri))] - constant_time_eq::constant_time_eq(&self.0, other) + self.0.ct_eq(other).into() } } impl Eq for Hash {} +/// This implementation is constant-time. +impl PartialOrd for Hash { + #[inline] + fn partial_cmp(&self, other: &Hash) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Hash { + #[inline] + fn cmp(&self, other: &Hash) -> Ordering { + let self_low = u128::from_le_bytes(self.0[..16].try_into().unwrap()); + let self_high = u128::from_le_bytes(self.0[16..].try_into().unwrap()); + let other_low = u128::from_le_bytes(other.0[..16].try_into().unwrap()); + let other_high = u128::from_le_bytes(other.0[16..].try_into().unwrap()); + let ordering = Ordering::conditional_select( + &Ordering::Less, + &Ordering::Greater, + self_high.ct_gt(&other_high), + ); + let ordering = Ordering::conditional_select( + &ordering, + &Ordering::Greater, + self_high.ct_eq(&other_high) & self_low.ct_gt(&other_low), + ); + let ordering = Ordering::conditional_select( + &ordering, + &Ordering::Equal, + self_high.ct_eq(&other_high) & self_low.ct_eq(&other_low), + ); + let ordering = Ordering::conditional_select( + &ordering, + &Ordering::Less, + self_high.ct_eq(&other_high) & self_low.ct_lt(&other_low), + ); + Ordering::conditional_select(&ordering, &Ordering::Less, self_high.ct_lt(&other_high)) + } +} + impl fmt::Display for Hash { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Formatting field as `&str` to reduce code size since the `Debug`