Skip to content

Runtime moduli #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
213e796
Implement Modular with add and mul
jellevos Oct 11, 2022
f13f399
Implement modular exponentiation
jellevos Oct 11, 2022
d1177b1
Merge remote-tracking branch 'upstream/master' into powmod
jellevos Oct 11, 2022
d3887f0
Fix obvious issues and add documentation
jellevos Oct 11, 2022
4177420
Use Word instead of u64
jellevos Oct 11, 2022
df3c898
Use e.g. U64 instead of UInt::<1>
jellevos Oct 11, 2022
bf77788
Rewrite uint operations to const fn
jellevos Oct 12, 2022
154d82e
Checkpoint: working modulus macro
jellevos Oct 14, 2022
1814a0d
Add tests and make remaining fn const
jellevos Oct 14, 2022
daf3299
Make pow a const fn
jellevos Oct 14, 2022
515d78d
Add missing documentation
jellevos Oct 17, 2022
a0ed6ff
Bump rustc version to 1.61
jellevos Oct 17, 2022
0cd95b7
Bump rustc version to 1.61 in Github actions
jellevos Oct 17, 2022
dc85a35
Address comments
jellevos Nov 3, 2022
e57e1d9
Add const keyword to modular add
jellevos Nov 4, 2022
57cb0b7
Initial implementation of runtime moduli
jellevos Nov 4, 2022
9051b11
Write docstring where required
jellevos Nov 4, 2022
beccc95
Implement inverse for residues
jellevos Nov 5, 2022
2fff3e1
Cargo fmt
jellevos Nov 5, 2022
9d51b10
Merge branch 'runtime-moduli' into powmod
jellevos Nov 5, 2022
423e60f
Remove runtime moduli (not in PR scope)
jellevos Nov 5, 2022
b455a09
Fix docstring and remove stale code
jellevos Nov 5, 2022
dcdb1f6
Revert ConstResidue to Residue
jellevos Nov 6, 2022
a2ee635
Impl two invs: panicking and ctoption
jellevos Nov 8, 2022
797ad32
Change Residue to DynResidue
jellevos Nov 11, 2022
04ce885
Merge remote-tracking branch 'upstream/master' into runtime-moduli
jellevos Nov 13, 2022
17963d8
Make residue traits public
jellevos Nov 13, 2022
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
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@
//! assert_eq!(b, U256::ZERO);
//! ```
//!
//! It also supports modular arithmetic over constant moduli using `Residue`.
//! It also supports modular arithmetic over constant moduli using `Residue`,
//! and over moduli set at runtime using `DynResidue`.
//! That includes modular exponentiation and multiplicative inverses.
//! These features are described in the [`modular`] module.
//!
Expand Down
5 changes: 0 additions & 5 deletions src/uint/modular/add.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use crate::UInt;

pub trait AddResidue {
/// Computes the (reduced) sum of two residues.
fn add(&self, rhs: &Self) -> Self;
}

pub(crate) const fn add_montgomery_form<const LIMBS: usize>(
a: &UInt<LIMBS>,
b: &UInt<LIMBS>,
Expand Down
2 changes: 1 addition & 1 deletion src/uint/modular/constant_mod/const_add.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use core::ops::AddAssign;

use crate::{
modular::add::{add_montgomery_form, AddResidue},
modular::{add::add_montgomery_form, AddResidue},
UInt,
};

Expand Down
2 changes: 1 addition & 1 deletion src/uint/modular/constant_mod/const_inv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::marker::PhantomData;
use subtle::{Choice, CtOption};

use crate::{
modular::inv::{inv_montgomery_form, InvResidue},
modular::{inv::inv_montgomery_form, InvResidue},
Word,
};

Expand Down
5 changes: 1 addition & 4 deletions src/uint/modular/constant_mod/const_mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use core::{
ops::{Mul, MulAssign},
};

use crate::modular::{
mul::{mul_montgomery_form, MulResidue},
reduction::montgomery_reduction,
};
use crate::modular::{mul::mul_montgomery_form, reduction::montgomery_reduction, MulResidue};

use super::{Residue, ResidueParams};

Expand Down
2 changes: 1 addition & 1 deletion src/uint/modular/constant_mod/const_pow.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
modular::pow::{pow_montgomery_form, PowResidue},
modular::{pow::pow_montgomery_form, PowResidue},
UInt, Word,
};

Expand Down
10 changes: 0 additions & 10 deletions src/uint/modular/inv.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
use subtle::CtOption;

use crate::{modular::reduction::montgomery_reduction, Limb, UInt, Word};

pub trait InvResidue
where
Self: Sized,
{
/// Computes the (reduced) multiplicative inverse of the residue. Returns CtOption, which is None if the residue was not invertible.
fn inv(self) -> CtOption<Self>;
}

