Skip to content

Commit

Permalink
arithmetic internals: Rewrite limbs_equal_limb_constant_time in Rust.
Browse files Browse the repository at this point in the history
  • Loading branch information
briansmith committed Jan 24, 2025
1 parent da0340b commit 3e2d0e9
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 42 deletions.
2 changes: 1 addition & 1 deletion src/arithmetic/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ impl<M> Elem<M, Unencoded> {
}

fn is_one(&self) -> bool {
limb::limbs_equal_limb_constant_time(&self.limbs, 1).leak()
limb::limbs_equal_limb_constant_time::<1>(&self.limbs).leak()
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/constant_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ mod tests {
use super::*;
use crate::{bssl, rand};

fn leak_in_test(a: BoolMask) -> bool {
a.leak()
}

#[test]
fn test_constant_time() -> Result<(), error::Unspecified> {
prefixed_extern! {
Expand Down Expand Up @@ -139,4 +143,12 @@ mod tests {

Ok(())
}

#[test]
fn test_bool_mask_bitwise_and_is_logical_and() {
assert!(leak_in_test(BoolMask::TRUE & BoolMask::TRUE));
assert!(!leak_in_test(BoolMask::TRUE & BoolMask::FALSE));
assert!(!leak_in_test(BoolMask::FALSE & BoolMask::TRUE));
assert!(!leak_in_test(BoolMask::FALSE & BoolMask::FALSE));
}
}
9 changes: 9 additions & 0 deletions src/constant_time/boolmask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

use super::Word;
use core::ops;

// BoolMask is either `BoolMask::TRUE` or `BoolMask::FALSE`.
#[repr(transparent)]
Expand All @@ -30,3 +31,11 @@ impl BoolMask {
self.0 != 0
}
}

impl ops::BitAnd for BoolMask {
type Output = Self;

fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
95 changes: 54 additions & 41 deletions src/limb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,19 @@ pub fn limbs_reject_even_leak_bit(limbs: &[Limb]) -> Result<(), error::Unspecifi

#[cfg(any(test, feature = "alloc"))]
#[inline]
pub fn limbs_equal_limb_constant_time(a: &[Limb], b: Limb) -> LimbMask {
prefixed_extern! {
fn LIMBS_equal_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask;
pub fn limbs_equal_limb_constant_time<const B: Limb>(a: &[Limb]) -> LimbMask {
// We intentionally don't provide a way to construct a LimbMask except from
// FFI code, so we can't just return LimbMask::FALSE on empty limbs. Thus,
// we need to handle zero specially. Checking for zero is better optimized
// differently anyway.
if B == 0 {
return limbs_are_zero_constant_time(a);
}
unsafe { LIMBS_equal_limb(a.as_ptr(), b, a.len()) }
let (bottom, rest): (Limb, &[Limb]) = match a {
&[bottom, ref rest @ ..] => (bottom, rest),
&[] => (0, a), // This requires B != 0
};
limb_is_zero_constant_time(bottom ^ B) & limbs_are_zero_constant_time(rest)
}

/// Returns the number of bits in `a`.
Expand Down Expand Up @@ -469,48 +477,53 @@ mod tests {
fn test_limbs_equal_limb() {
for zero in ZEROES {
let zero = &Vec::from_iter(zero.iter().copied().map(Limb::from));
assert!(leak_in_test(limbs_equal_limb_constant_time(
zero,
Limb::from(ZERO)
)));
assert!(leak_in_test(limbs_equal_limb_constant_time::<ZERO>(zero)));
}
for nonzero in NONZEROES {
let nonzero = &Vec::from_iter(nonzero.iter().copied().map(Limb::from));
assert!(!leak_in_test(limbs_equal_limb_constant_time(
nonzero,
Limb::from(ZERO)
)));
}
static EQUAL: &[(&[LeakyLimb], LeakyLimb)] = &[
(&[1], 1),
(&[MAX], MAX),
(&[1, 0], 1),
(&[MAX, 0, 0], MAX),
(&[0b100], 0b100),
(&[0b100, 0], 0b100),
];
for &(a, b) in EQUAL {
let a = &Vec::from_iter(a.iter().copied().map(Limb::from));
assert!(leak_in_test(limbs_equal_limb_constant_time(
a,
Limb::from(b)
assert!(!leak_in_test(limbs_equal_limb_constant_time::<ZERO>(
nonzero
)));
}
static UNEQUAL: &[(&[LeakyLimb], LeakyLimb)] = &[
(&[0], 1),
(&[2], 1),
(&[3], 1),
(&[1, 1], 1),
(&[0b100, 0b100], 0b100),
(&[1, 0, 0b100, 0, 0, 0, 0, 0], 1),
(&[1, 0, 0, 0, 0, 0, 0, 0b100], 1),
(&[MAX, MAX], MAX),
(&[MAX, 1], MAX),
];
for &(a, b) in UNEQUAL {
let a = &Vec::from_iter(a.iter().copied().map(Limb::from));
assert!(!leak_in_test(limbs_equal_limb_constant_time(a, b)));
}

// Equal
assert!(leak_in_test(limbs_equal_limb_constant_time::<1>(&[1])));
assert!(leak_in_test(limbs_equal_limb_constant_time::<MAX>(&[MAX])));
assert!(leak_in_test(limbs_equal_limb_constant_time::<1>(&[1, 0])));
assert!(leak_in_test(limbs_equal_limb_constant_time::<MAX>(&[
MAX, 0, 0
])));
assert!(leak_in_test(limbs_equal_limb_constant_time::<MAX>(&[
MAX, 0, 0
])));
assert!(leak_in_test(limbs_equal_limb_constant_time::<0b100>(&[
0b100
])));
assert!(leak_in_test(limbs_equal_limb_constant_time::<0b100>(&[
0b100, 0
])));

//
assert!(!leak_in_test(limbs_equal_limb_constant_time::<1>(&[0])));
assert!(!leak_in_test(limbs_equal_limb_constant_time::<1>(&[2])));
assert!(!leak_in_test(limbs_equal_limb_constant_time::<1>(&[3])));
assert!(!leak_in_test(limbs_equal_limb_constant_time::<1>(&[1, 1])));

assert!(!leak_in_test(limbs_equal_limb_constant_time::<0b100>(&[
0b100, 0b100
])));
assert!(!leak_in_test(limbs_equal_limb_constant_time::<1>(&[
1, 0, 0b100, 0, 0, 0, 0, 0
])));
assert!(!leak_in_test(limbs_equal_limb_constant_time::<1>(&[
1, 0, 0, 0, 0, 0, 0, 0b100
])));
assert!(!leak_in_test(limbs_equal_limb_constant_time::<MAX>(&[
MAX, MAX
])));
assert!(!leak_in_test(limbs_equal_limb_constant_time::<MAX>(&[
MAX, 1
])));
}

#[test]
Expand Down

0 comments on commit 3e2d0e9

Please sign in to comment.