├── src ├── polyarith │ ├── mod.rs │ └── lazy_ntt.rs ├── integer_arith │ ├── util.rs │ ├── mod.rs │ ├── butterfly.rs │ └── scalar.rs ├── utils.rs ├── traits.rs ├── randutils.rs ├── serialize.rs ├── rqpoly.rs └── lib.rs ├── .gitignore ├── CHANGELOG.md ├── benches ├── integerops.rs ├── butterfly.rs ├── polyops.rs └── scheme.rs ├── Cargo.toml ├── LICENSE ├── examples ├── rerandomize.rs ├── serialization.rs └── basic.rs ├── CONTRIBUTING.md ├── README.md └── CODE_OF_CONDUCT.md /src/polyarith/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lazy_ntt; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | src/*.md 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.1 (August 30, 2021) 2 | 3 | * Performance optimizations: Faster encryption/decryption based on lazy NTT 4 | 5 | ## 0.2.0 (June 1, 2021) 6 | 7 | * Added serailization support 8 | * Added ability to customize plaintext modulus 9 | -------------------------------------------------------------------------------- /benches/integerops.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | #[macro_use] 6 | extern crate bencher; 7 | use bencher::Bencher; 8 | use cupcake::integer_arith::scalar::Scalar; 9 | use cupcake::integer_arith::ArithUtils; 10 | 11 | #[allow(non_snake_case)] 12 | fn bench_mulmod(bench: &mut Bencher) { 13 | let q = Scalar::new_modulus(18014398492704769u64); 14 | let x = rand::random::(); 15 | let y = rand::random::(); 16 | 17 | let X = Scalar::from(x); 18 | let Y = Scalar::from(y); 19 | 20 | bench.iter(|| { 21 | let _ = Scalar::mul_mod(&X, &Y, &q); 22 | }) 23 | } 24 | 25 | benchmark_group!(integerops_group, bench_mulmod,); 26 | 27 | benchmark_main!(integerops_group); 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Cupcake" 3 | version = "0.2.1" 4 | authors = ["Hao Chen "] 5 | license = "MIT" 6 | edition = "2018" 7 | repository = "https://github.com/facebookresearch/Cupcake/" 8 | readme = "README.md" 9 | keywords = ["cryptography", "crypto", "homomorphic", "encryption"] 10 | description = "An implementation of a lattice-based additive homomorphic encryption scheme" 11 | 12 | [dependencies] 13 | rand = "0.5.5" 14 | modinverse = "0.1.1" 15 | 16 | [dev-dependencies] 17 | bencher = "0.1.5" 18 | 19 | [lib] 20 | name = "cupcake" 21 | path = "src/lib.rs" 22 | 23 | [features] 24 | bench = [] 25 | 26 | [[example]] 27 | name = "basic" 28 | 29 | [[example]] 30 | name = "serialization" 31 | 32 | [[bench]] 33 | name = "scheme" 34 | harness = false 35 | 36 | [[bench]] 37 | name = "polyops" 38 | harness = false 39 | required-features = ["bench"] 40 | 41 | [[bench]] 42 | name = "butterfly" 43 | harness = false 44 | 45 | [[bench]] 46 | name = "integerops" 47 | harness = false 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Facebook, Inc. and its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/integer_arith/util.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | /// computes floor(a*b/pow(2,64)) 7 | pub fn mul_high_word(a: u64, b:u64) -> u64{ 8 | ((a as u128 * b as u128) >> 64) as u64 9 | } 10 | 11 | /// computes floor(w*pow(2,64)/q) 12 | pub fn compute_harvey_ratio(w: u64, q: u64) -> u64{ 13 | (((w as u128) << 64 )/ q as u128) as u64 14 | } 15 | 16 | pub fn mul_low_word(a: u64, b: u64) -> u64 { 17 | let res = (a as u128) * (b as u128); 18 | (res >> 64) as u64 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | 25 | #[test] 26 | fn test_mul_high_word(){ 27 | assert_eq!(mul_high_word(1,1), 0); 28 | assert_eq!(mul_high_word(1u64 << 63,0), 0); 29 | assert_eq!(mul_high_word(1u64 << 63,2), 1); 30 | assert_eq!(mul_high_word(1u64 << 63,1u64 << 63), 1u64 << 62); 31 | } 32 | 33 | #[test] 34 | fn test_compute_harvey_ratio(){ 35 | assert_eq!(compute_harvey_ratio(0,100), 0); 36 | assert_eq!(compute_harvey_ratio(1,100), 184467440737095516); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/rerandomize.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | use cupcake::traits::{AdditiveHomomorphicScheme, KeyGeneration, PKEncryption, SKEncryption}; 6 | 7 | fn smartprint(v: &Vec) { 8 | println!("[{:?}, {:?}, ..., {:?}]", v[0], v[1], v[v.len() - 1]); 9 | } 10 | 11 | fn main() { 12 | let fv = cupcake::default(); 13 | 14 | let (pk, sk) = fv.generate_keypair(); 15 | 16 | println!("Encrypting a vector [0,1,2,3,...]"); 17 | let mut v = vec![]; 18 | for i in 0..fv.n { 19 | v.push(i as u8); 20 | } 21 | 22 | let mut ctv = fv.encrypt(&v, &pk); 23 | 24 | let pt_original: Vec = fv.decrypt(&ctv, &sk); 25 | print!("decrypted value: "); 26 | smartprint(&pt_original); 27 | 28 | println!("Rerandomizing the ciphertext..."); 29 | 30 | fv.rerandomize(&mut ctv, &pk); 31 | print!("decrypted value after reranromization: "); 32 | let pt_new: Vec = fv.decrypt(&ctv, &sk); 33 | smartprint(&pt_new); 34 | 35 | print!("Check that the plaintext has not changed..."); 36 | assert_eq!(pt_original, pt_new); 37 | println!("ok"); 38 | } 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Cupcake 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Pull Requests 6 | We actively welcome your pull requests. 7 | 8 | 1. Fork the repo and create your branch from `master`. 9 | 2. If you've added code that should be tested, add tests. 10 | 3. If you've changed APIs, update the documentation. 11 | 4. Ensure the test suite passes. 12 | 5. If you haven't already, complete the Contributor License Agreement ("CLA"). 13 | 14 | ## Contributor License Agreement ("CLA") 15 | In order to accept your pull request, we need you to submit a CLA. You only need 16 | to do this once to work on any of Facebook's open source projects. 17 | 18 | Complete your CLA here: 19 | 20 | ## Issues 21 | We use GitHub issues to track public bugs. Please ensure your description is 22 | clear and has sufficient instructions to be able to reproduce the issue. 23 | 24 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 25 | disclosure of security bugs. In those cases, please go through the process 26 | outlined on that page and do not file a public issue. 27 | 28 | ## License 29 | By contributing to Cupcake, you agree that your contributions will be licensed 30 | under the LICENSE file in the root directory of this source tree. 31 | -------------------------------------------------------------------------------- /examples/serialization.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | use cupcake::traits::*; 6 | fn smartprint(v: &Vec) { 7 | println!("[{:?}, {:?}, ..., {:?}]", v[0], v[1], v[v.len() - 1]); 8 | } 9 | 10 | fn main() { 11 | let fv = cupcake::default(); 12 | 13 | let (pk, sk) = fv.generate_keypair(); 14 | 15 | println!("Encrypting a constant vector v of 1s..."); 16 | let v = vec![1; fv.n]; 17 | 18 | let ctv = fv.encrypt(&v, &pk); 19 | 20 | println!("Encrypting a constant vector w of 2s..."); 21 | let w = vec![2; fv.n]; 22 | let ctw = fv.encrypt(&w, &pk); 23 | 24 | // serialize 25 | println!("Serializing the ciphertexts..."); 26 | 27 | let serialized_ctv = ctv.to_bytes(); 28 | let serialized_ctw = ctw.to_bytes(); 29 | 30 | // deserializing 31 | let mut deserialized_ctv = fv.from_bytes(&serialized_ctv); 32 | let deserialized_ctw = fv.from_bytes(&serialized_ctw); 33 | 34 | // add ctw into ctv 35 | println!("Adding the deserialized ciphertexts..."); 36 | fv.add_inplace(&mut deserialized_ctv, &deserialized_ctw); 37 | println!("Decrypting the sum..."); 38 | let pt_actual: Vec = fv.decrypt(&deserialized_ctv, &sk); 39 | println!("decrypted v+w: "); 40 | smartprint(&pt_actual); 41 | } 42 | -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | use cupcake::traits::*; 6 | 7 | fn smartprint(v: &Vec) { 8 | println!("[{:?}, {:?}, ..., {:?}]", v[0], v[1], v[v.len() - 1]); 9 | } 10 | 11 | fn main() { 12 | let fv = cupcake::default(); 13 | 14 | let (pk, sk) = fv.generate_keypair(); 15 | 16 | print!("Encrypting a constant vector v of 1s..."); 17 | let v = vec![1; fv.n]; 18 | 19 | let mut ctv = fv.encrypt(&v, &pk); 20 | 21 | let mut pt_actual: Vec = fv.decrypt(&ctv, &sk); 22 | print!("decrypted v: "); 23 | smartprint(&pt_actual); 24 | 25 | print!("Encrypting a constant vector w of 2s..."); 26 | let w = vec![2; fv.n]; 27 | let ctw = fv.encrypt(&w, &pk); 28 | 29 | pt_actual = fv.decrypt(&ctw, &sk); 30 | print!("decrypted w: "); 31 | smartprint(&pt_actual); 32 | 33 | // add ctw into ctv 34 | fv.add_inplace(&mut ctv, &ctw); 35 | print!("Decrypting the sum..."); 36 | pt_actual = fv.decrypt(&ctv, &sk); 37 | print!("decrypted v+w: "); 38 | smartprint(&pt_actual); 39 | 40 | // add the plaintext w into the ciphertext 41 | fv.add_plain_inplace(&mut ctv, &w); 42 | print!("Decrypting the sum..."); 43 | pt_actual = fv.decrypt(&ctv, &sk); 44 | print!("decrypted v+w+w: "); 45 | smartprint(&pt_actual); 46 | } 47 | -------------------------------------------------------------------------------- /src/polyarith/lazy_ntt.rs: -------------------------------------------------------------------------------- 1 | use crate::integer_arith::butterfly::{lazy_butterfly_u64, lazy_inverse_butterfly_u64}; 2 | 3 | pub fn lazy_ntt_u64(vec: &mut [u64], roots: &[u64], scaled_roots: &[u64], q: u64){ 4 | let n = vec.len(); 5 | let twoq = q << 1; 6 | 7 | let mut t = n; 8 | let mut m = 1; 9 | while m < n { 10 | t >>= 1; 11 | for i in 0..m { 12 | let j1 = 2 * i * t; 13 | let j2 = j1 + t - 1; 14 | let phi = roots[m + i]; 15 | for j in j1..j2 + 1 { 16 | let new = lazy_butterfly_u64(vec[j], vec[j+t], phi, scaled_roots[m+i], q, twoq); 17 | vec[j] = new.0; 18 | vec[j+t] = new.1; 19 | } 20 | } 21 | m <<= 1; 22 | } 23 | } 24 | 25 | pub fn lazy_inverse_ntt_u64(vec: &mut [u64], invroots: &[u64], scaled_invroots: &[u64], q: u64){ 26 | let twoq = q << 1; 27 | 28 | let mut t = 1; 29 | let mut m = vec.len(); 30 | while m > 1 { 31 | let mut j1 = 0; 32 | let h = m >> 1; 33 | for i in 0..h { 34 | let j2 = j1 + t - 1; 35 | for j in j1..j2 + 1 { 36 | // inverse butterfly 37 | let new = lazy_inverse_butterfly_u64(vec[j], vec[j+t], invroots[h+i], scaled_invroots[h+i], q, twoq); 38 | vec[j] = new.0; 39 | vec[j+t] = new.1; 40 | } 41 | j1 += 2 * t; 42 | } 43 | t <<= 1; 44 | m >>= 1; 45 | } 46 | } -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | pub(crate) fn reverse_bits_perm(input: &mut Vec) { 6 | let n = input.len(); 7 | if !n.is_power_of_two() { 8 | panic!("n must be a power of 2"); 9 | } 10 | for i in 0..n { 11 | let j = bit_reverse(i, n); 12 | if j > i { 13 | input.swap(i, j); 14 | } 15 | } 16 | } 17 | 18 | pub(crate) fn bit_reverse(i: usize, n: usize) -> usize { 19 | let mut mask = 1; 20 | let mut j = 0; 21 | while mask < n { 22 | let t = (i & mask) != 0; 23 | j += t as usize; 24 | mask <<= 1; 25 | if mask < n { 26 | j <<= 1; 27 | } 28 | } 29 | j 30 | } 31 | 32 | // tests 33 | #[cfg(test)] 34 | mod tests { 35 | use super::*; 36 | 37 | #[test] 38 | fn test_bit_reverse() { 39 | assert_eq!(bit_reverse(0, 4), 0); 40 | assert_eq!(bit_reverse(1, 4), 2); 41 | assert_eq!(bit_reverse(2, 4), 1); 42 | assert_eq!(bit_reverse(3, 4), 3); 43 | 44 | assert_eq!(bit_reverse(0, 8), 0); 45 | assert_eq!(bit_reverse(1, 8), 4); 46 | assert_eq!(bit_reverse(2, 8), 2); 47 | assert_eq!(bit_reverse(3, 8), 6); 48 | assert_eq!(bit_reverse(4, 8), 1); 49 | assert_eq!(bit_reverse(5, 8), 5); 50 | assert_eq!(bit_reverse(6, 8), 3); 51 | assert_eq!(bit_reverse(7, 8), 7); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cupcake 2 | 3 | Cupcake is an efficient Rust library for the (additive version of) Fan-Vercauteren homomorphic encryption scheme, offering capabilities to 4 | encrypt vectors, add/subtract two encrypted vectors, and rerandomize a ciphertext. 5 | 6 | 7 | ## Requirements 8 | Cupcake requires or works with 9 | * Mac OS X or Linux 10 | 11 | ## Installation 12 | Add the following line to the dependencies of your Cargo.toml: 13 | ``` 14 | Cupcake = "0.2.1" 15 | ``` 16 | 17 | ## Building from source 18 | 19 | ```bash 20 | git clone https://github.com/facebookresearch/Cupcake 21 | cd cupcake 22 | cargo build --release 23 | ``` 24 | 25 | ## Examples 26 | 27 | Several examples are included in `examples/.rs`, and can be run via 28 | `cargo run --example ` 29 | 30 | ## Documentation 31 | 32 | Documentation on the API can be built from `cargo doc`. 33 | 34 | ## Benchmarks and Tests 35 | 36 | We have included benchmarks and tests for both homomorphic operations and underlying arithmetic operations. They can be run using `cargo bench` and `cargo test`. 37 | 38 | ## Supported parameters 39 | 40 | Currently, we provide only one set of secure parameter, namely `FV::::default_2048();`. This parameter set has an estimated security level of about 128 bits according 41 | to the homomorphic encryption security standards [link](http://homomorphicencryption.org/wp-content/uploads/2018/11/HomomorphicEncryptionStandardv1.1.pdf). Use other parameters at your own risk! With the default parameter set, the plaintext type is vector of `u8` with a fixed length 2048. 42 | 43 | 44 | ## References 45 | 46 | - [The Fan-Vercauteren scheme](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.400.6346&rep=rep1&type=pdf) 47 | 48 | See the [CONTRIBUTING](CONTRIBUTING.md) file for how to help out. 49 | 50 | ## License 51 | Cupcake is MIT licensed, as found in the LICENSE file. 52 | -------------------------------------------------------------------------------- /src/integer_arith/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | pub mod scalar; 6 | pub mod butterfly; 7 | pub mod util; 8 | 9 | #[cfg(feature = "bigint")] 10 | pub mod bigint; 11 | 12 | /// The trait for utility functions related to scalar-like types. 13 | pub trait ArithUtils { 14 | 15 | /// Construct a new "modulus", which is a u64 plus information needed for fast modular reduction. 16 | fn new_modulus(a: u64) -> T; 17 | 18 | fn modulus(a: &T, q: &T) -> T; 19 | 20 | fn double(a: &T) -> T; 21 | 22 | // sample a value in [0, bound-1] 23 | fn sample_blw(bound: &T) -> T; 24 | 25 | fn sample_below_from_rng(bound: &T, rng: &mut dyn Rng) -> T; 26 | 27 | fn one() -> T { 28 | Self::from_u32_raw(1u32) 29 | } 30 | 31 | fn zero() -> T { 32 | Self::from_u32_raw(0u32) 33 | } 34 | 35 | fn add_mod(a: &T, b: &T, q: &T) -> T; 36 | fn sub_mod(a: &T, b: &T, q: &T) -> T; 37 | fn mul_mod(a: &T, b: &T, q: &T) -> T; 38 | fn inv_mod(a: &T, q: &T) -> T; 39 | 40 | fn from_u32(a: u32, q: &T) -> T; 41 | 42 | fn pow_mod(a: &T, b: &T, c: &T) -> T; 43 | 44 | fn add(a: &T, b: &T) -> T; 45 | 46 | fn sub(a: &T, b: &T) -> T; 47 | 48 | fn div(a: &T, b: &T) -> T; 49 | 50 | fn mul(a: &T, b: &T) -> T; 51 | 52 | // conversion 53 | fn from_u32_raw(a: u32) -> T; 54 | fn from_u64_raw(a: u64) -> T; 55 | fn to_u64(a: &T) -> u64; 56 | } 57 | 58 | pub trait ArithOperators{ 59 | fn add_u64(&mut self, a: u64); 60 | 61 | fn sub_u64(&mut self, a: u64); 62 | 63 | fn rep(&self) -> u64; 64 | } 65 | 66 | pub trait SuperTrait: ArithOperators + ArithUtils + Clone + From + From + PartialEq{} 67 | 68 | pub trait Rng: rand::CryptoRng + rand::RngCore {} -------------------------------------------------------------------------------- /benches/butterfly.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | #[macro_use] 6 | extern crate bencher; 7 | use bencher::Bencher; 8 | use cupcake::integer_arith::butterfly::{ 9 | butterfly, inverse_butterfly, lazy_butterfly, lazy_butterfly_u64, 10 | }; 11 | use cupcake::integer_arith::scalar::Scalar; 12 | use cupcake::integer_arith::ArithUtils; 13 | 14 | #[allow(non_snake_case)] 15 | fn bench_butterfly(bench: &mut Bencher) { 16 | let q = Scalar::new_modulus(18014398492704769u64); 17 | let x = rand::random::(); 18 | let y = rand::random::(); 19 | let w = rand::random::(); 20 | 21 | let mut X = Scalar::from(x); 22 | let mut Y = Scalar::from(y); 23 | let W = Scalar::from(w); 24 | 25 | bench.iter(|| { 26 | let _ = butterfly(&mut X, &mut Y, &W, &q); 27 | }) 28 | } 29 | 30 | #[allow(non_snake_case)] 31 | fn bench_inverse_butterfly(bench: &mut Bencher) { 32 | let q = Scalar::new_modulus(18014398492704769u64); 33 | let x = rand::random::(); 34 | let y = rand::random::(); 35 | let w = rand::random::(); 36 | 37 | let mut X = Scalar::from(x); 38 | let mut Y = Scalar::from(y); 39 | let W = Scalar::from(w); 40 | 41 | bench.iter(|| { 42 | let _ = inverse_butterfly(&mut X, &mut Y, &W, &q); 43 | }) 44 | } 45 | 46 | #[allow(non_snake_case)] 47 | fn bench_lazy_butterfly(bench: &mut Bencher) { 48 | let q = Scalar::new_modulus(18014398492704769u64); 49 | let x = rand::random::(); 50 | let y = rand::random::(); 51 | let w = rand::random::(); 52 | 53 | let mut X = Scalar::from(x); 54 | let mut Y = Scalar::from(y); 55 | let W = Scalar::from(w); 56 | 57 | let Wprime: u64 = cupcake::integer_arith::util::compute_harvey_ratio(W.rep(), q.rep()); 58 | 59 | let twoq: u64 = q.rep() << 1; 60 | 61 | bench.iter(|| { 62 | let _ = lazy_butterfly_u64(x, y, W.rep(), Wprime, q.rep(), twoq); 63 | }) 64 | } 65 | 66 | benchmark_group!( 67 | butterfly_group, 68 | bench_butterfly, 69 | bench_inverse_butterfly, 70 | bench_lazy_butterfly 71 | ); 72 | 73 | benchmark_main!(butterfly_group); 74 | -------------------------------------------------------------------------------- /benches/polyops.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | #[macro_use] 6 | extern crate bencher; 7 | use bencher::Bencher; 8 | use cupcake::rqpoly::RqPolyContext; 9 | use cupcake::traits::*; 10 | pub use std::sync::Arc; 11 | use cupcake::integer_arith::scalar::Scalar; 12 | use cupcake::integer_arith::ArithUtils; 13 | use cupcake::randutils; 14 | 15 | fn scalar_ntt(bench: &mut Bencher) { 16 | let q = Scalar::new_modulus(18014398492704769u64); 17 | let context = Arc::new(RqPolyContext::new(2048, &q)); 18 | let mut testpoly = randutils::sample_uniform_poly(context); 19 | 20 | bench.iter(|| { 21 | testpoly.is_ntt_form = false; 22 | let _ = testpoly.forward_transform(); 23 | }) 24 | } 25 | 26 | fn scalar_intt(bench: &mut Bencher) { 27 | let q = Scalar::new_modulus(18014398492704769u64); 28 | let context = Arc::new(RqPolyContext::new(2048, &q)); 29 | 30 | let mut testpoly = cupcake::randutils::sample_uniform_poly(context.clone()); 31 | 32 | bench.iter(|| { 33 | testpoly.is_ntt_form = true; 34 | let _ = testpoly.inverse_transform(); 35 | }) 36 | } 37 | 38 | fn sample_uniform(bench: &mut Bencher) { 39 | let q = Scalar::new_modulus(18014398492704769u64); 40 | let context = Arc::new(RqPolyContext::new(2048, &q)); 41 | 42 | bench.iter(|| { 43 | let _ = randutils::sample_uniform_poly(context.clone()); 44 | }) 45 | } 46 | 47 | fn sample_gaussian(bench: &mut Bencher) { 48 | let q = Scalar::new_modulus(18014398492704769u64); 49 | let context = Arc::new(RqPolyContext::new(2048, &q)); 50 | 51 | bench.iter(|| { 52 | let _ = randutils::sample_gaussian_poly(context.clone(), 3.14); 53 | }) 54 | } 55 | 56 | fn sample_ternary(bench: &mut Bencher) { 57 | let q = Scalar::new_modulus(18014398492704769u64); 58 | let context = Arc::new(RqPolyContext::new(2048, &q)); 59 | 60 | bench.iter(|| { 61 | let _ = randutils::sample_ternary_poly_prng(context.clone()); 62 | }) 63 | } 64 | 65 | benchmark_group!( 66 | polyops, 67 | sample_gaussian, 68 | sample_ternary, 69 | sample_uniform, 70 | scalar_ntt, 71 | scalar_intt, 72 | ); 73 | 74 | benchmark_main!(polyops); 75 | -------------------------------------------------------------------------------- /benches/scheme.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | #[macro_use] 6 | extern crate bencher; 7 | use bencher::Bencher; 8 | use cupcake::traits::*; 9 | pub use std::sync::Arc; 10 | 11 | fn encrypt_sk(bench: &mut Bencher) { 12 | let fv = cupcake::default(); 13 | 14 | let sk = fv.generate_key(); 15 | 16 | let v = (0..fv.n).map(|i| i as u8).collect::>(); 17 | 18 | bench.iter(|| { 19 | let _ = fv.encrypt_sk(&v, &sk); 20 | }) 21 | } 22 | 23 | fn decryption(bench: &mut Bencher) { 24 | let fv = cupcake::default(); 25 | 26 | let sk = fv.generate_key(); 27 | let mut v = vec![0; fv.n]; 28 | for i in 0..fv.n { 29 | v[i] = i as u8; 30 | } 31 | let ct = fv.encrypt_sk(&v, &sk); 32 | bench.iter(|| { 33 | let _: Vec = fv.decrypt(&ct, &sk); 34 | }) 35 | } 36 | 37 | fn encrypt_pk(bench: &mut Bencher) { 38 | let fv = cupcake::default(); 39 | 40 | let (pk, _sk) = fv.generate_keypair(); 41 | let mut v = vec![0; fv.n]; 42 | for i in 0..fv.n { 43 | v[i] = i as u8; 44 | } 45 | bench.iter(|| { 46 | let _ = fv.encrypt(&v, &pk); 47 | }) 48 | } 49 | 50 | fn encrypt_zero_pk(bench: &mut Bencher) { 51 | let fv = cupcake::default(); 52 | 53 | let (pk, _sk) = fv.generate_keypair(); 54 | let mut v = vec![0; fv.n]; 55 | for i in 0..fv.n { 56 | v[i] = i as u8; 57 | } 58 | bench.iter(|| { 59 | let _ = fv.encrypt_zero(&pk); 60 | }) 61 | } 62 | 63 | fn homomorphic_addition(bench: &mut Bencher) { 64 | let fv = cupcake::default(); 65 | 66 | let sk = fv.generate_key(); 67 | 68 | let mut v = vec![0; fv.n]; 69 | for i in 0..fv.n { 70 | v[i] = i as u8; 71 | } 72 | let mut ct1 = fv.encrypt_sk(&v, &sk); 73 | let ct2 = fv.encrypt_sk(&v, &sk); 74 | bench.iter(|| { 75 | fv.add_inplace(&mut ct1, &ct2); 76 | }) 77 | } 78 | 79 | fn rerandomize(bench: &mut Bencher) { 80 | let fv = cupcake::default(); 81 | 82 | let (pk, _) = fv.generate_keypair(); 83 | let mut v = vec![0; fv.n]; 84 | for i in 0..fv.n { 85 | v[i] = i as u8; 86 | } 87 | let mut ct = fv.encrypt(&v, &pk); 88 | 89 | bench.iter(|| { 90 | fv.rerandomize(&mut ct, &pk); 91 | }) 92 | } 93 | 94 | benchmark_group!( 95 | scheme, 96 | encrypt_sk, 97 | encrypt_pk, 98 | encrypt_zero_pk, 99 | decryption, 100 | homomorphic_addition, 101 | rerandomize, 102 | ); 103 | 104 | benchmark_main!(scheme); 105 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | /// Trait for key generation 6 | pub trait KeyGeneration: EncryptionOfZeros{ 7 | /// Generate a (pk, sk) keypair 8 | fn generate_keypair(&self) -> (PK, SK); 9 | 10 | /// Generate a secret key 11 | fn generate_key(&self) -> SK; 12 | } 13 | 14 | /// Trait for encryption of zeros. 15 | pub trait EncryptionOfZeros{ 16 | /// Use the secret key to generate a fresh encryption of zero 17 | fn encrypt_zero_sk(&self, sk: &SK) -> CT; 18 | 19 | /// Generate a fresh encryption of the zero plaintext 20 | fn encrypt_zero(&self, pk: &CT) -> CT; 21 | } 22 | 23 | /// Trait for symmetric key encryption. 24 | pub trait SKEncryption: KeyGeneration{ 25 | 26 | /// Encrypt a given plaintext 27 | fn encrypt_sk(&self, pt: &PT, sk: &SK) -> CT; 28 | 29 | /// Decrypt a ciphertext 30 | fn decrypt(&self, ct: &CT, sk: &SK) -> PT; 31 | } 32 | 33 | /// Trait for public key encryption. 34 | pub trait PKEncryption: SKEncryption { 35 | /// Encrypt a given plaintext 36 | fn encrypt(&self, pt: &PT, pk: &CT) -> CT; 37 | } 38 | 39 | /// Trait for adding a plaintext to a ciphertext. 40 | pub trait AddAndSubtract: { 41 | /// Add a plaintext into a ciphertext. 42 | fn add_plain_inplace(&self, ct1: &mut CT, pt: &PT); 43 | 44 | /// Add a plaintext into a ciphertext. 45 | fn sub_plain_inplace(&self, ct1: &mut CT, pt: &PT); 46 | } 47 | 48 | 49 | /// Trait for additive homomorphic operations. 50 | pub trait AdditiveHomomorphicScheme: EncryptionOfZeros { 51 | /// Add a ciphertext into another. 52 | fn add_inplace(&self, ct1: &mut CT, ct2: &CT); 53 | 54 | /// Rerandomize a ciphertext in-place. The resulting ciphertext will decrypt to the same 55 | /// plaintext, while being unlinkable to the input ciphertext. 56 | fn rerandomize(&self, ct: &mut CT, pk: &CT); 57 | } 58 | 59 | /// Trait for serialization. 60 | pub trait Serializable{ 61 | /// Serialize to a vector of bytes. 62 | fn to_bytes(&self) -> Vec; 63 | 64 | /// Deserialize from a vector of bytes. 65 | fn from_bytes(bytes: &Vec) -> Self; 66 | } 67 | 68 | /// Number-theoretic transform (NTT) and fast polynomial multiplication based on NTT. 69 | pub trait NTT: Clone { 70 | fn is_ntt_form(&self) -> bool; 71 | 72 | fn set_ntt_form(&mut self, value: bool); 73 | 74 | fn forward_transform(&mut self); 75 | 76 | fn inverse_transform(&mut self); 77 | } 78 | 79 | pub trait FastPolyMultiply: NTT { 80 | fn multiply_fast(&self, other: &Self) -> Self; 81 | 82 | fn coeffwise_multiply(&self, other: &Self) -> Self; 83 | } 84 | -------------------------------------------------------------------------------- /src/randutils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | /// Utility functions for generating random polynomials. 7 | use rand::distributions::{Distribution, Normal}; 8 | use rand::rngs::{OsRng, StdRng}; 9 | use rand::FromEntropy; 10 | use rand::{thread_rng, Rng}; 11 | 12 | use super::*; 13 | 14 | use crate::rqpoly::RqPolyContext; 15 | 16 | pub fn sample_ternary_poly(context: Arc>) -> RqPoly 17 | where 18 | T: SuperTrait, 19 | { 20 | let mut rng = OsRng::new().unwrap(); 21 | let q = context.q.rep() as i64; 22 | let c = (0..context.n).map(|_| { 23 | let mut t = rng.gen_range(-1i32, 2i32) as i64; 24 | if t < 0{ 25 | t += q; 26 | } 27 | T::from(t as u64) 28 | }).collect::>(); 29 | RqPoly { 30 | coeffs: c, 31 | is_ntt_form: false, 32 | context: Some(context), 33 | } 34 | } 35 | 36 | pub fn sample_ternary_poly_prng(context: Arc>) -> RqPoly 37 | where 38 | T: SuperTrait, 39 | { 40 | let mut rng = StdRng::from_entropy(); 41 | let q = context.q.rep(); 42 | let q_minus_one = q-1 as u64; 43 | 44 | let c = (0..context.n).map(|_| { 45 | let t = rng.gen_range(-1i32, 2i32) as i64; 46 | let mut s: u64 = t as u64; 47 | if t < 0 { 48 | s = q_minus_one; 49 | } 50 | T::from(s) 51 | }).collect::>(); 52 | 53 | RqPoly { 54 | coeffs: c, 55 | is_ntt_form: false, 56 | context: Some(context), 57 | } 58 | } 59 | 60 | /// Sample a polynomial with Gaussian coefficients in the ring Rq. 61 | pub fn sample_gaussian_poly(context: Arc>, stdev: f64) -> RqPoly 62 | where 63 | T: SuperTrait, 64 | { 65 | let normal = Normal::new(0.0, stdev); 66 | let mut rng = thread_rng(); 67 | let q: f64 = context.q.rep() as f64; 68 | 69 | let c = (0..context.n).map(|_| { 70 | let mut tmp = normal.sample(&mut rng); 71 | if tmp < 0.0 { 72 | tmp += q; 73 | } 74 | T::from(tmp as u64) 75 | }).collect::>(); 76 | 77 | RqPoly { 78 | coeffs: c, 79 | is_ntt_form: false, 80 | context: Some(context), 81 | } 82 | } 83 | 84 | /// Sample a uniform polynomial in the ring Rq. 85 | pub fn sample_uniform_poly(context: Arc>) -> RqPoly 86 | where 87 | T: SuperTrait, 88 | { 89 | let mut rng = thread_rng(); 90 | 91 | let c: Vec = vec![0;context.n].iter().map(|_| T::sample_below_from_rng(&context.q, &mut rng)).collect(); 92 | RqPoly { 93 | coeffs: c, 94 | is_ntt_form: false, 95 | context: Some(context), 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Open Source Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | Using welcoming and inclusive language 12 | Being respectful of differing viewpoints and experiences 13 | Gracefully accepting constructive criticism 14 | Focusing on what is best for the community 15 | Showing empathy towards other community members 16 | Examples of unacceptable behavior by participants include: 17 | 18 | The use of sexualized language or imagery and unwelcome sexual attention or advances 19 | Trolling, insulting/derogatory comments, and personal or political attacks 20 | Public or private harassment 21 | Publishing others’ private information, such as a physical or electronic address, without explicit permission 22 | Other conduct which could reasonably be considered inappropriate in a professional setting 23 | 24 | ## Our Responsibilities 25 | 26 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 27 | 28 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 29 | 30 | ## Scope 31 | 32 | This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 33 | 34 | ## Enforcement 35 | 36 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at opensource-conduct@fb.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 37 | 38 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. 39 | 40 | ## Attribution 41 | 42 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 43 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 44 | 45 | [homepage]: https://www.contributor-covenant.org -------------------------------------------------------------------------------- /src/serialize.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | use crate::integer_arith::scalar::Scalar; 6 | use crate::rqpoly::RqPoly; 7 | use crate::Serializable; 8 | use crate::FVCiphertext; 9 | 10 | #[cfg(test)] 11 | use crate::FV; 12 | #[cfg(test)] 13 | use crate::integer_arith::ArithUtils; 14 | #[cfg(test)] 15 | use crate::traits::*; 16 | 17 | use std::convert::TryInto; 18 | 19 | impl Serializable for Scalar { 20 | fn to_bytes(&self) -> std::vec::Vec { 21 | let bytes = self.rep().to_be_bytes(); 22 | let mut vec: Vec = vec![0; 8]; 23 | vec.copy_from_slice(&bytes); 24 | vec 25 | } 26 | fn from_bytes(bytes: &std::vec::Vec) -> Self { 27 | let a: u64 = u64::from_be_bytes(bytes.as_slice().try_into().unwrap()); 28 | Scalar::new(a) 29 | } 30 | } 31 | 32 | impl Serializable for RqPoly 33 | where 34 | T: Serializable + Clone, 35 | { 36 | fn to_bytes(&self) -> std::vec::Vec { 37 | let mut vec: Vec = vec![self.is_ntt_form as u8]; 38 | for coeff in &self.coeffs { 39 | let mut bytes = coeff.to_bytes(); 40 | vec.append(&mut bytes); 41 | } 42 | vec 43 | } 44 | fn from_bytes(bytes: &std::vec::Vec) -> Self { 45 | let mut coeffs = Vec::new(); 46 | let is_ntt_form = bytes[0] != 0; 47 | let mut i : usize = 1; 48 | while i + 8 <= bytes.len() { 49 | coeffs.push(T::from_bytes(&bytes[i..i+8].to_vec())); 50 | i += 8; 51 | } 52 | RqPoly::new_without_context(&coeffs, is_ntt_form) 53 | } 54 | } 55 | 56 | impl Serializable for FVCiphertext 57 | where 58 | T: Serializable + Clone, 59 | { 60 | fn to_bytes(&self) -> std::vec::Vec { 61 | let mut ct0_bytes = self.0.to_bytes(); 62 | let mut ct1_bytes = self.1.to_bytes(); 63 | ct0_bytes.append(&mut ct1_bytes); 64 | ct0_bytes 65 | } 66 | fn from_bytes(bytes: &std::vec::Vec) -> Self { 67 | let twon = bytes.len(); 68 | let n = twon / 2; 69 | (RqPoly::from_bytes(&bytes[0..n].to_vec()),RqPoly::from_bytes(&bytes[n..twon].to_vec())) 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests { 75 | use super::*; 76 | #[test] 77 | fn test_scalar_serialization() { 78 | let c = Scalar::new(2); 79 | let bytes = c.to_bytes(); 80 | let deserialized_c = Scalar::from_bytes(&bytes); 81 | assert_eq!(c, deserialized_c); 82 | } 83 | 84 | #[test] 85 | fn test_rqpoly_serialization() { 86 | let mut coeffs = Vec::::new(); 87 | for i in 0..4 { 88 | coeffs.push(Scalar::from_u64_raw(i)); 89 | } 90 | let testpoly = RqPoly::::new_without_context(&coeffs, false); 91 | let bytes = testpoly.to_bytes(); 92 | let deserialized = RqPoly::::from_bytes(&bytes); 93 | assert_eq!(testpoly, deserialized); 94 | } 95 | 96 | #[test] 97 | fn test_fvciphertext_serialization(){ 98 | // test ciphertext serialization 99 | let fv = FV::::default_2048(); 100 | let (pk, _) = fv.generate_keypair(); 101 | let mut v = vec![0; fv.n]; 102 | for i in 0..fv.n { 103 | v[i] = i as u8; 104 | } 105 | // encrypt v 106 | let ct = fv.encrypt(&v, &pk); 107 | let bytes = ct.to_bytes(); 108 | let ct_deserialized = FVCiphertext::from_bytes(&bytes); 109 | assert_eq!(ct_deserialized, ct); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/integer_arith/butterfly.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #[cfg(test)] 7 | use super::{SuperTrait}; 8 | 9 | use super::{ArithUtils}; 10 | 11 | // (X, Y) -> (X+Y, W(X-Y)) mod q 12 | #[allow(non_snake_case)] 13 | pub fn inverse_butterfly(X: &mut T, Y: &mut T, W: &T, q: &T) where T: ArithUtils{ 14 | let temp = T::sub_mod(X,Y, q); 15 | *X = T::add_mod(X, Y, q); 16 | *Y = T::mul_mod(W, &temp, q); 17 | } 18 | 19 | // (X, Y) -> (X+WY, X-WY) mod q 20 | #[allow(non_snake_case)] 21 | pub fn butterfly(X: &mut T, Y: &mut T, W: &T, q: &T) where T: ArithUtils{ 22 | let temp = T::mul_mod(Y, W, q); 23 | *Y = T::sub_mod(X, &temp, q); 24 | *X = T::add_mod(X, &temp, q); 25 | } 26 | 27 | // (X, Y) -> (X+WY, X-WY) 28 | // 0 <= X, Y < 4q => (0 <= X', Y' < 4q) 29 | #[allow(non_snake_case)] 30 | #[cfg(test)] 31 | pub fn lazy_butterfly(X: &mut T, Y: &mut T, W: u64, Wprime: u64, q: &T, twoq: u64) where T: SuperTrait{ 32 | let mut xx = X.rep(); 33 | if xx > twoq{ 34 | xx -= twoq; 35 | } 36 | let _qq = super::util::mul_high_word(Wprime, Y.rep()); 37 | let quo = W.wrapping_mul(Y.rep()) - _qq.wrapping_mul(q.rep()); 38 | // X += quo; 39 | *X = T::from(xx + quo); 40 | // Y = (x + 2q - quo); 41 | *Y = T::from(xx + twoq - quo); 42 | } 43 | 44 | #[allow(clippy::many_single_char_names)] 45 | pub fn lazy_butterfly_u64(mut x: u64, y:u64, w: u64, wprime: u64, q: u64, twoq: u64) -> (u64, u64){ 46 | // let twoq = 0; 47 | if x > twoq{ 48 | x -= twoq; 49 | } 50 | let _qq = super::util::mul_high_word(wprime, y); 51 | let wy = w.wrapping_mul(y); 52 | let qqq = _qq.wrapping_mul(q); 53 | let quo; 54 | if wy >= qqq { 55 | quo = wy - qqq; 56 | } 57 | else{ 58 | quo = u64::MAX - qqq + wy + 1; 59 | } 60 | (x + quo, x + twoq - quo) 61 | } 62 | 63 | #[allow(clippy::many_single_char_names)] 64 | pub fn lazy_inverse_butterfly_u64(x: u64, y:u64, w: u64, wprime: u64, q: u64, twoq: u64) -> (u64, u64){ 65 | let mut xx = x+y; 66 | 67 | if xx > twoq { 68 | xx -= twoq; 69 | } 70 | let t = twoq - y + x; 71 | let quo = super::util::mul_high_word(wprime, t); 72 | let wt = w.wrapping_mul(t); 73 | let qquo = quo.wrapping_mul(q); 74 | let yy; 75 | if wt >= qquo { 76 | yy = wt - qquo; 77 | } 78 | else{ 79 | yy = u64::MAX - qquo + wt + 1; 80 | } 81 | (xx, yy) 82 | } 83 | 84 | // (X,Y) -> (X+Y, W(X-Y)) mod q 85 | // 0 <= X, Y < 2q ==> 0 <= X', Y' < 2q 86 | #[allow(non_snake_case)] 87 | #[cfg(test)] 88 | pub(crate) fn lazy_inverse_butterfly(X: &mut T, Y: &mut T, W: u64, Wprime: u64, q: &T) where T: SuperTrait{ 89 | let mut xx = X.rep() + Y.rep(); 90 | 91 | let twoq = 2*q.rep(); 92 | if xx > twoq { 93 | xx -= twoq; 94 | } 95 | let t = twoq - Y.rep() + X.rep(); 96 | let quo = super::util::mul_high_word(Wprime, t); 97 | let yy = W.wrapping_mul(t) - quo.wrapping_mul(q.rep()); 98 | *X = T::from(xx); 99 | *Y = T::from(yy); 100 | } 101 | 102 | #[cfg(test)] 103 | mod tests { 104 | use super::*; 105 | use super::super::scalar::Scalar; 106 | 107 | fn butterfly_for_test(arr: [u64;4]) -> [u64; 2] { 108 | let mut x:Scalar = Scalar::from(arr[0]); 109 | let mut y:Scalar = Scalar::from(arr[1]); 110 | let w:Scalar = Scalar::from(arr[2]); 111 | let q:Scalar = Scalar::new_modulus(arr[3]); 112 | 113 | butterfly(&mut x, &mut y, &w, &q); 114 | [x.into(), y.into()] 115 | } 116 | 117 | fn inverse_butterfly_for_test(arr: [u64;4]) -> [u64; 2] { 118 | let mut x:Scalar = Scalar::from(arr[0]); 119 | let mut y:Scalar = Scalar::from(arr[1]); 120 | let w:Scalar = Scalar::from(arr[2]); 121 | let q:Scalar = Scalar::new_modulus(arr[3]); 122 | 123 | inverse_butterfly(&mut x, &mut y, &w, &q); 124 | [x.into(), y.into()] 125 | } 126 | 127 | fn lazy_butterfly_for_test(arr: [u64;4]) -> [u64; 2] { 128 | let mut x:Scalar = Scalar::from(arr[0]); 129 | let mut y:Scalar = Scalar::from(arr[1]); 130 | let w = arr[2]; 131 | let q:Scalar = Scalar::new_modulus(arr[3]); 132 | // W′ = ⌊W β/p⌋, 0 < W′ < β 133 | let wprime: u64 = super::super::util::compute_harvey_ratio(w, q.rep()); 134 | let twoq = q.rep() << 1; 135 | 136 | lazy_butterfly(&mut x, &mut y, w, wprime, &q, twoq); 137 | [x.into(), y.into()] 138 | } 139 | 140 | fn lazy_inverse_butterfly_for_test(arr: [u64;4]) -> [u64; 2] { 141 | let mut x:Scalar = Scalar::from(arr[0]); 142 | let mut y:Scalar = Scalar::from(arr[1]); 143 | let w = arr[2]; 144 | let q:Scalar = Scalar::new_modulus(arr[3]); 145 | // W′ = ⌊W β/p⌋, 0 < W′ < β 146 | let wprime: u64 = super::super::util::compute_harvey_ratio(w, q.rep()); 147 | 148 | lazy_inverse_butterfly(&mut x, &mut y, w, wprime, &q); 149 | [x.into(), y.into()] 150 | } 151 | 152 | macro_rules! lazy_butterfly_tests { 153 | ($($name:ident: $value:expr,)*) => { 154 | $( 155 | #[test] 156 | fn $name() { 157 | let input = $value; 158 | let butterfly_out = butterfly_for_test(input); 159 | let output = lazy_butterfly_for_test(input); 160 | println!("{:?}", butterfly_out); 161 | println!("{:?}", output); 162 | assert!(output[0] < 4*input[3]); 163 | assert!(output[1] < 4*input[3]); 164 | assert_eq!((output[1] - butterfly_out[1]) % input[3], 0); 165 | assert_eq!((output[0] - butterfly_out[0]) % input[3], 0); 166 | } 167 | )* 168 | } 169 | } 170 | 171 | macro_rules! lazy_inverse_butterfly_tests { 172 | ($($name:ident: $value:expr,)*) => { 173 | $( 174 | #[test] 175 | fn $name() { 176 | let input = $value; 177 | let butterfly_out = inverse_butterfly_for_test(input); 178 | let output = lazy_inverse_butterfly_for_test(input); 179 | println!("{:?}", butterfly_out); 180 | println!("{:?}", output); 181 | assert!(output[0] < 2*input[3]); 182 | assert!(output[1] < 2*input[3]); 183 | assert_eq!((output[1] - butterfly_out[1]) % input[3], 0); 184 | assert_eq!((output[0] - butterfly_out[0]) % input[3], 0); 185 | } 186 | )* 187 | } 188 | } 189 | 190 | macro_rules! inverse_butterfly_tests { 191 | ($($name:ident: $value:expr,)*) => { 192 | $( 193 | #[test] 194 | fn $name() { 195 | let (input, expected) = $value; 196 | assert_eq!(expected, inverse_butterfly_for_test(input)); 197 | } 198 | )* 199 | } 200 | } 201 | 202 | macro_rules! butterfly_tests { 203 | ($($name:ident: $value:expr,)*) => { 204 | $( 205 | #[test] 206 | fn $name() { 207 | let (input, expected) = $value; 208 | assert_eq!(expected, butterfly_for_test(input)); 209 | } 210 | )* 211 | } 212 | } 213 | 214 | butterfly_tests! { 215 | butterfly_0: ([0u64, 1u64, 0u64, 100u64], [0u64, 0u64]), 216 | butterfly_1: ([1u64, 1u64, 1u64, 100u64], [2u64, 0u64]), 217 | butterfly_2: ([50u64, 50u64, 1u64, 100u64], [0u64, 0u64]), 218 | butterfly_3: ([1u64, 1u64, 50u64, 100u64], [51u64, 51u64]), 219 | } 220 | 221 | inverse_butterfly_tests! { 222 | inverse_butterfly_0: ([0u64, 1u64, 0u64, 100u64], [1u64, 0u64]), 223 | inverse_butterfly_1: ([1u64, 1u64, 1u64, 100u64], [2u64, 0u64]), 224 | inverse_butterfly_2: ([50u64, 50u64, 1u64, 100u64], [0u64, 0u64]), 225 | inverse_butterfly_3: ([2u64, 1u64, 50u64, 100u64], [3u64, 50u64]), 226 | } 227 | 228 | lazy_butterfly_tests! { 229 | lazy_butterfly_0: [0u64, 1u64, 0u64, 100u64], 230 | lazy_butterfly_1: [1u64, 1u64, 1u64, 100u64], 231 | lazy_butterfly_2: [50u64, 50u64, 1u64, 100u64], 232 | lazy_butterfly_3: [1u64, 1u64, 50u64, 100u64], 233 | } 234 | 235 | lazy_inverse_butterfly_tests! { 236 | lazy_inverse_butterfly_0: [0u64, 1u64, 0u64, 100u64], 237 | lazy_inverse_butterfly_1: [1u64, 1u64, 1u64, 100u64], 238 | lazy_inverse_butterfly_2: [50u64, 50u64, 1u64, 100u64], 239 | lazy_inverse_butterfly_3: [1u64, 1u64, 50u64, 100u64], 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/rqpoly.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | use crate::integer_arith::{SuperTrait, ArithUtils}; 6 | use crate::polyarith::lazy_ntt::{lazy_ntt_u64, lazy_inverse_ntt_u64}; 7 | use crate::integer_arith::util::compute_harvey_ratio; 8 | use crate::utils::reverse_bits_perm; 9 | use std::sync::Arc; 10 | use crate::traits::*; 11 | 12 | /// Holds the context information for RqPolys, including degree n, modulus q, and optionally precomputed 13 | /// roots of unity for NTT purposes. 14 | #[derive(Debug)] 15 | pub struct RqPolyContext { 16 | pub n: usize, 17 | pub q: T, 18 | pub is_ntt_enabled: bool, 19 | pub roots: Vec, 20 | pub invroots: Vec, 21 | pub scaled_roots: Vec, // for use in lazy ntt 22 | pub scaled_invroots: Vec, // for use in lazy inverse ntt 23 | } 24 | 25 | /// Polynomials in Rq = Zq[x]/(x^n + 1). 26 | #[derive(Clone, Debug)] 27 | pub struct RqPoly { 28 | pub(crate) context: Option>>, 29 | pub coeffs: Vec, 30 | pub is_ntt_form: bool, 31 | } 32 | 33 | impl RqPoly where T:Clone{ 34 | pub fn new_without_context(coeffs: &[T], is_ntt_form:bool) -> Self{ 35 | RqPoly{ 36 | context: None, 37 | coeffs: coeffs.to_vec(), 38 | is_ntt_form, 39 | } 40 | } 41 | } 42 | 43 | impl RqPoly where T:Clone + ArithUtils{ 44 | pub fn new(context: Arc>) -> Self{ 45 | let n = context.n; 46 | RqPoly{ 47 | context: Some(context), 48 | coeffs: vec![T::zero(); n], 49 | is_ntt_form: false 50 | } 51 | } 52 | 53 | pub(crate) fn set_context(&mut self, context: Arc>){ 54 | self.context = Some(context); 55 | } 56 | } 57 | 58 | impl PartialEq for RqPoly where T: PartialEq { 59 | fn eq(&self, other: &Self) -> bool { 60 | if self.coeffs.len() != other.coeffs.len() { 61 | return false 62 | } 63 | for i in 0..self.coeffs.len(){ 64 | if self.coeffs[i] != other.coeffs[i] { return false; } 65 | } 66 | if self.is_ntt_form != other.is_ntt_form { return false; } 67 | true 68 | } 69 | } 70 | 71 | /// Arithmetics on general ring elements. 72 | pub trait FiniteRingElt { 73 | fn add_inplace(&mut self, other: &Self); 74 | 75 | fn sub_inplace(&mut self, other: &Self); 76 | 77 | fn negate_inplace(&mut self); 78 | 79 | fn multiply(&self, other: &Self) -> Self; 80 | } 81 | 82 | impl RqPolyContext 83 | where 84 | T: SuperTrait, 85 | { 86 | pub fn new(n: usize, q: &T) -> Self { 87 | let mut a = RqPolyContext { 88 | n, 89 | q: q.clone(), 90 | is_ntt_enabled: false, 91 | invroots: vec![], 92 | roots: vec![], 93 | scaled_roots: vec![], 94 | scaled_invroots: vec![], 95 | }; 96 | a.compute_roots(); 97 | a.compute_scaled_roots(); 98 | 99 | a 100 | } 101 | 102 | fn compute_scaled_roots(&mut self){ 103 | if !self.is_ntt_enabled{ 104 | return; 105 | } 106 | // compute scaled roots as wiprime = wi 107 | for i in 0..self.n { 108 | self.scaled_roots.push(T::from(compute_harvey_ratio(self.roots[i].rep(), self.q.rep()))); 109 | } 110 | 111 | for i in 0..self.n { 112 | self.scaled_invroots.push(T::from(compute_harvey_ratio(self.invroots[i].rep(), self.q.rep()))); 113 | } 114 | } 115 | 116 | fn compute_roots(&mut self) { 117 | let mut roots = vec![]; 118 | 119 | let root = self.find_root(); 120 | if root.is_none() { 121 | self.is_ntt_enabled = false; 122 | return; 123 | } 124 | let phi = root.unwrap(); 125 | 126 | let mut s = T::one(); 127 | for _ in 0..self.n { 128 | roots.push(s.clone()); 129 | s = T::mul_mod(&s, &phi, &self.q); 130 | } 131 | reverse_bits_perm(&mut roots); 132 | self.roots = roots; 133 | 134 | let mut invroots: Vec = vec![]; 135 | for x in self.roots.iter() { 136 | invroots.push(T::inv_mod(x, &self.q)); 137 | } 138 | self.invroots = invroots; 139 | self.is_ntt_enabled = true; 140 | } 141 | 142 | pub fn find_root(&self) -> Option { 143 | let bign = T::from(self.n as u32); 144 | let q_minus_one = T::sub(&self.q, &T::one()); 145 | 146 | let power = T::div(&q_minus_one, &T::double(&bign)); 147 | let mut s = T::one(); 148 | let one = T::one(); 149 | s = T::modulus(&s, &self.q); 150 | let mut spow = T::pow_mod(&s, &power, &self.q); 151 | let max_iter = 100; 152 | let mut iter = 0; 153 | while T::pow_mod(&spow, &bign, &self.q) != q_minus_one && iter < max_iter { 154 | s = T::add_mod(&s, &one, &self.q); 155 | // s = T::sample_blw(&self.q); 156 | spow = T::pow_mod(&s, &power, &self.q); 157 | iter += 1; 158 | } 159 | if iter < max_iter { 160 | Some(spow) 161 | } else { 162 | None 163 | } 164 | } 165 | } 166 | 167 | impl RqPoly 168 | where 169 | T: SuperTrait 170 | { 171 | fn lazy_ntt(&mut self) 172 | { 173 | let context = self.context.as_ref().unwrap(); 174 | if self.is_ntt_form { 175 | panic!("is already in ntt"); 176 | } 177 | let q = context.q.rep(); 178 | 179 | let mut coeffs_u64: Vec = self.coeffs.iter() 180 | .map(|elm| elm.rep()) 181 | .collect(); 182 | 183 | let roots_u64: Vec = context.roots.iter() 184 | .map(|elm| elm.rep()) 185 | .collect(); 186 | let scaledroots_u64: Vec = context.scaled_roots.iter() 187 | .map(|elm| elm.rep()) 188 | .collect(); 189 | 190 | lazy_ntt_u64(&mut coeffs_u64, &roots_u64, &scaledroots_u64, q); 191 | 192 | for (coeff, coeff_u64) in self.coeffs.iter_mut().zip(coeffs_u64.iter()){ 193 | *coeff = T::modulus(&T::from(*coeff_u64), &context.q); 194 | } 195 | self.set_ntt_form(true); 196 | } 197 | 198 | fn lazy_inverse_ntt(&mut self){ 199 | let context = self.context.as_ref().unwrap(); 200 | if !self.is_ntt_form { 201 | panic!("is already not in ntt"); 202 | } 203 | let n = context.n; 204 | let q = context.q.clone(); 205 | let ninv = T::inv_mod(&T::from_u32(n as u32, &q), &q); 206 | 207 | let mut coeffs_u64: Vec = self.coeffs.iter() 208 | .map(|elm| elm.rep()) 209 | .collect(); 210 | 211 | let invroots_u64: Vec = context.invroots.iter() 212 | .map(|elm| elm.rep()) 213 | .collect(); 214 | 215 | let scaled_invroots_u64: Vec = context.scaled_invroots.iter() 216 | .map(|elm| elm.rep()) 217 | .collect(); 218 | 219 | lazy_inverse_ntt_u64(&mut coeffs_u64, &invroots_u64, &scaled_invroots_u64, q.rep()); 220 | 221 | for (coeff, coeff_u64) in self.coeffs.iter_mut().zip(coeffs_u64.iter()){ 222 | *coeff = T::mul_mod(&ninv, &T::from(*coeff_u64), &context.q); 223 | } 224 | self.set_ntt_form(false); 225 | } 226 | } 227 | 228 | // NTT implementation(lazy version) 229 | impl NTT for RqPoly 230 | where 231 | T: SuperTrait 232 | { 233 | fn is_ntt_form(&self) -> bool { 234 | self.is_ntt_form 235 | } 236 | 237 | fn set_ntt_form(&mut self, value: bool) { 238 | self.is_ntt_form = value; 239 | } 240 | 241 | fn forward_transform(&mut self) { 242 | self.lazy_ntt() 243 | } 244 | 245 | fn inverse_transform(&mut self) { 246 | self.lazy_inverse_ntt() 247 | } 248 | } 249 | 250 | impl FastPolyMultiply for RqPoly 251 | where T: SuperTrait{ 252 | fn multiply_fast(&self, other: &Self) -> Self { 253 | let mut a: Self = self.clone(); 254 | let mut b = other.clone(); 255 | 256 | if !a.is_ntt_form { 257 | a.forward_transform(); 258 | } 259 | if !b.is_ntt_form { 260 | b.forward_transform(); 261 | } 262 | let mut c = a.coeffwise_multiply(&b); 263 | c.inverse_transform(); 264 | c 265 | } 266 | 267 | fn coeffwise_multiply(&self, other: &Self) -> Self { 268 | let context = self.context.as_ref().unwrap(); 269 | let mut c = self.clone(); 270 | for (inputs, cc) in self 271 | .coeffs 272 | .iter() 273 | .zip(other.coeffs.iter()) 274 | .zip(c.coeffs.iter_mut()) 275 | { 276 | *cc = T::mul_mod(inputs.0, inputs.1, &context.q); 277 | } 278 | c 279 | } 280 | } 281 | 282 | 283 | impl FiniteRingElt for RqPoly 284 | where 285 | T: ArithUtils + Clone, 286 | { 287 | fn add_inplace(&mut self, other: &Self) { 288 | let context = self.context.as_ref().unwrap(); 289 | let iter = self.coeffs.iter_mut().zip(other.coeffs.iter()); 290 | for (x, y) in iter { 291 | *x = T::add_mod(x, y, &context.q); 292 | } 293 | } 294 | 295 | fn sub_inplace(&mut self, other: &Self) { 296 | let context = self.context.as_ref().unwrap(); 297 | let iter = self.coeffs.iter_mut().zip(other.coeffs.iter()); 298 | for (x, y) in iter { 299 | *x = T::sub_mod(x, y, &context.q); 300 | } 301 | } 302 | 303 | fn negate_inplace(&mut self) { 304 | let context = self.context.as_ref().unwrap(); 305 | for x in self.coeffs.iter_mut() { 306 | *x = T::sub_mod(&context.q, x, &context.q); 307 | } 308 | } 309 | 310 | // naive multiplication 311 | fn multiply(&self, other: &Self) -> Self { 312 | // check context exists 313 | let context = self.context.as_ref().unwrap(); 314 | let f = &self.coeffs; 315 | let g = &other.coeffs; 316 | let n = context.n; 317 | let q = context.q.clone(); 318 | let mut res = vec![T::zero(); n]; 319 | 320 | for i in 0..n { 321 | for j in 0..i + 1 { 322 | let tmp = T::mul_mod(&f[j], &g[i - j], &q); 323 | res[i] = T::add_mod(&res[i], &tmp, &q); 324 | } 325 | for j in i + 1..context.n { 326 | let tmp = T::mul_mod(&f[j], &g[n + i - j], &q); 327 | res[i] = T::sub_mod(&res[i], &tmp, &q); 328 | } 329 | res[i] = T::modulus(&res[i], &q); 330 | } 331 | RqPoly { 332 | coeffs: res, 333 | is_ntt_form: false, 334 | context: Some(context.clone()), 335 | } 336 | } 337 | } 338 | 339 | #[cfg(test)] 340 | mod tests { 341 | use super::*; 342 | use crate::integer_arith::scalar::Scalar; 343 | 344 | fn from_vec(v: &Vec, context: Arc>) -> RqPoly 345 | where 346 | T: ArithUtils + From, 347 | { 348 | let mut c = vec![]; 349 | for _x in 0..context.n { 350 | let tmp = T::from(v[_x]); 351 | c.push(T::modulus(&tmp, &context.q)); 352 | } 353 | RqPoly { 354 | coeffs: c, 355 | is_ntt_form: false, 356 | context: Some(context.clone()), 357 | } 358 | } 359 | #[test] 360 | fn test_ntt_constant_scalar() { 361 | let q = Scalar::new_modulus(18014398492704769u64); 362 | let context = Arc::new(RqPolyContext::new(8, &q)); 363 | let v: Vec = vec![0; 8]; 364 | 365 | let mut testpoly = from_vec(&v, context.clone()); 366 | 367 | let tmp = Scalar::modulus(&Scalar::from(101 as u32), &q); 368 | testpoly.coeffs[0] = tmp.clone(); 369 | 370 | testpoly.forward_transform(); 371 | 372 | for i in 0..context.n { 373 | assert_eq!(testpoly.coeffs[i], tmp); 374 | } 375 | } 376 | 377 | #[test] 378 | fn test_ntt_scalar_compose_inverse() { 379 | let q = Scalar::new_modulus(18014398492704769u64); 380 | let context = RqPolyContext::new(2048, &q); 381 | let arc = Arc::new(context); 382 | let a = crate::randutils::sample_uniform_poly(arc.clone()); 383 | let mut aa = a.clone(); 384 | aa.forward_transform(); 385 | aa.inverse_transform(); 386 | 387 | assert_eq!(a.coeffs, aa.coeffs); 388 | 389 | // first inverse, then forward 390 | aa.set_ntt_form(true); 391 | 392 | aa.inverse_transform(); 393 | 394 | aa.forward_transform(); 395 | 396 | assert_eq!(a.coeffs, aa.coeffs); 397 | } 398 | 399 | #[test] 400 | fn test_fast_multiply_with_one() { 401 | let context = RqPolyContext::new(4, &Scalar::new_modulus(12289)); 402 | let arc = Arc::new(context); 403 | let a = from_vec(&vec![1, 0, 0, 0], arc.clone()); 404 | let b = from_vec(&vec![3, 4, 5, 6], arc.clone()); 405 | let c1 = a.multiply_fast(&b); 406 | assert_eq!(b.coeffs, c1.coeffs); 407 | } 408 | 409 | #[test] 410 | fn test_fast_multiply_scalar_2048() { 411 | let q = Scalar::new_modulus(18014398492704769u64); 412 | let context = RqPolyContext::new(2048, &q); 413 | let arc = Arc::new(context); 414 | 415 | let a = crate::randutils::sample_uniform_poly(arc.clone()); 416 | let b = crate::randutils::sample_uniform_poly(arc.clone()); 417 | let c = a.multiply(&b); 418 | let c1 = a.multiply_fast(&b); 419 | assert_eq!(c.coeffs, c1.coeffs); 420 | } 421 | 422 | #[test] 423 | fn test_find_root_scalar(){ 424 | let context2 = RqPolyContext::new(4, &Scalar::new_modulus(12289)); 425 | assert_eq!(context2.find_root().unwrap(), Scalar::from_u64_raw(8246u64)); 426 | } 427 | 428 | #[test] 429 | fn test_lazy_ntt(){ 430 | let q = Scalar::new_modulus(18014398492704769u64); 431 | let context = RqPolyContext::new(4, &q); 432 | let arc = Arc::new(context); 433 | let mut a = crate::randutils::sample_uniform_poly(arc.clone()); 434 | let mut aa = a.clone(); 435 | 436 | aa.forward_transform(); 437 | a.lazy_ntt(); 438 | 439 | // assert 440 | assert_eq!(aa.coeffs, a.coeffs); 441 | } 442 | 443 | #[test] 444 | fn test_lazy_inverse_ntt(){ 445 | let q = Scalar::new_modulus(18014398492704769u64); 446 | let context = RqPolyContext::new(4, &q); 447 | let arc = Arc::new(context); 448 | let mut a = crate::randutils::sample_uniform_poly(arc.clone()); 449 | a.set_ntt_form(true); 450 | let mut aa = a.clone(); 451 | aa.inverse_transform(); 452 | a.lazy_inverse_ntt(); 453 | 454 | // assert 455 | assert_eq!(aa.coeffs, a.coeffs); 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /src/integer_arith/scalar.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | use crate::integer_arith::{ArithOperators, ArithUtils, SuperTrait}; 6 | use modinverse::modinverse; 7 | use rand::rngs::{StdRng,ThreadRng}; 8 | use rand::{FromEntropy}; 9 | use super::Rng; 10 | use ::std::ops; 11 | pub use std::sync::Arc; 12 | 13 | impl Rng for StdRng {} 14 | impl Rng for ThreadRng {} 15 | 16 | /// The ScalarContext class contains useful auxilliary information for fast modular reduction against a Scalar instance. 17 | #[derive(Debug, PartialEq, Eq, Clone)] 18 | struct ScalarContext { 19 | barrett_ratio: (u64, u64), 20 | } 21 | 22 | impl ScalarContext { 23 | fn new(q: u64) -> Self { 24 | let ratio = Self::compute_barrett_ratio(q); 25 | ScalarContext { 26 | barrett_ratio: ratio, 27 | } 28 | } 29 | 30 | /// Compute floor(2^128/q) and put it in 2 u64s as (low-word, high-word) 31 | fn compute_barrett_ratio(q: u64) -> (u64, u64) { 32 | // 2^127 = s*q + t. 33 | let a = 1u128 << 127; 34 | let mut t = a % (q as u128); 35 | let mut s = (a - t) / (q as u128); 36 | 37 | s <<= 1; 38 | t <<= 1; 39 | if t >= (q as u128) { 40 | s += 1; 41 | } 42 | (s as u64, (s >> 64) as u64) 43 | } 44 | } 45 | 46 | /// The Scalar struct is a wrapper around u64 which has optional fast modular arithmetic through ScalarContext. 47 | #[derive(Debug, Clone)] 48 | pub struct Scalar { 49 | context: Option, 50 | rep: u64, 51 | bit_count: usize, 52 | } 53 | 54 | impl Scalar { 55 | /// Construct a new scalar from u64. 56 | pub fn new(a: u64) -> Self { 57 | Scalar { 58 | rep: a, 59 | context: None, 60 | bit_count: 0, 61 | } 62 | } 63 | 64 | pub fn rep(&self) -> u64{ 65 | self.rep 66 | } 67 | } 68 | 69 | /// Trait implementations 70 | impl SuperTrait for Scalar {} 71 | 72 | impl PartialEq for Scalar { 73 | fn eq(&self, other: &Self) -> bool { 74 | self.rep == other.rep 75 | } 76 | } 77 | 78 | // Conversions 79 | impl From for Scalar { 80 | fn from(item: u32) -> Self { 81 | Scalar { context: None, rep: item as u64, bit_count: 0 } 82 | } 83 | } 84 | 85 | impl From for Scalar { 86 | fn from(item: u64) -> Self { 87 | Scalar { context: None, rep: item, bit_count: 0 } 88 | } 89 | } 90 | 91 | impl From for u64{ 92 | fn from(item: Scalar) -> u64 { 93 | item.rep 94 | } 95 | } 96 | 97 | // Operators 98 | impl ops::Add<&Scalar> for Scalar { 99 | type Output = Scalar; 100 | fn add(self, v: &Scalar) -> Scalar { 101 | Scalar::new(self.rep + v.rep) 102 | } 103 | } 104 | 105 | impl ops::Add for Scalar { 106 | type Output = Scalar; 107 | fn add(self, v: Scalar) -> Scalar { 108 | self + &v 109 | } 110 | } 111 | 112 | impl ops::Sub<&Scalar> for Scalar { 113 | type Output = Scalar; 114 | fn sub(self, v: &Scalar) -> Scalar { 115 | Scalar::new(self.rep - v.rep) 116 | } 117 | } 118 | 119 | impl ops::Sub for Scalar { 120 | type Output = Scalar; 121 | fn sub(self, v: Scalar) -> Scalar { 122 | self - &v 123 | } 124 | } 125 | 126 | impl ops::Mul for Scalar { 127 | type Output = Scalar; 128 | fn mul(self, v: u64) -> Scalar { 129 | Scalar::new(self.rep * v) 130 | } 131 | } 132 | 133 | impl ArithOperators for Scalar{ 134 | fn add_u64(&mut self, a: u64){ 135 | self.rep += a; 136 | } 137 | 138 | fn sub_u64(&mut self, a: u64){ 139 | self.rep -= a; 140 | } 141 | 142 | fn rep(&self) -> u64{ 143 | self.rep 144 | } 145 | } 146 | 147 | // Trait implementation 148 | impl ArithUtils for Scalar { 149 | fn new_modulus(q: u64) -> Scalar { 150 | Scalar { 151 | rep: q, 152 | context: Some(ScalarContext::new(q)), 153 | bit_count: 64 - q.leading_zeros() as usize, 154 | } 155 | } 156 | 157 | fn sub(a: &Scalar, b: &Scalar) -> Scalar { 158 | Scalar::new(a.rep - b.rep) 159 | } 160 | 161 | fn div(a: &Scalar, b: &Scalar) -> Scalar { 162 | Scalar::new(a.rep / b.rep) 163 | } 164 | 165 | fn add_mod(a: &Scalar, b: &Scalar, q: &Scalar) -> Scalar { 166 | let mut sum = a.rep + b.rep; 167 | if sum >= q.rep { 168 | sum -= q.rep; 169 | } 170 | Scalar::new(sum) 171 | } 172 | 173 | fn sub_mod(a: &Scalar, b: &Scalar, q: &Scalar) -> Scalar { 174 | Scalar::_sub_mod(a, b, q.rep) 175 | } 176 | 177 | fn mul_mod(a: &Scalar, b: &Scalar, q: &Scalar) -> Scalar { 178 | let res = Scalar::_barret_multiply(a, b, q.context.as_ref().unwrap().barrett_ratio, q.rep); 179 | Scalar::new(res) 180 | } 181 | 182 | fn inv_mod(a: &Scalar, q: &Scalar) -> Scalar { 183 | Scalar::_inv_mod(a, q.rep) 184 | } 185 | 186 | fn from_u32(a: u32, q: &Scalar) -> Scalar { 187 | Scalar::new((a as u64) % q.rep) 188 | } 189 | 190 | fn from_u32_raw(a: u32) -> Scalar { 191 | Scalar::new(a as u64) 192 | } 193 | 194 | fn from_u64_raw(a: u64) -> Scalar { 195 | Scalar::new(a) 196 | } 197 | 198 | fn pow_mod(base: &Scalar, b: &Scalar, q: &Scalar) -> Scalar { 199 | let bits: Vec = b.get_bits(); 200 | let mut res = Self::one(); 201 | res = Self::modulus(&res, q); 202 | let mut pow = Scalar::new(base.rep); 203 | for bit in bits.iter() { 204 | if *bit { 205 | res = Self::mul_mod(&res, &pow, q); 206 | } 207 | pow = Self::mul_mod(&pow, &pow, q); 208 | } 209 | res 210 | } 211 | 212 | fn double(a: &Scalar) -> Scalar { 213 | Scalar::new(a.rep << 1) 214 | } 215 | 216 | fn sample_blw(upper_bound: &Scalar) -> Scalar { 217 | loop { 218 | let n = Self::_sample(upper_bound.bit_count); 219 | if n < upper_bound.rep { 220 | return Scalar::new(n); 221 | } 222 | } 223 | } 224 | 225 | // sample below using a given rng. 226 | fn sample_below_from_rng(upper_bound: &Scalar, rng: &mut dyn Rng) -> Self { 227 | upper_bound.sample(rng) 228 | } 229 | 230 | fn modulus(a: &Scalar, q: &Scalar) -> Scalar { 231 | match &q.context{ 232 | Some(context) => {Scalar::from(Scalar::_barret_reduce((a.rep(), 0), context.barrett_ratio, q.rep()))} 233 | None => Scalar::new(a.rep % q.rep) 234 | } 235 | } 236 | 237 | fn mul(a: &Scalar, b: &Scalar) -> Scalar { 238 | Scalar::new(a.rep * b.rep) 239 | } 240 | 241 | fn to_u64(a: &Scalar) -> u64 { 242 | a.rep 243 | } 244 | 245 | fn add(a: &Scalar, b: &Scalar) -> Scalar { 246 | Scalar::new(a.rep + b.rep) 247 | } 248 | } 249 | 250 | impl Scalar { 251 | /// Bit length of this scalar. 252 | fn bit_length(&self) -> usize { 253 | 64 - self.rep.leading_zeros() as usize 254 | } 255 | 256 | /// Return a vector of booleans representing the bits of this scalar, starting from the least significant bit. 257 | fn get_bits(&self) -> Vec { 258 | let len = self.bit_length(); 259 | let mut res = vec![]; 260 | let mut mask = 1u64; 261 | for _ in 0..len { 262 | res.push((self.rep & mask) != 0); 263 | mask <<= 1; 264 | } 265 | res 266 | } 267 | 268 | fn sample(&self, rng: &mut dyn Rng) -> Scalar { 269 | let max_multiple = self.rep() * (u64::MAX / self.rep() ); 270 | loop{ 271 | let a = rng.next_u64(); 272 | if a < max_multiple { 273 | return Scalar::modulus(&Scalar::from(a), self); 274 | } 275 | } 276 | } 277 | 278 | fn _sample_from_rng(bit_size: usize, rng: &mut dyn Rng) -> u64 { 279 | let bytes = (bit_size - 1) / 8 + 1; 280 | let mut buf: Vec = vec![0; bytes]; 281 | rng.fill_bytes(&mut buf); 282 | 283 | // from vector to u64. 284 | let mut a = 0u64; 285 | for x in buf.iter() { 286 | a <<= 8; 287 | a += *x as u64; 288 | } 289 | a >>= bytes * 8 - bit_size; 290 | a 291 | } 292 | 293 | fn _sample(bit_size: usize) -> u64 { 294 | let mut rng = StdRng::from_entropy(); 295 | Self::_sample_from_rng(bit_size, &mut rng) 296 | } 297 | 298 | fn _sub_mod(a: &Scalar, b: &Scalar, q: u64) -> Self { 299 | let diff; 300 | if a.rep >= b.rep { 301 | diff = a.rep - b.rep; 302 | } else { 303 | diff = a.rep + q - b.rep; 304 | } 305 | Scalar::new(diff) 306 | } 307 | 308 | fn _slowmul_mod(a: &Scalar, b: &Scalar, q: u64) -> Self { 309 | let res = (a.rep as u128) * (b.rep as u128); 310 | Scalar::new((res % (q as u128)) as u64) 311 | } 312 | 313 | fn _multiply_u64(a: u64, b: u64) -> (u64, u64) { 314 | let res = (a as u128) * (b as u128); 315 | (res as u64, (res >> 64) as u64) 316 | } 317 | 318 | fn _add_u64(a: u64, b: u64) -> (u64, bool) { 319 | let res = (a as u128 + b as u128) as u64; 320 | (res, res < a) 321 | } 322 | 323 | fn _barret_reduce(a: (u64, u64), ratio: (u64, u64), q: u64) -> u64 { 324 | // compute w = a*ratio >> 128. 325 | 326 | // start with lw(a1r1) 327 | let mut w = 0; 328 | if a.1 != 0{ 329 | w = a.1.wrapping_mul(ratio.1); 330 | } 331 | let a0r0 = Scalar::_multiply_u64(a.0, ratio.0); 332 | 333 | let a0r1 = Scalar::_multiply_u64(a.0, ratio.1); 334 | 335 | // w += hw(a0r1) 336 | w += a0r1.1; 337 | 338 | // compute hw(a0r0) + lw(a0r1), add carry into w. put result into tmp. 339 | let (tmp, carry) = Scalar::_add_u64(a0r0.1, a0r1.0); 340 | w += carry as u64; 341 | 342 | // Round2 343 | if a.1 != 0{ 344 | let a1r0 = Scalar::_multiply_u64(a.1, ratio.0); 345 | w += a1r0.1; 346 | // final carry 347 | let (_, carry2) = Scalar::_add_u64(a1r0.0, tmp); 348 | w += carry2 as u64; 349 | } 350 | 351 | // low = w*q mod 2^64. 352 | // let low = Scalar::multiply_u64(w, q).0; 353 | let low = w.wrapping_mul(q); 354 | 355 | let mut res; 356 | if a.0 >= low { 357 | res = a.0 - low; 358 | } else { 359 | // res = a.0 + 2^64 - low. 360 | res = a.0 + (!low) + 1; 361 | } 362 | 363 | if res >= q { 364 | res -= q; 365 | } 366 | res 367 | } 368 | 369 | fn _inv_mod(a: &Scalar, q: u64) -> Self { 370 | Scalar::new(modinverse(a.rep as i128, q as i128).unwrap() as u64) 371 | } 372 | 373 | fn _barret_multiply(a: &Scalar, b: &Scalar, ratio: (u64, u64), q: u64) -> u64 { 374 | let prod = Scalar::_multiply_u64(a.rep, b.rep); 375 | Scalar::_barret_reduce(prod, ratio, q) 376 | } 377 | } 378 | 379 | #[cfg(test)] 380 | mod tests { 381 | use super::*; 382 | #[test] 383 | fn test_bitlength() { 384 | assert_eq!(Scalar::from(2u32).bit_length(), 2); 385 | assert_eq!(Scalar::from(16u32).bit_length(), 5); 386 | assert_eq!(Scalar::from_u64_raw(18014398492704769u64).bit_length(), 54); 387 | } 388 | 389 | #[test] 390 | fn test_getbits() { 391 | assert_eq!(Scalar::from(1u32).get_bits(), vec![true]); 392 | assert_eq!(Scalar::from(2u32).get_bits(), vec![false, true]); 393 | assert_eq!(Scalar::from(5u32).get_bits(), vec![true, false, true]); 394 | assert_eq!( 395 | Scalar::from_u64_raw(127).get_bits(), 396 | vec![true, true, true, true, true, true, true] 397 | ); 398 | } 399 | 400 | #[test] 401 | fn test_sample_bitsize() { 402 | let bit_size = 54; 403 | let bound = 1u64 << bit_size; 404 | for _ in 0..10 { 405 | let a = Scalar::_sample(bit_size); 406 | assert!(a < bound); 407 | } 408 | } 409 | 410 | #[test] 411 | fn test_sample_below() { 412 | let q: u64 = 18014398492704769; 413 | let q_scalar = Scalar::new_modulus(q); 414 | for _ in 0..10 { 415 | assert!(Scalar::sample_blw(&q_scalar).rep < q); 416 | } 417 | } 418 | 419 | #[test] 420 | fn test_sample_below_prng() { 421 | use rand::{thread_rng}; 422 | let q: u64 = 18014398492704769; 423 | let q_scalar = Scalar::new_modulus(q); 424 | let mut rng = thread_rng(); 425 | for _ in 0..10 { 426 | assert!(Scalar::sample_below_from_rng(&q_scalar, &mut rng).rep < q); 427 | } 428 | } 429 | #[test] 430 | fn test_equality() { 431 | assert_eq!(Scalar::zero(), Scalar::zero()); 432 | } 433 | 434 | #[test] 435 | fn test_subtraction() { 436 | let a = Scalar::zero(); 437 | let b = Scalar::one(); 438 | let c = Scalar::_sub_mod(&a, &b, 12289); 439 | assert_eq!(c.rep, 12288); 440 | } 441 | 442 | #[test] 443 | fn test_inverse() { 444 | let q = Scalar::new(11); 445 | let c = Scalar::new(2); 446 | let a = Scalar::inv_mod(&c, &q); 447 | assert_eq!(a.rep, 6); 448 | } 449 | 450 | #[test] 451 | fn test_mul_mod() { 452 | let q = 11u64; 453 | let c = Scalar::new(4); 454 | let a = Scalar::_slowmul_mod(&c, &c, q); 455 | assert_eq!(a.rep, 5); 456 | } 457 | 458 | #[test] 459 | fn test_pow_mod() { 460 | let q = Scalar::new_modulus(11); 461 | let c = Scalar::new(4); 462 | let a = Scalar::pow_mod(&c, &c, &q); 463 | assert_eq!(a.rep, 3); 464 | } 465 | 466 | #[test] 467 | fn test_pow_mod_large() { 468 | let q = Scalar::new_modulus(12289); 469 | let two = Scalar::new(2); 470 | let mut a: Scalar = Scalar::from_u64_raw(3); 471 | a = Scalar::modulus(&a, &q); 472 | 473 | for _ in 0..10 { 474 | a = Scalar::pow_mod(&a, &two, &q); 475 | assert!(a.rep < q.rep); 476 | } 477 | } 478 | 479 | #[test] 480 | fn test_barret_ratio() { 481 | let q = 18014398492704769u64; 482 | assert_eq!( 483 | ScalarContext::compute_barrett_ratio(q), 484 | (17592185012223u64, 1024u64) 485 | ); 486 | } 487 | 488 | #[test] 489 | fn test_barret_reduction() { 490 | let q = 18014398492704769; 491 | let ratio = (17592185012223u64, 1024u64); 492 | 493 | let a: (u64, u64) = (1, 0); 494 | let b = Scalar::_barret_reduce(a, ratio, q); 495 | assert_eq!(b, 1); 496 | 497 | let a: (u64, u64) = (q, 0); 498 | let b = Scalar::_barret_reduce(a, ratio, q); 499 | assert_eq!(b, 0); 500 | 501 | let a: (u64, u64) = (0, 1); 502 | let b = Scalar::_barret_reduce(a, ratio, q); 503 | assert_eq!(b, 17179868160); 504 | } 505 | 506 | #[test] 507 | fn test_barret_multiply() { 508 | let q: u64 = 18014398492704769; 509 | let ratio = (17592185012223u64, 1024u64); 510 | 511 | let a = Scalar::new(q - 2); 512 | let b = Scalar::new(q - 3); 513 | let c = Scalar::_barret_multiply(&a, &b, ratio, q); 514 | 515 | assert_eq!(c, 6); 516 | } 517 | 518 | #[test] 519 | fn test_operator_add(){ 520 | let a = Scalar::new(123); 521 | let b = Scalar::new(123); 522 | let c = a + &b; 523 | assert_eq!(u64::from(c), 246u64); 524 | } 525 | } 526 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | //! An implementation of the (additive homomorphism only) Fan-Vercauteren (FV) lattice-based homomorphic encryption scheme. 6 | //! # Overview 7 | //! Homomorphic encryption supports operations on encrypted data without knowing the decryption key. 8 | //! 9 | //! In order to use lattice-based homomorphic encryption, we first need to decide on the scheme to use and set up the parameters, including the polynomial degree (n) and the modulus (q). 10 | //! 11 | //! Currently, we only support one scheme (FV) and one set of parameters, corresponding to a polynomial degree of 2048 and a 54-bit prime modulus. 12 | //! ``` 13 | //! let scheme = cupcake::default(); 14 | //! ``` 15 | //! # Setup 16 | //! In order to encrypt and decrypt data, we needs to generate a keypair, i.e. a secret key and a public key. 17 | //! ``` 18 | //! let scheme = cupcake::default(); 19 | //! use cupcake::traits::{KeyGeneration}; 20 | //! let (pk, sk) = scheme.generate_keypair(); 21 | //! ``` 22 | //! The public key can be used for encryption and the secret key can be used for encryption or decryption. 23 | //! 24 | //! # Encryption and Decryption 25 | //! 26 | //! The default plaintext space of Cupcake is `Vec` of fixed size n. We can encrypt a vector under a public key like so 27 | //! ``` 28 | //! # let scheme = cupcake::default(); 29 | //! # use cupcake::traits::KeyGeneration; 30 | //! # let (pk, sk) = scheme.generate_keypair(); 31 | //! use cupcake::traits::{SKEncryption, PKEncryption}; 32 | //! let v = vec![1; scheme.n]; 33 | //! let ct = scheme.encrypt(&v, &pk); 34 | //! ``` 35 | //! Then, the ciphertext `ct` can be decrypted using the secret key: 36 | //! ``` 37 | //! # let scheme = cupcake::default(); 38 | //! # use cupcake::traits::{KeyGeneration, SKEncryption, PKEncryption}; 39 | //! # let (pk, sk) = scheme.generate_keypair(); 40 | //! # let v = vec![1; scheme.n]; 41 | //! # let ct = scheme.encrypt(&v, &pk); 42 | //! let w: Vec= scheme.decrypt(&ct, &sk); 43 | //! assert_eq!(v, w); 44 | //! ``` 45 | //! You may also use other plaintext types, which are vectors of `Scalar` of size n. Here `Scalar` represents an integer modulo a fixed modulus t. 46 | //! See the following example: 47 | //! ``` 48 | //! # use cupcake::traits::{KeyGeneration, PKEncryption, SKEncryption}; 49 | //! use cupcake::integer_arith::scalar::Scalar; 50 | //! let t = 199; 51 | //! let scheme = cupcake::default_with_plaintext_mod(t); 52 | //! let (pk, sk) = scheme.generate_keypair(); 53 | //! let plain_modulus = scheme.t.clone(); 54 | //! let pt = vec![Scalar::from(t-1 as u32); scheme.n]; 55 | //! let ct = scheme.encrypt(&pt, &pk); 56 | //! let pt_actual: Vec = scheme.decrypt(&ct, &sk); 57 | //! assert_eq!(pt_actual, pt); 58 | //! ``` 59 | //! # Homomorphic Operations 60 | //! 61 | //! We can encrypt two vectors and add up the resulting ciphertexts. 62 | //! ``` 63 | //! # let scheme = cupcake::default(); 64 | //! # use cupcake::traits::{KeyGeneration, SKEncryption, PKEncryption}; 65 | //! # let (pk, sk) = scheme.generate_keypair(); 66 | //! use cupcake::traits::{AdditiveHomomorphicScheme}; 67 | //! let z1 = vec![1; scheme.n]; 68 | //! let mut ctz1 = scheme.encrypt(&z1, &pk); 69 | //! let z2 = vec![2; scheme.n]; 70 | //! let ctz2 = scheme.encrypt(&z2, &pk); 71 | //! scheme.add_inplace(&mut ctz1, &ctz2); 72 | //! // Now ctz1 should decrypt to vec![3; scheme.n]; 73 | //! let expected = vec![3; scheme.n]; 74 | //! let actual: Vec = scheme.decrypt(&ctz1, &sk); 75 | //! assert_eq!(actual, expected); 76 | //! ``` 77 | //! Alternatively, we can add a plaintext vector into a ciphertext 78 | //! ``` 79 | //! # let scheme = cupcake::default(); 80 | //! # use cupcake::traits::{KeyGeneration, SKEncryption, PKEncryption}; 81 | //! # let (pk, sk) = scheme.generate_keypair(); 82 | //! use cupcake::traits::AddAndSubtract; 83 | //! let z = vec![1; scheme.n]; 84 | //! let mut ctz = scheme.encrypt(&z, &pk); 85 | //! let p = vec![4; scheme.n]; 86 | //! scheme.add_plain_inplace(&mut ctz, &p); 87 | //! // Now ctz should decrypt to vec![5; scheme.n] 88 | //! let expected = vec![5; scheme.n]; 89 | //! let actual: Vec = scheme.decrypt(&ctz, &sk); 90 | //! assert_eq!(actual, expected); 91 | //! ``` 92 | //! # Rerandomization 93 | //! Furthermore, you can rerandomize a ciphertext using the public key. The output is another ciphertext which will be still decrypt to the same plaintext, but cannot be linked to the input. 94 | //! ``` 95 | //! # let scheme = cupcake::default(); 96 | //! # use cupcake::traits::{KeyGeneration, SKEncryption, PKEncryption}; 97 | //! # let (pk, sk) = scheme.generate_keypair(); 98 | //! # use cupcake::traits::{AdditiveHomomorphicScheme}; 99 | //! let mu = vec![1; scheme.n]; 100 | //! let mut ct = scheme.encrypt(&mu, &pk); 101 | //! scheme.rerandomize(&mut ct, &pk); 102 | //! // The new ct should still decrypt to mu. 103 | //! let actual: Vec = scheme.decrypt(&ct, &sk); 104 | //! let expected = mu; 105 | //! assert_eq!(actual, expected); 106 | //! ``` 107 | //! # Serialization 108 | //! We provide methods to serialize a ciphertext into a ```Vec```. Note that after deserialization, a proper context needs to be set before the further operations can be done on the ciphertext. See 109 | //! the following example: 110 | //! ``` 111 | //! # let scheme = cupcake::default(); 112 | //! # use cupcake::traits::{KeyGeneration, SKEncryption, PKEncryption}; 113 | //! # let (pk, sk) = scheme.generate_keypair(); 114 | //! # use cupcake::traits::{AdditiveHomomorphicScheme}; 115 | //! use crate::cupcake::traits::Serializable; 116 | //! let v = vec![1; scheme.n]; 117 | //! let w = vec![1; scheme.n]; 118 | //! let ctv = scheme.encrypt(&v, &pk); 119 | //! let ctw = scheme.encrypt(&w, &pk); 120 | //! ``` 121 | //! We can call the `to_bytes` function to serialize. 122 | //! ``` 123 | //! # let scheme = cupcake::default(); 124 | //! # use cupcake::traits::{KeyGeneration, SKEncryption, PKEncryption}; 125 | //! # let (pk, sk) = scheme.generate_keypair(); 126 | //! # use cupcake::traits::{AdditiveHomomorphicScheme}; 127 | //! # use crate::cupcake::traits::Serializable; 128 | //! # let v = vec![1; scheme.n]; 129 | //! # let w = vec![1; scheme.n]; 130 | //! # let ctv = scheme.encrypt(&v, &pk); 131 | //! # let ctw = scheme.encrypt(&w, &pk); 132 | //! let ctv_serialized = ctv.to_bytes(); 133 | //! let ctw_serialized = ctw.to_bytes(); 134 | //! ``` 135 | //! In order to deserialize, use `scheme.from_bytes`. 136 | //! ``` 137 | //! # let scheme = cupcake::default(); 138 | //! # use cupcake::traits::{KeyGeneration, SKEncryption, PKEncryption}; 139 | //! # let (pk, sk) = scheme.generate_keypair(); 140 | //! # use cupcake::traits::{AdditiveHomomorphicScheme}; 141 | //! # use crate::cupcake::traits::Serializable; 142 | //! # let v = vec![1; scheme.n]; 143 | //! # let w = vec![1; scheme.n]; 144 | //! # let ctv = scheme.encrypt(&v, &pk); 145 | //! # let ctw = scheme.encrypt(&w, &pk); 146 | //! # let ctv_serialized = ctv.to_bytes(); 147 | //! # let ctw_serialized = ctw.to_bytes(); 148 | //! let mut ctv_deserialized = scheme.from_bytes(&ctv_serialized); 149 | //! let ctw_deserialized = scheme.from_bytes(&ctw_serialized); 150 | //! assert_eq!(ctv, ctv_deserialized); 151 | //! ``` 152 | //! We can perform homomorphic operations on deserialized ciphertexts. 153 | //! ``` 154 | //! # let scheme = cupcake::default(); 155 | //! # let (pk, sk) = scheme.generate_keypair(); 156 | //! # use crate::cupcake::traits::*; 157 | //! # let v = vec![1; scheme.n]; 158 | //! # let w = vec![1; scheme.n]; 159 | //! # let ctv = scheme.encrypt(&v, &pk); 160 | //! # let ctw = scheme.encrypt(&w, &pk); 161 | //! # let ctv_serialized = ctv.to_bytes(); 162 | //! # let ctw_serialized = ctw.to_bytes(); 163 | //! # let mut ctv_deserialized = scheme.from_bytes(&ctv_serialized); 164 | //! # let ctw_deserialized = scheme.from_bytes(&ctw_serialized); 165 | //! scheme.add_inplace(&mut ctv_deserialized, &ctw_deserialized); 166 | //! let expected = vec![2; scheme.n]; 167 | //! let actual: Vec= scheme.decrypt(&ctv_deserialized, &sk); 168 | //! assert_eq!(actual, expected); 169 | //! ``` 170 | 171 | 172 | pub mod integer_arith; 173 | pub mod polyarith; 174 | #[cfg(feature = "bench")] 175 | pub mod rqpoly; 176 | #[cfg(not(feature = "bench"))] 177 | mod rqpoly; 178 | pub mod traits; 179 | mod serialize; 180 | mod utils; 181 | #[cfg(feature = "bench")] 182 | pub mod randutils; 183 | #[cfg(not(feature = "bench"))] 184 | mod randutils; 185 | 186 | use integer_arith::scalar::Scalar; 187 | use integer_arith::{SuperTrait, ArithUtils}; 188 | use traits::*; 189 | use std::sync::Arc; 190 | 191 | /// Plaintext type 192 | pub type FVPlaintext = Vec; 193 | /// Default plaintext type 194 | pub type DefaultFVPlaintext = Vec; 195 | /// Ciphertext type 196 | pub type FVCiphertext = (RqPoly, RqPoly); 197 | 198 | /// Default scheme type 199 | pub type DefaultSchemeType = FV; 200 | 201 | /// SecretKey type 202 | pub struct SecretKey(RqPoly); 203 | use rqpoly::{FiniteRingElt, RqPoly, RqPolyContext}; 204 | 205 | pub fn default() -> DefaultSchemeType { 206 | FV::::default_2048() 207 | } 208 | 209 | pub fn default_with_plaintext_mod(t: u32) -> DefaultSchemeType { 210 | FV::::default_2048_with_plaintext_mod(t) 211 | } 212 | 213 | /// (Additive only version of) the Fan-Vercauteren homomoprhic encryption scheme. 214 | pub struct FV 215 | where 216 | T: ArithUtils, 217 | { 218 | pub n: usize, 219 | pub t: T, 220 | pub q: T, 221 | pub delta: T, 222 | pub stdev: f64, 223 | pub qdivtwo: T, 224 | pub flooding_stdev: f64, 225 | context: Arc>, 226 | poly_multiplier: fn(&RqPoly, &RqPoly) -> RqPoly, 227 | } 228 | 229 | impl FV 230 | where 231 | T: ArithUtils{ 232 | fn convert_pt_u8_to_scalar(&self, pt: &DefaultFVPlaintext) -> FVPlaintext{ 233 | if T::to_u64(&self.t) != 256u64{ 234 | panic!("plaintext modulus should be 256") 235 | } 236 | let mut pt1 = vec![]; 237 | for pt_coeff in pt.iter(){ 238 | pt1.push(T::from_u32(*pt_coeff as u32, &self.t)); 239 | } 240 | pt1 241 | } 242 | 243 | fn convert_pt_scalar_to_u8(&self, pt: FVPlaintext) -> DefaultFVPlaintext{ 244 | if T::to_u64(&self.t) != 256u64{ 245 | panic!("plaintext modulus should be 256") 246 | } 247 | let mut pt1 = vec![]; 248 | for pt_coeff in pt.iter(){ 249 | pt1.push(T::to_u64(pt_coeff) as u8); 250 | } 251 | pt1 252 | } 253 | } 254 | 255 | impl AddAndSubtract, FVPlaintext> for FV 256 | where 257 | RqPoly: FiniteRingElt, 258 | T: Clone + ArithUtils + PartialEq, 259 | { 260 | // add a plaintext into a FVCiphertext. 261 | fn add_plain_inplace(&self, ct: &mut FVCiphertext, pt: &FVPlaintext) { 262 | for (ct_coeff, pt_coeff) in ct.1.coeffs.iter_mut().zip(pt.iter()) { 263 | let temp = T::mul(pt_coeff, &self.delta); 264 | *ct_coeff = T::add_mod(ct_coeff, &temp, &self.q); 265 | } 266 | } 267 | 268 | fn sub_plain_inplace(&self, ct: &mut FVCiphertext, pt: &FVPlaintext) { 269 | for (ct_coeff, pt_coeff) in ct.1.coeffs.iter_mut().zip(pt.iter()) { 270 | let temp = T::mul(&pt_coeff, &self.delta); 271 | *ct_coeff = T::sub_mod(ct_coeff, &temp, &self.q); 272 | } 273 | } 274 | } 275 | 276 | 277 | impl AddAndSubtract, DefaultFVPlaintext> for FV 278 | where 279 | RqPoly: FiniteRingElt, 280 | T: Clone + ArithUtils + PartialEq + From, 281 | { 282 | // add a plaintext into a FVCiphertext. 283 | fn add_plain_inplace(&self, ct: &mut FVCiphertext, pt: &DefaultFVPlaintext) { 284 | for (ct_coeff, pt_coeff) in ct.1.coeffs.iter_mut().zip(pt.iter()) { 285 | let temp = T::mul(&T::from(*pt_coeff as u32), &self.delta); 286 | *ct_coeff = T::add_mod(ct_coeff, &temp, &self.q); 287 | } 288 | } 289 | 290 | fn sub_plain_inplace(&self, ct: &mut FVCiphertext, pt: &DefaultFVPlaintext) { 291 | for (ct_coeff, pt_coeff) in ct.1.coeffs.iter_mut().zip(pt.iter()) { 292 | let temp = T::mul(&T::from(*pt_coeff as u32), &self.delta); 293 | *ct_coeff = T::sub_mod(ct_coeff, &temp, &self.q); 294 | } 295 | } 296 | } 297 | 298 | 299 | impl AdditiveHomomorphicScheme, SecretKey> for FV 300 | where 301 | RqPoly: FiniteRingElt, 302 | T: SuperTrait, 303 | { 304 | fn add_inplace(&self, ct1: &mut FVCiphertext, ct2: &FVCiphertext) { 305 | ct1.0.add_inplace(&ct2.0); 306 | ct1.1.add_inplace(&ct2.1); 307 | } 308 | 309 | // rerandomize a ciphertext 310 | fn rerandomize(&self, ct: &mut FVCiphertext, pk: &FVCiphertext) { 311 | // add a public key encryption of zero. 312 | let c_mask = self.encrypt_zero(pk); 313 | self.add_inplace(ct, &c_mask); 314 | 315 | // add large noise poly for noise flooding. 316 | let elarge = 317 | randutils::sample_gaussian_poly(self.context.clone(), self.flooding_stdev); 318 | ct.1.add_inplace(&elarge); 319 | } 320 | } 321 | 322 | // constructor and random poly sampling 323 | impl FV 324 | where 325 | T: SuperTrait+ PartialEq + Serializable, 326 | RqPoly: FiniteRingElt + NTT, 327 | { 328 | pub fn new(n: usize, q: &T) -> Self { 329 | Self::new_with_ptxt_mod(n, &T::new_modulus(256), q) 330 | } 331 | 332 | pub fn new_with_ptxt_mod(n: usize, t: &T, q: &T) -> Self { 333 | let context = Arc::new(RqPolyContext::new(n, q)); 334 | type RqPolyMultiplier = fn(&RqPoly, &RqPoly) -> RqPoly; 335 | let default_multiplier: RqPolyMultiplier; 336 | if context.is_ntt_enabled { 337 | default_multiplier = 338 | |op1: &RqPoly, op2: &RqPoly| -> RqPoly { op1.multiply_fast(op2) }; 339 | } else { 340 | default_multiplier = 341 | |op1: &RqPoly, op2: &RqPoly| -> RqPoly { op1.multiply(op2) }; 342 | } 343 | FV { 344 | n, 345 | t: t.clone(), 346 | flooding_stdev: 2f64.powi(40), 347 | delta: T::div(q, t), // &q/t, 348 | qdivtwo: T::div(q, &T::from(2_u32)), // &q/2, 349 | q: q.clone(), 350 | stdev: 3.2, 351 | context, 352 | poly_multiplier: default_multiplier, 353 | } 354 | } 355 | 356 | pub fn from_bytes(&self, bytes: &Vec) -> FVCiphertext{ 357 | let mut ct = FVCiphertext::::from_bytes(bytes); 358 | self.set_context(&mut ct); 359 | ct 360 | } 361 | 362 | fn set_context(&self, ctxt: &mut FVCiphertext){ 363 | ctxt.0.set_context(self.context.clone()); 364 | ctxt.1.set_context(self.context.clone()); 365 | } 366 | } 367 | 368 | impl FV { 369 | /// Construct a scheme with default parameters and plaintext modulus 256. 370 | pub fn default_2048() -> FV { 371 | let q = Scalar::new_modulus(18014398492704769u64); 372 | Self::new(2048, &q) 373 | } 374 | 375 | /// Construct a scheme with provided plaintext modulus. 376 | pub fn default_2048_with_plaintext_mod(t: u32) -> FV { 377 | if t > 2u32.pow(10){ 378 | panic!("plain text modulus should not be more than 10 bits.") 379 | } 380 | let q = Scalar::new_modulus(18014398492704769u64); 381 | let t = Scalar::new_modulus(t as u64); 382 | Self::new_with_ptxt_mod(2048, &t, &q) 383 | } 384 | } 385 | 386 | #[cfg(feature = "bigint")] 387 | impl FV { 388 | pub fn default_2048() -> FV { 389 | let q = BigInt::from_hex("3fffffff000001"); 390 | let context = Arc::new(RqPolyContext::new(2048, &q)); 391 | let multiplier = |op1: &RqPoly, op2: &RqPoly| -> RqPoly { 392 | op1.multiply_fast(op2) 393 | }; 394 | 395 | FV { 396 | n: 2048, 397 | q: q.clone(), 398 | delta: &q / 256, 399 | qdivtwo: &q / 2, 400 | stdev: 3.2, 401 | flooding_stdev: 1e40_f64, 402 | context: context, 403 | poly_multiplier: multiplier, 404 | } 405 | } 406 | } 407 | 408 | 409 | impl KeyGeneration, SecretKey> for FV 410 | where 411 | RqPoly: FiniteRingElt, 412 | T: SuperTrait, 413 | { 414 | fn generate_key(&self) -> SecretKey { 415 | let mut skpoly = randutils::sample_ternary_poly(self.context.clone()); 416 | if self.context.is_ntt_enabled { 417 | skpoly.forward_transform(); 418 | } 419 | SecretKey(skpoly) 420 | } 421 | 422 | fn generate_keypair(&self) -> (FVCiphertext, SecretKey) { 423 | let sk = self.generate_key(); 424 | let mut pk = self.encrypt_zero_sk(&sk); 425 | if self.context.is_ntt_enabled { 426 | pk.0.forward_transform(); 427 | pk.1.forward_transform(); 428 | } 429 | (pk, sk) 430 | } 431 | } 432 | 433 | impl EncryptionOfZeros, SecretKey> for FV 434 | where 435 | RqPoly: FiniteRingElt, 436 | T: SuperTrait, 437 | { 438 | fn encrypt_zero(&self, pk: &FVCiphertext) -> FVCiphertext { 439 | let mut u = randutils::sample_ternary_poly_prng(self.context.clone()); 440 | let e1 = randutils::sample_gaussian_poly(self.context.clone(), self.stdev); 441 | let e2 = randutils::sample_gaussian_poly(self.context.clone(), self.stdev); 442 | 443 | if self.context.is_ntt_enabled { 444 | u.forward_transform(); 445 | } 446 | // c0 = au + e1 447 | // let mut c0 = RqPoly::new(self.context.clone()); 448 | let mut c0 = (self.poly_multiplier)(&pk.0, &u); 449 | c0.add_inplace(&e1); 450 | 451 | // c1 = bu + e2 452 | // let mut c1 = RqPoly::new(self.context.clone()); 453 | 454 | let mut c1 = (self.poly_multiplier)(&pk.1, &u); 455 | c1.add_inplace(&e2); 456 | 457 | (c0, c1) 458 | } 459 | 460 | fn encrypt_zero_sk(&self, sk: &SecretKey) -> FVCiphertext { 461 | let e = randutils::sample_gaussian_poly(self.context.clone(), self.stdev); 462 | let a = randutils::sample_uniform_poly(self.context.clone()); 463 | let mut b = (self.poly_multiplier)(&a, &sk.0); 464 | b.add_inplace(&e); 465 | (a, b) 466 | } 467 | } 468 | 469 | impl PKEncryption, FVPlaintext, SecretKey> for FV 470 | where 471 | RqPoly: FiniteRingElt, 472 | T: SuperTrait, 473 | { 474 | fn encrypt(&self, pt: &FVPlaintext, pk: &FVCiphertext) -> FVCiphertext { 475 | // use public key to encrypt 476 | // pk = (a, as+e) = (a,b) 477 | let (c0, mut c1) = self.encrypt_zero(pk); 478 | // c1 = bu+e2 + Delta*m 479 | let iter = c1.coeffs.iter_mut().zip(pt.iter()); 480 | for (x, y) in iter { 481 | let temp = T::mul(y, &self.delta); 482 | *x = T::add_mod(x, &temp, &self.q); 483 | } 484 | (c0, c1) 485 | } 486 | } 487 | 488 | impl PKEncryption, DefaultFVPlaintext, SecretKey> for FV 489 | where 490 | RqPoly: FiniteRingElt, 491 | T: SuperTrait, 492 | { 493 | fn encrypt(&self, pt: &DefaultFVPlaintext, pk: &FVCiphertext) -> FVCiphertext { 494 | let pt1 = self.convert_pt_u8_to_scalar(pt); 495 | self.encrypt(&pt1, pk) 496 | } 497 | } 498 | 499 | impl SKEncryption, DefaultFVPlaintext, SecretKey> for FV 500 | where 501 | RqPoly: FiniteRingElt, 502 | T: SuperTrait, 503 | { 504 | fn encrypt_sk(&self, pt: &DefaultFVPlaintext, sk: &SecretKey) -> FVCiphertext 505 | { 506 | let pt1 = self.convert_pt_u8_to_scalar(pt); 507 | self.encrypt_sk(&pt1, sk) 508 | } 509 | 510 | fn decrypt(&self, ct: &FVCiphertext, sk: &SecretKey) -> DefaultFVPlaintext{ 511 | let pt1 = self.decrypt(ct, sk); 512 | self.convert_pt_scalar_to_u8(pt1) 513 | } 514 | } 515 | 516 | // This implements the sk-encryption for BFV scheme. 517 | impl SKEncryption, FVPlaintext, SecretKey> for FV 518 | where 519 | RqPoly: FiniteRingElt, 520 | T: SuperTrait, 521 | { 522 | fn encrypt_sk(&self, pt: &FVPlaintext, sk: &SecretKey) -> FVCiphertext { 523 | let e = randutils::sample_gaussian_poly(self.context.clone(), self.stdev); 524 | let a = randutils::sample_uniform_poly(self.context.clone()); 525 | 526 | 527 | let mut b = (self.poly_multiplier)(&a, &sk.0); 528 | b.add_inplace(&e); 529 | 530 | // add scaled plaintext to 531 | let iter = b.coeffs.iter_mut().zip(pt.iter()); 532 | for (x, y) in iter { 533 | let temp = T::mul(y, &self.delta); 534 | *x = T::add_mod(x, &temp, &self.q); 535 | } 536 | (a, b) 537 | } 538 | 539 | fn decrypt(&self, ct: &FVCiphertext, sk: &SecretKey) -> FVPlaintext { 540 | let temp1 = (self.poly_multiplier)(&ct.0, &sk.0); 541 | let mut phase = ct.1.clone(); 542 | phase.sub_inplace(&temp1); 543 | // then, extract value from phase. 544 | let tt: u64 = self.t.rep(); 545 | let qq: u64 = self.q.rep(); 546 | let qdivtwo = qq / 2; 547 | 548 | let my_closure = |elm: &T| -> T{ 549 | let mut tmp:u64 = elm.rep(); 550 | tmp *= tt; 551 | tmp += qdivtwo; 552 | tmp /= qq; 553 | tmp %= tt; 554 | T::from(tmp) 555 | }; 556 | 557 | return phase.coeffs.iter() 558 | .map(my_closure) 559 | .collect(); 560 | } 561 | } 562 | 563 | #[cfg(test)] 564 | mod fv_scalar_tests { 565 | use super::*; 566 | #[test] 567 | fn test_sk_encrypt_toy_param_scalar() { 568 | let fv = FV::new(16, &Scalar::new_modulus(65537)); 569 | 570 | let sk = fv.generate_key(); 571 | 572 | let v = vec![0; fv.n]; 573 | 574 | let ct = fv.encrypt_sk(&v, &sk); 575 | 576 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 577 | 578 | assert_eq!(v, pt_actual); 579 | } 580 | 581 | #[test] 582 | fn test_sk_encrypt_scalar() { 583 | let fv = FV::::default_2048(); 584 | 585 | let sk = fv.generate_key(); 586 | 587 | let v = vec![0; fv.n]; 588 | 589 | let ct = fv.encrypt_sk(&v, &sk); 590 | 591 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 592 | 593 | assert_eq!(v, pt_actual); 594 | } 595 | 596 | #[test] 597 | fn test_encrypt_default_param_scalar() { 598 | let fv = FV::::default_2048(); 599 | 600 | let (pk, sk) = fv.generate_keypair(); 601 | 602 | let v = vec![0; fv.n]; 603 | 604 | let ct = fv.encrypt(&v, &pk); 605 | 606 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 607 | 608 | assert_eq!(v, pt_actual); 609 | } 610 | 611 | #[test] 612 | fn test_rerandomize_scalar() { 613 | let fv = FV::::default_2048(); 614 | 615 | let (pk, sk) = fv.generate_keypair(); 616 | 617 | let v = vec![0; fv.n]; 618 | 619 | let mut ct = fv.encrypt(&v, &pk); 620 | 621 | fv.rerandomize(&mut ct, &pk); 622 | 623 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 624 | 625 | assert_eq!(v, pt_actual); 626 | } 627 | 628 | #[test] 629 | fn test_add_scalar() { 630 | let fv = FV::::default_2048(); 631 | let (pk, sk) = fv.generate_keypair(); 632 | 633 | let v = vec![1; fv.n]; 634 | 635 | let w = vec![2; fv.n]; 636 | 637 | let vplusw = vec![3; fv.n]; 638 | 639 | // encrypt v 640 | let mut ctv = fv.encrypt_sk(&v, &sk); 641 | let ctw = fv.encrypt(&w, &pk); 642 | 643 | // ct_v + ct_w. 644 | fv.add_inplace(&mut ctv, &ctw); 645 | let pt_after_add: DefaultFVPlaintext = fv.decrypt(&ctv, &sk); 646 | assert_eq!(pt_after_add, vplusw); 647 | } 648 | 649 | #[test] 650 | fn test_add_plain_scalar() { 651 | let fv = FV::::default_2048(); 652 | let (pk, sk) = fv.generate_keypair(); 653 | 654 | let v = vec![1; fv.n]; 655 | 656 | let w = vec![2; fv.n]; 657 | 658 | let vplusw = vec![3; fv.n]; 659 | 660 | // encrypt v 661 | let mut ct = fv.encrypt(&v, &pk); 662 | 663 | // ct_v + w. 664 | fv.add_plain_inplace(&mut ct, &w); 665 | 666 | let pt_after_add: DefaultFVPlaintext = fv.decrypt(&ct, &sk); 667 | 668 | assert_eq!(pt_after_add, vplusw); 669 | } 670 | 671 | #[test] 672 | fn test_sub_plain() { 673 | let fv = FV::::default_2048(); 674 | let sk = fv.generate_key(); 675 | 676 | let v = vec![3; fv.n]; 677 | 678 | let w = vec![1; fv.n]; 679 | 680 | let v_minus_w = vec![2;fv.n]; 681 | 682 | // encrypt v 683 | let mut ct = fv.encrypt_sk(&v, &sk); 684 | 685 | // ct_v - w. 686 | fv.sub_plain_inplace(&mut ct, &w); 687 | 688 | let pt_after_sub : DefaultFVPlaintext= fv.decrypt(&ct, &sk); 689 | 690 | assert_eq!(pt_after_sub, v_minus_w); 691 | } 692 | 693 | #[test] 694 | fn test_flexible_plaintext_encrypt() { 695 | let t = 199; 696 | let fv = crate::default_with_plaintext_mod(t); 697 | let (pk, sk) = fv.generate_keypair(); 698 | let plain_modulus = fv.t.clone(); 699 | let pt = vec![Scalar::from_u32(t-1, &plain_modulus); fv.n]; 700 | let ct = fv.encrypt(&pt, &pk); 701 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 702 | assert_eq!(pt_actual, pt); 703 | } 704 | 705 | #[test] 706 | fn test_flexible_plaintext_addition() { 707 | let t = 199; 708 | let fv = crate::default_with_plaintext_mod(t); 709 | let (pk, sk) = fv.generate_keypair(); 710 | let plain_modulus = fv.t.clone(); 711 | let v = vec![Scalar::from_u32(t-1, &plain_modulus); fv.n]; 712 | let w = vec![Scalar::from_u32(t-1, &plain_modulus); fv.n]; 713 | let v_plus_w = vec![Scalar::from_u32(t-2, &plain_modulus); fv.n]; 714 | let mut ctv = fv.encrypt(&v, &pk); 715 | let ctw = fv.encrypt(&w, &pk); 716 | fv.add_inplace(&mut ctv, &ctw); 717 | let pt_actual: Vec= fv.decrypt(&ctv, &sk); 718 | assert_eq!(pt_actual, v_plus_w); 719 | } 720 | 721 | #[test] 722 | fn test_flexible_plaintext_add_plaintext() { 723 | let t = 199; 724 | let fv = crate::default_with_plaintext_mod(t); 725 | let (pk, sk) = fv.generate_keypair(); 726 | let plain_modulus = fv.t.clone(); 727 | let v = vec![Scalar::from_u32(t-1, &plain_modulus); fv.n]; 728 | let w = vec![Scalar::from_u32(1, &plain_modulus); fv.n]; 729 | let v_plus_w = vec![Scalar::from_u32(0, &plain_modulus); fv.n]; 730 | let mut ct = fv.encrypt(&v, &pk); 731 | fv.add_plain_inplace(&mut ct, &w); 732 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 733 | assert_eq!(pt_actual, v_plus_w); 734 | } 735 | } 736 | 737 | // unit tests. 738 | #[cfg(feature = "bigint")] 739 | #[cfg(test)] 740 | mod fv_bigint_tests { 741 | use super::*; 742 | #[test] 743 | fn test_sk_encrypt() { 744 | let fv = FV::new(16, &BigInt::from(12289)); 745 | 746 | let sk = fv.generate_key(); 747 | 748 | let mut v = vec![0; fv.n]; 749 | 750 | let ct = fv.encrypt_sk(&v, &sk); 751 | 752 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 753 | 754 | assert_eq!(v, pt_actual); 755 | } 756 | 757 | #[test] 758 | fn test_encrypt_toy_param() { 759 | let fv = FV::new(4, &BigInt::from(65537)); 760 | 761 | let (pk, sk) = fv.generate_keypair(); 762 | 763 | let mut v = vec![0; fv.n]; 764 | 765 | for _ in 0..10 { 766 | let ct = fv.encrypt(&v, &pk); 767 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 768 | assert_eq!(v, pt_actual); 769 | } 770 | } 771 | 772 | #[test] 773 | fn test_encrypt_nonntt_toy_param() { 774 | let fv = FV::new(4, &BigInt::from(1000000)); 775 | 776 | let (pk, sk) = fv.generate_keypair(); 777 | 778 | let mut v = vec![0; fv.n]; 779 | 780 | for _ in 0..10 { 781 | let ct = fv.encrypt(&v, &pk); 782 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 783 | assert_eq!(v, pt_actual); 784 | } 785 | } 786 | 787 | #[test] 788 | fn test_encrypt_large_param() { 789 | let fv = FV::::default_2048(); 790 | 791 | let (pk, sk) = fv.generate_keypair(); 792 | 793 | let mut v = vec![0; fv.n]; 794 | 795 | let ct = fv.encrypt(&v, &pk); 796 | 797 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 798 | 799 | assert_eq!(v, pt_actual); 800 | } 801 | 802 | #[test] 803 | fn test_rerandomize() { 804 | let fv = FV::::default_2048(); 805 | 806 | let (pk, sk) = fv.generate_keypair(); 807 | 808 | let mut v = vec![0; fv.n]; 809 | 810 | let mut ct = fv.encrypt(&v, &pk); 811 | 812 | fv.rerandomize(&mut ct, &pk); 813 | 814 | let pt_actual: Vec = fv.decrypt(&ct, &sk); 815 | 816 | assert_eq!(v, pt_actual); 817 | } 818 | #[test] 819 | fn test_add() { 820 | let fv = FV::new(16, &BigInt::from(12289)); 821 | 822 | let sk = fv.generate_key(); 823 | 824 | let mut v = vec![0; fv.n]; 825 | 826 | let mut w = vec![2; fv.n]; 827 | 828 | let mut vplusw = vec![2; fv.n]; 829 | 830 | // encrypt v 831 | let mut ctv = fv.encrypt_sk(&v, &sk); 832 | let ctw = fv.encrypt_sk(&w, &sk); 833 | 834 | // ct_v + ct_w. 835 | fv.add_inplace(&mut ctv, &ctw); 836 | 837 | let pt_after_add = fv.decrypt(&ctv, &sk); 838 | 839 | assert_eq!(pt_after_add, vplusw); 840 | } 841 | 842 | #[test] 843 | fn test_add_plain() { 844 | let fv = FV::new(16, &BigInt::from(12289)); 845 | let sk = fv.generate_key(); 846 | 847 | let mut v = vec![0; fv.n]; 848 | 849 | let mut w = vec![1; fv.n]; 850 | 851 | let mut vplusw = vec![1; fv.n]; 852 | 853 | // encrypt v 854 | let mut ct = fv.encrypt_sk(&v, &sk); 855 | 856 | // ct_v + w. 857 | fv.add_plain_inplace(&mut ct, &w); 858 | 859 | let pt_after_add = fv.decrypt(&ct, &sk); 860 | 861 | assert_eq!(pt_after_add, vplusw); 862 | } 863 | } 864 | --------------------------------------------------------------------------------