Skip to content

Commit

Permalink
crypto-bigint: add Limb::is_odd and UInt::is_odd (#505)
Browse files Browse the repository at this point in the history
Both return a `Choice`
  • Loading branch information
tarcieri authored Jun 22, 2021
1 parent bbc99fd commit e955688
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 23 deletions.
83 changes: 83 additions & 0 deletions crypto-bigint/src/limb/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ impl Limb {
self.ct_eq(&Self::ZERO)
}

/// Is this limb an odd number?
#[inline]
pub fn is_odd(&self) -> Choice {
Choice::from(self.0 as u8 & 1)
}

/// Perform a comparison of the inner value in variable-time.
///
/// Note that the [`PartialOrd`] and [`Ord`] impls wrap constant-time
Expand Down Expand Up @@ -74,3 +80,80 @@ impl PartialEq for Limb {
self.ct_eq(other).into()
}
}

#[cfg(test)]
mod tests {
use crate::Limb;
use core::cmp::Ordering;
use subtle::{ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};

#[test]
fn is_zero() {
assert!(bool::from(Limb::ZERO.is_zero()));
assert!(!bool::from(Limb::ONE.is_zero()));
assert!(!bool::from(Limb::MAX.is_zero()));
}

#[test]
fn is_odd() {
assert!(!bool::from(Limb::ZERO.is_odd()));
assert!(bool::from(Limb::ONE.is_odd()));
assert!(bool::from(Limb::MAX.is_odd()));
}

#[test]
fn ct_eq() {
let a = Limb::ZERO;
let b = Limb::MAX;

assert!(bool::from(a.ct_eq(&a)));
assert!(!bool::from(a.ct_eq(&b)));
assert!(!bool::from(b.ct_eq(&a)));
assert!(bool::from(b.ct_eq(&b)));
}

#[test]
fn ct_gt() {
let a = Limb::ZERO;
let b = Limb::ONE;
let c = Limb::MAX;

assert!(bool::from(b.ct_gt(&a)));
assert!(bool::from(c.ct_gt(&a)));
assert!(bool::from(c.ct_gt(&b)));

assert!(!bool::from(a.ct_gt(&a)));
assert!(!bool::from(b.ct_gt(&b)));
assert!(!bool::from(c.ct_gt(&c)));

assert!(!bool::from(a.ct_gt(&b)));
assert!(!bool::from(a.ct_gt(&c)));
assert!(!bool::from(b.ct_gt(&c)));
}

#[test]
fn ct_lt() {
let a = Limb::ZERO;
let b = Limb::ONE;
let c = Limb::MAX;

assert!(bool::from(a.ct_lt(&b)));
assert!(bool::from(a.ct_lt(&c)));
assert!(bool::from(b.ct_lt(&c)));

assert!(!bool::from(a.ct_lt(&a)));
assert!(!bool::from(b.ct_lt(&b)));
assert!(!bool::from(c.ct_lt(&c)));

assert!(!bool::from(b.ct_lt(&a)));
assert!(!bool::from(c.ct_lt(&a)));
assert!(!bool::from(c.ct_lt(&b)));
}

#[test]
fn cmp() {
assert_eq!(Limb::ZERO.cmp(&Limb::ONE), Ordering::Less);
assert_eq!(Limb::ONE.cmp(&Limb::ONE), Ordering::Equal);
assert_eq!(Limb::MAX.cmp(&Limb::ONE), Ordering::Greater);
}
}
24 changes: 1 addition & 23 deletions crypto-bigint/src/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod array;

use crate::{Concat, Encoding, Limb, Split};
use core::fmt;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
use subtle::{Choice, ConditionallySelectable};

#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
Expand Down Expand Up @@ -55,15 +55,6 @@ impl<const LIMBS: usize> UInt<LIMBS> {
pub const fn into_limbs(self) -> [Limb; LIMBS] {
self.limbs
}

/// Determine if this [`UInt`] is equal to zero.
///
/// # Returns
///
/// If zero, return `Choice(1)`. Otherwise, return `Choice(0)`.
pub fn is_zero(&self) -> Choice {
self.ct_eq(&Self::ZERO)
}
}

// TODO(tarcieri): eventually phase this out?
Expand Down Expand Up @@ -196,13 +187,6 @@ impl_split! {
mod tests {
use crate::{Concat, Split, U128, U64};

// 2-limb example that's twice as wide as the native word size
#[cfg(target_pointer_width = "32")]
use crate::U64 as UIntEx;

#[cfg(target_pointer_width = "64")]
use crate::U128 as UIntEx;

#[test]
#[cfg(feature = "alloc")]
fn display() {
Expand All @@ -213,12 +197,6 @@ mod tests {
assert_eq!(hex, n.to_string());
}

#[test]
fn is_zero() {
assert!(bool::from(UIntEx::ZERO.is_zero()));
assert!(!bool::from(UIntEx::ONE.is_zero()));
}

#[test]
fn concat() {
let hi = U64::from_u64(0x0011223344556677);
Expand Down
34 changes: 34 additions & 0 deletions crypto-bigint/src/uint/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ use crate::Limb;
use core::cmp::Ordering;
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};

impl<const LIMBS: usize> UInt<LIMBS> {
/// Determine if this [`UInt`] is equal to zero.
///
/// # Returns
///
/// If zero, return `Choice(1)`. Otherwise, return `Choice(0)`.
pub fn is_zero(&self) -> Choice {
self.ct_eq(&Self::ZERO)
}

/// Is this [`UInt`] an odd number?
#[inline]
pub fn is_odd(&self) -> Choice {
self.limbs
.first()
.map(|limb| limb.is_odd())
.unwrap_or_else(|| Choice::from(0))
}
}

impl<const LIMBS: usize> ConstantTimeEq for UInt<LIMBS> {
fn ct_eq(&self, other: &Self) -> Choice {
self.limbs
Expand Down Expand Up @@ -69,6 +89,20 @@ mod tests {
Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess,
};

#[test]
fn is_zero() {
assert!(bool::from(U128::ZERO.is_zero()));
assert!(!bool::from(U128::ONE.is_zero()));
assert!(!bool::from(U128::MAX.is_zero()));
}

#[test]
fn is_odd() {
assert!(!bool::from(U128::ZERO.is_odd()));
assert!(bool::from(U128::ONE.is_odd()));
assert!(bool::from(U128::MAX.is_odd()));
}

#[test]
fn conditional_select() {
let a = U128::ZERO;
Expand Down

0 comments on commit e955688

Please sign in to comment.