#[cfg(feature = "stream")]
use crate::{Error, Vec, VsssResult};
#[cfg(feature = "stream")]
use futures_core::Stream;
#[cfg(feature = "stream")]
pub(crate) async fn collect_stream_exact<T>(
count: usize,
stream: impl Stream<Item = T>,
exhausted: Error,
) -> VsssResult<Vec<T>> {
let mut stream = core::pin::pin!(stream);
let mut items = Vec::with_capacity(count);
for _ in 0..count {
let item = core::future::poll_fn(|cx| stream.as_mut().poll_next(cx))
.await
.ok_or(exhausted)?;
items.push(item);
}
Ok(items)
}
#[inline]
pub(crate) fn uniform_nonzero_u8(bits: u32, modulus: u32) -> u8 {
(bits % modulus + 1) as u8
}
#[inline]
pub(crate) fn field_bounded_add(a: u8, b: u8, field_size: u16) -> u8 {
let sum = a as u16 + b as u16;
let in_range = ((sum < field_size) as u8).wrapping_neg();
(sum as u8) & in_range
}
pub trait CtIsZero {
fn ct_is_zero(&self) -> subtle::Choice;
}
pub trait CtIsNotZero {
fn ct_is_not_zero(&self) -> subtle::Choice;
}
impl CtIsZero for &[u8] {
fn ct_is_zero(&self) -> subtle::Choice {
let mut t = 0i8;
for b in *self {
t |= *b as i8;
}
t.ct_is_zero()
}
}
impl CtIsZero for [u8] {
fn ct_is_zero(&self) -> subtle::Choice {
let mut t = 0i8;
for b in self {
t |= *b as i8;
}
t.ct_is_zero()
}
}
impl CtIsZero for u8 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self as i8;
let a = ((t | t.wrapping_neg()) >> 7) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsZero for i8 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 7) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsZero for u16 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self as i16;
let a = ((t | t.wrapping_neg()) >> 15) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsZero for i16 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 15) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsZero for u32 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self as i32;
let a = ((t | t.wrapping_neg()) >> 31) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsZero for i32 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 31) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsZero for u64 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self as i64;
let a = ((t | t.wrapping_neg()) >> 63) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsZero for i64 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 63) + 1;
subtle::Choice::from(a as u8)
}
}
#[cfg(target_pointer_width = "64")]
impl CtIsZero for u128 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self as i128;
let a = ((t | t.wrapping_neg()) >> 127) + 1;
subtle::Choice::from(a as u8)
}
}
#[cfg(target_pointer_width = "64")]
impl CtIsZero for i128 {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 127) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsZero for usize {
fn ct_is_zero(&self) -> subtle::Choice {
let t = *self as isize;
let a = ((t | t.wrapping_neg()) >> (usize::BITS - 1)) + 1;
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for &[u8] {
fn ct_is_not_zero(&self) -> subtle::Choice {
let mut t = 0i8;
for b in *self {
t |= *b as i8;
}
t.ct_is_not_zero()
}
}
impl CtIsNotZero for [u8] {
fn ct_is_not_zero(&self) -> subtle::Choice {
let mut t = 0i8;
for b in self {
t |= *b as i8;
}
t.ct_is_not_zero()
}
}
impl CtIsNotZero for u8 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self as i8;
let a = ((t | t.wrapping_neg()) >> 7).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for i8 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 7).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for u16 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self as i16;
let a = ((t | t.wrapping_neg()) >> 15).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for i16 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 15).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for u32 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self as i32;
let a = ((t | t.wrapping_neg()) >> 31).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for i32 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 31).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for u64 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self as i64;
let a = ((t | t.wrapping_neg()) >> 63).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for i64 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 63).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
#[cfg(target_pointer_width = "64")]
impl CtIsNotZero for u128 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self as i128;
let a = ((t | t.wrapping_neg()) >> 127).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
#[cfg(target_pointer_width = "64")]
impl CtIsNotZero for i128 {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self;
let a = ((t | t.wrapping_neg()) >> 127).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
impl CtIsNotZero for usize {
fn ct_is_not_zero(&self) -> subtle::Choice {
let t = *self as isize;
let a = ((t | t.wrapping_neg()) >> (usize::BITS - 1)).wrapping_neg();
subtle::Choice::from(a as u8)
}
}
#[cfg(test)]
mod tests {
use super::{CtIsNotZero, CtIsZero, field_bounded_add, uniform_nonzero_u8};
#[test]
fn uniform_nonzero_u8_maps_into_one_through_modulus() {
assert_eq!(uniform_nonzero_u8(0, 15), 1);
assert_eq!(uniform_nonzero_u8(14, 15), 15);
assert_eq!(uniform_nonzero_u8(15, 15), 1);
assert_eq!(uniform_nonzero_u8(u32::MAX, 255), 1);
}
#[test]
fn field_bounded_add_returns_zero_on_exhaustion() {
assert_eq!(field_bounded_add(1, 2, 16), 3);
assert_eq!(field_bounded_add(14, 1, 16), 15);
assert_eq!(field_bounded_add(15, 1, 16), 0);
assert_eq!(field_bounded_add(255, 1, 256), 0);
}
#[test]
fn byte_slices_report_zero_and_nonzero_in_constant_time_form() {
let zeros = [0u8; 4];
let nonzeros = [0u8, 0, 7, 0];
assert_eq!(zeros.as_slice().ct_is_zero().unwrap_u8(), 1);
assert_eq!(zeros.as_slice().ct_is_not_zero().unwrap_u8(), 0);
assert_eq!(nonzeros.as_slice().ct_is_zero().unwrap_u8(), 0);
assert_eq!(nonzeros.as_slice().ct_is_not_zero().unwrap_u8(), 1);
assert_eq!(zeros[..].ct_is_zero().unwrap_u8(), 1);
assert_eq!(nonzeros[..].ct_is_not_zero().unwrap_u8(), 1);
}
#[test]
fn integers_report_zero_and_nonzero_in_constant_time_form() {
macro_rules! assert_integer_zero_checks {
($($ty:ty => $nonzero:expr),+$(,)*) => {
$(
let zero: $ty = 0;
let nonzero: $ty = $nonzero;
assert_eq!(zero.ct_is_zero().unwrap_u8(), 1);
assert_eq!(zero.ct_is_not_zero().unwrap_u8(), 0);
assert_eq!(nonzero.ct_is_zero().unwrap_u8(), 0);
assert_eq!(nonzero.ct_is_not_zero().unwrap_u8(), 1);
)+
};
}
assert_integer_zero_checks!(
u8 => 7,
i8 => -7,
u16 => 7,
i16 => -7,
u32 => 7,
i32 => -7,
u64 => 7,
i64 => -7,
usize => 7,
);
#[cfg(target_pointer_width = "64")]
assert_integer_zero_checks!(
u128 => 7,
i128 => -7,
);
}
}