Skip to main content

Crate tc_constant_time

Crate tc_constant_time 

Source
Expand description

Conditional selection, comparison, and masked updates without heap allocation.

Choice holds one bit. ConditionallySelectable chooses between two values, and ConstantTimeEq compares them without an early exit on a mismatch. Both traits support all primitive integer types and fixed-size arrays. Equality also supports slices with public lengths. ConstantTimeOrd orders all primitive integers, and ConditionallyNegatable provides wrapping negation. fixed_time_eq deliberately reveals a byte-slice comparison. The crate is no_std and has no dependencies or feature flags.

§Selecting and comparing

A zero choice selects the first argument; a one choice selects the second. Keep intermediate results as Choice values when combining predicates. Calling Choice::unwrap_u8 exposes the bit for ordinary control flow.

use tc_constant_time::{Choice, ConditionallySelectable, ConstantTimeEq};

let expected = [1_u8, 2, 3, 4];
let received = [1_u8, 2, 3, 4];
let enabled = Choice::from_lsb(1);
let accept = expected.ct_eq(&received) & enabled;
let selected = <[u8; 4]>::conditional_select(&[0; 4], &received, accept);
assert_eq!(selected, received);
assert_eq!(accept.unwrap_u8(), 1);

Arrays retain their length and process every element. Two empty arrays compare equal, and selecting between them produces an empty array.

use tc_constant_time::{Choice, ConditionallySelectable, ConstantTimeEq};

let empty: [u32; 0] = [];
assert_eq!(empty.ct_eq(&empty).unwrap_u8(), 1);
assert_eq!(<[u32; 0]>::conditional_select(&empty, &empty, Choice::from_lsb(1)), empty);

§Signed integers

Selection, equality, ordering, and conditional negation support i8, i16, i32, i64, i128, and isize. Negation wraps at the type’s width, including its minimum value. Ordering follows each type’s signed numeric order.

use tc_constant_time::{
    Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq,
};

let yes = Choice::from_lsb(1);
assert_eq!(i8::conditional_select(&-7, &9, yes), 9);
let mut value = -3_i16;
value.conditional_assign(&5, yes);
assert_eq!(value, 5);
let mut minimum = i128::MIN;
minimum.conditional_negate(yes);
assert_eq!(minimum, i128::MIN);
let (mut a, mut b) = (isize::MIN, isize::MAX);
isize::conditional_swap(&mut a, &mut b, yes);
assert_eq!((a, b), (isize::MAX, isize::MIN));
assert_eq!(a.ct_eq(&isize::MAX).unwrap_u8(), 1);

§Timing contract

Trait implementations must avoid control flow and memory addresses that depend on secret input values. Public sizes, including array length, may affect execution time. Array implementations inherit the timing properties of their element implementations.

The built-in operations use integer masks and full array scans. core::hint::black_box is a best-effort optimization barrier, not a guarantee of constant-time machine code. Review generated code for the target compiler and hardware before relying on timing properties. Ordinary comparisons or branches after revealing a Choice are outside this contract.

Structs§

Choice
A one-bit value for masked selection and composable predicates.

Traits§

ConditionallyNegatable
Conditionally negates a value with wrapping arithmetic.
ConditionallySelectable
Selects without branches or addresses depending on the choice or values.
ConstantTimeEq
Equality without early exits on secret values.
ConstantTimeOrd
Numeric ordering without value-dependent branches or addresses.

Functions§

fixed_time_eq
Compares byte slices and deliberately reveals the equality result.