Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions src/int/shl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ impl<const LIMBS: usize> Int<LIMBS> {
/// When used with a fixed `shift`, this function is constant-time with respect
/// to `self`.
#[inline(always)]
pub const fn overflowing_shl_vartime(&self, shift: u32) -> CtOption<Self> {
Self::from_uint_opt(self.0.overflowing_shl_vartime(shift))
pub const fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self> {
if let Some(uint) = self.0.overflowing_shl_vartime(shift) {
Some(*uint.as_int())
} else {
None
}
}

/// Computes `self << shift` in a panic-free manner, returning zero if the shift exceeds the
Expand Down Expand Up @@ -90,17 +94,18 @@ impl<const LIMBS: usize> WrappingShl for Int<LIMBS> {
}

impl<const LIMBS: usize> ShlVartime for Int<LIMBS> {
fn overflowing_shl_vartime(&self, shift: u32) -> CtOption<Self> {
self.overflowing_shl(shift)
fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self> {
self.overflowing_shl_vartime(shift)
}

fn wrapping_shl_vartime(&self, shift: u32) -> Self {
self.wrapping_shl(shift)
self.wrapping_shl_vartime(shift)
}
}

#[cfg(test)]
mod tests {
use crate::I256;
use crate::{I256, ShlVartime};

const N: I256 =
I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
Expand Down Expand Up @@ -131,6 +136,8 @@ mod tests {
#[test]
fn shl1() {
assert_eq!(N << 1, TWO_N);
assert_eq!(ShlVartime::overflowing_shl_vartime(&N, 1), Some(TWO_N));
assert_eq!(ShlVartime::wrapping_shl_vartime(&N, 1), TWO_N);
}

#[test]
Expand All @@ -151,7 +158,7 @@ mod tests {
#[test]
fn shl256_const() {
assert!(N.overflowing_shl(256).is_none().to_bool_vartime());
assert!(N.overflowing_shl_vartime(256).is_none().to_bool_vartime());
assert!(ShlVartime::overflowing_shl_vartime(&N, 256).is_none());
}

#[test]
Expand All @@ -164,4 +171,18 @@ mod tests {
fn shl64() {
assert_eq!(N << 64, SIXTY_FOUR);
}

#[test]
fn wrapping_shl() {
assert_eq!(I256::MAX.wrapping_shl(257), I256::ZERO);
assert_eq!(I256::MIN.wrapping_shl(257), I256::ZERO);
assert_eq!(
ShlVartime::wrapping_shl_vartime(&I256::MAX, 257),
I256::ZERO
);
assert_eq!(
ShlVartime::wrapping_shl_vartime(&I256::MIN, 257),
I256::ZERO
);
}
}
90 changes: 34 additions & 56 deletions src/int/shr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! [`Int`] bitwise right shift operations.

use crate::{Choice, CtOption, Int, Limb, ShrVartime, Uint, WrappingShr};
use crate::{Choice, CtOption, Int, ShrVartime, Uint, WrappingShr};
use core::ops::{Shr, ShrAssign};

impl<const LIMBS: usize> Int<LIMBS> {
Expand All @@ -23,7 +23,7 @@ impl<const LIMBS: usize> Int<LIMBS> {
/// Panics if `shift >= Self::BITS`.
pub const fn shr_vartime(&self, shift: u32) -> Self {
self.overflowing_shr_vartime(shift)
.expect_copied("`shift` within the bit size of the integer")
.expect("`shift` within the bit size of the integer")
}

/// Computes `self >> shift`.
Expand All @@ -41,14 +41,8 @@ impl<const LIMBS: usize> Int<LIMBS> {
let mut result = *self;
let mut i = 0;
while i < shift_bits {
let bit = Choice::from_u32_lsb((shift >> i) & 1);
result = Int::select(
&result,
&result
.overflowing_shr_vartime(1 << i)
.expect_copied("shift within range"),
bit,
);
let bit = Choice::from_u32_lsb(shift >> i);
result = Int::select(&result, &result.shr_vartime(1 << i), bit);
i += 1;
}

Expand All @@ -67,47 +61,17 @@ impl<const LIMBS: usize> Int<LIMBS> {
/// When used with a fixed `shift`, this function is constant-time with respect
/// to `self`.
#[inline(always)]
pub const fn overflowing_shr_vartime(&self, shift: u32) -> CtOption<Self> {
let is_negative = self.is_negative();

if shift >= Self::BITS {
return CtOption::new(
Self::select(&Self::ZERO, &Self::MINUS_ONE, is_negative),
Choice::FALSE,
pub const fn overflowing_shr_vartime(&self, shift: u32) -> Option<Self> {
if let Some(ret) = self.0.overflowing_shr_vartime(shift) {
let neg_upper = Uint::select(
&Uint::ZERO,
&Uint::MAX.restrict_bits(Self::BITS - shift).not(),
self.is_negative(),
);
Some(*ret.bitor(&neg_upper).as_int())
} else {
None
}

// Select the base limb, based on the sign of this value.
let base = Limb::select(Limb::ZERO, Limb::MAX, is_negative);
let mut limbs = [base; LIMBS];

let shift_num = (shift / Limb::BITS) as usize;
let rem = shift % Limb::BITS;

let mut i = 0;
while i < LIMBS - shift_num {
limbs[i] = self.0.limbs[i + shift_num];
i += 1;
}

if rem == 0 {
return CtOption::some(Self(Uint::new(limbs)));
}

// construct the carry s.t. the `rem`-most significant bits of `carry` are 1 when self
// is negative, i.e., shift in 1s when the msb is 1.
let mut carry = Limb::select(Limb::ZERO, Limb::MAX, is_negative);
carry = carry.bitxor(carry.shr(rem)); // logical shift right; shifts in zeroes.

while i > 0 {
i -= 1;
let shifted = limbs[i].shr(rem);
let new_carry = limbs[i].shl(Limb::BITS - rem);
limbs[i] = shifted.bitor(carry);
carry = new_carry;
}

CtOption::some(Self(Uint::new(limbs)))
}

/// Computes `self >> shift` in a panic-free manner.
Expand All @@ -126,8 +90,11 @@ impl<const LIMBS: usize> Int<LIMBS> {
/// - `0` when `self` is non-negative, and
/// - `-1` when `self` is negative.
pub const fn wrapping_shr_vartime(&self, shift: u32) -> Self {
let default = Self::select(&Self::ZERO, &Self::MINUS_ONE, self.is_negative());
ctutils::unwrap_or!(self.overflowing_shr_vartime(shift), default, Self::select)
if let Some(ret) = self.overflowing_shr_vartime(shift) {
ret
} else {
Self::select(&Self::ZERO, &Self::MINUS_ONE, self.is_negative())
}
}
}

Expand Down Expand Up @@ -170,19 +137,20 @@ impl<const LIMBS: usize> WrappingShr for Int<LIMBS> {
}

impl<const LIMBS: usize> ShrVartime for Int<LIMBS> {
fn overflowing_shr_vartime(&self, shift: u32) -> CtOption<Self> {
self.overflowing_shr(shift)
fn overflowing_shr_vartime(&self, shift: u32) -> Option<Self> {
self.overflowing_shr_vartime(shift)
}

fn wrapping_shr_vartime(&self, shift: u32) -> Self {
self.wrapping_shr(shift)
self.wrapping_shr_vartime(shift)
}
}

#[cfg(test)]
mod tests {
use core::ops::Div;

use crate::I256;
use crate::{I256, ShrVartime};

const N: I256 =
I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
Expand All @@ -199,6 +167,8 @@ mod tests {
#[test]
fn shr1() {
assert_eq!(N >> 1, N_2);
assert_eq!(ShrVartime::overflowing_shr_vartime(&N, 1), Some(N_2));
assert_eq!(ShrVartime::wrapping_shr_vartime(&N, 1), N_2);
}

#[test]
Expand Down Expand Up @@ -228,7 +198,7 @@ mod tests {
#[test]
fn shr256_const() {
assert!(N.overflowing_shr(256).is_none().to_bool_vartime());
assert!(N.overflowing_shr_vartime(256).is_none().to_bool_vartime());
assert!(ShrVartime::overflowing_shr_vartime(&N, 256).is_none());
}

#[test]
Expand All @@ -241,5 +211,13 @@ mod tests {
fn wrapping_shr() {
assert_eq!(I256::MAX.wrapping_shr(257), I256::ZERO);
assert_eq!(I256::MIN.wrapping_shr(257), I256::MINUS_ONE);
assert_eq!(
ShrVartime::wrapping_shr_vartime(&I256::MAX, 257),
I256::ZERO
);
assert_eq!(
ShrVartime::wrapping_shr_vartime(&I256::MIN, 257),
I256::MINUS_ONE
);
}
}
8 changes: 1 addition & 7 deletions src/modular/bingcd/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,7 @@ impl<const LIMBS: usize> Uint<LIMBS> {
let mut i = 0;
while i < shift_bits {
let bit = Choice::from_u32_lsb((shift >> i) & 1);
result = Uint::select(
&result,
&result
.overflowing_shr_vartime(1 << i)
.expect_copied("shift within range"),
bit,
);
result = Uint::select(&result, &result.shr_vartime(1 << i), bit);
i += 1;
}

Expand Down
4 changes: 2 additions & 2 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ pub trait ShlVartime: Sized {
/// Computes `self << shift`.
///
/// Returns `None` if `shift >= self.bits_precision()`.
fn overflowing_shl_vartime(&self, shift: u32) -> CtOption<Self>;
fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self>;

/// Computes `self << shift` in a panic-free manner, masking off bits of `shift`
/// which would cause the shift to exceed the type's width.
Expand All @@ -960,7 +960,7 @@ pub trait ShrVartime: Sized {
/// Computes `self >> shift`.
///
/// Returns `None` if `shift >= self.bits_precision()`.
fn overflowing_shr_vartime(&self, shift: u32) -> CtOption<Self>;
fn overflowing_shr_vartime(&self, shift: u32) -> Option<Self>;

/// Computes `self >> shift` in a panic-free manner, masking off bits of `shift`
/// which would cause the shift to exceed the type's width.
Expand Down
47 changes: 28 additions & 19 deletions src/uint/boxed/shl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! [`BoxedUint`] bitwise left shift operations.

use crate::{BoxedUint, Choice, CtOption, Limb, Shl, ShlAssign, ShlVartime, WrappingShl};
use crate::{BoxedUint, Choice, Limb, Shl, ShlAssign, ShlVartime, WrappingShl};

impl BoxedUint {
/// Computes `self << shift`.
Expand All @@ -22,7 +22,7 @@ impl BoxedUint {

/// Computes `self << shift`.
///
/// Returns a zero and a truthy `Choice` if `shift >= self.bits_precision()`,
/// Returns `self` and a truthy `Choice` if `shift >= self.bits_precision()`,
/// or the result and a falsy `Choice` otherwise.
pub fn overflowing_shl(&self, shift: u32) -> (Self, Choice) {
let mut result = self.clone();
Expand All @@ -32,14 +32,15 @@ impl BoxedUint {

/// Computes `self << shift` in variable-time.
///
/// Returns a zero and a truthy `Choice` if `shift >= self.bits_precision()`,
/// or the result and a falsy `Choice` otherwise.
pub fn overflowing_shl_vartime(&self, shift: u32) -> (Self, Choice) {
let mut result = self.clone();
let overflow = result
.as_mut_uint_ref()
.overflowing_shl_assign_vartime(shift);
(result, overflow)
/// Returns `None` if `shift >= self.bits_precision()`, otherwise the shifted result.
pub fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self> {
if shift < self.bits_precision() {
let mut result = self.clone();
result.as_mut_uint_ref().wrapping_shl_assign_vartime(shift);
Some(result)
} else {
None
}
}

/// Computes `self <<= shift`.
Expand All @@ -51,22 +52,30 @@ impl BoxedUint {

/// Computes `self <<= shift` in variable-time.
///
/// Returns a truthy `Choice` if `shift >= self.bits_precision()` or a falsy `Choice` otherwise.
pub fn overflowing_shl_assign_vartime(&mut self, shift: u32) -> Choice {
self.as_mut_uint_ref().overflowing_shl_assign_vartime(shift)
/// If `shift >= self.bits_precision()`, shifts `self` in place and returns `false`.
/// Otherwise returns `true` and leaves `self` unmodified.
pub fn overflowing_shl_assign_vartime(&mut self, shift: u32) -> bool {
if shift < self.bits_precision() {
self.as_mut_uint_ref().wrapping_shl_assign_vartime(shift);
false
} else {
true
}
}

/// Computes `self << shift` in a panic-free manner, masking off bits of `shift` which would cause the shift to
/// exceed the type's width.
pub fn wrapping_shl(&self, shift: u32) -> Self {
self.overflowing_shl(shift).0
let mut result = self.clone();
result.wrapping_shl_assign(shift);
result
}

/// Computes `self <<= shift` in a panic-free manner, masking off bits of `shift` which would cause the shift to
/// exceed the type's width.
pub fn wrapping_shl_assign(&mut self, shift: u32) {
// self is zeroed in the case of an overflowing shift
self.as_mut_uint_ref().overflowing_shl_assign(shift);
self.as_mut_uint_ref().wrapping_shl_assign(shift);
}

/// Computes `self << shift` in variable-time in a panic-free manner, masking off bits of `shift` which would cause
Expand Down Expand Up @@ -167,12 +176,12 @@ impl WrappingShl for BoxedUint {
}

impl ShlVartime for BoxedUint {
fn overflowing_shl_vartime(&self, shift: u32) -> CtOption<Self> {
let (result, overflow) = self.overflowing_shl(shift);
CtOption::new(result, !overflow)
fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self> {
self.overflowing_shl_vartime(shift)
}

fn wrapping_shl_vartime(&self, shift: u32) -> Self {
self.wrapping_shl(shift)
self.wrapping_shl_vartime(shift)
}
}

Expand Down
Loading