Skip to content

Commit

Permalink
Merge pull request #7 from dfns-labs/enc-trait
Browse files Browse the repository at this point in the history
Add `AnyEncryptioKey` trait
  • Loading branch information
survived authored Sep 21, 2023
2 parents ed548d6 + 72b2fc6 commit 2fcc313
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/decryption_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ impl<FastExp: utils::FactorizedExp> DecryptionKey<FastExp> {
}

/// Returns a (public) encryption key corresponding to the (secret) decryption key
pub fn encryption_key(&self) -> EncryptionKey {
self.ek.clone()
pub fn encryption_key(&self) -> &EncryptionKey {
&self.ek
}

/// The Paillier modulus
Expand Down
183 changes: 183 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ pub mod utils;
#[cfg(feature = "serde")]
mod serde;

use std::fmt;

use rand_core::{CryptoRng, RngCore};
use rug::Integer;

pub type Ciphertext = Integer;
Expand Down Expand Up @@ -44,3 +47,183 @@ impl From<Bug> for Error {
Error(Reason::Bug(err))
}
}

mod sealed {
pub trait Sealed {}
impl Sealed for crate::EncryptionKey {}
impl Sealed for crate::DecryptionKey {}
}

/// Any key capable of encryption
///
/// Both encryption and decryption keys can be used to carry out encryption. Moreover, encryption
/// using decryption key is faster.
///
/// ## Example
/// This trait can be used, for instance, to accept an encryption key as an argument to the function
/// and benefit from faster encryption if decryption key is provided.
///
/// ```rust
/// use fast_paillier::{AnyEncryptionKey, Error};
/// use rug::Integer;
///
/// // This function accepts both encryption and decryption key. If decryption key is provided,
/// // it'll be more efficient
/// fn some_function(ek: &dyn AnyEncryptionKey) -> Result<Integer, Error> {
/// // ...
/// # let x = Integer::from(123); let r = Integer::from(321);
/// let ciphertext = ek.encrypt_with(&x, &r)?;
/// Ok(ciphertext)
/// }
/// ```
pub trait AnyEncryptionKey: sealed::Sealed {
/// Returns `N`
fn n(&self) -> &Integer;
/// Returns `N^2`
fn nn(&self) -> &Integer;
/// Returns `N/2`
fn half_n(&self) -> &Integer;

/// Encrypts the plaintext `x` in `{-N/2, .., N_2}` with `nonce` in `Z*_n`
///
/// Returns error if inputs are not in specified range
fn encrypt_with(&self, x: &Plaintext, nonce: &Nonce) -> Result<Ciphertext, Error>;

/// Homomorphic addition of two ciphertexts
///
/// ```text
/// oadd(Enc(a1), Enc(a2)) = Enc(a1 + a2)
/// ```
fn oadd(&self, c1: &Ciphertext, c2: &Ciphertext) -> Result<Ciphertext, Error>;
/// Homomorphic subtraction of two ciphertexts
///
/// ```text
/// osub(Enc(a1), Enc(a2)) = Enc(a1 - a2)
/// ```
fn osub(&self, c1: &Ciphertext, c2: &Ciphertext) -> Result<Ciphertext, Error>;
/// Homomorphic multiplication of scalar at ciphertext
///
/// ```text
/// omul(a, Enc(c)) = Enc(a * c)
/// ```
fn omul(&self, scalar: &Integer, ciphertext: &Ciphertext) -> Result<Ciphertext, Error>;
/// Homomorphic negation of a ciphertext
///
/// ```text
/// oneg(Enc(a)) = Enc(-a)
/// ```
fn oneg(&self, ciphertext: &Ciphertext) -> Result<Ciphertext, Error>;

/// Checks whether `x` is `{-N/2, .., N/2}`
fn in_signed_group(&self, x: &Integer) -> bool;
}

/// Additional functionality implemented for [AnyEncryptionKey]
pub trait AnyEncryptionKeyExt: AnyEncryptionKey {
/// Encrypts the plaintext `x` in `{-N/2, .., N_2}`
///
/// Nonce is sampled randomly using `rng`.
///
/// Returns error if plaintext is not in specified range
fn encrypt_with_random(
&self,
rng: &mut (impl RngCore + CryptoRng),
x: &Plaintext,
) -> Result<(Ciphertext, Nonce), Error>;
}

impl<E: AnyEncryptionKey> AnyEncryptionKeyExt for E {
fn encrypt_with_random(
&self,
rng: &mut (impl RngCore + CryptoRng),
x: &Plaintext,
) -> Result<(Ciphertext, Nonce), Error> {
let nonce = utils::sample_in_mult_group(rng, self.n());
let ciphertext = self.encrypt_with(x, &nonce)?;
Ok((ciphertext, nonce))
}
}

impl AnyEncryptionKey for EncryptionKey {
fn n(&self) -> &Integer {
self.n()
}

fn nn(&self) -> &Integer {
self.nn()
}

fn half_n(&self) -> &Integer {
self.half_n()
}

fn encrypt_with(&self, x: &Plaintext, nonce: &Nonce) -> Result<Ciphertext, Error> {
self.encrypt_with(x, nonce)
}

fn oadd(&self, c1: &Ciphertext, c2: &Ciphertext) -> Result<Ciphertext, Error> {
self.oadd(c1, c2)
}

fn osub(&self, c1: &Ciphertext, c2: &Ciphertext) -> Result<Ciphertext, Error> {
self.osub(c1, c2)
}

fn omul(&self, scalar: &Integer, ciphertext: &Ciphertext) -> Result<Ciphertext, Error> {
self.omul(scalar, ciphertext)
}

fn oneg(&self, ciphertext: &Ciphertext) -> Result<Ciphertext, Error> {
self.oneg(ciphertext)
}

fn in_signed_group(&self, x: &Integer) -> bool {
self.in_signed_group(x)
}
}

impl AnyEncryptionKey for DecryptionKey {
fn n(&self) -> &Integer {
self.encryption_key().n()
}

fn nn(&self) -> &Integer {
self.encryption_key().nn()
}

fn half_n(&self) -> &Integer {
self.encryption_key().half_n()
}

fn encrypt_with(&self, x: &Plaintext, nonce: &Nonce) -> Result<Ciphertext, Error> {
self.encrypt_with(x, nonce)
}

fn oadd(&self, c1: &Ciphertext, c2: &Ciphertext) -> Result<Ciphertext, Error> {
self.encryption_key().oadd(c1, c2)
}

fn osub(&self, c1: &Ciphertext, c2: &Ciphertext) -> Result<Ciphertext, Error> {
self.encryption_key().osub(c1, c2)
}

fn omul(&self, scalar: &Integer, ciphertext: &Ciphertext) -> Result<Ciphertext, Error> {
self.encryption_key().omul(scalar, ciphertext)
}

fn oneg(&self, ciphertext: &Ciphertext) -> Result<Ciphertext, Error> {
self.encryption_key().oneg(ciphertext)
}

fn in_signed_group(&self, x: &Integer) -> bool {
self.encryption_key().in_signed_group(x)
}
}

impl<'a> fmt::Debug for dyn AnyEncryptionKey + 'a {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PaillierEncKey")
.field("N", self.n())
.finish_non_exhaustive()
}
}

0 comments on commit 2fcc313

Please sign in to comment.