Getting started

Note: rust-timing-shield has not yet reached version 1.0. It may contain butterflies, unicorns, or a colourful assortment of small invertebrates.

The core idea behind rust-timing-shield is to leverage Rust’s type system to limit what operations can be performed on secret values. The programmer marks which values require timing leak protection, and rust-timing-shield enforces those constraints at compile time.

To add timing protection to a program, indicate which variables require timing protection by using one of the timing-protected (Tp) types provided by rust-timing-shield:

let protected_value = TpU32::protect(some_u32);

Timing-protected types only allow operations that are possible to perform in constant-time (e.g. addition, subtraction, bit shifting). Other operations (e.g. division) will trigger a compile-time type error.

let shifted_value = protected_value << 3;

// Compile-time error (division is variable-time and therefore not allowed):
let divided = shifted_value / 5;

Operations on timing-protected values return values that are also timing-protected.

There’s an escape hatch. The expose() method will recover the original unprotected value:

let unprotected = shifted_value.expose();
// unprotected is a u32

Be careful about using expose(), since any code using the exposed value can easily introduce a timing leak. Typically, expose() is used on public values (e.g. a ciphertext). expose() can also be used when a secret value must be provided to an interface (e.g. networking, file I/O) that does not use rust-timing-shield.

If you have to use expose(), use it as late as possible in a computation, and think carefully about what values you are exposing:

// DANGEROUS (may leak a and b):
if a.expose() == b.expose() { ... }

// Safer (only leaks result of equality test):
if a.tp_eq(&b).expose() { ... }

Comparison operators

With rust-timing-shield, if an input to an operator is timing-protected, then the output will be timing-protected as well. This is an important feature, since knowing the output of an operation will, in most cases, easily reveal information about the inputs.

Rust does not yet have full support for overloading the ==, !=, <, <=, >, and >= operators. In particular, it requires the outputs of these operations to be an unprotected bool, which would violate the principle stated above.

Instead, timing-protected types implement the traits TpEq and TpOrd:

let a = TpU32::protect(a_unprotected);
let b = TpU32::protect(b_unprotected);

// all comparison ops return a TpBool
let is_equal = a.tp_eq(&b);
let is_not_equal = a.tp_not_eq(&b);
let is_less_than = a.tp_lt(&b);
let is_less_than_or_equal = a.tp_lt_eq(&b);
let is_greater_than = a.tp_gt(&b);
let is_greater_than_or_equal = a.tp_gt_eq(&b);

Mixed-input operations

Some operations (e.g. addition, tp_eq) do not require all inputs to be timing protected. For example, it is legal to add a TpU32 to a u32, or to compare a u64 to a TpU64 using tp_eq.

Operations are only required to protect inputs with Tp types. It is perfectly legal for an implementation of tp_eq to leak information about one of the inputs if that input is not a timing-protected type. If a value requires protection, you must indicate this by using a protect method from the corresponding Tp type.

Timing-protected conditional swap, select

For convenience, the TpBool type provides a method for conditionally swapping two values without leaking whether the swap occurred:

// Swaps a and b if and only if some_condition is true
some_condition.cond_swap(&mut a, &mut b);

Additionally, TpBool provides a method for selecting one of two values without leaking which value was selected:

// If another_condition is true, then c = a, otherwise c = b
let c = another_condition.select(a, b);

Vectors, slices, etc

rust-timing-shield supports Vecs and slices of Tp types for some operations such as tp_eq:

let actual_mac: Vec<TpU8>;
let expected_mac: Vec<TpU8>;

// Compare the two byte vectors in constant-time
if actual_mac.tp_eq(&expected_mac).expose() {
	// ...
}

Note that a Vec<TpU8> is a vector of timing-protected bytes, not a timing-protected vector of bytes. As a result, tp_eq can (and will) leak information like the number of items in the vector, since Vec is not a timing-protected type. The actual values in the vector, however, will remain protected because TpU8 is a timing-protected type.

Vector and slice support is a work in progress. If there is a particular use case that would benefit from more comprehensive support for collection types in rust-timing-shield, I encourage you to open an issue on GitHub to provide feedback.

Function signatures

In order to make code easier to read and audit, it is recommended that users of rust-timing-shield follow these conventions for function signatures:

A function’s parameter types indicate what can be expected of the function. A function’s return type indicates what is expected of callers of the function.