ppad-fixed-0.1.3: Large fixed-width words and constant-time arithmetic.
Copyright(c) 2025 Jared Tobin
LicenseMIT
MaintainerJared Tobin <jared@ppad.tech>
Safe HaskellNone
LanguageHaskell2010

Data.Choice

Description

Primitives for constant-time choice.

The Choice type encodes truthy and falsy values as unboxed Word# bit masks.

Use the standard logical primitives (or, and, xor, not, eq') to manipulate in-flight Choice values. Use one of the selection functions to use a Choice to select a value in constant time, or decide to reduce a Choice to a Bool at the end of a sensitive computation.

Synopsis

Choice

data Choice :: TYPE 'WordRep Source #

Constant-time choice, encoded as a mask.

Note that Choice is defined as an unlifted newtype, and so a Choice value cannot be bound at the top level. You should work with it locally in the context of a computation.

Use one of the selection functions to select a Choice value in constant time, or decide to reduce it to a Bool at the end of a sensitive computation.

>>> decide (or# (false# ()) (true# ()))
True

decide :: Choice -> Bool Source #

Decide a Choice by reducing it to a Bool.

The decide function itself runs in constant time, but once it reduces a Choice to a Bool, any subsequent branching on the result is liable to introduce variable-time behaviour.

You should decide only at the end of a computation, after all security-sensitive computations have been carried out.

>>> decide (true# ())
True

true# :: () -> Choice Source #

Construct the truthy Choice.

>>> decide (true# ())
True

false# :: () -> Choice Source #

Construct the falsy Choice.

>>> decide (false# ())
False

to_word# :: Choice -> Word# Source #

Convert a Choice to an unboxed Word#.

This essentially "unboxes" the Choice for direct manipulation.

>>> import qualified GHC.Exts as Exts
>>> Exts.isTrue# (Exts.eqWord# 0## (to_word# (false# ())))
True

Construction

from_full_mask# :: Word# -> Choice Source #

Construct a Choice from an unboxed full-word mask.

The input is not checked to be a full-word mask.

>>> decide (from_full_mask# 0##)
False
>>> decide (from_full_mask# 0xFFFFFFFFF_FFFFFFFF##)
True

from_bit# :: Word# -> Choice Source #

Construct a Choice from an unboxed word, which should be either 0## or 1##.

The input is not checked to be a bit.

>>> decide (from_bit# 1##)
True

from_word_nonzero# :: Word# -> Choice Source #

Construct a Choice from a nonzero unboxed word.

The input is not checked to be nonzero.

>>> decide (from_word_nonzero# 2##)
True

from_word_eq# :: Word# -> Word# -> Choice Source #

Construct a Choice from an equality comparison.

>>> decide (from_word_eq# 0## 1##)
False
decide (from_word_eq# 1## 1##)
True

from_word_le# :: Word# -> Word# -> Choice Source #

Construct a 'Choice from an at-most comparison.

>>> decide (from_word_le# 0## 1##)
True
>>> decide (from_word_le# 1## 1##)
True

from_word_lt# :: Word# -> Word# -> Choice Source #

Construct a Choice from a less-than comparison.

>>> decide (from_word_lt# 0## 1##)
True
>>> decide (from_word_lt# 1## 1##)
False

from_word_gt# :: Word# -> Word# -> Choice Source #

Construct a Choice from a greater-than comparison.

>>> decide (from_word_gt# 0## 1##)
False
>>> decide (from_word_gt# 1## 1##)
False

Manipulation

or :: Choice -> Choice -> Choice Source #

Logical disjunction on Choice values.

>>> decide (or (true# ()) (false# ()))
True

and :: Choice -> Choice -> Choice Source #

Logical conjunction on Choice values.

>>> decide (and (true# ()) (false# ()))
False

xor :: Choice -> Choice -> Choice Source #

Logical inequality on Choice values.

>>> decide (xor (true# ()) (false# ()))
True

not :: Choice -> Choice Source #

Logically negate a Choice.

>>> decide (not (true# ()))
False
>>> decide (not (false# ()))
True

ne :: Choice -> Choice -> Choice Source #

Logical inequality on Choice values.

>>> decide (ne (true# ()) (false# ()))
True

eq :: Choice -> Choice -> Choice Source #

Logical equality on Choice values.

>>> decide (eq (true# ()) (false# ()))
False

Constant-time Selection

select_word# :: Word# -> Word# -> Choice -> Word# Source #

Select an unboxed word without branching, given a Choice.

>>> let w = C.select_word# 0## 1## (C.true# ()) in GHC.Word.W# w
1

select_wide# :: Limb2 -> Limb2 -> Choice -> Limb2 Source #

Select an unboxed two-limb word without branching, given a Choice.

select_wider# :: Limb4 -> Limb4 -> Choice -> Limb4 Source #

Select an unboxed four-limb word without branching, given a Choice.

Constant-time Equality

eq_word# :: Word# -> Word# -> Choice Source #

Compare unboxed words for equality in constant time.

>>> decide (eq_word# 0## 1##)
False

eq_wide# :: Limb2 -> Limb2 -> Choice Source #

Compare unboxed two-limb words for equality in constant time.

>>> decide (eq_wide (# 0##, 0## #) (# 0##, 0## #))
True

eq_wider# :: Limb4 -> Limb4 -> Choice Source #

Compare unboxed four-limb words for equality in constant time.

>>> let zero = (# 0##, 0##, 0##, 0## #) in decide (eq_wider# zero zero)
True