Skip to content
Merged
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
14 changes: 13 additions & 1 deletion src/limb/bit_or.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Limb bit or operations.

use super::Limb;
use core::ops::BitOr;
use core::ops::{BitOr, BitOrAssign};

impl Limb {
/// Calculates `a | b`.
Expand All @@ -17,3 +17,15 @@ impl BitOr for Limb {
self.bitor(rhs)
}
}

impl BitOrAssign for Limb {
fn bitor_assign(&mut self, other: Self) {
*self = self.bitor(other);
}
}

impl BitOrAssign<&Limb> for Limb {
fn bitor_assign(&mut self, other: &Self) {
*self = self.bitor(*other);
}
}
2 changes: 2 additions & 0 deletions src/uint/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
mod add;
mod add_mod;
mod bit_and;
mod bit_or;
mod bits;
mod cmp;
mod div;
pub(crate) mod encoding;
mod inv_mod;
mod modular;
mod mul;
mod shl;
Expand Down
118 changes: 118 additions & 0 deletions src/uint/boxed/bit_or.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//! [`BoxedUint`] bitwise OR operations.

use crate::{BoxedUint, Limb, Wrapping};
use core::ops::{BitOr, BitOrAssign};
use subtle::{Choice, CtOption};

impl BoxedUint {
/// Computes bitwise `a & b`.
#[inline(always)]
pub fn bitor(&self, rhs: &Self) -> Self {
Self::chain(self, rhs, Limb::ZERO, |a, b, z| (a.bitor(b), z)).0
}

/// Perform wrapping bitwise `OR`.
///
/// There's no way wrapping could ever happen.
/// This function exists so that all operations are accounted for in the wrapping operations
pub fn wrapping_or(&self, rhs: &Self) -> Self {
self.bitor(rhs)
}

/// Perform checked bitwise `OR`, returning a [`CtOption`] which `is_some` always
pub fn checked_or(&self, rhs: &Self) -> CtOption<Self> {
let result = self.bitor(rhs);
CtOption::new(result, Choice::from(1))
}
}

impl BitOr for BoxedUint {
type Output = Self;

fn bitor(self, rhs: Self) -> BoxedUint {
self.bitor(&rhs)
}
}

impl BitOr<&BoxedUint> for BoxedUint {
type Output = BoxedUint;

#[allow(clippy::needless_borrow)]
fn bitor(self, rhs: &BoxedUint) -> BoxedUint {
(&self).bitor(rhs)
}
}

impl BitOr<BoxedUint> for &BoxedUint {
type Output = BoxedUint;

fn bitor(self, rhs: BoxedUint) -> BoxedUint {
self.bitor(&rhs)
}
}

impl BitOr<&BoxedUint> for &BoxedUint {
type Output = BoxedUint;

fn bitor(self, rhs: &BoxedUint) -> BoxedUint {
self.bitor(rhs)
}
}

impl BitOrAssign for BoxedUint {
fn bitor_assign(&mut self, other: Self) {
Self::bitor_assign(self, &other)
}
}

impl BitOrAssign<&BoxedUint> for BoxedUint {
fn bitor_assign(&mut self, other: &Self) {
for (a, b) in self.limbs.iter_mut().zip(other.limbs.iter()) {
*a |= *b;
}
}
}

impl BitOr for Wrapping<BoxedUint> {
type Output = Self;

fn bitor(self, rhs: Self) -> Wrapping<BoxedUint> {
Wrapping(self.0.bitor(&rhs.0))
}
}

impl BitOr<&Wrapping<BoxedUint>> for Wrapping<BoxedUint> {
type Output = Wrapping<BoxedUint>;

fn bitor(self, rhs: &Wrapping<BoxedUint>) -> Wrapping<BoxedUint> {
Wrapping(self.0.bitor(&rhs.0))
}
}

impl BitOr<Wrapping<BoxedUint>> for &Wrapping<BoxedUint> {
type Output = Wrapping<BoxedUint>;

fn bitor(self, rhs: Wrapping<BoxedUint>) -> Wrapping<BoxedUint> {
Wrapping(BoxedUint::bitor(&self.0, &rhs.0))
}
}

impl BitOr<&Wrapping<BoxedUint>> for &Wrapping<BoxedUint> {
type Output = Wrapping<BoxedUint>;

fn bitor(self, rhs: &Wrapping<BoxedUint>) -> Wrapping<BoxedUint> {
Wrapping(BoxedUint::bitor(&self.0, &rhs.0))
}
}

impl BitOrAssign for Wrapping<BoxedUint> {
fn bitor_assign(&mut self, other: Self) {
self.0.bitor_assign(&other.0)
}
}

impl BitOrAssign<&Wrapping<BoxedUint>> for Wrapping<BoxedUint> {
fn bitor_assign(&mut self, other: &Self) {
self.0.bitor_assign(&other.0)
}
}
51 changes: 49 additions & 2 deletions src/uint/boxed/bits.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Bit manipulation functions.

