Skip to content

Commit

Permalink
feat: implement PartialOrd and Ord
Browse files Browse the repository at this point in the history
constant time implementation based on `crypto-bigint`
  • Loading branch information
dignifiedquire committed Oct 25, 2024
1 parent 064207e commit 2ecb7e1
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 1 deletion.
54 changes: 54 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,40 @@ impl Hash {
}
Ok(Hash::from(hash_bytes))
}

/// Returns the ordering between `self` and `rhs` as an i8.
/// Values correspond to the Ordering enum:
/// -1 is Less
/// 0 is Equal
/// 1 is Greater
#[inline]
const fn cmp(lhs: &Self, rhs: &Self) -> i8 {
// Implementation is based on https://github.com/RustCrypto/crypto-bigint/blob/master/src/uint/cmp.rs#L80

let mut i = 0;
let mut borrow = 0;
let mut diff = 0;

while i < 32 {
let (w, b) = sbb(rhs.0[i], lhs.0[i], borrow);
diff |= w;
borrow = b;
i += 1;
}
let sgn = ((borrow & 2) as i8) - 1;

((diff != 0) as i8) * sgn
}
}

/// Computes `lhs - (rhs + borrow)`, returning the result along with the new borrow.
#[inline(always)]
pub const fn sbb(lhs: u8, rhs: u8, borrow: u8) -> (u8, u8) {
let a = lhs as u16;
let b = rhs as u16;
let borrow = (borrow >> (8 - 1)) as u16;
let ret = a.wrapping_sub(b + borrow);
(ret as u8, (ret >> 8) as u8)
}

impl From<[u8; OUT_LEN]> for Hash {
Expand Down Expand Up @@ -317,6 +351,26 @@ impl PartialEq<[u8]> for Hash {

impl Eq for Hash {}

/// This implementation is constant-time.
impl Ord for Hash {
fn cmp(&self, other: &Self) -> cmp::Ordering {
let c = Self::cmp(self, other);
match c {
-1 => cmp::Ordering::Less,
0 => cmp::Ordering::Equal,
_ => cmp::Ordering::Greater,
}
}
}

/// This implementation is constant-time.
impl PartialOrd for Hash {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}

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`
Expand Down
13 changes: 12 additions & 1 deletion src/test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{CVBytes, CVWords, IncrementCounter, BLOCK_LEN, CHUNK_LEN, OUT_LEN};
use crate::{CVBytes, CVWords, Hash, IncrementCounter, BLOCK_LEN, CHUNK_LEN, OUT_LEN};
use arrayref::array_ref;
use arrayvec::ArrayVec;
use core::usize;
Expand Down Expand Up @@ -689,3 +689,14 @@ fn test_zeroize() {
));
assert_eq!(output_reader.position_within_block, 0);
}


#[test]
fn test_ordering() {
let a = Hash::from_bytes([0u8; 32]);
let b = Hash::from_bytes([1u8; 32]);

assert_eq!(a.cmp(&b), core::cmp::Ordering::Less);
assert_eq!(a.cmp(&a), core::cmp::Ordering::Equal);
assert_eq!(b.cmp(&a), core::cmp::Ordering::Greater);
}

0 comments on commit 2ecb7e1

Please sign in to comment.