#![no_std]
#![deny(missing_docs)]
#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")]
#![doc(html_root_url = "https://docs.rs/subtle/2.6.0")]
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
use core::cmp;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not};
use core::option::Option;
#[cfg(feature = "core_hint_black_box")]
use core::hint::black_box;
#[derive(Copy, Clone, Debug)]
pub struct Choice(u8);
impl Choice {
#[inline]
pub fn unwrap_u8(&self) -> u8 {
self.0
}
}
impl From<Choice> for bool {
#[inline]
fn from(source: Choice) -> bool {
debug_assert!((source.0 == 0u8) | (source.0 == 1u8));
source.0 != 0
}
}
impl BitAnd for Choice {
type Output = Choice;
#[inline]
fn bitand(self, rhs: Choice) -> Choice {
(self.0 & rhs.0).into()
}
}
impl BitAndAssign for Choice {
#[inline]
fn bitand_assign(&mut self, rhs: Choice) {
*self = *self & rhs;
}
}
impl BitOr for Choice {
type Output = Choice;
#[inline]
fn bitor(self, rhs: Choice) -> Choice {
(self.0 | rhs.0).into()
}
}
impl BitOrAssign for Choice {
#[inline]
fn bitor_assign(&mut self, rhs: Choice) {
*self = *self | rhs;
}
}
impl BitXor for Choice {
type Output = Choice;
#[inline]
fn bitxor(self, rhs: Choice) -> Choice {
(self.0 ^ rhs.0).into()
}
}
impl BitXorAssign for Choice {
#[inline]
fn bitxor_assign(&mut self, rhs: Choice) {
*self = *self ^ rhs;
}
}
impl Not for Choice {
type Output = Choice;
#[inline]
fn not(self) -> Choice {
(1u8 & (!self.0)).into()
}
}
#[cfg(not(feature = "core_hint_black_box"))]
#[inline(never)]
fn black_box<T: Copy>(input: T) -> T {
unsafe {
core::ptr::read_volatile(&input)
}
}
impl From<u8> for Choice {
#[inline]
fn from(input: u8) -> Choice {
debug_assert!((input == 0u8) | (input == 1u8));
Choice(black_box(input))
}
}
#[allow(unused_attributes)] pub trait ConstantTimeEq {
#[inline]
#[allow(unused_attributes)]
fn ct_eq(&self, other: &Self) -> Choice;
#[inline]
fn ct_ne(&self, other: &Self) -> Choice {
!self.ct_eq(other)
}
}
impl<T: ConstantTimeEq> ConstantTimeEq for [T] {
#[inline]
fn ct_eq(&self, _rhs: &[T]) -> Choice {
let len = self.len();
if len != _rhs.len() {
return Choice::from(0);
}
let mut x = 1u8;
for (ai, bi) in self.iter().zip(_rhs.iter()) {
x &= ai.ct_eq(bi).unwrap_u8();
}
x.into()
}
}
impl ConstantTimeEq for Choice {
#[inline]
fn ct_eq(&self, rhs: &Choice) -> Choice {
!(*self ^ *rhs)
}
}
macro_rules! generate_integer_equal {
($t_u:ty, $t_i:ty, $bit_width:expr) => {
impl ConstantTimeEq for $t_u {
#[inline]
fn ct_eq(&self, other: &$t_u) -> Choice {
let x: $t_u = self ^ other;
let y: $t_u = (x | x.wrapping_neg()) >> ($bit_width - 1);
((y ^ (1 as $t_u)) as u8).into()
}
}
impl ConstantTimeEq for $t_i {
#[inline]
fn ct_eq(&self, other: &$t_i) -> Choice {
(*self as $t_u).ct_eq(&(*other as $t_u))
}
}
};
}
generate_integer_equal!(u8, i8, 8);
generate_integer_equal!(u16, i16, 16);
generate_integer_equal!(u32, i32, 32);
generate_integer_equal!(u64, i64, 64);
#[cfg(feature = "i128")]
generate_integer_equal!(u128, i128, 128);
generate_integer_equal!(usize, isize, ::core::mem::size_of::<usize>() * 8);
impl ConstantTimeEq for cmp::Ordering {
#[inline]
fn ct_eq(&self, other: &Self) -> Choice {
(*self as i8).ct_eq(&(*other as i8))
}
}
#[allow(unused_attributes)] pub trait ConditionallySelectable: Copy {
#[inline]
#[allow(unused_attributes)]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self;
#[inline]
fn conditional_assign(&mut self, other: &Self, choice: Choice) {
*self = Self::conditional_select(self, other, choice);
}
#[inline]
fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
let t: Self = *a;
a.conditional_assign(&b, choice);
b.conditional_assign(&t, choice);
}
}
macro_rules! to_signed_int {
(u8) => {
i8
};
(u16) => {
i16
};
(u32) => {
i32
};
(u64) => {
i64
};
(u128) => {
i128
};
(i8) => {
i8
};
(i16) => {
i16
};
(i32) => {
i32
};
(i64) => {
i64
};
(i128) => {
i128
};
}
macro_rules! generate_integer_conditional_select {
($($t:tt)*) => ($(
impl ConditionallySelectable for $t {
#[inline]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t;
a ^ (mask & (a ^ b))
}
#[inline]
fn conditional_assign(&mut self, other: &Self, choice: Choice) {
let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t;
*self ^= mask & (*self ^ *other);
}
#[inline]
fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t;
let t = mask & (*a ^ *b);
*a ^= t;
*b ^= t;
}
}
)*)
}
generate_integer_conditional_select!( u8 i8);
generate_integer_conditional_select!( u16 i16);
generate_integer_conditional_select!( u32 i32);
generate_integer_conditional_select!( u64 i64);
#[cfg(feature = "i128")]
generate_integer_conditional_select!(u128 i128);
impl ConditionallySelectable for cmp::Ordering {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let a = *a as i8;
let b = *b as i8;
let ret = i8::conditional_select(&a, &b, choice);
unsafe { *((&ret as *const _) as *const cmp::Ordering) }
}
}
impl ConditionallySelectable for Choice {
#[inline]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Choice(u8::conditional_select(&a.0, &b.0, choice))
}
}
#[cfg(feature = "const-generics")]
impl<T, const N: usize> ConditionallySelectable for [T; N]
where
T: ConditionallySelectable,
{
#[inline]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut output = *a;
output.conditional_assign(b, choice);
output
}
fn conditional_assign(&mut self, other: &Self, choice: Choice) {
for (a_i, b_i) in self.iter_mut().zip(other) {
a_i.conditional_assign(b_i, choice)
}
}
}
#[allow(unused_attributes)] pub trait ConditionallyNegatable {
#[inline]
#[allow(unused_attributes)]
fn conditional_negate(&mut self, choice: Choice);
}
impl<T> ConditionallyNegatable for T
where
T: ConditionallySelectable,
for<'a> &'a T: Neg<Output = T>,
{
#[inline]
fn conditional_negate(&mut self, choice: Choice) {
let self_neg: T = -(self as &T);
self.conditional_assign(&self_neg, choice);
}
}
#[derive(Clone, Copy, Debug)]
pub struct CtOption<T> {
value: T,
is_some: Choice,
}
impl<T> From<CtOption<T>> for Option<T> {
fn from(source: CtOption<T>) -> Option<T> {
if source.is_some().unwrap_u8() == 1u8 {
Option::Some(source.value)
} else {
None
}
}
}
impl<T> CtOption<T> {
#[inline]
pub fn new(value: T, is_some: Choice) -> CtOption<T> {
CtOption {
value: value,
is_some: is_some,
}
}
pub fn expect(self, msg: &str) -> T {
assert_eq!(self.is_some.unwrap_u8(), 1, "{}", msg);
self.value
}
#[inline]
pub fn unwrap(self) -> T {
assert_eq!(self.is_some.unwrap_u8(), 1);
self.value
}
#[inline]
pub fn unwrap_or(self, def: T) -> T
where
T: ConditionallySelectable,
{
T::conditional_select(&def, &self.value, self.is_some)
}
#[inline]
pub fn unwrap_or_else<F>(self, f: F) -> T
where
T: ConditionallySelectable,
F: FnOnce() -> T,
{
T::conditional_select(&f(), &self.value, self.is_some)
}
#[inline]
pub fn is_some(&self) -> Choice {
self.is_some
}
#[inline]
pub fn is_none(&self) -> Choice {
!self.is_some
}
#[inline]
pub fn map<U, F>(self, f: F) -> CtOption<U>
where
T: Default + ConditionallySelectable,
F: FnOnce(T) -> U,
{
CtOption::new(
f(T::conditional_select(
&T::default(),
&self.value,
self.is_some,
)),
self.is_some,
)
}
#[inline]
pub fn and_then<U, F>(self, f: F) -> CtOption<U>
where
T: Default + ConditionallySelectable,
F: FnOnce(T) -> CtOption<U>,
{
let mut tmp = f(T::conditional_select(
&T::default(),
&self.value,
self.is_some,
));
tmp.is_some &= self.is_some;
tmp
}
#[inline]
pub fn or_else<F>(self, f: F) -> CtOption<T>
where
T: ConditionallySelectable,
F: FnOnce() -> CtOption<T>,
{
let is_none = self.is_none();
let f = f();
Self::conditional_select(&self, &f, is_none)
}
pub fn into_option(self) -> Option<T> {
self.into()
}
}
impl<T: ConditionallySelectable> ConditionallySelectable for CtOption<T> {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
CtOption::new(
T::conditional_select(&a.value, &b.value, choice),
Choice::conditional_select(&a.is_some, &b.is_some, choice),
)
}
}
impl<T: ConstantTimeEq> ConstantTimeEq for CtOption<T> {
#[inline]
fn ct_eq(&self, rhs: &CtOption<T>) -> Choice {
let a = self.is_some();
let b = rhs.is_some();
(a & b & self.value.ct_eq(&rhs.value)) | (!a & !b)
}
}
pub trait ConstantTimeGreater {
fn ct_gt(&self, other: &Self) -> Choice;
}
macro_rules! generate_unsigned_integer_greater {
($t_u: ty, $bit_width: expr) => {
impl ConstantTimeGreater for $t_u {
#[inline]
fn ct_gt(&self, other: &$t_u) -> Choice {
let gtb = self & !other; let mut ltb = !self & other; let mut pow = 1;
while pow < $bit_width {
ltb |= ltb >> pow; pow += pow;
}
let mut bit = gtb & !ltb; let mut pow = 1;
while pow < $bit_width {
bit |= bit >> pow; pow += pow;
}
Choice::from((bit & 1) as u8)
}
}
};
}
generate_unsigned_integer_greater!(u8, 8);
generate_unsigned_integer_greater!(u16, 16);
generate_unsigned_integer_greater!(u32, 32);
generate_unsigned_integer_greater!(u64, 64);
#[cfg(feature = "i128")]
generate_unsigned_integer_greater!(u128, 128);
impl ConstantTimeGreater for cmp::Ordering {
#[inline]
fn ct_gt(&self, other: &Self) -> Choice {
let a = (*self as i8) + 1;
let b = (*other as i8) + 1;
(a as u8).ct_gt(&(b as u8))
}
}
pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater {
#[inline]
fn ct_lt(&self, other: &Self) -> Choice {
!self.ct_gt(other) & !self.ct_eq(other)
}
}
impl ConstantTimeLess for u8 {}
impl ConstantTimeLess for u16 {}
impl ConstantTimeLess for u32 {}
impl ConstantTimeLess for u64 {}
#[cfg(feature = "i128")]
impl ConstantTimeLess for u128 {}
impl ConstantTimeLess for cmp::Ordering {
#[inline]
fn ct_lt(&self, other: &Self) -> Choice {
let a = (*self as i8) + 1;
let b = (*other as i8) + 1;
(a as u8).ct_lt(&(b as u8))
}
}
#[derive(Clone, Copy, Debug)]
pub struct BlackBox<T: Copy>(T);
impl<T: Copy> BlackBox<T> {
pub fn new(value: T) -> Self {
Self(value)
}
pub fn get(self) -> T {
black_box(self.0)
}
}