use crate::{BoxedUint, Limb, Zero};
use subtle::{ConditionallySelectable, ConstantTimeEq};
use crate::{BoxedUint, Limb, Word, Zero};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};

impl BoxedUint {
/// Calculate the number of bits needed to represent this number, i.e. the index of the highest
Expand All @@ -28,12 +28,40 @@ impl BoxedUint {
pub fn bits_precision(&self) -> usize {
self.limbs.len() * Limb::BITS
}

/// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`.
pub(crate) fn set_bit(&mut self, index: usize, bit_value: Choice) {
let limb_num = index / Limb::BITS;
let index_in_limb = index % Limb::BITS;
let index_mask = 1 << index_in_limb;

for i in 0..self.nlimbs() {
let limb = &mut self.limbs[i];
let is_right_limb = (i as Word).ct_eq(&(limb_num as Word));
let old_limb = *limb;
let new_limb = Limb::conditional_select(
&Limb(old_limb.0 & !index_mask),
&Limb(old_limb.0 | index_mask),
bit_value,
);
*limb = Limb::conditional_select(&old_limb, &new_limb, is_right_limb);
}
}
}

#[cfg(test)]
mod tests {
use super::BoxedUint;
use hex_literal::hex;
use subtle::Choice;

fn uint_with_bits_at(positions: &[usize]) -> BoxedUint {
let mut result = BoxedUint::zero_with_precision(256);
for &pos in positions {
result |= BoxedUint::one_with_precision(256).shl_vartime(pos);
}
result
}

#[test]
fn bits() {
Expand All @@ -46,4 +74,23 @@ mod tests {
let n2 = BoxedUint::from_be_slice(&hex!("00000000004000000000000000000000"), 128).unwrap();
assert_eq!(87, n2.bits());
}

#[test]
fn set_bit() {
let mut u = uint_with_bits_at(&[16, 79, 150]);
u.set_bit(127, Choice::from(1));
assert_eq!(u, uint_with_bits_at(&[16, 79, 127, 150]));

let mut u = uint_with_bits_at(&[16, 79, 150]);
u.set_bit(150, Choice::from(1));
assert_eq!(u, uint_with_bits_at(&[16, 79, 150]));

let mut u = uint_with_bits_at(&[16, 79, 150]);
u.set_bit(127, Choice::from(0));
assert_eq!(u, uint_with_bits_at(&[16, 79, 150]));

let mut u = uint_with_bits_at(&[16, 79, 150]);
u.set_bit(150, Choice::from(0));
assert_eq!(u, uint_with_bits_at(&[16, 79]));
}
}
71 changes: 71 additions & 0 deletions src/uint/boxed/inv_mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//! [`BoxedUint`] modular inverse (i.e. reciprocal) operations.

use crate::{BoxedUint, Word};
use subtle::{Choice, ConstantTimeLess};

impl BoxedUint {
/// Computes 1/`self` mod `2^k`.
///
/// Conditions: `self` < 2^k and `self` must be odd
pub fn inv_mod2k(&self, k: usize) -> Self {
// This is the same algorithm as in `inv_mod2k_vartime()`,
// but made constant-time w.r.t `k` as well.

let mut x = Self::zero_with_precision(self.bits_precision()); // keeps `x` during iterations
let mut b = Self::one_with_precision(self.bits_precision()); // keeps `b_i` during iterations

for i in 0..self.bits_precision() {
// Only iterations for i = 0..k need to change `x`,
// the rest are dummy ones performed for the sake of constant-timeness.
let within_range = (i as Word).ct_lt(&(k as Word));

// X_i = b_i mod 2
let x_i = b.limbs[0].0 & 1;
let x_i_choice = Choice::from(x_i as u8);
// b_{i+1} = (b_i - a * X_i) / 2
b = Self::conditional_select(&b, &b.wrapping_sub(self), x_i_choice).shr_vartime(1);

// Store the X_i bit in the result (x = x | (1 << X_i))
// Don't change the result in dummy iterations.
let x_i_choice = x_i_choice & within_range;
x.set_bit(i, x_i_choice);
}

x
}
}

#[cfg(test)]
mod tests {
use super::BoxedUint;
use hex_literal::hex;

#[test]
fn inv_mod2k() {
let v = BoxedUint::from_be_slice(
&hex!("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"),
256,
)
.unwrap();
let e = BoxedUint::from_be_slice(
&hex!("3642e6faeaac7c6663b93d3d6a0d489e434ddc0123db5fa627c7f6e22ddacacf"),
256,
)
.unwrap();
let a = v.inv_mod2k(256);
assert_eq!(e, a);

let v = BoxedUint::from_be_slice(
&hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"),
256,
)
.unwrap();
let e = BoxedUint::from_be_slice(
&hex!("261776f29b6b106c7680cf3ed83054a1af5ae537cb4613dbb4f20099aa774ec1"),
256,
)
.unwrap();
let a = v.inv_mod2k(256);
assert_eq!(e, a);
}
}