tc_constant_time 0.1.0

Constant-time selection, comparison, and conditional arithmetic primitives.
Documentation
use tc_constant_time::{
    Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq, ConstantTimeOrd,
};

#[test]
fn select_and_equal_all_bytes() {
    for a in 0..=255_u8 {
        for b in 0..=255_u8 {
            assert_eq!(u8::conditional_select(&a, &b, Choice::from_lsb(0)), a);
            assert_eq!(u8::conditional_select(&a, &b, Choice::from_lsb(1)), b);
            assert_eq!(a.ct_eq(&b).unwrap_u8(), (a == b) as u8);
        }
    }
}

macro_rules! wide_integer_tests {
    ($name:ident, $word:ty) => {
        #[test]
        fn $name() {
            for bit in 0..<$word>::BITS {
                let single = (1 as $word) << bit;
                let values = [0, 1, single, !single, <$word>::MAX];
                for a in values {
                    for b in values {
                        assert_eq!(<$word>::conditional_select(&a, &b, Choice::from_lsb(0)), a);
                        assert_eq!(<$word>::conditional_select(&a, &b, Choice::from_lsb(1)), b);
                        assert_eq!(a.ct_eq(&b).unwrap_u8(), u8::from(a == b));
                    }
                }
            }
        }
    };
}

wide_integer_tests!(select_and_equal_u32_bit_boundaries, u32);
wide_integer_tests!(select_and_equal_u64_bit_boundaries, u64);
wide_integer_tests!(select_and_equal_usize_bit_boundaries, usize);

macro_rules! unsigned_boundaries {
    ($name:ident, $t:ty) => {
        #[test]
        fn $name() {
            let mut values = [0 as $t; 3 * <$t>::BITS as usize + 3];
            values[1] = <$t>::MAX;
            values[2] = 1;
            for bit in 0..<$t>::BITS {
                let power = (1 as $t) << bit;
                values[3 + 3 * bit as usize] = power;
                values[4 + 3 * bit as usize] = power.wrapping_sub(1);
                values[5 + 3 * bit as usize] = power.wrapping_add(1);
            }
            for a in values {
                for b in values {
                    assert_eq!(a.ct_eq(&b).unwrap_u8(), u8::from(a == b));
                    assert_eq!(a.ct_lt(&b).unwrap_u8(), u8::from(a < b));
                    assert_eq!(a.ct_gt(&b).unwrap_u8(), u8::from(a > b));
                    assert_eq!(a.ct_le(&b).unwrap_u8(), u8::from(a <= b));
                    assert_eq!(a.ct_ge(&b).unwrap_u8(), u8::from(a >= b));
                    for bit in 0..=1 {
                        let choice = Choice::from_lsb(bit);
                        assert_eq!(
                            <$t>::conditional_select(&a, &b, choice),
                            if bit == 0 { a } else { b }
                        );
                        let mut assigned = a;
                        assigned.conditional_assign(&b, choice);
                        assert_eq!(assigned, if bit == 0 { a } else { b });
                        let (mut left, mut right) = (a, b);
                        <$t>::conditional_swap(&mut left, &mut right, choice);
                        assert_eq!((left, right), if bit == 0 { (a, b) } else { (b, a) });
                        let mut negated = a;
                        negated.conditional_negate(choice);
                        assert_eq!(negated, if bit == 0 { a } else { a.wrapping_neg() });
                    }
                }
            }
        }
    };
}
unsigned_boundaries!(unsigned_u8_boundaries, u8);
unsigned_boundaries!(unsigned_u16_boundaries, u16);
unsigned_boundaries!(unsigned_u32_boundaries, u32);
unsigned_boundaries!(unsigned_u64_boundaries, u64);
unsigned_boundaries!(unsigned_u128_boundaries, u128);
unsigned_boundaries!(unsigned_usize_boundaries, usize);

macro_rules! signed_boundaries {
    ($name:ident, $t:ty) => {
        #[test]
        fn $name() {
            for bit in 0..<$t>::BITS {
                let power = (1 as $t) << bit;
                let values = [
                    <$t>::MIN,
                    <$t>::MIN + 1,
                    -1,
                    0,
                    1,
                    <$t>::MAX,
                    power,
                    !power,
                    power.wrapping_sub(1),
                    power.wrapping_add(1),
                    power.wrapping_neg(),
                ];
                for a in values {
                    for b in values {
                        assert_eq!(a.ct_eq(&b).unwrap_u8(), u8::from(a == b));
                        assert_eq!(a.ct_lt(&b).unwrap_u8(), u8::from(a < b));
                        assert_eq!(a.ct_gt(&b).unwrap_u8(), u8::from(a > b));
                        assert_eq!(a.ct_le(&b).unwrap_u8(), u8::from(a <= b));
                        assert_eq!(a.ct_ge(&b).unwrap_u8(), u8::from(a >= b));
                        for bit in 0..=1 {
                            let choice = Choice::from_lsb(bit);
                            assert_eq!(
                                <$t>::conditional_select(&a, &b, choice),
                                if bit == 0 { a } else { b }
                            );
                            let mut assigned = a;
                            assigned.conditional_assign(&b, choice);
                            assert_eq!(assigned, if bit == 0 { a } else { b });
                            let (mut left, mut right) = (a, b);
                            <$t>::conditional_swap(&mut left, &mut right, choice);
                            assert_eq!((left, right), if bit == 0 { (a, b) } else { (b, a) });
                            let mut negated = a;
                            negated.conditional_negate(choice);
                            assert_eq!(negated, if bit == 0 { a } else { a.wrapping_neg() });
                        }
                    }
                }
            }
        }
    };
}
signed_boundaries!(signed_i8_boundaries, i8);
signed_boundaries!(signed_i16_boundaries, i16);
signed_boundaries!(signed_i32_boundaries, i32);
signed_boundaries!(signed_i64_boundaries, i64);
signed_boundaries!(signed_i128_boundaries, i128);
signed_boundaries!(signed_isize_boundaries, isize);

#[test]
fn exhaustive_signed_byte_api_matches_public_references() {
    for a in i8::MIN..=i8::MAX {
        for b in i8::MIN..=i8::MAX {
            assert_eq!(a.ct_eq(&b).unwrap_u8(), u8::from(a == b));
            assert_eq!(a.ct_lt(&b).unwrap_u8(), u8::from(a < b));
            assert_eq!(a.ct_gt(&b).unwrap_u8(), u8::from(a > b));
            assert_eq!(a.ct_le(&b).unwrap_u8(), u8::from(a <= b));
            assert_eq!(a.ct_ge(&b).unwrap_u8(), u8::from(a >= b));
            for bit in 0..=1 {
                let choice = Choice::from_lsb(bit);
                assert_eq!(
                    i8::conditional_select(&a, &b, choice),
                    if bit == 0 { a } else { b }
                );
                let mut assigned = a;
                assigned.conditional_assign(&b, choice);
                assert_eq!(assigned, if bit == 0 { a } else { b });
                let (mut left, mut right) = (a, b);
                i8::conditional_swap(&mut left, &mut right, choice);
                assert_eq!((left, right), if bit == 0 { (a, b) } else { (b, a) });
                let mut negated = a;
                negated.conditional_negate(choice);
                assert_eq!(negated, if bit == 0 { a } else { a.wrapping_neg() });
            }
        }
    }
}