pub const fn inv_montgomery_form<const LIMBS: usize>(
x: UInt<LIMBS>,
modulus: UInt<LIMBS>,
Expand Down
49 changes: 47 additions & 2 deletions src/uint/modular/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,62 @@
use crate::UInt;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR looks good aside from the use of the 2015 edition module style (i.e. mod.rs)

We've adopted the 2018 edition module style, where this file would be modular.rs.

Unfortunately I don't think they ever added a lint for mixed module styles.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ho, went looking for that the other day and didn't find it... weird

use subtle::CtOption;

use self::{add::AddResidue, inv::InvResidue, mul::MulResidue, pow::PowResidue};
use crate::{UInt, Word};

mod reduction;

/// Implements `Residue`s, supporting modular arithmetic with a constant modulus.
pub mod constant_mod;
/// Implements `DynResidue`s, supporting modular arithmetic with a modulus set at runtime.
pub mod runtime_mod;

mod add;
mod inv;
mod mul;
mod pow;

/// Provides a consistent interface to add two residues of the same type together.
pub trait AddResidue {
/// Computes the (reduced) sum of two residues.
fn add(&self, rhs: &Self) -> Self;
}

/// Provides a consistent interface to multiply two residues of the same type together.
pub trait MulResidue
where
Self: Sized,
{
/// Computes the (reduced) product of two residues.
fn mul(&self, rhs: &Self) -> Self;

/// Computes the same as `self.mul(self)`, but may be more efficient.
fn square(&self) -> Self {
self.mul(self)
}
}

/// Provides a consistent interface to exponentiate a residue.
pub trait PowResidue<const LIMBS: usize>
where
Self: Sized,
{
/// Computes the (reduced) exponentiation of a residue.
fn pow(self, exponent: &UInt<LIMBS>) -> Self {
self.pow_specific(exponent, LIMBS * Word::BITS as usize)
}

/// Computes the (reduced) exponentiation of a residue, here `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern.
fn pow_specific(self, exponent: &UInt<LIMBS>, exponent_bits: usize) -> Self;
}

/// Provides a consistent interface to invert a residue.
pub trait InvResidue
where
Self: Sized,
{
/// Computes the (reduced) multiplicative inverse of the residue. Returns CtOption, which is `None` if the residue was not invertible.
fn inv(self) -> CtOption<Self>;
}

/// The `GenericResidue` trait provides a consistent API for dealing with residues with a constant modulus.
pub trait GenericResidue<const LIMBS: usize>:
AddResidue + MulResidue + PowResidue<LIMBS> + InvResidue
Expand Down
12 changes: 0 additions & 12 deletions src/uint/modular/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,6 @@ use crate::{Limb, UInt};

use super::reduction::montgomery_reduction;

pub trait MulResidue
where
Self: Sized,
{
/// Computes the (reduced) product of two residues.
fn mul(&self, rhs: &Self) -> Self;

fn square(&self) -> Self {
self.mul(self)
}
}

pub(crate) const fn mul_montgomery_form<const LIMBS: usize>(
a: &UInt<LIMBS>,
b: &UInt<LIMBS>,
Expand Down
13 changes: 0 additions & 13 deletions src/uint/modular/pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,6 @@ use crate::{Limb, UInt, Word};

use super::mul::{mul_montgomery_form, square_montgomery_form};

pub trait PowResidue<const LIMBS: usize>
where
Self: Sized,
{
/// Computes the (reduced) exponentiation of a residue.
fn pow(self, exponent: &UInt<LIMBS>) -> Self {
self.pow_specific(exponent, LIMBS * Word::BITS as usize)
}

/// Computes the (reduced) exponentiation of a residue, here `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern.
fn pow_specific(self, exponent: &UInt<LIMBS>, exponent_bits: usize) -> Self;
}

/// Performs modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern.
pub const fn pow_montgomery_form<const LIMBS: usize>(
x: UInt<LIMBS>,
Expand Down
85 changes: 85 additions & 0 deletions src/uint/modular/runtime_mod/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use crate::{Limb, UInt, Word};

use super::{reduction::montgomery_reduction, GenericResidue};

/// Additions between residues with a modulus set at runtime
mod runtime_add;
/// Multiplicative inverses of residues with a modulus set at runtime
mod runtime_inv;
/// Multiplications between residues with a modulus set at runtime
mod runtime_mul;
/// Exponentiation of residues with a modulus set at runtime
mod runtime_pow;

/// The parameters to efficiently go to and from the Montgomery form for a modulus provided at runtime.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DynResidueParams<const LIMBS: usize> {
// The constant modulus
modulus: UInt<LIMBS>,
// Parameter used in Montgomery reduction
r: UInt<LIMBS>,
// R^2, used to move into Montgomery form
r2: UInt<LIMBS>,
// R^3, used to compute the multiplicative inverse
r3: UInt<LIMBS>,
// The lowest limbs of -(MODULUS^-1) mod R
// We only need the LSB because during reduction this value is multiplied modulo 2**64.
mod_neg_inv: Limb,
}

impl<const LIMBS: usize> DynResidueParams<LIMBS> {
/// Instantiates a new set of `ResidueParams` representing the given `modulus`.
pub fn new(modulus: UInt<LIMBS>) -> Self {
let r = UInt::MAX.ct_reduce(&modulus).0.wrapping_add(&UInt::ONE);
let r2 = UInt::ct_reduce_wide(r.square_wide(), &modulus).0;
let mod_neg_inv =
Limb(Word::MIN.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0));
let r3 = montgomery_reduction(r2.square_wide(), modulus, mod_neg_inv);

Self {
modulus,
r,
r2,
r3,
mod_neg_inv,
}
}
}

/// A residue represented using `LIMBS` limbs. The odd modulus of this residue is set at runtime.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DynResidue<const LIMBS: usize> {
montgomery_form: UInt<LIMBS>,
residue_params: DynResidueParams<LIMBS>,
}

