Skip to content

Commit

Permalink
arithmetic: Rewrite limbs_equal_limb_constant_time in Rust.
Browse files Browse the repository at this point in the history
Specialize it to the specific task we need.
  • Loading branch information
briansmith committed Jan 24, 2025
1 parent 91d81df commit 912c2d3
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 67 deletions.
1 change: 0 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,6 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {
"LIMBS_add_mod",
"LIMBS_are_zero",
"LIMBS_equal",
"LIMBS_equal_limb",
"LIMBS_less_than",
"LIMBS_reduce_once",
"LIMBS_select_512_32",
Expand Down
11 changes: 0 additions & 11 deletions crypto/limbs/limbs.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,6 @@ Limb LIMBS_equal(const Limb a[], const Limb b[], size_t num_limbs) {
return eq;
}

/* Returns 0xffff..f if |a == b|, and zero otherwise. |num_limbs| may be zero. */
Limb LIMBS_equal_limb(const Limb a[], Limb b, size_t num_limbs) {
if (num_limbs == 0) {
return constant_time_is_zero_w(b);
}
debug_assert_nonsecret(num_limbs >= 1);
Limb lo_equal = constant_time_eq_w(a[0], b);
Limb hi_zero = LIMBS_are_zero(&a[1], num_limbs - 1);
return constant_time_select_w(lo_equal, hi_zero, 0);
}

/* Returns 0xffff...f if |a| is less than |b|, and zero otherwise. */
Limb LIMBS_less_than(const Limb a[], const Limb b[], size_t num_limbs) {
debug_assert_nonsecret(num_limbs >= 1);
Expand Down
1 change: 0 additions & 1 deletion crypto/limbs/limbs.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ typedef crypto_word_t Limb;

Limb LIMBS_are_zero(const Limb a[], size_t num_limbs);
Limb LIMBS_equal(const Limb a[], const Limb b[], size_t num_limbs);
Limb LIMBS_equal_limb(const Limb a[], Limb b, size_t num_limbs);
void LIMBS_reduce_once(Limb r[], const Limb m[], size_t num_limbs);
void LIMBS_add_mod(Limb r[], const Limb a[], const Limb b[], const Limb m[],
size_t num_limbs);
Expand Down
11 changes: 2 additions & 9 deletions src/arithmetic/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,6 @@ impl<M> Elem<M, Unencoded> {
// See Falko Strenzke, "Manger's Attack revisited", ICICS 2010.
limb::big_endian_from_limbs(&self.limbs, out)
}

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

pub fn elem_mul<M, AF, BF>(
Expand Down Expand Up @@ -705,11 +701,8 @@ pub fn verify_inverses_consttime<M>(
b: Elem<M, Unencoded>,
m: &Modulus<M>,
) -> Result<(), error::Unspecified> {
if elem_mul(a, b, m).is_one() {
Ok(())
} else {
Err(error::Unspecified)
}
let r = elem_mul(a, b, m);
limb::verify_limbs_equal_1_leak_bit(&r.limbs)
}

#[inline]
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)
}
}
71 changes: 26 additions & 45 deletions src/limb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,14 @@ 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 verify_limbs_equal_1_leak_bit(a: &[Limb]) -> Result<(), error::Unspecified> {
if let [bottom, ref rest @ ..] = *a {
let equal = limb_is_zero_constant_time(bottom ^ 1) & limbs_are_zero_constant_time(rest);
if equal.leak() {
return Ok(());
}
}
unsafe { LIMBS_equal_limb(a.as_ptr(), b, a.len()) }
Err(error::Unspecified)
}

/// Returns the number of bits in `a`.
Expand Down Expand Up @@ -425,8 +428,6 @@ mod tests {
}
}

const ZERO: LeakyLimb = 0;

static ZEROES: &[&[LeakyLimb]] = &[
&[],
&[0],
Expand Down Expand Up @@ -463,49 +464,29 @@ mod tests {

#[test]
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)
)));
}
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 {
// Equal
static EQUAL: &[&[LeakyLimb]] = &[&[1], &[1, 0], &[1, 0, 0], &[1, 0, 0, 0, 0, 0, 0]];
for a 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!(matches!(verify_limbs_equal_1_leak_bit(a), Ok(())));
}
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),

// Unequal
static UNEQUAL: &[&[LeakyLimb]] = &[
&[0],
&[2],
&[3],
&[MAX],
&[0, 1],
&[1, 1],
&[0, 0, 0, 0, 0, 0, 0, 1],
&[0, 0, 0, 0, 1, 0, 0, 0],
&[0, 0, 0, 0, 1, 0, 0, 1],
&[MAX, 1],
];
for &(a, b) in UNEQUAL {
for a in UNEQUAL {
let a = &Vec::from_iter(a.iter().copied().map(Limb::from));
assert!(!leak_in_test(limbs_equal_limb_constant_time(a, b)));
assert!(matches!(verify_limbs_equal_1_leak_bit(a), Err(_)));
}
}

Expand Down

0 comments on commit 912c2d3

Please sign in to comment.