impl<const LIMBS: usize> DynResidue<LIMBS> {
/// Instantiates a new `Residue` that represents this `integer` mod `MOD`.
pub const fn new(integer: UInt<LIMBS>, residue_params: DynResidueParams<LIMBS>) -> Self {
let mut modular_integer = Self {
montgomery_form: integer,
residue_params,
};

let product = integer.mul_wide(&residue_params.r2);
modular_integer.montgomery_form =
montgomery_reduction(product, residue_params.modulus, residue_params.mod_neg_inv);

modular_integer
}

/// Retrieves the integer currently encoded in this `Residue`, guaranteed to be reduced.
pub const fn retrieve(&self) -> UInt<LIMBS> {
montgomery_reduction(
(self.montgomery_form, UInt::ZERO),
self.residue_params.modulus,
self.residue_params.mod_neg_inv,
)
}
}

impl<const LIMBS: usize> GenericResidue<LIMBS> for DynResidue<LIMBS> {
fn retrieve(&self) -> UInt<LIMBS> {
self.retrieve()
}
}
80 changes: 80 additions & 0 deletions src/uint/modular/runtime_mod/runtime_add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use core::ops::{Add, AddAssign};

use crate::{
modular::{add::add_montgomery_form, AddResidue},
UInt,
};

use super::DynResidue;

impl<const LIMBS: usize> AddResidue for DynResidue<LIMBS> {
fn add(&self, rhs: &Self) -> Self {
debug_assert_eq!(self.residue_params, rhs.residue_params);
Self {
montgomery_form: add_montgomery_form(
&self.montgomery_form,
&rhs.montgomery_form,
&self.residue_params.modulus,
),
residue_params: self.residue_params,
}
}
}

impl<const LIMBS: usize> AddAssign for DynResidue<LIMBS> {
fn add_assign(&mut self, rhs: Self) {
self.montgomery_form = add_montgomery_form(
&self.montgomery_form,
&rhs.montgomery_form,
&self.residue_params.modulus,
);
}
}

impl<const LIMBS: usize> AddAssign<UInt<LIMBS>> for DynResidue<LIMBS> {
fn add_assign(&mut self, rhs: UInt<LIMBS>) {
self.montgomery_form = add_montgomery_form(
&self.montgomery_form,
&DynResidue::new(rhs, self.residue_params).montgomery_form,
&self.residue_params.modulus,
);
}
}

impl<const LIMBS: usize> Add for DynResidue<LIMBS> {
type Output = DynResidue<LIMBS>;

fn add(mut self, rhs: Self) -> Self::Output {
self += rhs;
self
}
}

#[cfg(test)]
mod tests {
use crate::{
modular::runtime_mod::{DynResidue, DynResidueParams},
U256,
};

#[test]
fn add_overflow() {
let params = DynResidueParams::new(U256::from_be_hex(
"ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
));

let x =
U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56");
let mut x_mod = DynResidue::new(x, params);

let y =
U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251");

x_mod += y;

let expected =
U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956");

assert_eq!(expected, x_mod.retrieve());
}
}
26 changes: 26 additions & 0 deletions src/uint/modular/runtime_mod/runtime_inv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use subtle::{Choice, CtOption};

use crate::{
modular::{inv::inv_montgomery_form, InvResidue},
Word,
};

use super::DynResidue;

impl<const LIMBS: usize> InvResidue for DynResidue<LIMBS> {
fn inv(self) -> CtOption<Self> {
let (montgomery_form, error) = inv_montgomery_form(
self.montgomery_form,
self.residue_params.modulus,
&self.residue_params.r3,
self.residue_params.mod_neg_inv,
);

let value = Self {
montgomery_form,
residue_params: self.residue_params,
};

CtOption::new(value, Choice::from((error == Word::MAX) as u8))
}
}
Loading