From b565b9cc028a6a847416b1b0997ee7d390c50bd6 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 12:11:20 +0200 Subject: [PATCH 1/6] move explicit constructions out of this repo --- benches/benchmarks.rs | 5 +- benches/k_pke.rs | 145 ---- benches/pfdh.rs | 60 -- benches/regev.rs | 59 -- src/construction.rs | 23 - src/construction/hash.rs | 31 - src/construction/hash/sha256.rs | 373 ---------- src/construction/hash/sis.rs | 270 ------- src/construction/identity_based_encryption.rs | 82 --- .../dual_regev_ibe.rs | 577 --------------- src/construction/pk_encryption.rs | 173 ----- .../pk_encryption/ccs_from_ibe.rs | 143 ---- .../ccs_from_ibe/dual_regev_ibe_pfdh.rs | 127 ---- src/construction/pk_encryption/dual_regev.rs | 670 ----------------- .../dual_regev_discrete_gauss.rs | 697 ------------------ src/construction/pk_encryption/k_pke.rs | 276 ------- src/construction/pk_encryption/lpr.rs | 693 ----------------- src/construction/pk_encryption/regev.rs | 672 ----------------- .../pk_encryption/regev_discrete_gauss.rs | 692 ----------------- src/construction/pk_encryption/ring_lpr.rs | 645 ---------------- src/construction/signature.rs | 49 -- src/construction/signature/fdh.rs | 42 -- src/construction/signature/fdh/gpv.rs | 202 ----- src/construction/signature/fdh/gpv_ring.rs | 229 ------ src/construction/signature/pfdh.rs | 42 -- src/construction/signature/pfdh/gpv.rs | 173 ----- src/lib.rs | 1 - 27 files changed, 1 insertion(+), 7150 deletions(-) delete mode 100644 benches/k_pke.rs delete mode 100644 benches/pfdh.rs delete mode 100644 benches/regev.rs delete mode 100644 src/construction.rs delete mode 100644 src/construction/hash.rs delete mode 100644 src/construction/hash/sha256.rs delete mode 100644 src/construction/hash/sis.rs delete mode 100644 src/construction/identity_based_encryption.rs delete mode 100644 src/construction/identity_based_encryption/dual_regev_ibe.rs delete mode 100644 src/construction/pk_encryption.rs delete mode 100644 src/construction/pk_encryption/ccs_from_ibe.rs delete mode 100644 src/construction/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs delete mode 100644 src/construction/pk_encryption/dual_regev.rs delete mode 100644 src/construction/pk_encryption/dual_regev_discrete_gauss.rs delete mode 100644 src/construction/pk_encryption/k_pke.rs delete mode 100644 src/construction/pk_encryption/lpr.rs delete mode 100644 src/construction/pk_encryption/regev.rs delete mode 100644 src/construction/pk_encryption/regev_discrete_gauss.rs delete mode 100644 src/construction/pk_encryption/ring_lpr.rs delete mode 100644 src/construction/signature.rs delete mode 100644 src/construction/signature/fdh.rs delete mode 100644 src/construction/signature/fdh/gpv.rs delete mode 100644 src/construction/signature/fdh/gpv_ring.rs delete mode 100644 src/construction/signature/pfdh.rs delete mode 100644 src/construction/signature/pfdh/gpv.rs diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index 8ba6809..b40c2c7 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -9,9 +9,6 @@ use criterion::criterion_main; -pub mod k_pke; -pub mod pfdh; pub mod psf; -pub mod regev; -criterion_main! {regev::benches, pfdh::benches, k_pke::benches, psf::benches} +criterion_main! {psf::benches} diff --git a/benches/k_pke.rs b/benches/k_pke.rs deleted file mode 100644 index e42622d..0000000 --- a/benches/k_pke.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright © 2025 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -use criterion::*; -use qfall_crypto::construction::pk_encryption::PKEncryptionScheme; -use qfall_crypto::construction::pk_encryption::KPKE; - -/// Performs a full-cycle of gen, enc, dec with [`KPKE`]. -fn kpke_cycle(k_pke: &KPKE) { - let (pk, sk) = k_pke.gen(); - let cipher = k_pke.enc(&pk, 1); - let _ = k_pke.dec(&sk, &cipher); -} - -/// Benchmark [kpke_cycle] with [KPKE::ml_kem_512]. -/// -/// This benchmark can be run with for example: -/// - `cargo criterion K-PKE\ cycle\ 512` -/// - `cargo bench --bench benchmarks K-PKE\ cycle\ 512` -/// - `cargo flamegraph --bench benchmarks -- --bench K-PKE\ cycle\ 512` -fn bench_kpke_cycle_512(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_512(); - - c.bench_function("K-PKE cycle 512", |b| b.iter(|| kpke_cycle(&k_pke))); -} - -/// Benchmark [KPKE::gen] with [KPKE::ml_kem_512]. -fn bench_kpke_gen_512(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_512(); - - c.bench_function("K-PKE gen 512", |b| b.iter(|| k_pke.gen())); -} - -/// Benchmark [KPKE::enc] with [KPKE::ml_kem_512]. -fn bench_kpke_enc_512(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_512(); - let (pk, _) = k_pke.gen(); - let msg = i64::MAX; - - c.bench_function("K-PKE enc 512", |b| b.iter(|| k_pke.enc(&pk, msg))); -} - -/// Benchmark [KPKE::dec] with [KPKE::ml_kem_512]. -fn bench_kpke_dec_512(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_512(); - let (pk, sk) = k_pke.gen(); - let cipher = k_pke.enc(&pk, i64::MAX); - - c.bench_function("K-PKE dec 512", |b| b.iter(|| k_pke.dec(&sk, &cipher))); -} - -/// Benchmark [kpke_cycle] with [KPKE::ml_kem_768]. -/// -/// This benchmark can be run with for example: -/// - `cargo criterion K-PKE\ cycle\ 768` -/// - `cargo bench --bench benchmarks K-PKE\ cycle\ 768` -/// - `cargo flamegraph --bench benchmarks -- --bench K-PKE\ cycle\ 768` -fn bench_kpke_cycle_768(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_768(); - - c.bench_function("K-PKE cycle 768", |b| b.iter(|| kpke_cycle(&k_pke))); -} - -/// Benchmark [KPKE::gen] with [KPKE::ml_kem_768]. -fn bench_kpke_gen_768(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_768(); - - c.bench_function("K-PKE gen 768", |b| b.iter(|| k_pke.gen())); -} - -/// Benchmark [KPKE::enc] with [KPKE::ml_kem_768]. -fn bench_kpke_enc_768(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_768(); - let (pk, _) = k_pke.gen(); - let msg = i64::MAX; - - c.bench_function("K-PKE enc 768", |b| b.iter(|| k_pke.enc(&pk, msg))); -} - -/// Benchmark [KPKE::dec] with [KPKE::ml_kem_768]. -fn bench_kpke_dec_768(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_768(); - let (pk, sk) = k_pke.gen(); - let cipher = k_pke.enc(&pk, i64::MAX); - - c.bench_function("K-PKE dec 768", |b| b.iter(|| k_pke.dec(&sk, &cipher))); -} - -/// Benchmark [kpke_cycle] with [KPKE::ml_kem_1024]. -/// -/// This benchmark can be run with for example: -/// - `cargo criterion K-PKE\ cycle\ 1024` -/// - `cargo bench --bench benchmarks K-PKE\ cycle\ 1024` -/// - `cargo flamegraph --bench benchmarks -- --bench K-PKE\ cycle\ 1024` -fn bench_kpke_cycle_1024(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_1024(); - - c.bench_function("K-PKE cycle 1024", |b| b.iter(|| kpke_cycle(&k_pke))); -} - -/// Benchmark [KPKE::gen] with [KPKE::ml_kem_1024]. -fn bench_kpke_gen_1024(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_1024(); - - c.bench_function("K-PKE gen 1024", |b| b.iter(|| k_pke.gen())); -} - -/// Benchmark [KPKE::enc] with [KPKE::ml_kem_1024]. -fn bench_kpke_enc_1024(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_1024(); - let (pk, _) = k_pke.gen(); - let msg = i64::MAX; - - c.bench_function("K-PKE enc 1024", |b| b.iter(|| k_pke.enc(&pk, msg))); -} - -/// Benchmark [KPKE::dec] with [KPKE::ml_kem_1024]. -fn bench_kpke_dec_1024(c: &mut Criterion) { - let k_pke = KPKE::ml_kem_1024(); - let (pk, sk) = k_pke.gen(); - let cipher = k_pke.enc(&pk, i64::MAX); - - c.bench_function("K-PKE dec 1024", |b| b.iter(|| k_pke.dec(&sk, &cipher))); -} - -criterion_group!( - benches, - bench_kpke_cycle_512, - bench_kpke_gen_512, - bench_kpke_enc_512, - bench_kpke_dec_512, - bench_kpke_cycle_768, - bench_kpke_gen_768, - bench_kpke_enc_768, - bench_kpke_dec_768, - bench_kpke_cycle_1024, - bench_kpke_gen_1024, - bench_kpke_enc_1024, - bench_kpke_dec_1024, -); diff --git a/benches/pfdh.rs b/benches/pfdh.rs deleted file mode 100644 index 7bded95..0000000 --- a/benches/pfdh.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright © 2023 Marvin Beckmann -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -use criterion::{criterion_group, Criterion}; -use qfall_crypto::construction::signature::{pfdh::PFDHGPV, SignatureScheme}; - -/// Performs a full instantiation with an additional signing and verifying of a signature. -fn pfdh_cycle(n: i64) { - let mut pfdh = PFDHGPV::setup(n, 113, 17, 128); - - let m = "Hello World!"; - - let (pk, sk) = pfdh.gen(); - let sigma = pfdh.sign(m.to_owned(), &sk, &pk); - - pfdh.vfy(m.to_owned(), &sigma, &pk); -} - -/// Benchmark [bench_pfdh_full_cycle] with `n = 8`. -/// -/// This benchmark can be run with for example: -/// - `cargo criterion Full\ Cycle\ PFDH\ n=8` -/// - `cargo bench --bench benchmarks Full\ Cycle\ PFDH\ n=8` -/// - `cargo flamegraph --bench benchmarks -- --bench Full\ Cycle\ PFDH\ n=8` -/// -/// Shorter variants or regex expressions can also be used to specify the -/// benchmark name. The `\ ` is used to escape the space, alternatively, -/// quotation marks can be used. -fn bench_pfdh_full_cycle(c: &mut Criterion) { - c.bench_function("Full Cycle PFDH n=8", |b| b.iter(|| pfdh_cycle(8))); -} - -/// Benchmark [bench_pfdh_signature] with `n = 8`. -/// -/// This benchmark can be run with for example: -/// - `cargo criterion Signing\ PFDH\ n=8` -/// - `cargo bench --bench benchmarks Signing\ PFDH\ n=8` -/// - `cargo flamegraph --bench benchmarks -- --bench Signing\ PFDH\ n=8` -/// -/// Shorter variants or regex expressions can also be used to specify the -/// benchmark name. The `\ ` is used to escape the space, alternatively, -/// quotation marks can be used. -fn bench_pfdh_signature(c: &mut Criterion) { - let mut pfdh = PFDHGPV::setup(8, 113, 17, 128); - - let m = "Hello World!"; - - let (pk, sk) = pfdh.gen(); - - c.bench_function("Signing PFDH n=8", |b| { - b.iter(|| pfdh.sign(m.to_owned(), &sk, &pk)) - }); -} - -criterion_group!(benches, bench_pfdh_full_cycle, bench_pfdh_signature); diff --git a/benches/regev.rs b/benches/regev.rs deleted file mode 100644 index 0c11f4b..0000000 --- a/benches/regev.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © 2023 Sven Moog -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -use criterion::*; -use qfall_crypto::construction::pk_encryption::PKEncryptionScheme; -use qfall_crypto::construction::pk_encryption::Regev; -use qfall_math::integer::Z; - -/// Performs a full-cycle of gen, enc, dec with regev. -fn regev_cycle(n: i64) { - let msg = Z::ONE; - let regev = Regev::new_from_n(n); - - let (pk, sk) = regev.gen(); - let cipher = regev.enc(&pk, &msg); - let _ = regev.dec(&sk, &cipher); -} - -/// Benchmark [regev_cycle] with `n = 50`. -/// -/// This benchmark can be run with for example: -/// - `cargo criterion Regev\ n=50` -/// - `cargo bench --bench benchmarks Regev\ n=50` -/// - `cargo flamegraph --bench benchmarks -- --bench Regev\ n=50` -/// -/// Shorter variants or regex expressions can also be used to specify the -/// benchmark name. The `\ ` is used to escape the space, alternatively, -/// quotation marks can be used. -fn bench_regev_cycle(c: &mut Criterion) { - c.bench_function("Regev n=50", |b| b.iter(|| regev_cycle(50))); -} - -/// Benchmark [regev_cycle] with `n = 10, 20, 30, 40, 50, 60` -/// -/// This benchmark can be run with for example: -/// - `cargo criterion "Regev\ n\ sweep"` -/// - `cargo criterion Regev\ n\ sweep/n=20` (only run the n=20 benchmark). -/// - `cargo criterion 'Regev.*n=20'` (only run the n=20 benchmark). -/// - `cargo bench --bench benchmarks Regev\ n\ sweep` -/// -/// Shorter variants or regex expressions can also be used to specify the -/// benchmark name. The `\ ` is used to escape the space, alternatively, -/// quotation marks can be used. -fn bench_regev_cycle_n_sweep(c: &mut Criterion) { - let mut group = c.benchmark_group("Regev n sweep"); - - for n in [10, 20, 30, 40, 50, 60].iter() { - group.bench_function(format!("n={n}"), |b| b.iter(|| regev_cycle(*n))); - } - - group.finish(); -} - -criterion_group!(benches, bench_regev_cycle, bench_regev_cycle_n_sweep); diff --git a/src/construction.rs b/src/construction.rs deleted file mode 100644 index d93bfd9..0000000 --- a/src/construction.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © 2023 Niklas Siemer, Marvin Beckmann -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains fundamental cryptographic constructions, on which other -//! constructions can be build on. -//! -//! Among others, these include encryption schemes and signature schemes. -//! A construction is always build the same way: -//! -//! 1. A trait that combines the common feature, e.g. -//! [`public key encryption`](pk_encryption::PKEncryptionScheme). -//! 2. Explicit implementations of the trait, e.g. -//! [`RingLPR`](pk_encryption::RingLPR). - -pub mod hash; -pub mod identity_based_encryption; -pub mod pk_encryption; -pub mod signature; diff --git a/src/construction/hash.rs b/src/construction/hash.rs deleted file mode 100644 index ec3bfea..0000000 --- a/src/construction/hash.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains implementations of hash functions. -//! -//! The main references are listed in the following: -//! - \[1\] Peikert, Chris (2016). -//! A decade of lattice cryptography. -//! In: Theoretical Computer Science 10.4. -//! - -pub mod sha256; -mod sis; - -pub use sis::SISHash; - -/// This trait should be implemented by hashes with domain [`str`]. -pub trait HashInto { - /// Hashes a given String literal. - /// - /// Paramters: - /// - `m`: specifies the string message to be hashed - /// - /// Returns a hash of type Domain. - fn hash(&self, m: &str) -> DigestSpace; -} diff --git a/src/construction/hash/sha256.rs b/src/construction/hash/sha256.rs deleted file mode 100644 index 45336ad..0000000 --- a/src/construction/hash/sha256.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright © 2023 Phil Milewski -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains sha256 hashes into different domains. - -use super::HashInto; -use qfall_math::traits::FromCoefficientEmbedding; -use qfall_math::utils::index::evaluate_indices; -use qfall_math::{ - integer::{MatPolyOverZ, Z}, - integer_mod_q::{MatPolynomialRingZq, MatZq, Modulus, ModulusPolynomialRingZq, Zq}, - traits::MatrixSetEntry, -}; -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::fmt::Display; - -/// Computes the sha256 hash value of a given String literal. -/// -/// Parameters: -/// - `string`: specifies the value that is hashed. -/// -/// Returns the sha256 value of the given string as a hex string. -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::hash::sha256::sha256; -/// -/// let string = "Hello World!"; -/// let hash = sha256(string); -/// assert_eq!("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", hash); -/// ``` -pub fn sha256(string: &str) -> String { - let mut hasher = Sha256::new(); - hasher.update(string); - let result = hasher.finalize(); - format!("{result:x}") -} - -/// Hashes a given String literal into a [`Zq`] using sha256. -/// -/// Parameters: -/// - `string`: specifies the value that is hashed. -/// - `modulus`: specifies the modulus of the returned [`Zq`] value -/// -/// Returns a [`Zq`] as a hash value for the given string. -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::hash::sha256::hash_to_zq_sha256; -/// use qfall_math::integer_mod_q::Zq; -/// -/// let string = "Hello World!"; -/// -/// let hash: Zq = hash_to_zq_sha256("Hello World!", 7); -/// assert_eq!(Zq::from((2, 7)), hash) -/// ``` -/// -/// # Panics ... -/// - if `modulus <= 1`. -pub fn hash_to_zq_sha256(string: &str, modulus: impl Into) -> Zq { - let modulus = modulus.into(); - let modulus_new = Z::from(&modulus); - let bitsize = modulus_new.bits(); - let mut hex = "".to_string(); - let string2 = format!("{modulus_new} {string}"); - - for i in 0..=bitsize / 128 - // hashing into e.g. Zq with 256 bit length of q from 256 bit will result in - // lower values to be up to two times as likely as higher values. - // Doubling the bit size of the hashed number will - // reduce this difference to 1/2^n which is negligible. - // https://crypto.stackexchange.com/questions/37305/how-can-i-instantiate-a-generalized-hash-function - { - hex = hex + &sha256(&format!("{i} {string2}")); - } - - Zq::from((Z::from_str_b(&hex, 16).unwrap(), modulus)) -} - -/// Hashes a given String literal into a [`MatZq`] using sha256. -/// -/// Parameters: -/// - `string`: specifies the value that is hashed -/// - `num_rows`: specifies the number of rows of the result -/// - `num_cols`: specifies the number of columns of the result -/// - `modulus`: specifies the modulus of the returned [`MatZq`] value -/// -/// Returns a [`MatZq`] as a hash for the given string. -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::hash::sha256::hash_to_mat_zq_sha256; -/// use qfall_math::integer_mod_q::MatZq; -/// use std::str::FromStr; -/// -/// let string = "Hello World!"; -/// -/// let hash: MatZq = hash_to_mat_zq_sha256(string, 2, 2, 7); -/// assert_eq!(MatZq::from_str("[[6, 3],[5, 2]] mod 7").unwrap(), hash); -/// ``` -/// -/// # Panics ... -/// - if `modulus <= 1`. -/// - if the number of rows or columns is less or equal to `0` or does not fit into an [`i64`]. -pub fn hash_to_mat_zq_sha256( - string: &str, - num_rows: impl TryInto + Display, - num_cols: impl TryInto + Display, - modulus: impl Into, -) -> MatZq { - let modulus = modulus.into(); - let (num_rows_new, num_cols_new) = evaluate_indices(num_rows, num_cols).unwrap(); - let mut matrix = MatZq::new(num_rows_new, num_cols_new, modulus.clone()); - - let new_string = format!("{num_rows_new} {num_cols_new} {string}"); - for i in 0..num_rows_new { - for j in 0..num_cols_new { - matrix - .set_entry( - i, - j, - hash_to_zq_sha256(&format!("{i} {j} {new_string}"), &modulus), - ) - .unwrap(); - } - } - matrix -} - -/// Object for hashing Strings into a [`MatZq`]. -/// The object fixes the modulus and the corresponding dimensions. -/// -/// Parameters: -/// - `modulus`: Defines the range in which each entry is hashed -/// - `rows`: Defines the number of rows of the hash value -/// - `cols`: Defines the number of columns of the hash value -/// -/// Returns a [`MatZq`] as a hash for the given string. -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::hash::{HashInto, sha256::{HashMatZq, hash_to_mat_zq_sha256}}; -/// use qfall_math::integer_mod_q::{Modulus, MatZq}; -/// use std::str::FromStr; -/// -/// let modulus = Modulus::from(7); -/// -/// let hasher = HashMatZq { -/// modulus, -/// rows: 17, -/// cols: 3, -/// }; -/// let hash_val = hasher.hash("Hello"); -/// ``` -#[derive(Serialize, Deserialize)] -pub struct HashMatZq { - pub modulus: Modulus, - pub rows: i64, - pub cols: i64, -} - -impl HashInto for HashMatZq { - /// Hashes a given String literal into a [`MatZq`] using sha256. - /// The dimensions and the modulus is fixed by the hash object. - /// - /// Parameters: - /// - `string`: specifies the value that is hashed - /// - /// Returns a [`MatZq`] as a hash for the given string. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::hash::{HashInto, sha256::{HashMatZq, hash_to_mat_zq_sha256}}; - /// use qfall_math::integer_mod_q::{Modulus, MatZq}; - /// use std::str::FromStr; - /// - /// let modulus = Modulus::from(7); - /// - /// let hasher = HashMatZq { - /// modulus, - /// rows: 17, - /// cols: 3, - /// }; - /// let hash_val = hasher.hash("Hello"); - /// ``` - fn hash(&self, m: &str) -> MatZq { - hash_to_mat_zq_sha256(m, self.rows, self.cols, &self.modulus) - } -} - -/// Object for hashing Strings into a [`MatPolynomialRingZq`]. -/// The object fixes the modulus and the corresponding dimensions. -/// -/// Parameters: -/// - `modulus`: Defines the range in which each entry is hashed -/// - `rows`: Defines the number of rows of the hash value -/// - `cols`: Defines the number of columns of the hash value -/// -/// Returns a [`MatZq`] as a hash for the given string. -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::hash::{HashInto, sha256::HashMatPolynomialRingZq}; -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; -/// -/// let gp = GadgetParametersRing::init_default(10, 99); -/// -/// let hasher = HashMatPolynomialRingZq { -/// modulus: gp.modulus, -/// rows: 17, -/// cols: 3, -/// }; -/// let hash_val = hasher.hash("Hello"); -/// ``` -#[derive(Serialize, Deserialize)] -pub struct HashMatPolynomialRingZq { - pub modulus: ModulusPolynomialRingZq, - pub rows: i64, - pub cols: i64, -} - -impl HashInto for HashMatPolynomialRingZq { - /// Hashes a given String literal into a [`MatPolynomialRingZq`] using sha256. - /// The dimensions and the modulus is fixed by the hash object. - /// - /// Parameters: - /// - `string`: specifies the value that is hashed - /// - /// Returns a [`MatPolynomialRingZq`] as a hash for the given string. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::hash::{HashInto, sha256::{HashMatPolynomialRingZq}}; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; - /// - /// let gp = GadgetParametersRing::init_default(10, 99); - /// - /// let hasher = HashMatPolynomialRingZq { - /// modulus: gp.modulus, - /// rows: 17, - /// cols: 3, - /// }; - /// let hash_val = hasher.hash("Hello"); - /// ``` - fn hash(&self, m: &str) -> MatPolynomialRingZq { - let highest_deg = self.modulus.get_degree(); - let embedding = - hash_to_mat_zq_sha256(m, self.rows * highest_deg, self.cols, self.modulus.get_q()) - .get_representative_least_nonnegative_residue(); - let poly_mat = MatPolyOverZ::from_coefficient_embedding((&embedding, highest_deg - 1)); - MatPolynomialRingZq::from((&poly_mat, &self.modulus)) - } -} - -#[cfg(test)] -mod tests_sha { - use super::{hash_to_mat_zq_sha256, hash_to_zq_sha256, sha256, Z}; - use qfall_math::{ - integer_mod_q::{MatZq, Zq}, - traits::{Distance, Pow}, - }; - use std::str::FromStr; - - /// Ensure sha256 works. - #[test] - fn test_sha256() { - let str1 = "Hello World!"; - let str2 = "qfall"; - - let hash1 = sha256(str1); - let hash2 = sha256(str2); - - assert_eq!( - "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", - hash1 - ); - assert_eq!( - "eb6ed1369a670050bd04b24036e8c29144b0f6b10166dc9c8b4987a6026c715f", - hash2 - ); - } - - /// Ensure hashing into [`Zq`] works as intended. - #[test] - fn test_hash_to_zq_sha256() { - let str1 = "Hello World!"; - let str2 = "qfall"; - - let hash1 = hash_to_zq_sha256(str1, 256); - let hash2 = hash_to_zq_sha256(str2, 16); - - assert_eq!(Zq::from((150, 256)), hash1); - assert_eq!(Zq::from((12, 16)), hash2); - } - - /// Ensure hashing into [`Zq`] hits the whole domain not just the first 256 bit. - #[test] - fn test_hash_to_zq_sha256_large() { - let str1 = "Hello World!"; - - let mut large = false; - for i in 0..5 { - if hash_to_zq_sha256(&(i.to_string() + str1), Z::from(271).pow(100).unwrap()) - .get_representative_least_nonnegative_residue() - .distance(Z::ZERO) - > u64::MAX - { - large = true; - } - } - - assert!(large); - } - - /// Ensure hashing into [`MatZq`] works as intended. - #[test] - fn test_hash_to_mat_zq_sha256() { - let str1 = "Hello World!"; - let str2 = "qfall"; - - let hash1 = hash_to_mat_zq_sha256(str1, 2, 2, 256); - let hash2 = hash_to_mat_zq_sha256(str2, 2, 2, 16); - - assert_eq!( - MatZq::from_str("[[159, 26],[249, 141]] mod 256").unwrap(), - hash1 - ); - assert_eq!(MatZq::from_str("[[3, 12],[9, 12]] mod 16").unwrap(), hash2); - } - - /// Ensure hashing into [`MatZq`] works as intended. - #[test] - #[should_panic] - fn test_hash_to_mat_zq_sha256_negative_dimensions() { - let str1 = "Hello World!"; - - let _ = hash_to_mat_zq_sha256(str1, 0, 0, 16); - } -} - -#[cfg(test)] -mod hash_into_mat_polynomial_ring_zq { - use super::{HashInto, HashMatPolynomialRingZq}; - use crate::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; - use qfall_math::{integer::PolyOverZ, traits::*}; - - /// Ensure that the hash function maps into the correct dimension and it is also - /// static, i.e. the same value is returned, when the same value is hashed. - #[test] - fn correct_dimensions() { - let gp = GadgetParametersRing::init_default(10, 99); - - let hasher = HashMatPolynomialRingZq { - modulus: gp.modulus, - rows: 17, - cols: 3, - }; - let hash_val = hasher.hash("Hello"); - let hash_val_2 = hasher.hash("Hello"); - let entry: PolyOverZ = hash_val.get_entry(0, 0).unwrap(); - - assert_eq!(hasher.rows, hash_val.get_num_rows()); - assert_eq!(hasher.cols, hash_val.get_num_columns()); - assert_eq!(hash_val, hash_val_2); - assert_eq!(9, entry.get_degree()) - } -} diff --git a/src/construction/hash/sis.rs b/src/construction/hash/sis.rs deleted file mode 100644 index 01588e4..0000000 --- a/src/construction/hash/sis.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains an implementation of the collision-resistant -//! SIS-based hash function. - -use qfall_math::{error::MathError, integer::Z, integer_mod_q::MatZq, traits::MatrixDimensions}; -use serde::{Deserialize, Serialize}; - -/// This struct keeps an instance of the [`SISHash`] including -/// its key and public parameters implicitly stored as `n = key.#rows()`, -/// `m = key.#columns`, and `q = key.modulus`. -/// -/// This construction is implemented according to the description in [\[1\]](). -/// -/// Attributes: -/// - `n`: specifies the security parameter, which is not equal to the bit-security level -/// - `m`: defines the number of columns of `A` defining this SIS instance -/// - `q`: specifies the modulus -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::hash::SISHash; -/// use qfall_math::integer_mod_q::MatZq; -/// // setup public parameters and key pair -/// let hash = SISHash::gen(5, 18, 11).unwrap(); -/// -/// // check provable collision-resistance of hash -/// assert!(hash.check_security().is_ok()); -/// -/// // generate something to hash -/// let msg = MatZq::sample_uniform(18, 1, 11); -/// -/// // hash the message -/// let result = hash.hash(&msg); -/// ``` -#[derive(Debug, Serialize, Deserialize)] -pub struct SISHash { - key: MatZq, // implicitly contains n = nrows, m = ncols, q = modulus -} - -impl SISHash { - /// Generates a new secret key for an [`SISHash`] instance. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - `m`: specifies the number of columns of matrix `A` - /// - `q`: specifies the modulus - /// - /// Returns a new instance of a [`SISHash`] function with freshly - /// chosen secret key `A` of type [`MatZq`], dimensions `n x m`, - /// and modulus `q`. Otherwise, a [`MathError`] is returned, if `n <= 0`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::hash::SISHash; - /// - /// let hash = SISHash::gen(5, 18, 11).unwrap(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if `n <= 0`. - pub fn gen(n: impl Into, m: impl Into, q: impl Into) -> Result { - let n: Z = n.into(); - let m: Z = m.into(); - let q: Z = q.into(); - - if n < Z::ONE { - return Err(MathError::InvalidIntegerInput(String::from( - "n must be chosen bigger than 0.", - ))); - } - - let mat_a = MatZq::sample_uniform(&n, &m, q); - - Ok(Self { key: mat_a }) - } - - /// Checks whether the [`SISHash`] instance is provably collision-resistant. - /// - /// Returns an empty result if the instance is provably secure. - /// Otherwise, a [`MathError`] is returned, if or `m < n log q`, - /// or `q <= ⌈sqrt(n log q)⌉` as collision-resistance - /// would otherwise not be ensured. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::hash::SISHash; - /// let hash = SISHash::gen(5, 18, 11).unwrap(); - /// - /// assert!(hash.check_security().is_ok()); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if `m < n log q`, or `q <= ⌈sqrt(n log q)⌉` - /// as collision-resistance would otherwise not be ensured. - pub fn check_security(&self) -> Result<(), MathError> { - let n: Z = self.key.get_num_rows().into(); - let m: Z = self.key.get_num_columns().into(); - let q: Z = self.key.get_mod().into(); - - // computed according to bullet point 3 of section 4.1.1 in Decade - let m_bar = (&n * q.log(2).unwrap()).ceil(); - - // m >= m_bar according to bullet point 3 of section 4.1.1 in Decade - if m < m_bar { - return Err(MathError::InvalidIntegerInput(String::from( - "m was chosen smaller than n log q, but it must be larger to satisfy the pigeonhole principle.", - ))); - } - // q > ⌈sqrt(m_bar)⌉ according to bullet point 3 + 1 of section 4.1.1 in Decade - if q <= m_bar.sqrt().ceil() { - return Err(MathError::InvalidIntegerInput(String::from( - "q was chosen smaller than ⌈sqrt(n log q)⌉, but it must be larger to satisfy the pigeonhole principle.", - ))); - } - - Ok(()) - } - - /// Applies f_A to `value`, i.e. computes `A * value`. - /// - /// Parameters: - /// - `value`: specifies an element from the domain, - /// i.e. a column vector of length `m` with modulus `q` - /// - /// Returns the hash digest of dimension `n`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::hash::SISHash; - /// use qfall_math::integer_mod_q::MatZq; - /// use std::str::FromStr; - /// let hash = SISHash::gen(1, 3, 7).unwrap(); - /// let value = MatZq::from_str("[[1],[2],[3]] mod 7").unwrap(); - /// - /// hash.hash(&value); - /// ``` - /// - /// # Panics ... - /// - if `value` isn't a column vector. - /// - if `value` has a different modulus than the [`SISHash`] instance. - /// - if `value` has mismatching dimensions, i.e. the vector length isn't `m`. - pub fn hash(&self, value: &MatZq) -> MatZq { - if !value.is_column_vector() { - panic!("The hashed value has to be a column vector!"); - } - - &self.key * value - } -} - -#[cfg(test)] -mod test_gen { - use super::{SISHash, Z}; - use qfall_math::traits::MatrixDimensions; - - /// Checks whether too small chosen `n` results in an error. - #[test] - fn invalid_n() { - let res_0 = SISHash::gen(0, 2, 2); - let res_1 = SISHash::gen(-1, 2, 2); - let res_2 = SISHash::gen(i64::MIN, 2, 2); - - assert!(res_0.is_err()); - assert!(res_1.is_err()); - assert!(res_2.is_err()); - } - - /// Checks whether too small chosen `m` results in an error in the security check. - #[test] - fn insecure_m() { - let res_0 = SISHash::gen(1, 1, 4).unwrap(); - let res_1 = SISHash::gen(2, 2, 2).unwrap(); - let res_2 = SISHash::gen(4, 5, i64::MAX).unwrap(); - - assert!(res_0.check_security().is_err()); - assert!(res_1.check_security().is_err()); - assert!(res_2.check_security().is_err()); - } - - /// Checks whether too small chosen `q` results in an error in the security check. - #[test] - fn insecure_q() { - let res_0 = SISHash::gen(10, 50, 6).unwrap(); - let res_1 = SISHash::gen(5, 50, 4).unwrap(); - - assert!(res_0.check_security().is_err()); - assert!(res_1.check_security().is_err()); - } - - /// Ensures that a working example returns a proper instance. - #[test] - fn working_example() { - let hash = SISHash::gen(5, 18, 11).unwrap(); - - assert!(hash.check_security().is_ok()); - assert_eq!(5, hash.key.get_num_rows()); - assert_eq!(18, hash.key.get_num_columns()); - assert_eq!(Z::from(11), Z::from(hash.key.get_mod())); - } - - /// Ensures that the expected availability is provided. - #[test] - fn availability() { - let _ = SISHash::gen(4i8, 4i8, 4i8); - let _ = SISHash::gen(4i8, 4i16, 4i32); - let _ = SISHash::gen(4u8, 4i64, 4u16); - let _ = SISHash::gen(4u64, 4u32, 4); - let _ = SISHash::gen(Z::ONE, 4i64, 4u16); - let _ = SISHash::gen(Z::ONE, Z::from(2), Z::from(2)); - } -} - -#[cfg(test)] -mod test_hash { - use super::{MatZq, SISHash, Z}; - use qfall_math::traits::MatrixDimensions; - - /// Ensures that non-column-vectors result in a panic. - #[should_panic] - #[test] - fn not_column_vec() { - let hash = SISHash::gen(1, 3, 7).unwrap(); - let value = MatZq::new(1, 3, 7); - - hash.hash(&value); - } - - /// Ensures that mismatching dimensions result in a panic. - #[should_panic] - #[test] - fn mismatching_dimensions() { - let hash = SISHash::gen(1, 3, 7).unwrap(); - let value = MatZq::new(4, 1, 7); - - hash.hash(&value); - } - - /// Ensures that mismatching moduli result in a panic. - #[should_panic] - #[test] - fn mismatching_moduli() { - let hash = SISHash::gen(1, 3, 7).unwrap(); - let value = MatZq::new(3, 1, 8); - - hash.hash(&value); - } - - /// Ensures that a working example returns a proper instance. - #[test] - fn working_example() { - let hash = SISHash::gen(5, 18, 11).unwrap(); - let value = MatZq::new(18, 1, 11); - - let res = hash.hash(&value); - - assert_eq!(5, res.get_num_rows()); - assert_eq!(1, res.get_num_columns()); - assert_eq!(Z::from(11), Z::from(res.get_mod())); - } -} diff --git a/src/construction/identity_based_encryption.rs b/src/construction/identity_based_encryption.rs deleted file mode 100644 index 10cf514..0000000 --- a/src/construction/identity_based_encryption.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright © 2023 Phil Milewski -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module provides the trait a struct should implement if it is an -//! instance of a identity based public key encryption scheme. Furthermore, -//! it contains cryptographic schemes implementing the [`IBEScheme`] trait. -//! -//! The main references are listed in the following -//! and will be further referenced in submodules by these numbers: -//! - \[1\] Gentry, Craig and Peikert, Chris and Vaikuntanathan, Vinod (2008). -//! Trapdoors for hard lattices and new cryptographic constructions. -//! In: Proceedings of the fortieth annual ACM symposium on Theory of computing. -//! -//! - \[2\] Regev, Oded (2009). -//! On lattices, learning with errors, random linear codes, and cryptography. -//! In: Journal of the ACM 6. -//! - -mod dual_regev_ibe; - -pub use dual_regev_ibe::DualRegevIBE; -use qfall_math::integer::Z; - -/// This trait should be implemented by every identity-based encryption scheme. -/// It offers a simple interface to use and implements the main functions supported by -/// IBEs. -pub trait IBEScheme { - type MasterPublicKey; - type MasterSecretKey; - type SecretKey; - type Cipher; - type Identity; - - /// Generates a master public key pair `(mpk, msk)` suitable for the specific identity-based encryption scheme (IBE). - /// - /// Returns a tuple `(mpk, msk)` consisting of [`Self::MasterPublicKey`] and [`Self::MasterSecretKey`]. - fn setup(&self) -> (Self::MasterPublicKey, Self::MasterSecretKey); - - /// Extracts a secret key corresponding to the specified `identity` using the master secret key `msk`. - /// - /// Parameters: - /// - `master_pk`: specifies the master public key - /// - `master_sk`: specifies the master secret key used for extracting the secret of `identity` - /// - `identity`: specifies the identity for which the secret key should be extracted - /// - /// Returns a secret key for the specified `identity` as a [`Self::SecretKey`]. - fn extract( - &mut self, - master_pk: &Self::MasterPublicKey, - master_sk: &Self::MasterSecretKey, - identity: &Self::Identity, - ) -> Self::SecretKey; - - /// Encrypts the provided `message` using the master public key `mpk` and `identity` of the recipient. - /// - /// Parameters: - /// - `master_pk`: specifies the master public key used for this IBE - /// - `identity`: specifies the recipient that should be able to decrypt the encrypted message - /// - `message`: specifies the message to be encrypted - /// - /// Returns the encryption of `message` as a [`Self::Cipher`] instance. - fn enc( - &self, - master_pk: &Self::MasterPublicKey, - identity: &Self::Identity, - message: impl Into, - ) -> Self::Cipher; - - /// Decrypts the provided `cipher` using the extracted secret key `sk`. - /// - /// Parameters: - /// - `sk`: specifies the extracted secret key used for decryption - /// - `cipher`: specifies the ciphertext to be decrypted - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; -} diff --git a/src/construction/identity_based_encryption/dual_regev_ibe.rs b/src/construction/identity_based_encryption/dual_regev_ibe.rs deleted file mode 100644 index 029edd4..0000000 --- a/src/construction/identity_based_encryption/dual_regev_ibe.rs +++ /dev/null @@ -1,577 +0,0 @@ -// Copyright © 2023 Phil Milewski -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains an implementation of the IND-CPA secure -//! identity based public key encryption scheme. The encryption scheme is based -//! on [`DualRegevIBE`]. - -use super::IBEScheme; -use crate::{ - construction::{ - hash::sha256::hash_to_mat_zq_sha256, - pk_encryption::{DualRegev, PKEncryptionScheme}, - }, - primitive::psf::{PSF, PSFGPV}, - sample::g_trapdoor::gadget_parameters::GadgetParameters, -}; -use qfall_math::{ - error::MathError, - integer::{MatZ, Z}, - integer_mod_q::{MatZq, Modulus}, - rational::{MatQ, Q}, - traits::{Concatenate, MatrixDimensions, Pow}, -}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// This struct manages and stores the public parameters of a [`IBEScheme`] -/// public key encryption instance based on [\[1\]](). -/// -/// Attributes: -/// - `r`: specifies the Gaussian parameter used by the [`PSF`] -/// - `dual_regev`: a [`DualRegev`] instance with fitting parameters `n`, `m`, `q`, `alpha` -/// - `psf`: specifies the PSF used for extracting secret keys -/// - `storage`: is a [`HashMap`] which stores all previously computed secret keys -/// corresponding to their identities -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::identity_based_encryption::{DualRegevIBE, IBEScheme}; -/// use qfall_math::integer::Z; -/// // setup public parameters and key pair -/// let mut ibe = DualRegevIBE::default(); -/// let (pk, sk) = ibe.setup(); -/// -/// // extract a identity based secret key -/// let identity = String::from("identity"); -/// let id_sk = ibe.extract(&pk, &sk, &identity); -/// -/// // encrypt a bit -/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 -/// let cipher = ibe.enc(&pk, &identity, &msg); -/// -/// // decrypt -/// let m = ibe.dec(&id_sk, &cipher); -/// -/// assert_eq!(msg, m) -/// ``` -#[derive(Serialize, Deserialize)] -pub struct DualRegevIBE { - pub dual_regev: DualRegev, - pub psf: PSFGPV, - storage: HashMap, -} - -impl DualRegevIBE { - /// Initializes a [`DualRegevIBE`] struct with parameters generated by - /// `DualRegev::new(n, q, r, alpha)` - /// - /// Returns an [`DualRegevIBE`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::DualRegevIBE; - /// - /// let ibe = DualRegevIBE::new(4, 54983, 14, 0.0025); - /// ``` - pub fn new( - n: impl Into, // security parameter - q: impl Into, // modulus - r: impl Into, // Gaussian parameter for sampleD - alpha: impl Into, // Gaussian parameter for sampleZ - ) -> Self { - let n = n.into(); - let q = q.into(); - let r = r.into(); - let alpha = alpha.into(); - - let gadget = GadgetParameters::init_default(&n, &q); - - let log_q = Z::from(&q).log_ceil(2).unwrap(); - let n_log_q = &n * &log_q; - let m = &gadget.m_bar + n_log_q; - - let psf = PSFGPV { gp: gadget, s: r }; - Self { - psf, - dual_regev: DualRegev::new(n, m, q, alpha), - storage: HashMap::new(), - } - } - - /// Initializes a [`DualRegevIBE`] struct with parameters generated by `DualRegev::new_from_n(n)`. - /// - /// **WARNING:** Due to the [`PSF`] this schemes extract algorithm is slow for n > 5. - /// - /// Returns an [`DualRegevIBE`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::DualRegevIBE; - /// - /// let dual_regev = DualRegevIBE::new_from_n(4); - /// ``` - pub fn new_from_n(n: impl Into) -> Self { - let n: Z = n.into(); - if n < 2 { - panic!("Security parameter n has to be larger than 1"); - } - - let n_i64 = i64::try_from(&n).unwrap(); - // these powers are chosen according to experience s.t. at least every - // fifth generation of public parameters outputs a valid pair - // the exponent is only tested for n < 8 - let power = match n_i64 { - 2..=3 => 10, - 4 => 7, - 5..=7 => 6, - _ => 5, - }; - - // generate prime q in [n^power / 2, n^power] - let upper_bound: Z = n.pow(power).unwrap(); - let lower_bound = upper_bound.div_ceil(2); - // prime used due to guide from GPV08 after Proposition 8.1 - // on how to choose appropriate parameters, but prime is not - // necessarily needed for this scheme to be correct or secure - let q = Modulus::from(Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap()); - - let gadget = GadgetParameters::init_default(&n, &q); - let log_q = Z::from(&q).log_ceil(2).unwrap(); - let n_log_q = &n * &log_q; - - // m is computed due to the [`PSFGPV`] implementation - let m = &gadget.m_bar + n_log_q; - let r: Q = m.sqrt(); - let alpha = 1 / (&r * 2 * (&m + Z::ONE).sqrt() * (n).log(2).unwrap()); - - let psf = PSFGPV { gp: gadget, s: r }; - Self { - psf, - dual_regev: DualRegev::new(n, m, q, alpha), - storage: HashMap::new(), - } - } - - /// Checks the public parameters for security according to Theorem 1.1 - /// and Lemma 5.4 of [\[2\]](), as well as - /// the requirements of [\[1\]]()`s eprint version - /// at Section 7.1 of [GPV08 - eprint](https://eprint.iacr.org/2007/432.pdf). - /// - /// The required properties are: - /// - q >= 5 * r * (m + 1) - /// - r >= sqrt(m) - /// - m > (n + 1) * log(q) - /// - /// Returns an empty result if the public parameters guarantees security w.r.t. `n` - /// or a [`MathError`] if the instance would not be secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::DualRegevIBE; - /// let ibe = DualRegevIBE::default(); - /// - /// assert!(ibe.check_security().is_ok()); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// secure Dual Regev public key encryption instance. - pub fn check_security(&self) -> Result<(), MathError> { - let q = Q::from(&self.dual_regev.q); - - // Security requirements - // q >= 5 * r * (m + 1) - if q < (5 * &self.psf.s) * (&self.dual_regev.m + Q::ONE) { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as q < 5 * r * (m + 1), but q >= 5 * r * (m + 1) is required.", - ))); - } - - // r >= sqrt(m) - if self.psf.s < self.dual_regev.m.sqrt() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as r < sqrt(m), but r >= sqrt(m) is required.", - ))); - } - - // m >= (n + 1) * log(q) - if self.dual_regev.m <= (&self.dual_regev.n + 1) * &q.log(2).unwrap() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as m <= (n + 1) * log(q), \ - but m > (n + 1) * log(q) is required.", - ))); - } - - Ok(()) - } - - /// Checks the public parameters for - /// correctness according to Lemma 5.1 of [\[2\]](). - /// - /// The required properties are: - /// - α <= 1/(2 * r * sqrt(m) * log(n)) - /// - /// **WARNING:** Some requirements are missing to ensure overwhelming correctness of the scheme. - /// - /// Returns an empty result if the public parameters guarantee correctness - /// with overwhelming probability or a [`MathError`] if the instance would - /// not be correct. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::DualRegevIBE; - /// let ibe = DualRegevIBE::default(); - /// - /// assert!(ibe.check_correctness().is_ok()); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// correct Dual Regev IBE public key encryption instance. - pub fn check_correctness(&self) -> Result<(), MathError> { - if self.dual_regev.n <= Z::ONE { - return Err(MathError::InvalidIntegerInput(String::from( - "n must be chosen bigger than 1.", - ))); - } - - // α <= 1/(2 * r * sqrt(m) * log(n)) - if self.dual_regev.alpha - > 1 / (2 * &self.psf.s * (&self.dual_regev.m + Z::ONE).sqrt()) - * self.dual_regev.n.log(2).unwrap() - { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α > 1/(r * sqrt(m) * log(n)), but α <= 1/(2 * r * sqrt(m) * log(n)) is required.", - ))); - } - - Ok(()) - } -} - -impl Default for DualRegevIBE { - /// Initializes a [`DualRegevIBE`] struct with parameters generated by `DualRegevIBE::new_from_n(4)`. - /// This parameter choice is not secure as the dimension of the lattice is too small, - /// but it provides an efficient working example. - /// - /// Returns an [`DualRegevIBE`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::DualRegevIBE; - /// - /// let ibe = DualRegevIBE::default(); - /// ``` - fn default() -> Self { - DualRegevIBE::new_from_n(4) - } -} - -impl IBEScheme for DualRegevIBE { - type Cipher = MatZq; - type MasterPublicKey = MatZq; - type MasterSecretKey = (MatZ, MatQ); - type SecretKey = MatZ; - type Identity = String; - - /// Generates a (pk, sk) pair for the Dual Regev public key encryption scheme - /// by following these steps: - /// - s <- Z_q^n - /// - A <- Z_q^{n x m} - /// - x <- χ^m - /// - p = A^t * s + x - /// - /// Then, `pk = (A, p)` and `sk = s` is output. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::{DualRegevIBE, IBEScheme}; - /// let ibe = DualRegevIBE::default(); - /// - /// let (pk, sk) = ibe.setup(); - /// ``` - fn setup(&self) -> (Self::MasterPublicKey, Self::MasterSecretKey) { - self.psf.trap_gen() - } - - /// Given an identity it extracts a corresponding secret key by using samp_p - /// of the given [`PSF`]. - /// - /// Parameters: - /// - `master_pk`: The master public key for the encryption scheme - /// - `master_sk`: Zhe master secret key of the encryption scheme, namely - /// the trapdoor for the [`PSF`] - /// - `identity`: The identity, for which the corresponding secret key - /// should be returned - /// - /// Returns the corresponding secret key of `identity` under public key - /// `master_pk`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::{IBEScheme, DualRegevIBE}; - /// let mut ibe = DualRegevIBE::default(); - /// let (master_pk, master_sk) = ibe.setup(); - /// - /// let id = String::from("identity"); - /// let sk = ibe.extract(&master_pk, &master_sk, &id); - /// ``` - fn extract( - &mut self, - master_pk: &Self::MasterPublicKey, - master_sk: &Self::MasterSecretKey, - identity: &Self::Identity, - ) -> Self::SecretKey { - // check if it is in the HashMap - if let Some(value) = self.storage.get(&format!( - "{master_pk} {} {} {identity}", - master_sk.0, master_sk.1 - )) { - return value.clone(); - } - - let u = hash_to_mat_zq_sha256(identity, &self.dual_regev.n, 1, &self.dual_regev.q); - let secret_key = self.psf.samp_p(master_pk, master_sk, &u); - - // insert secret key in HashMap - self.storage.insert( - format!("{master_pk} {} {} {identity}", master_sk.0, master_sk.1), - secret_key.clone(), - ); - - secret_key - } - - /// Generates an encryption of `message mod 2` for the provided public key - /// and identity by by calling [`DualRegev::enc()`] on - /// pk = [master_pk | H(id)] which corresponds to to [A | u] in - /// [GPV08 - eprint](https://eprint.iacr.org/2007/432.pdf). - /// Constructing the public key this way yields a identity based public key - /// which secret key can be extracted by the [`PSF`]. - /// - /// Then, `cipher = [u | c]` is output. - /// - /// Parameters: - /// - `master_pk`: specifies the public key, which cis matrix `pk = A` - /// - `identity`: specifies the identity used for encryption - /// - `message`: specifies the message that should be encrypted - /// - /// Returns a cipher of type [`MatZq`] for master_pk an identity. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::{DualRegevIBE, IBEScheme}; - /// let ibe = DualRegevIBE::default(); - /// let (pk, sk) = ibe.setup(); - /// - /// let id = String::from("identity"); - /// let cipher = ibe.enc(&pk, &id, 1); - /// ``` - fn enc( - &self, - master_pk: &Self::MasterPublicKey, - identity: &Self::Identity, - message: impl Into, - ) -> Self::Cipher { - let identity_based_pk = - hash_to_mat_zq_sha256(identity, master_pk.get_num_rows(), 1, master_pk.get_mod()); - self.dual_regev.enc( - &master_pk.concat_horizontal(&identity_based_pk).unwrap(), - message, - ) - } - - /// Decrypts the provided `cipher` using the secret key `sk` by using - /// [`DualRegev::dec()`] - /// - /// Parameters: - /// - `sk_id`: specifies the secret key `sk = s` obtained by extract - /// - `cipher`: specifies the cipher containing `cipher = c` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::identity_based_encryption::{DualRegevIBE, IBEScheme}; - /// use qfall_math::integer::Z; - /// // setup public parameters and key pair - /// let mut ibe = DualRegevIBE::default(); - /// let (pk, sk) = ibe.setup(); - /// - /// // extract a identity based secret key - /// let identity = String::from("identity"); - /// let id_sk = ibe.extract(&pk, &sk, &identity); - /// - /// // encrypt a bit - /// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 - /// let cipher = ibe.enc(&pk, &identity, &msg); - /// - /// // decrypt - /// let m = ibe.dec(&id_sk, &cipher); - /// - /// assert_eq!(msg, m) - /// ``` - fn dec(&self, sk_id: &Self::SecretKey, cipher: &Self::Cipher) -> Z { - self.dual_regev.dec(sk_id, cipher) - } -} - -#[cfg(test)] -mod test_dual_regev_ibe { - use super::DualRegevIBE; - use crate::construction::identity_based_encryption::IBEScheme; - use qfall_math::integer::Z; - - /// Checks whether `new` is available for types implementing [`Into`]. - #[test] - fn new_availability() { - let _ = DualRegevIBE::new(2u8, 2u16, 2u32, 2u64); - let _ = DualRegevIBE::new(2u16, 2u64, 2i32, 2i64); - let _ = DualRegevIBE::new(2i16, 2i64, 2u32, 2u8); - let _ = DualRegevIBE::new(Z::from(2), Z::from(2), 2u8, 2i8); - } - - /// Ensures that `new_from_n` is available for types implementing [`Into`]. - #[test] - #[allow(clippy::needless_borrows_for_generic_args)] - fn availability() { - let _ = DualRegevIBE::new_from_n(4u8); - let _ = DualRegevIBE::new_from_n(4u16); - let _ = DualRegevIBE::new_from_n(4u32); - let _ = DualRegevIBE::new_from_n(4u64); - let _ = DualRegevIBE::new_from_n(4i8); - let _ = DualRegevIBE::new_from_n(4i16); - let _ = DualRegevIBE::new_from_n(4i32); - let _ = DualRegevIBE::new_from_n(4i64); - let _ = DualRegevIBE::new_from_n(Z::from(4)); - let _ = DualRegevIBE::new_from_n(&Z::from(4)); - } - - /// Checks whether `new_from_n` returns an error for invalid input n. - #[test] - #[should_panic] - fn invalid_n() { - DualRegevIBE::new_from_n(1); - } - - /// Checks whether the full-cycle of gen, extract, enc, dec works properly - /// for message 0 and the default. - #[test] - fn cycle_zero_default() { - let msg = Z::ZERO; - let id = String::from("Hello World!"); - let mut cryptosystem = DualRegevIBE::default(); - - let (pk, sk) = cryptosystem.setup(); - let id_sk = cryptosystem.extract(&pk, &sk, &id); - let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); - - assert_eq!(msg, m) - } - - /// Checks whether the full-cycle of gen, extract, enc, dec works properly - /// for message 1 and the default. - #[test] - fn cycle_one_default() { - let msg = Z::ONE; - let id = String::from("Hello World!"); - let mut cryptosystem = DualRegevIBE::default(); - - let (pk, sk) = cryptosystem.setup(); - let id_sk = cryptosystem.extract(&pk, &sk, &id); - let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); - - assert_eq!(msg, m) - } - - /// Checks whether the full-cycle of gen, extract, enc, dec works properly - /// for message 0 and small n. - #[test] - fn cycle_zero_small_n() { - let msg = Z::ZERO; - let id = String::from("Hel213lo World!"); - let mut cryptosystem = DualRegevIBE::new_from_n(5); - - let (pk, sk) = cryptosystem.setup(); - let id_sk = cryptosystem.extract(&pk, &sk, &id); - let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, extract, enc, dec works properly - /// for message 1 and small n. - #[test] - fn cycle_one_small_n() { - let msg = Z::ONE; - let id = String::from("Hel213lo World!"); - let mut cryptosystem = DualRegevIBE::new_from_n(5); - - let (pk, sk) = cryptosystem.setup(); - let id_sk = cryptosystem.extract(&pk, &sk, &id); - let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); - assert_eq!(msg, m); - } - - /// multi test for different identities, message 1 and small n - #[test] - fn new_from_n() { - for i in 1..=5 { - let msg = Z::ONE; - let id = format!("Hello World!{i}"); - let mut cryptosystem = DualRegevIBE::default(); - - cryptosystem.check_security().unwrap(); - cryptosystem.check_correctness().unwrap(); - - let (pk, sk) = cryptosystem.setup(); - - let id_sk = cryptosystem.extract(&pk, &sk, &id); - for _j in 1..=100 { - let cipher = cryptosystem.enc(&pk, &id, &msg); - let m = cryptosystem.dec(&id_sk, &cipher); - - assert_eq!(msg, m); - } - } - } - - /// checking whether the storage works properly - #[test] - fn extract_storage_same_identity_mk_pk() { - let id = "Hello World!".to_string(); - let mut cryptosystem = DualRegevIBE::default(); - let (pk, sk) = cryptosystem.setup(); - - let id_sk_1 = cryptosystem.extract(&pk, &sk, &id); - let id_sk_2 = cryptosystem.extract(&pk, &sk, &id); - - assert_eq!(id_sk_1, id_sk_2) - } - - /// checking whether the storage works properly for different master secret and public key - /// may fail with small probability - #[test] - fn extract_storage_same_identity_different_mk_pk() { - let id = "Hello World!".to_string(); - let mut cryptosystem = DualRegevIBE::default(); - let (pk_1, sk_1) = cryptosystem.setup(); - let (pk_2, sk_2) = cryptosystem.setup(); - - let id_sk_1 = cryptosystem.extract(&pk_1, &sk_1, &id); - let id_sk_2 = cryptosystem.extract(&pk_2, &sk_2, &id); - - assert_ne!(id_sk_1, id_sk_2) - } -} diff --git a/src/construction/pk_encryption.rs b/src/construction/pk_encryption.rs deleted file mode 100644 index acb84bc..0000000 --- a/src/construction/pk_encryption.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module provides the trait a struct should implement if it is an -//! instance of a public key encryption scheme. Furthermore, it contains -//! cryptographic schemes implementing the [`PKEncryptionScheme`] or [`PKEncryptionSchemeMut`] trait. -//! -//! The main references are listed in the following -//! and will be further referenced in submodules by these numbers: -//! - \[1\] Peikert, Chris (2016). -//! A decade of lattice cryptography. -//! In: Theoretical Computer Science 10.4. -//! -//! - \[2\] Gentry, Craig and Peikert, Chris and Vaikuntanathan, Vinod (2008). -//! Trapdoors for hard lattices and new cryptographic constructions. -//! In: Proceedings of the fortieth annual ACM symposium on Theory of computing. -//! -//! - \[3\] Regev, Oded (2009). -//! On lattices, learning with errors, random linear codes, and cryptography. -//! In: Journal of the ACM 6. -//! -//! - \[4\] Lindner, R., and C. Peikert (2011). -//! Better key sizes (and attacks) for LWE-based encryption. -//! In: Topics in Cryptology - RSA Conference 2011, Springer. -//! -//! - \[5\] Canetti, R., Halevi, S., and Katz, J. (2004). -//! Chosen-ciphertext security from identity-based encryption. -//! In: Advances in Cryptology - EUROCRYPT 2004. -//! -//! - \[6\] National Institute of Standards and Technology (2024). -//! Module-Lattice-Based Key-Encapsulation Mechanism Standard. -//! Federal Information Processing Standards Publication (FIPS 203). -//! - -mod ccs_from_ibe; -mod dual_regev; -mod dual_regev_discrete_gauss; -mod k_pke; -mod lpr; -mod regev; -mod regev_discrete_gauss; -mod ring_lpr; - -pub use ccs_from_ibe::CCSfromIBE; -pub use dual_regev::DualRegev; -pub use dual_regev_discrete_gauss::DualRegevWithDiscreteGaussianRegularity; -pub use k_pke::KPKE; -pub use lpr::LPR; -use qfall_math::integer::Z; -pub use regev::Regev; -pub use regev_discrete_gauss::RegevWithDiscreteGaussianRegularity; -pub use ring_lpr::RingLPR; - -/// This trait should be implemented by every public key encryption scheme. -/// It offers a simple interface to use and implement PKEs. -pub trait PKEncryptionScheme { - type PublicKey; - type SecretKey; - type Cipher; - - /// Generates a public key pair `(pk, sk)` suitable for the specific scheme. - /// - /// Returns a tuple `(pk, sk)` consisting of [`Self::PublicKey`] and [`Self::SecretKey`]. - fn gen(&self) -> (Self::PublicKey, Self::SecretKey); - - /// Encrypts the provided `message` using the public key `pk`. - /// - /// Parameters: - /// - `pk`: specifies the public key used for encryption - /// - `message`: specifies the message to be encrypted - /// - /// Returns the encryption of `message` as a [`Self::Cipher`] instance. - fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher; - - /// Decrypts the provided `cipher` using the secret key `sk`. - /// - /// Parameters: - /// - `sk`: specifies the secret key used for decryption - /// - `cipher`: specifies the ciphertext to be decrypted - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; -} - -/// This trait just exists s.t. we can pass `self` in as mutable for more advanced constructions, which use a storage. -/// Otherwise, it does exactly the same as [`PKEncryptionScheme`]. -pub trait PKEncryptionSchemeMut { - type PublicKey; - type SecretKey; - type Cipher; - - /// Generates a public key pair `(pk, sk)` suitable for the specific scheme. - /// - /// Returns a tuple `(pk, sk)` consisting of [`Self::PublicKey`] and [`Self::SecretKey`]. - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey); - - /// Encrypts the provided `message` using the public key `pk`. - /// - /// Parameters: - /// - `pk`: specifies the public key used for encryption - /// - `message`: specifies the message to be encrypted - /// - /// Returns the encryption of `message` as a [`Self::Cipher`] instance. - fn enc(&mut self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher; - - /// Decrypts the provided `cipher` using the secret key `sk`. - /// - /// Parameters: - /// - `sk`: specifies the secret key used for decryption - /// - `cipher`: specifies the ciphertext to be decrypted - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec(&mut self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z; -} - -/// This trait generically implements multi-bit encryption -/// for any scheme implementing the [`PKEncryptionScheme`] trait. -/// -/// It splits the given ciphertext up into its bits and -/// stores the individual encrypted bits as a vector of ciphertexts. -pub trait GenericMultiBitEncryption: PKEncryptionScheme { - /// Encrypts multiple bits by appending several encryptions of single bits. - /// The order of single ciphers is `[c0, c1, ..., cn]`, where `c0` is the least significant bit. - /// Negative values are not allowed. Hence, the absolute value is being encrypted. - /// - /// Parameters: - /// - `pk`: specifies the public key - /// - `message`: specifies the message that should be encryted - /// - /// Returns a cipher of type [`Vec`] containing [`PKEncryptionScheme::Cipher`]. - fn enc_multiple_bits(&self, pk: &Self::PublicKey, message: impl Into) -> Vec { - let message: Z = message.into().abs(); - - let bits = message.to_bits(); - let mut out = vec![]; - for bit in bits { - if bit { - out.push(self.enc(pk, Z::ONE)); - } else { - out.push(self.enc(pk, Z::ZERO)); - } - } - - out - } - - /// Decrypts a multiple bit ciphertext. - /// - /// Parameters: - /// - `sk`: specifies the secret key used for decryption - /// - `cipher`: specifies a slice of ciphers containing several [`PKEncryptionScheme::Cipher`] instances - /// to be decrypted - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - fn dec_multiple_bits(&self, sk: &Self::SecretKey, cipher: &[Self::Cipher]) -> Z { - let mut bits = vec![]; - - for item in cipher { - if self.dec(sk, item) == Z::ZERO { - bits.push(false); - } else { - bits.push(true); - } - } - - Z::from_bits(&bits) - } -} diff --git a/src/construction/pk_encryption/ccs_from_ibe.rs b/src/construction/pk_encryption/ccs_from_ibe.rs deleted file mode 100644 index 397f462..0000000 --- a/src/construction/pk_encryption/ccs_from_ibe.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains a general implementation of an IND-CCA secure -//! public key encryption scheme constructed -//! via an [`IBEScheme`] and a [`SignatureScheme`]. - -use super::PKEncryptionSchemeMut; -use crate::construction::{identity_based_encryption::IBEScheme, signature::SignatureScheme}; -use qfall_math::integer::Z; -use serde::{Deserialize, Serialize}; - -pub mod dual_regev_ibe_pfdh; - -/// This struct manages and stores the public parameters of an [`CCSfromIBE`] -/// public key encryption construction based on [\[5\]](). -/// -/// Attributes: -/// - `ibe`: specifies the IBE scheme used in this construction -/// - `signature`: specifies the signature scheme used in this construction -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; -/// use qfall_math::integer::Z; -/// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); -/// -/// let (pk, sk) = scheme.gen(); -/// let cipher = scheme.enc(&pk, 0); -/// let m = scheme.dec(&sk, &cipher); -/// -/// assert_eq!(Z::ZERO, m); -/// ``` -#[derive(Serialize, Deserialize, Clone)] -pub struct CCSfromIBE -where - IBE::Cipher: ToString, -{ - pub ibe: IBE, - pub signature: Signature, -} - -impl PKEncryptionSchemeMut for CCSfromIBE -where - IBE: IBEScheme, - Signature: SignatureScheme, - IBE::Cipher: ToString, - IBE::MasterPublicKey: Clone, - Signature::PublicKey: Into + Clone, -{ - type Cipher = (Signature::PublicKey, IBE::Cipher, Signature::Signature); - type PublicKey = IBE::MasterPublicKey; - type SecretKey = (IBE::MasterPublicKey, IBE::MasterSecretKey); - - /// Generates a (pk, sk) pair for the CCS construction - /// by following these steps: - /// - (mpk, msk) = ibe.setup() - /// - /// Then, `pk = mpk` and `sk = (mpk, msk)` are returned. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; - /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); - /// - /// let (pk, sk) = scheme.gen(); - /// ``` - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { - let (pk, sk) = self.ibe.setup(); - (pk.clone(), (pk, sk)) - } - - /// Generates an encryption of `message` for the provided public key by following these steps: - /// - (vrfy_key, sign_key) = signature.gen() - /// - c = ibe.enc(mpk, vrfy_key, message), i.e. encrypt `message` with respect to identity `vrfy_key` - /// - sigma = signature.sign(c, sign_key, vrfy_key), i.e. sign message `c` - /// - /// Then, the ciphertext `(vrfy_key, c, sigma)` is returned. - /// - /// Parameters: - /// - `pk`: specifies the public key `pk = A` - /// - `message`: specifies the message that should be encrypted - /// - /// Returns a cipher consisting of a tuple `cipher = (vrfy_key, c, sigma)`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; - /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); - /// - /// let (pk, sk) = scheme.gen(); - /// let cipher = scheme.enc(&pk, 1); - /// ``` - fn enc(&mut self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - let (vrfy_key, sign_key) = self.signature.gen(); - - let c = self.ibe.enc(pk, &vrfy_key.clone().into(), message); - let sigma = self.signature.sign(c.to_string(), &sign_key, &vrfy_key); - (vrfy_key, c, sigma) - } - - /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: - /// - if signature.vrfy(c, sigma, vrfy_key) is not successful, output -1, otherwise proceed - /// - secret_key = ibe.extract(mpk, msk, vrfy_key), i.e. extract the secret key for identity `vrfy_key` - /// - ibe.dec(secret_key, c) - /// - /// Then, the resulting decryption is returned. - /// - /// Parameters: - /// - `sk`: specifies the secret key `sk = (mpk, msk)` - /// - `cipher`: specifies the cipher containing `cipher = (vrfy_key, c, sigma)` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{CCSfromIBE, PKEncryptionSchemeMut}; - /// use qfall_math::integer::Z; - /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); - /// - /// let (pk, sk) = scheme.gen(); - /// let cipher = scheme.enc(&pk, 1); - /// let m = scheme.dec(&sk, &cipher); - /// - /// assert_eq!(Z::ONE, m); - /// ``` - fn dec(&mut self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { - if !self - .signature - .vfy(cipher.1.to_string(), &cipher.2, &cipher.0) - { - return Z::MINUS_ONE; - } - - let secret = self.ibe.extract(&sk.0, &sk.1, &cipher.0.clone().into()); - self.ibe.dec(&secret, &cipher.1) - } -} diff --git a/src/construction/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs b/src/construction/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs deleted file mode 100644 index d0dc655..0000000 --- a/src/construction/pk_encryption/ccs_from_ibe/dual_regev_ibe_pfdh.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! A classical implementation of the [`CCSfromIBE`] scheme using -//! the [`DualRegevIBE`] and [`PFDH`]. - -use super::CCSfromIBE; -use crate::construction::{identity_based_encryption::DualRegevIBE, signature::pfdh::PFDHGPV}; -use qfall_math::{integer::Z, integer_mod_q::Modulus, rational::Q}; - -impl CCSfromIBE { - /// Initializes a [`CCSfromIBE`] PK encryption scheme from a [`DualRegevIBE`] and - /// a PFDH signature, here [`PFDHGPV`]. - /// - /// Parameters: - /// - `n`: specifies the security parameter - /// - `q`: specifies the modulus - /// - `r`: specifies the Gaussian parameter used for the PSF for the [`PFDHGPV`] - /// - `randomness_length`: specifies the number of bits added to the message before signing - /// - `alpha`: specifies the Gaussian parameter used for encryption in - /// [`DualRegev`](crate::construction::pk_encryption::DualRegev) in the [`DualRegevIBE`] - /// - /// Returns an explicit implementation of an IND-CCA-secure public key - /// encryption scheme. - /// - /// # Example - /// ``` - /// use qfall_crypto::construction::pk_encryption::CCSfromIBE; - /// - /// let mut scheme = CCSfromIBE::init_dr_pfdh(4, 13933, 4, 10.77, 0.0021); - /// ``` - /// - /// # Panics ... - /// - if `q <= 1`. - /// - if `n < 1` or `n` does not fit into an [`i64`]. - pub fn init_dr_pfdh( - n: impl Into, // security parameter - q: impl Into, - randomness_length: impl Into, // added to the message before signing - r: impl Into, // Gaussian parameter for PSF - alpha: impl Into, // Gaussian parameter for Dual Regev Encryption - ) -> Self { - let n = n.into(); - let q = q.into(); - let r = r.into(); - - let dr_ibe = DualRegevIBE::new(&n, &q, &r, alpha); - let pfdh = PFDHGPV::setup(n, q, r, randomness_length); - - Self { - ibe: dr_ibe, - signature: pfdh, - } - } - - /// Initializes a [`CCSfromIBE`] PK encryption scheme from a [`DualRegevIBE`] and - /// a PFDH signature, here [`PFDHGPV`], from a given `n > 0`. - /// - /// Parameters: - /// - `n`: specifies the security parameter - /// - /// Returns an explicit implementation of an IND-CCA-secure public key - /// encryption scheme chosen with appropriate parameters for given `n`.. - /// - /// # Example - /// ``` - /// use qfall_crypto::construction::pk_encryption::CCSfromIBE; - /// - /// let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); - /// ``` - /// - /// # Panics ... - /// - if `n < 4` or `n` does not fit into an [`i64`]. - pub fn init_dr_pfdh_from_n(n: impl Into) -> Self { - let n = n.into(); - assert!( - n > 3, - "n needs to be chosen larger than 3 for this function to work properly." - ); - - let ibe = DualRegevIBE::new_from_n(&n); - let pfdh = PFDHGPV::setup(&n, &ibe.dual_regev.q, &ibe.psf.s, &n); - - Self { - ibe, - signature: pfdh, - } - } -} - -#[cfg(test)] -mod test_ccs_from_ibe { - use super::CCSfromIBE; - use crate::construction::pk_encryption::PKEncryptionSchemeMut; - use qfall_math::integer::Z; - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and small n. - #[test] - fn cycle_zero() { - let msg = Z::ZERO; - let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc(&pk, &msg); - let m = scheme.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and small n. - #[test] - fn cycle_one() { - let msg = Z::ONE; - let mut scheme = CCSfromIBE::init_dr_pfdh_from_n(4); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc(&pk, &msg); - let m = scheme.dec(&sk, &cipher); - assert_eq!(msg, m); - } -} diff --git a/src/construction/pk_encryption/dual_regev.rs b/src/construction/pk_encryption/dual_regev.rs deleted file mode 100644 index ea38751..0000000 --- a/src/construction/pk_encryption/dual_regev.rs +++ /dev/null @@ -1,670 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains an implementation of the IND-CPA secure -//! public key Dual Regev encryption scheme. - -use super::{GenericMultiBitEncryption, PKEncryptionScheme}; -use qfall_math::{ - error::MathError, - integer::{MatZ, Z}, - integer_mod_q::{MatZq, Modulus, Zq}, - rational::Q, - traits::{Concatenate, Distance, MatrixGetEntry, MatrixSetEntry, Pow}, -}; -use serde::{Deserialize, Serialize}; - -/// This struct manages and stores the public parameters of a [`DualRegev`] -/// public key encryption instance. -/// -/// Attributes: -/// - `n`: specifies the security parameter, which is not equal to the bit-security level -/// - `m`: defines the dimension of the underlying lattice -/// - `q`: specifies the modulus over which the encryption is computed -/// - `alpha`: specifies the Gaussian parameter used for independent -/// sampling from the discrete Gaussian distribution -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::pk_encryption::{DualRegev, PKEncryptionScheme}; -/// use qfall_math::integer::Z; -/// // setup public parameters and key pair -/// let dual_regev = DualRegev::default(); -/// let (pk, sk) = dual_regev.gen(); -/// -/// // encrypt a bit -/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 -/// let cipher = dual_regev.enc(&pk, &msg); -/// -/// // decrypt -/// let m = dual_regev.dec(&sk, &cipher); -/// -/// assert_eq!(msg, m); -/// ``` -#[derive(Debug, Serialize, Deserialize)] -pub struct DualRegev { - pub(crate) n: Z, // security parameter - pub(crate) m: Z, // number of rows of matrix A - pub(crate) q: Modulus, // modulus - pub(crate) alpha: Q, // Gaussian parameter for sampleZ -} - -impl DualRegev { - /// Instantiates a [`DualRegev`] PK encryption instance with the - /// specified parameters. - /// - /// **WARNING:** The given parameters are not checked for security nor - /// correctness of the scheme. - /// If you want to check your parameters for provable security and correctness, - /// use [`DualRegev::check_correctness`] and [`DualRegev::check_security`]. - /// Or use [`DualRegev::new_from_n`] for generating secure and correct - /// public parameters for [`DualRegev`] according to your choice of `n`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - `m`: specifies the number of columns of matrix `A` - /// - `q`: specifies the modulus - /// - `alpha`: specifies the Gaussian parameter used for independent - /// sampling from the discrete Gaussian distribution - /// - /// Returns [`DualRegev`] PK encryption instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegev; - /// - /// let dual_regev = DualRegev::new(3, 16, 13, 2); - /// ``` - /// - /// # Panics ... - /// - if the given modulus `q <= 1`. - pub fn new( - n: impl Into, - m: impl Into, - q: impl Into, - alpha: impl Into, - ) -> Self { - let n: Z = n.into(); - let m: Z = m.into(); - let q: Modulus = q.into(); - let alpha: Q = alpha.into(); - - Self { n, m, q, alpha } - } - - /// Generates a new [`DualRegev`] instance, i.e. a new set of suitable - /// (provably secure and correct) public parameters, - /// given the security parameter `n` for `n >= 10`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a correct and secure [`DualRegev`] PK encryption instance or - /// a [`MathError`] if the given `n < 10`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegev; - /// - /// let dual_regev = DualRegev::new_from_n(15); - /// ``` - /// - /// Panics... - /// - if `n < 10`. - /// - if `n` does not fit into an [`i64`]. - pub fn new_from_n(n: impl Into) -> Self { - let n = n.into(); - if n < 10 { - panic!("Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise."); - } - - let mut m: Z; - let mut q: Modulus; - let mut alpha: Q; - (m, q, alpha) = Self::gen_new_public_parameters(&n); - let mut out = Self { - n: n.clone(), - m, - q, - alpha, - }; - while out.check_correctness().is_err() || out.check_security().is_err() { - (m, q, alpha) = Self::gen_new_public_parameters(&n); - out = Self { - n: n.clone(), - m, - q, - alpha, - }; - } - - out - } - - /// Generates new public parameters, which must not be secure or correct - /// depending on the random choice of `q`. At least every fifth execution - /// of this function should output a valid set of public parameters, - /// ensuring a secure and correct PK encryption scheme. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a set of public parameters `(m, q, alpha)` chosen according to - /// the provided `n`. - /// - /// # Examples - /// ```compile_fail - /// use qfall_crypto::construction::pk_encryption::DualRegev; - /// use qfall_math::integer::Z; - /// let n = Z::from(2); - /// - /// let (m, q, alpha) = DualRegev::gen_new_public_parameters(&n); - /// ``` - /// - /// Panics... - /// - if `n` does not fit into an [`i64`]. - fn gen_new_public_parameters(n: &Z) -> (Z, Modulus, Q) { - let n_i64 = i64::try_from(n).unwrap(); - // these powers are chosen according to experience s.t. at least every - // fifth generation of public parameters outputs a valid pair - let power = match n_i64 { - 2..=4 => 5, - 5 => 4, - _ => 3, - }; - - // generate prime q in [n^power / 2, n^power] - let upper_bound: Z = n.pow(power).unwrap(); - let lower_bound = upper_bound.div_ceil(2); - // prime used due to guide from GPV08 after Proposition 8.1 - // on how to choose appropriate parameters, but prime is not - // necessarily needed for this scheme to be correct or secure - let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); - - // choose m = (n+1) log q - let m = (n + Z::ONE) * q.log(2).unwrap().ceil(); - - // α = 1/(2 * sqrt(n) * log^2 n) - let alpha = 1 / (2 * n.sqrt() * n.log(2).unwrap().pow(2).unwrap()); - - let q = Modulus::from(q); - - (m, q, alpha) - } - - /// Checks the public parameters for - /// correctness according to Lemma 5.1 of [\[3\]](). - /// - /// The required properties are: - /// - α = o (1 / ( sqrt(n) * log n ) ) - /// - concentration bound with r=5: r * sqrt(m) * α > q/4 - /// - /// **WARNING:** Some requirements are missing to ensure overwhelming correctness of the scheme. - /// - /// Returns an empty result if the public parameters guarantee correctness - /// with overwhelming probability or a [`MathError`] if the instance would - /// not be correct. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegev; - /// let dual_regev = DualRegev::default(); - /// - /// let is_valid = dual_regev.check_correctness().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// correct Dual Regev public key encryption instance. - pub fn check_correctness(&self) -> Result<(), MathError> { - let q = Z::from(&self.q); - - if self.n <= Z::ONE { - return Err(MathError::InvalidIntegerInput(String::from( - "n must be chosen bigger than 1.", - ))); - } - - // Correctness requirements - // α = o (1 / ( sqrt(n) * log n ) ) - if self.alpha > 1 / (self.n.sqrt() * self.n.log(2).unwrap()) { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log n), but α < 1 / (sqrt(n) * log n) is required." - ))); - } - // concentration bound with r=5 -> r * sqrt(m) * α > q/4 - if 20 * self.m.sqrt() * &self.alpha > q { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as 5 * sqrt(m) * α > q/4, but 5 * sqrt(m) * α <= q/4 is required." - ))); - } - - Ok(()) - } - - /// Checks the public parameters for security according to Theorem 1.1 - /// and Lemma 5.4 of [\[3\]](). - /// - /// The required properties are: - /// - q * α >= 2 sqrt(n) - /// - m > (n + 1) log q - /// - /// Returns an empty result if the public parameters guarantees security w.r.t. `n` - /// or a [`MathError`] if the instance would not be secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegev; - /// let dual_regev = DualRegev::default(); - /// - /// let is_valid = dual_regev.check_security().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// secure Dual Regev public key encryption instance. - pub fn check_security(&self) -> Result<(), MathError> { - let q = Z::from(&self.q); - - // Security requirements - // q * α >= 2 sqrt(n) - if &q * &self.alpha < 2 * self.n.sqrt() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.", - ))); - } - // m > (n + 1) log q - if self.m <= ((&self.n + Z::ONE) * q.log(2).unwrap()).ceil() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as m <= (n + 1) log q, but m > (n + 1) log q is required.", - ))); - } - - Ok(()) - } - - /// This function instantiates a 128-bit secure [`DualRegev`] scheme. - /// - /// The public parameters used for this scheme were generated via `DualRegev::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(230, 5313, 7764299, 0.0011) - } -} - -impl Default for DualRegev { - /// Initializes a [`DualRegev`] struct with parameters generated by `DualRegev::new_from_n(13)`. - /// This parameter choice is not secure as the dimension of the lattice is too small, - /// but it provides an efficient working example. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegev; - /// - /// let dual_regev = DualRegev::default(); - /// ``` - fn default() -> Self { - let n = Z::from(13); - let m = Z::from(154); - let q = Modulus::from(1427); - let alpha = Q::from(0.01); - - Self { n, m, q, alpha } - } -} - -impl PKEncryptionScheme for DualRegev { - type Cipher = MatZq; - type PublicKey = MatZq; - type SecretKey = MatZ; - - /// Generates a (pk, sk) pair for the Dual Regev public key encryption scheme - /// by following these steps: - /// - A <- Z_q^{n x m} - /// - x <- {0,1}^m - /// - u = A * x - /// - A = [A | u] - /// - /// Then, `pk = A` and `sk = x` of type [`MatZq`] are returned. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, DualRegev}; - /// let dual_regev = DualRegev::default(); - /// - /// let (pk, sk) = dual_regev.gen(); - /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { - // A <- Z_q^{n x m} - let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); - // x <- Z_2^m - let vec_x = MatZ::sample_uniform(&self.m, 1, 0, 2).unwrap(); - - // u = A * x - let vec_u = &mat_a * &vec_x; - - // A = [A | u] - let mat_a = mat_a.concat_horizontal(&vec_u).unwrap(); - - // pk = A, sk = x - (mat_a, vec_x) - } - - /// Generates an encryption of `message mod 2` for the provided public key by following these steps: - /// - s <- Z_q^n - /// - e <- χ^(m+1) - /// - c^t = s^t * A + e^t + [0^{1xn} | msg * ⌊q/2⌋] - /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. - /// - /// Then, the ciphertext `c` is returned as a vector of type [`MatZq`]. - /// - /// Parameters: - /// - `pk`: specifies the public key `pk = A` - /// - `message`: specifies the message that should be encrypted - /// - /// Returns a cipher `c` of type [`MatZq`]. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, DualRegev}; - /// let dual_regev = DualRegev::default(); - /// let (pk, sk) = dual_regev.gen(); - /// - /// let cipher = dual_regev.enc(&pk, 1); - /// ``` - fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - // generate message = message mod 2 - let message: Z = message.into() % 2; - - // s <- Z_q^n - let vec_s_t = MatZq::sample_uniform(1, &self.n, &self.q); - // e <- χ^(m+1) - let vec_e_t = MatZq::sample_discrete_gauss( - 1, - &(&self.m + 1), - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); - - // c^t = s^t * A + e^t + [0^{1xn} | msg * ⌊q/2⌋] - let mut c = (vec_s_t * pk + vec_e_t).transpose(); - - // hide message in last entry - // compute msg * ⌊q/2⌋ - let msg_q_half = message * Z::from(&self.q).div_floor(2); - // set last entry of c = last_entry + msg * ⌊q/2⌋ - let last_entry: Zq = c.get_entry(-1, 0).unwrap(); - c.set_entry(-1, 0, last_entry + msg_q_half).unwrap(); - - c - } - - /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: - /// - x = c^t * [-sk^t | 1]^t - /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. - /// - /// Parameters: - /// - `sk`: specifies the secret key `sk = x` - /// - `cipher`: specifies the cipher containing `cipher = c` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, DualRegev}; - /// use qfall_math::integer::Z; - /// let dual_regev = DualRegev::default(); - /// let (pk, sk) = dual_regev.gen(); - /// let cipher = dual_regev.enc(&pk, 1); - /// - /// let m = dual_regev.dec(&sk, &cipher); - /// - /// assert_eq!(Z::ONE, m); - /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { - let tmp = (Z::MINUS_ONE * sk) - .concat_vertical(&MatZ::identity(1, 1)) - .unwrap(); - let result: Zq = (cipher.transpose() * tmp).get_entry(0, 0).unwrap(); - let result: Z = result.get_representative_least_absolute_residue().abs(); - - let q_half = Z::from(&self.q).div_floor(2); - - if result.distance(Z::ZERO) > result.distance(q_half) { - Z::ONE - } else { - Z::ZERO - } - } -} - -// adds generic multi-bit encryption to this scheme -impl GenericMultiBitEncryption for DualRegev {} - -#[cfg(test)] -mod test_pp_generation { - use super::DualRegev; - use super::Z; - - /// Checks whether `new` is available for types implementing [`Into`]. - #[test] - fn new_availability() { - let _ = DualRegev::new(2u8, 2u16, 2u32, 2u64); - let _ = DualRegev::new(2u16, 2u64, 2i32, 2i64); - let _ = DualRegev::new(2i16, 2i64, 2u32, 2u8); - let _ = DualRegev::new(Z::from(2), Z::from(2), 2u8, 2i8); - } - - /// Checks whether `new_from_n` works properly for different choices of n. - #[test] - fn suitable_security_params() { - let n_choices = [ - 10, 11, 12, 13, 14, 25, 50, 100, 250, 500, 1000, 2500, 5000, 5001, 10000, - ]; - - for n in n_choices { - let _ = DualRegev::new_from_n(n); - } - } - - /// Checks whether the [`Default`] parameter choice is suitable. - #[test] - fn default_suitable() { - let dr = DualRegev::default(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } - - /// Checks whether the generated public parameters from `new_from_n` are - /// valid choices according to security and correctness of the scheme. - #[test] - fn choice_valid() { - let n_choices = [10, 14, 25, 50, 125, 300, 600, 1200, 4000, 6000]; - - for n in n_choices { - let dr = DualRegev::new_from_n(n); - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } - } - - /// Ensures that `new_from_n` is available for types implementing [`Into`]. - #[test] - #[allow(clippy::needless_borrows_for_generic_args)] - fn availability() { - let _ = DualRegev::new_from_n(10u8); - let _ = DualRegev::new_from_n(10u16); - let _ = DualRegev::new_from_n(10u32); - let _ = DualRegev::new_from_n(10u64); - let _ = DualRegev::new_from_n(10i8); - let _ = DualRegev::new_from_n(10i16); - let _ = DualRegev::new_from_n(10i32); - let _ = DualRegev::new_from_n(10i64); - let _ = DualRegev::new_from_n(Z::from(10)); - let _ = DualRegev::new_from_n(&Z::from(10)); - } - - /// Checks whether `new_from_n` returns an error for invalid input n. - #[test] - #[should_panic] - fn invalid_n() { - DualRegev::new_from_n(9); - } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let dr = DualRegev::secure128(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } -} - -#[cfg(test)] -mod test_dual_regev { - use super::DualRegev; - use crate::construction::pk_encryption::PKEncryptionScheme; - use qfall_math::integer::Z; - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and small n. - #[test] - fn cycle_zero_small_n() { - let msg = Z::ZERO; - let dr = DualRegev::default(); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and small n. - #[test] - fn cycle_one_small_n() { - let msg = Z::ONE; - let dr = DualRegev::default(); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and larger n. - #[test] - fn cycle_zero_large_n() { - let msg = Z::ZERO; - let dr = DualRegev::new_from_n(50); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and larger n. - #[test] - fn cycle_one_large_n() { - let msg = Z::ONE; - let dr = DualRegev::new_from_n(50); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks that modulus 2 is applied correctly. - #[test] - fn modulus_application() { - let messages = [2, 3, i64::MAX, i64::MIN]; - let dr = DualRegev::default(); - let (pk, sk) = dr.gen(); - - for msg in messages { - let msg_mod = Z::from(msg.rem_euclid(2)); - - let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg_mod, m); - } - } -} - -#[cfg(test)] -mod test_multi_bits { - use super::{DualRegev, GenericMultiBitEncryption, PKEncryptionScheme}; - use qfall_math::integer::Z; - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large positive values. - #[test] - fn positive() { - let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; - - for value in values { - let msg = Z::from(value); - let scheme = DualRegev::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for zero. - #[test] - fn zero() { - let msg = Z::ZERO; - let scheme = DualRegev::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large negative values, which are not encrypted itself, - /// but their absolute value. - #[test] - fn negative() { - let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; - - for value in values { - let msg = Z::from(value); - let scheme = DualRegev::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg.abs(), m); - } - } -} diff --git a/src/construction/pk_encryption/dual_regev_discrete_gauss.rs b/src/construction/pk_encryption/dual_regev_discrete_gauss.rs deleted file mode 100644 index 70ab723..0000000 --- a/src/construction/pk_encryption/dual_regev_discrete_gauss.rs +++ /dev/null @@ -1,697 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains an implementation of the IND-CPA secure -//! public key Dual Regev encryption scheme with an instantiation of the regularity lemma -//! via a discrete Gaussian distribution. - -use super::{GenericMultiBitEncryption, PKEncryptionScheme}; -use qfall_math::{ - error::MathError, - integer::Z, - integer_mod_q::{MatZq, Modulus, Zq}, - rational::Q, - traits::{Distance, Pow}, -}; -use serde::{Deserialize, Serialize}; - -/// This struct manages and stores the public parameters of a [`DualRegevWithDiscreteGaussianRegularity`] -/// public key encryption instance. -/// -/// Attributes: -/// - `n`: specifies the security parameter, which is not equal to the bit-security level -/// - `m`: defines the dimension of the underlying lattice -/// - `q`: specifies the modulus over which the encryption is computed -/// - `r`: specifies the Gaussian parameter used for SampleD, -/// i.e. used for encryption -/// - `alpha`: specifies the Gaussian parameter used for independent -/// sampling from the discrete Gaussian distribution -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::pk_encryption::{DualRegevWithDiscreteGaussianRegularity, PKEncryptionScheme}; -/// use qfall_math::integer::Z; -/// // setup public parameters and key pair -/// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); -/// let (pk, sk) = dual_regev.gen(); -/// -/// // encrypt a bit -/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 -/// let cipher = dual_regev.enc(&pk, &msg); -/// -/// // decrypt -/// let m = dual_regev.dec(&sk, &cipher); -/// -/// assert_eq!(msg, m); -/// ``` -#[derive(Debug, Serialize, Deserialize)] -pub struct DualRegevWithDiscreteGaussianRegularity { - n: Z, // security parameter - m: Z, // number of rows of matrix A - q: Modulus, // modulus - r: Q, // Gaussian parameter for sampleD - alpha: Q, // Gaussian parameter for sampleZ -} - -impl DualRegevWithDiscreteGaussianRegularity { - /// Instantiates a [`DualRegevWithDiscreteGaussianRegularity`] PK encryption instance with the - /// specified parameters. - /// - /// **WARNING:** The given parameters are not checked for security nor - /// correctness of the scheme. - /// If you want to check your parameters for provable security and correctness, - /// use [`DualRegevWithDiscreteGaussianRegularity::check_correctness`] and [`DualRegevWithDiscreteGaussianRegularity::check_security`]. - /// Or use [`DualRegevWithDiscreteGaussianRegularity::new_from_n`] for generating secure and correct - /// public parameters for [`DualRegevWithDiscreteGaussianRegularity`] according to your choice of `n`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - `m`: specifies the number of columns of matrix `A` - /// - `q`: specifies the modulus - /// - `r`: specifies the Gaussian parameter used for SampleD, - /// i.e. used for encryption - /// - `alpha`: specifies the Gaussian parameter used for independent - /// sampling from the discrete Gaussian distribution - /// - /// Returns a [`DualRegevWithDiscreteGaussianRegularity`] PK encryption instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegevWithDiscreteGaussianRegularity; - /// - /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::new(2, 16, 443, 4, 0.15625); - /// ``` - /// - /// # Panics ... - /// - if the given modulus `q <= 1`. - pub fn new( - n: impl Into, - m: impl Into, - q: impl Into, - r: impl Into, - alpha: impl Into, - ) -> Self { - let n: Z = n.into(); - let m: Z = m.into(); - let q: Modulus = q.into(); - let r: Q = r.into(); - let alpha: Q = alpha.into(); - - Self { n, m, q, r, alpha } - } - - /// Generates a new [`DualRegevWithDiscreteGaussianRegularity`] instance, i.e. a new set of suitable - /// (provably secure and correct) public parameters, - /// given the security parameter `n`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a correct and secure [`DualRegevWithDiscreteGaussianRegularity`] PK encryption instance or - /// a [`MathError`] if the given `n <= 1`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegevWithDiscreteGaussianRegularity; - /// - /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::new_from_n(2); - /// ``` - /// - /// # Panics ... - /// - if `n <= 1`. - pub fn new_from_n(n: impl Into) -> Self { - let n = n.into(); - if n <= Z::ONE { - panic!("n must be chosen bigger than 1."); - } - - let mut m: Z; - let mut q: Modulus; - let mut r: Q; - let mut alpha: Q; - (m, q, r, alpha) = Self::gen_new_public_parameters(&n); - let mut out = Self { - n: n.clone(), - m, - q, - r, - alpha, - }; - while out.check_correctness().is_err() || out.check_security().is_err() { - (m, q, r, alpha) = Self::gen_new_public_parameters(&n); - out = Self { - n: n.clone(), - m, - q, - r, - alpha, - }; - } - - out - } - - /// Generates new public parameters, which must not be secure or correct - /// depending on the random choice of `q`. At least every fifth execution - /// of this function should output a valid set of public parameters, - /// ensuring a secure and correct PK encryption scheme. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a set of public parameters `(m, q, r, alpha)` chosen according to - /// the provided `n`. - /// - /// # Examples - /// ```compile_fail - /// use qfall_crypto::construction::pk_encryption::DualRegevWithDiscreteGaussianRegularity; - /// use qfall_math::integer::Z; - /// let n = Z::from(2); - /// - /// let (m, q, r, alpha) = DualRegevWithDiscreteGaussianRegularity::gen_new_public_parameters(&n); - /// ``` - fn gen_new_public_parameters(n: &Z) -> (Z, Modulus, Q, Q) { - let n_i64 = i64::try_from(n).unwrap(); - // these powers are chosen according to experience s.t. at least every - // fifth generation of public parameters outputs a valid pair - let power = match n_i64 { - 2 => 9, - 3 => 8, - 4..=5 => 7, - 6..=8 => 6, - 9..=12 => 5, - 13..=30 => 4, - _ => 3, - }; - - // generate prime q in [n^power / 2, n^power] - let upper_bound: Z = n.pow(power).unwrap(); - let lower_bound = upper_bound.div_ceil(2); - // prime used due to guide from GPV08 after Proposition 8.1 - // on how to choose appropriate parameters, but prime is not - // necessarily needed for this scheme to be correct or secure - let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); - - // choose m = 2 (n+1) lg q - let m = (Z::from(2) * (n + Z::ONE) * q.log(10).unwrap()).ceil(); - - // choose r = log m - let r = m.log(2).unwrap(); - - // alpha = 1/(sqrt(m) * log^2 m) - let alpha = 1 / (m.sqrt() * m.log(2).unwrap().pow(2).unwrap()); - - let q = Modulus::from(&q); - - (m, q, r, alpha) - } - - /// Checks the public parameters for correctness according to - /// Theorem 7.1 and Lemma 8.2 of [\[2\]](). - /// - /// The required properties are: - /// - n >= 1 - /// - q >= 5 * r * m - /// - α <= 1/(r * sqrt(m) * ω(sqrt(log n)) - /// - /// Returns an empty result if the public parameters guarantee correctness - /// with overwhelming probability or a [`MathError`] if the instance would - /// not be correct. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegevWithDiscreteGaussianRegularity; - /// let dr = DualRegevWithDiscreteGaussianRegularity::default(); - /// - /// let is_valid = dr.check_correctness().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// correct DualRegevWithDiscreteGaussianRegularity public key encryption instance. - pub fn check_correctness(&self) -> Result<(), MathError> { - let q: Z = Z::from(&self.q); - - if self.n <= Z::ONE { - return Err(MathError::InvalidIntegerInput(String::from( - "n must be chosen bigger than 1.", - ))); - } - - // Correctness requirements - // q >= 5 * r * (m+1) - if q < 5 * &self.r * (&self.m + Z::ONE) { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as q < 5rm, but q >= 5rm is required.", - ))); - } - // α <= 1/(r * sqrt(m+1) * ω(sqrt(log n)) - if self.alpha > 1 / (&self.r * (&self.m + Z::ONE).sqrt() * self.n.log(2).unwrap().sqrt()) { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α > 1/(r*sqrt(m)*ω(sqrt(log n)), but α <= 1/(r*sqrt(m)*ω(sqrt(log n)) is required.", - ))); - } - - Ok(()) - } - - /// Checks the public parameters for security according to - /// Theorem 7.1 and Lemma 8.4 of [\[2\]](). - /// - /// The required properties are: - /// - q * α >= n - /// - m >= 2(n + 1) lg (q) - /// - r >= ω( sqrt( log m ) ) - /// - /// Returns an empty result if the public parameters guarantees security w.r.t. `n` - /// or a [`MathError`] if the instance would not be secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegevWithDiscreteGaussianRegularity; - /// let dr = DualRegevWithDiscreteGaussianRegularity::default(); - /// - /// let is_valid = dr.check_security().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// secure DualRegevWithDiscreteGaussianRegularity public key encryption instance. - pub fn check_security(&self) -> Result<(), MathError> { - let q: Z = Z::from(&self.q); - - // Security requirements - // q * α >= n - if &q * &self.alpha < self.n { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as q * α < n, but q * α >= n is required.", - ))); - } - // m >= 2n lg (q) - if self.m < 2 * &self.n * q.log(10).unwrap() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as m < 2(n + 1) lg (q), but m >= 2(n + 1) lg (q) is required.", - ))); - } - // r >= ω( sqrt( log m ) ) - if self.r < self.m.log(2).unwrap().sqrt() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as r < sqrt( log m ) and r >= ω(sqrt(log m)) is required." - ))); - } - - Ok(()) - } - - /// This function instantiates a 128-bit secure [`DualRegevWithDiscreteGaussianRegularity`] scheme. - /// - /// The public parameters used for this scheme were generated - /// via `DualRegevWithDiscreteGaussianRegularity::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(350, 5248, 29892991, 12.357, 0.00009) - } -} - -impl Default for DualRegevWithDiscreteGaussianRegularity { - /// Initializes a [`DualRegevWithDiscreteGaussianRegularity`] struct with parameters - /// generated by `DualRegevWithDiscreteGaussianRegularity::new_from_n(2)`. - /// This parameter choice is not secure as the dimension of the lattice is too small, - /// but it provides an efficient working example. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::DualRegevWithDiscreteGaussianRegularity; - /// - /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); - /// ``` - fn default() -> Self { - let n = Z::from(2); - let m = Z::from(16); - let q = Modulus::from(443); - let r = Q::from(4); - let alpha = Q::from((1, 64)); - - Self { n, m, q, r, alpha } - } -} - -impl PKEncryptionScheme for DualRegevWithDiscreteGaussianRegularity { - type Cipher = (MatZq, Zq); - type PublicKey = (MatZq, MatZq); - type SecretKey = MatZq; - - /// Generates a (pk, sk) pair for the Dual Regev public key encryption scheme - /// by following these steps: - /// - e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r - /// - A <- Z_q^{n x m} - /// - p = A * e - /// - /// Then, `pk = (A, u)` and `sk = e` are returned. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; - /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); - /// - /// let (pk, sk) = dual_regev.gen(); - /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { - // e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r - let vec_e = MatZq::sample_d_common(&self.m, &self.q, &self.n, &self.r).unwrap(); - // A <- Z_q^{n x m} - let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); - - // u = A * e - let vec_u = &mat_a * &vec_e; - - // pk = (A, u), sk = e - ((mat_a, vec_u), vec_e) - } - - /// Generates an encryption of `message mod 2` for the provided public key by following these steps: - /// - vec_x <- χ^m, x <- χ - /// - p = A^t * s + vec_x - /// - c = u^t * s + x + message * ⌊q/2⌋ - /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. - /// - /// Then, `cipher = (p, c)` is returned. - /// - /// Parameters: - /// - `pk`: specifies the public key, which contains two matrices `pk = (A, u)` - /// - `message`: specifies the message that should be encrypted - /// - /// Returns a cipher of the form `cipher = (p, c)` for [`MatZq`] `u` and [`Zq`] `c`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; - /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); - /// let (pk, sk) = dual_regev.gen(); - /// - /// let cipher = dual_regev.enc(&pk, 1); - /// ``` - fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - // generate message = message mod 2 - let message: Z = message.into() % 2; - - // s <- Z_q^n - let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); - // vec_x <- χ^m - let vec_x = MatZq::sample_discrete_gauss( - &self.m, - 1, - &self.q, - &self.n, - 0, - &(&self.alpha * Z::from(&self.q)), - ) - .unwrap(); - - // x <- χ - let x = Z::sample_discrete_gauss(&self.n, 0, &(&self.alpha * Z::from(&self.q))).unwrap(); - - // p = u^t * s + vec_x - let vec_p = &pk.0.transpose() * &vec_s + vec_x; - // c = u^t * s + x + msg * ⌊q/2⌋ - let q_half = Z::from(&self.q).div_floor(2); - let c = pk.1.dot_product(&vec_s).unwrap() + x + message * q_half; - - (vec_p, c) - } - - /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: - /// - x = c - e^t * p - /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. - /// - /// Parameters: - /// - `sk`: specifies the secret key `sk = e` - /// - `cipher`: specifies the cipher containing `cipher = (p, c)` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, DualRegevWithDiscreteGaussianRegularity}; - /// use qfall_math::integer::Z; - /// let dual_regev = DualRegevWithDiscreteGaussianRegularity::default(); - /// let (pk, sk) = dual_regev.gen(); - /// let cipher = dual_regev.enc(&pk, 1); - /// - /// let m = dual_regev.dec(&sk, &cipher); - /// - /// assert_eq!(Z::ONE, m); - /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { - let result = &cipher.1 - sk.dot_product(&cipher.0).unwrap(); - let result: Z = result.get_representative_least_absolute_residue().abs(); - - let q_half = Z::from(&self.q).div_floor(2); - - if result.distance(Z::ZERO) > result.distance(q_half) { - Z::ONE - } else { - Z::ZERO - } - } -} - -// adds generic multi-bit encryption to this scheme -impl GenericMultiBitEncryption for DualRegevWithDiscreteGaussianRegularity {} - -#[cfg(test)] -mod test_pp_generation { - use super::DualRegevWithDiscreteGaussianRegularity; - use super::Z; - - /// Checks whether `new` is available for types implementing [`Into`]. - #[test] - fn new_availability() { - let _ = DualRegevWithDiscreteGaussianRegularity::new(2u8, 2u16, 2u32, 2u64, 2i8); - let _ = DualRegevWithDiscreteGaussianRegularity::new(2u16, 2u64, 2i32, 2i64, 2i16); - let _ = DualRegevWithDiscreteGaussianRegularity::new(2i16, 2i64, 2u32, 2u8, 2u16); - let _ = - DualRegevWithDiscreteGaussianRegularity::new(Z::from(2), Z::from(2), 2u8, 2i8, 2u32); - } - - /// Checks whether `new_from_n` works properly for different choices of n. - #[test] - fn suitable_security_params() { - let n_choices = [ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 5001, - 10000, - ]; - - for n in n_choices { - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(n); - } - } - - /// Checks whether the [`Default`] parameter choice is suitable. - #[test] - fn default_suitable() { - let dr = DualRegevWithDiscreteGaussianRegularity::default(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } - - /// Checks whether the generated public parameters from `new_from_n` are - /// valid choices according to security and correctness of the scheme. - #[test] - fn choice_valid() { - let n_choices = [ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 5001, - 10000, - ]; - - for n in n_choices { - let dr = DualRegevWithDiscreteGaussianRegularity::new_from_n(n); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } - } - - /// Ensures that `new_from_n` is available for types implementing [`Into`]. - #[test] - #[allow(clippy::needless_borrows_for_generic_args)] - fn new_from_n_availability() { - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2u8); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2u16); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2u32); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2u64); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2i8); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2i16); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2i32); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(2i64); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(Z::from(2)); - let _ = DualRegevWithDiscreteGaussianRegularity::new_from_n(&Z::from(2)); - } - - /// Checks whether `new_from_n` returns an error for invalid input n. - #[test] - #[should_panic] - fn invalid_n() { - DualRegevWithDiscreteGaussianRegularity::new_from_n(1); - } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let dr = DualRegevWithDiscreteGaussianRegularity::secure128(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } -} - -#[cfg(test)] -mod test_dual_regev { - use super::DualRegevWithDiscreteGaussianRegularity; - use crate::construction::pk_encryption::PKEncryptionScheme; - use qfall_math::integer::Z; - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and small n. - #[test] - fn cycle_zero_small_n() { - let msg = Z::ZERO; - let dr = DualRegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and small n. - #[test] - fn cycle_one_small_n() { - let msg = Z::ONE; - let dr = DualRegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and larger n. - #[test] - fn cycle_zero_large_n() { - let msg = Z::ZERO; - let dr = DualRegevWithDiscreteGaussianRegularity::new_from_n(30); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and larger n. - #[test] - fn cycle_one_large_n() { - let msg = Z::ONE; - let dr = DualRegevWithDiscreteGaussianRegularity::new_from_n(30); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks that modulus 2 is applied correctly. - #[test] - fn modulus_application() { - let messages = [2, 3, i64::MAX, i64::MIN]; - let dr = DualRegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = dr.gen(); - - for msg in messages { - let msg_mod = Z::from(msg.rem_euclid(2)); - - let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg_mod, m); - } - } -} - -#[cfg(test)] -mod test_multi_bits { - use super::{ - DualRegevWithDiscreteGaussianRegularity, GenericMultiBitEncryption, PKEncryptionScheme, - }; - use qfall_math::integer::Z; - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large positive values. - #[test] - fn positive() { - let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; - - for value in values { - let msg = Z::from(value); - let scheme = DualRegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for zero. - #[test] - fn zero() { - let msg = Z::ZERO; - let scheme = DualRegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large negative values, which are not encrypted itself, - /// but their absolute value. - #[test] - fn negative() { - let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; - - for value in values { - let msg = Z::from(value); - let scheme = DualRegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg.abs(), m); - } - } -} diff --git a/src/construction/pk_encryption/k_pke.rs b/src/construction/pk_encryption/k_pke.rs deleted file mode 100644 index b47ec55..0000000 --- a/src/construction/pk_encryption/k_pke.rs +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright © 2025 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains a naive implementation of the K-PKE scheme -//! used as foundation for ML-KEM. -//! -//! **WARNING:** This implementation is a toy implementation of the basics below -//! ML-KEM and mostly supposed to showcase the prototyping capabilities of the `qfall`-library. - -use crate::{ - construction::pk_encryption::PKEncryptionScheme, - utils::{ - common_encodings::{ - decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, - }, - common_moduli::new_anticyclic, - }, -}; -use qfall_math::{ - integer::Z, - integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq}, -}; -use serde::{Deserialize, Serialize}; - -/// This is a naive toy-implementation of the [`PKEncryptionScheme`] used -/// as a basis for ML-KEM. -/// -/// This implementation is not supposed to be an implementation of the FIPS 203 standard in [\[6\]](), but -/// is supposed to showcase the prototyping capabilities of `qfall` and does not cover compression algorithms -/// as specified in the FIPS 203 document or might deviate for the choice of matrix multiplication algorithms. -/// Especially, NTT-representation, sampling and multiplication are not part of this prototype. -/// -/// Attributes: -/// - `q`: defines the modulus polynomial `(X^n + 1) mod p` -/// - `k`: defines the width and height of matrix `A` -/// - `eta_1`: defines that vectors `s`, `e`, and `y` are sampled according to Bin(eta_1, 1/2) centered around 0 -/// - `eta_2`: defines that vector `e_1` and `e_2` are sampled according to Bin(eta_2, 1/2) centered around 0 -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::pk_encryption::{KPKE, PKEncryptionScheme}; -/// -/// // setup public parameters -/// let k_pke = KPKE::ml_kem_512(); -/// -/// // generate (pk, sk) pair -/// let (pk, sk) = k_pke.gen(); -/// -/// // encrypt a message -/// let msg = 250; -/// let cipher = k_pke.enc(&pk, &msg); -/// -/// // decrypt the ciphertext -/// let m = k_pke.dec(&sk, &cipher); -/// -/// assert_eq!(msg, m); -/// ``` -#[derive(Debug, Serialize, Deserialize)] -pub struct KPKE { - q: ModulusPolynomialRingZq, // modulus (X^n + 1) mod p - k: i64, // defines both dimensions of matrix A - eta_1: i64, // defines the binomial distribution of the secret and error drawn in `gen` - eta_2: i64, // defines the binomial distribution of the error drawn in `enc` -} - -impl KPKE { - /// Returns a [`KPKE`] instance with public parameters according to the ML-KEM-512 specification. - pub fn ml_kem_512() -> Self { - let q = new_anticyclic(256, 3329).unwrap(); - Self { - q, - k: 2, - eta_1: 3, - eta_2: 2, - } - } - - /// Returns a [`KPKE`] instance with public parameters according to the ML-KEM-768 specification. - pub fn ml_kem_768() -> Self { - let q = new_anticyclic(256, 3329).unwrap(); - Self { - q, - k: 3, - eta_1: 2, - eta_2: 2, - } - } - - /// Returns a [`KPKE`] instance with public parameters according to the ML-KEM-1024 specification. - pub fn ml_kem_1024() -> Self { - let q = new_anticyclic(256, 3329).unwrap(); - Self { - q, - k: 4, - eta_1: 2, - eta_2: 2, - } - } -} - -impl PKEncryptionScheme for KPKE { - type PublicKey = (MatPolynomialRingZq, MatPolynomialRingZq); - type SecretKey = MatPolynomialRingZq; - type Cipher = (MatPolynomialRingZq, PolynomialRingZq); - - /// Generates a `(pk, sk)` pair by following these steps: - /// - A <- R_q^{k x k} - /// - s <- Bin(eta_1, 0.5)^k centered around 0 - /// - e <- Bin(eta_1, 0.5)^k centered around 0 - /// - t = A * s + e - /// - /// Then, `pk = (A^T, t)` and `sk = s` are returned. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, KPKE}; - /// let k_pke = KPKE::ml_kem_512(); - /// - /// let (pk, sk) = k_pke.gen(); - /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { - // 5 𝐀[𝑖,𝑗] ← SampleNTT(𝜌‖𝑗‖𝑖) - // Reminder: NTT-representation, sampling and multiplication are not part of this prototype - let mat_a = MatPolynomialRingZq::sample_uniform(self.k, self.k, &self.q); - // 9 𝐬[𝑖] ← SamplePolyCBD_𝜂_1(PRF_𝜂_1 (𝜎, 𝑁)) - let vec_s = MatPolynomialRingZq::sample_binomial_with_offset( - self.k, - 1, - &self.q, - -self.eta_1, - 2 * self.eta_1, - 0.5, - ) - .unwrap(); - // 13 𝐞[𝑖] ← SamplePolyCBD_𝜂_1(PRF_𝜂_1 (𝜎, 𝑁)) - let vec_e = MatPolynomialRingZq::sample_binomial_with_offset( - self.k, - 1, - &self.q, - -self.eta_1, - 2 * self.eta_1, - 0.5, - ) - .unwrap(); - - // 18 𝐭 ← 𝐀 ∘ 𝐬 + 𝐞 - let vec_t = &mat_a * &vec_s + vec_e; - - let pk = (mat_a.transpose(), vec_t); - let sk = vec_s; - (pk, sk) - } - - /// Encrypts a `message` with the provided public key by following these steps: - /// - y <- Bin(eta_1, 0.5)^k centered around 0 - /// - e_1 <- Bin(eta_2, 0.5)^k centered around 0 - /// - e_2 <- Bin(eta_2, 0.5) centered around 0 - /// - u = A^T * y + e_1 - /// - v = t^T * y + e_2 + 𝜇, where 𝜇 is the {q/2, 0} encoding of the bits of `message` - /// - /// Then, ciphertext `(u, v)` is returned. - /// - /// Parameters: - /// - `pk`: specifies the public key `pk = (A, t)` - /// - `message`: specifies the message that should be encrypted, which should not extend 256 bits (and be positive) - /// - /// Returns a ciphertext `(u, v)` of type [`MatPolynomialRingZq`] and [`PolynomialRingZq`]. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, KPKE}; - /// let k_pke = KPKE::ml_kem_512(); - /// let (pk, sk) = k_pke.gen(); - /// - /// let c = k_pke.enc(&pk, 1); - /// ``` - fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - // 10 𝐲[𝑖] ← SamplePolyCBD_𝜂_1(PRF_𝜂_1 (𝑟, 𝑁)) - let vec_y = MatPolynomialRingZq::sample_binomial_with_offset( - self.k, - 1, - &self.q, - -self.eta_1, - 2 * self.eta_1, - 0.5, - ) - .unwrap(); - // 𝐞_𝟏[𝑖] ← SamplePolyCBD_𝜂_2(PRF_𝜂_2 (𝑟, 𝑁)) - let vec_e_1 = MatPolynomialRingZq::sample_binomial_with_offset( - self.k, - 1, - &self.q, - -self.eta_2, - 2 * self.eta_2, - 0.5, - ) - .unwrap(); - // 𝑒_2 ← SamplePolyCBD_𝜂_2(PRF_𝜂_2 (𝑟, 𝑁)) - let e_2 = PolynomialRingZq::sample_binomial_with_offset( - &self.q, - -self.eta_2, - 2 * self.eta_2, - 0.5, - ) - .unwrap(); - - // 19 𝐮 ← NTT^−1(𝐀^⊺ ∘ 𝐲) + 𝐞_𝟏 - let vec_u = &pk.0 * &vec_y + vec_e_1; - - // 20 𝜇 ← Decompress_1(ByteDecode_1(𝑚)) - let mu = encode_z_bitwise_in_polynomialringzq(&self.q, &message.into()); - - // 21 𝑣 ← NTT^−1(𝐭^⊺ ∘ 𝐲) + 𝑒_2 + 𝜇 - let v = pk.1.dot_product(&vec_y).unwrap() + e_2 + mu; - - (vec_u, v) - } - - /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: - /// - w = v - s^T * u - /// - returns the decoding of `w` with 1 and 0 set in the returned [`Z`] instance - /// if the corresponding coefficient was closer to q/2 or 0 respectively - /// - /// Parameters: - /// - `sk`: specifies the secret key `sk = s` - /// - `cipher`: specifies the ciphertext containing `cipher = (u, v)` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, KPKE}; - /// let k_pke = KPKE::ml_kem_512(); - /// let (pk, sk) = k_pke.gen(); - /// let c = k_pke.enc(&pk, 1); - /// - /// let m = k_pke.dec(&sk, &c); - /// - /// assert_eq!(1, m); - /// ``` - fn dec(&self, sk: &Self::SecretKey, (u, v): &Self::Cipher) -> Z { - // 6 𝑤 ← 𝑣 − NTT^−1(𝐬^⊺ ∘ NTT(𝐮)) - let w = v - sk.dot_product(u).unwrap(); - - // 7 𝑚 ← ByteEncode_1(Compress_1(𝑤)) - decode_z_bitwise_from_polynomialringzq(self.q.get_q(), &w) - } -} - -#[cfg(test)] -mod test_kpke { - use crate::construction::pk_encryption::{k_pke::KPKE, PKEncryptionScheme}; - - /// Ensures that [`KPKE`] works for all ML-KEM specifications by - /// performing a round trip of several messages. - #[test] - fn correctness() { - let k_pkes = [KPKE::ml_kem_512(), KPKE::ml_kem_768(), KPKE::ml_kem_1024()]; - for k_pke in k_pkes { - let messages = [0, 1, 13, 255, 2047, 4294967295_u32]; - - for message in messages { - let (pk, sk) = k_pke.gen(); - let c = k_pke.enc(&pk, message); - let m = k_pke.dec(&sk, &c); - - assert_eq!(message, m); - } - } - } -} diff --git a/src/construction/pk_encryption/lpr.rs b/src/construction/pk_encryption/lpr.rs deleted file mode 100644 index db97709..0000000 --- a/src/construction/pk_encryption/lpr.rs +++ /dev/null @@ -1,693 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains an implementation of the IND-CPA secure -//! public key LPR encryption scheme. - -use super::{GenericMultiBitEncryption, PKEncryptionScheme}; -use qfall_math::{ - error::MathError, - integer::Z, - integer_mod_q::{MatZq, Modulus, Zq}, - rational::Q, - traits::{Concatenate, Distance, MatrixGetEntry, MatrixSetEntry, Pow}, -}; -use serde::{Deserialize, Serialize}; - -/// This struct manages and stores the public parameters of a [`LPR`] -/// public key encryption instance. -/// -/// Attributes: -/// - `n`: specifies the security parameter, which is not equal to the bit-security level -/// - `q`: specifies the modulus over which the encryption is computed -/// - `alpha`: specifies the Gaussian parameter used for independent -/// sampling from the discrete Gaussian distribution -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::pk_encryption::{LPR, PKEncryptionScheme}; -/// use qfall_math::integer::Z; -/// // setup public parameters and key pair -/// let lpr = LPR::default(); -/// let (pk, sk) = lpr.gen(); -/// -/// // encrypt a bit -/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 -/// let cipher = lpr.enc(&pk, &msg); -/// -/// // decrypt -/// let m = lpr.dec(&sk, &cipher); -/// -/// assert_eq!(msg, m); -/// ``` -#[derive(Debug, Serialize, Deserialize)] -pub struct LPR { - n: Z, // security parameter - q: Modulus, // modulus - alpha: Q, // Gaussian parameter for sampleZ -} - -impl LPR { - /// Instantiates a [`LPR`] PK encryption instance with the - /// specified parameters. - /// - /// **WARNING:** The given parameters are not checked for security nor - /// correctness of the scheme. - /// If you want to check your parameters for provable security and correctness, - /// use [`LPR::check_correctness`] and [`LPR::check_security`]. - /// Or use [`LPR::new_from_n`] for generating secure and correct - /// public parameters for [`LPR`] according to your choice of `n`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - `q`: specifies the modulus - /// - `alpha`: specifies the Gaussian parameter used for independent - /// sampling from the discrete Gaussian distribution - /// - /// Returns a correct and secure [`LPR`] PK encryption instance or - /// a [`MathError`] if the instance would not be correct or secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::LPR; - /// - /// let lpr = LPR::new(3, 13, 2); - /// ``` - /// - /// # Panics ... - /// - if the given modulus `q <= 1`. - pub fn new(n: impl Into, q: impl Into, alpha: impl Into) -> Self { - let n: Z = n.into(); - let q: Modulus = q.into(); - let alpha: Q = alpha.into(); - - Self { n, q, alpha } - } - - /// Generates a new [`LPR`] instance, i.e. a new set of suitable - /// (provably secure and correct) public parameters, - /// given the security parameter `n` for `n >= 10`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a correct and secure [`LPR`] PK encryption instance or - /// a [`MathError`] if the given `n < 10`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::LPR; - /// - /// let lpr = LPR::new_from_n(15); - /// ``` - /// - /// Panics... - /// - if `n < 10` - /// - if `n` does not fit into an [`i64`]. - pub fn new_from_n(n: impl Into) -> Self { - let n = n.into(); - assert!( - n >= 10, - "Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise." - ); - - let mut q: Modulus; - let mut alpha: Q; - (q, alpha) = Self::gen_new_public_parameters(&n); - let mut out = Self { - n: n.clone(), - q, - alpha, - }; - while out.check_correctness().is_err() || out.check_security().is_err() { - (q, alpha) = Self::gen_new_public_parameters(&n); - out = Self { - n: n.clone(), - q, - alpha, - }; - } - - out - } - - /// Generates new public parameters, which must not be secure or correct - /// depending on the random choice of `q`. At least every fifth execution - /// of this function should output a valid set of public parameters, - /// ensuring a secure and correct PK encryption scheme. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a set of public parameters `(q, alpha)` chosen according to - /// the provided `n`. - /// - /// # Examples - /// ```compile_fail - /// use qfall_crypto::construction::pk_encryption::LPR; - /// use qfall_math::integer::Z; - /// let n = Z::from(2); - /// - /// let (q, alpha) = LPR::gen_new_public_parameters(&n); - /// ``` - /// - /// Panics... - /// - if `n` does not fit into an [`i64`]. - fn gen_new_public_parameters(n: &Z) -> (Modulus, Q) { - let n_i64 = i64::try_from(n).unwrap(); - - // generate prime q in [n^3 / 2, n^3] - let upper_bound: Z = n.pow(3).unwrap(); - let lower_bound = upper_bound.div_ceil(2); - // prime used due to guide from GPV08 after Proposition 8.1 - // on how to choose appropriate parameters, but prime is not - // necessarily needed for this scheme to be correct or secure - let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); - - // Found out by experience as the bound is not tight enough to ensure correctness for large n. - // Hence, a small factor roughly of max(log n - 4, 1) has to be applied. - // Checked for 100 parameter sets with 10 cycles each and no mismatching decryptions occurred. - let factor = match n_i64 { - 1..=20 => 1, - 21..=40 => 2, - 41..=80 => 3, - 81..=160 => 4, - _ => 5, - }; - // α = 1/(sqrt(n) * log^2 n) - let alpha = 1 / (factor * n.sqrt() * n.log(2).unwrap().pow(3).unwrap()); - - let q = Modulus::from(q); - - (q, alpha) - } - - /// Checks the public parameters for - /// correctness according to Lemma 3.1 of [\[4\]](). - /// - /// The required properties are: - /// - α = o (1 / (sqrt(n) * log^3 n)) - /// - /// **WARNING**: This bound is not tight. Hence, we added a small factor - /// loosely corresponding to max(log n - 4, 1) below to ensure correctness - /// with overwhelming probability. - /// - /// Returns an empty result if the public parameters guarantee correctness - /// with overwhelming probability or a [`MathError`] if the instance would - /// not be correct. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::LPR; - /// let lpr = LPR::default(); - /// - /// let is_valid = lpr.check_correctness().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// correct LPR public key encryption instance. - /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError) - /// if the value does not fit into an [`i64`] - pub fn check_correctness(&self) -> Result<(), MathError> { - let n_i64 = i64::try_from(&self.n)?; - - if self.n <= Z::ONE { - return Err(MathError::InvalidIntegerInput(String::from( - "n must be chosen bigger than 1.", - ))); - } - - // Found out by experience as the bound is not tight enough to ensure correctness for large n. - // Hence, a small factor roughly of max(log n - 4, 1) has to be applied. - // Checked for 100 parameter sets with 10 cycles each and no mismatching decryptions occurred. - let factor = match n_i64 { - 1..=20 => 1, - 21..=40 => 2, - 41..=80 => 3, - 81..=160 => 4, - _ => 5, - }; - // α = o (1 / sqrt(n) * log^3 n )) - if self.alpha > 1 / (factor * self.n.sqrt() * self.n.log(2).unwrap().pow(3).unwrap()) { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log^3 n), but α < 1 / (sqrt(n) * log^3 n) is required. Please check the documentation!" - ))); - } - - Ok(()) - } - - /// Checks the public parameters for security according to Section 2.2 - /// and Lemma 3.2 of [\[4\]](). - /// - /// The required properties are: - /// - q * α >= 2 sqrt(n) - /// - /// Returns an empty result if the public parameters guarantee security - /// w.r.t. `n` or a [`MathError`] if the instance would - /// not be secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::LPR; - /// let lpr = LPR::default(); - /// - /// let is_valid = lpr.check_security().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// secure LPR public key encryption instance. - pub fn check_security(&self) -> Result<(), MathError> { - let q = Z::from(&self.q); - - // Security requirements - // q * α >= 2 sqrt(n) - if &q * &self.alpha < 2 * self.n.sqrt() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.", - ))); - } - - Ok(()) - } - - /// This function instantiates a 128-bit secure [`LPR`] scheme. - /// - /// The public parameters used for this scheme were generated via `LPR::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(500, 76859609, 0.000005) - } -} - -impl Default for LPR { - /// Initializes a [`LPR`] struct with parameters generated by `LPR::new_from_n(3)`. - /// This parameter choice is not secure as the dimension of the lattice is too small, - /// but it provides an efficient working example. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::LPR; - /// - /// let lpr = LPR::default(); - /// ``` - fn default() -> Self { - let n = Z::from(10); - let q = Modulus::from(983); - let alpha = Q::from(0.0072); - - Self { n, q, alpha } - } -} - -impl PKEncryptionScheme for LPR { - type Cipher = MatZq; - type PublicKey = MatZq; - type SecretKey = MatZq; - - /// Generates a (pk, sk) pair for the LPR public key encryption scheme - /// by following these steps: - /// - A <- Z_q^{n x n} - /// - s <- χ^n - /// - e <- χ^n - /// - b^t = s^t * A + e^t - /// - A = [A^t | b]^t - /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. - /// - /// Then, `pk = A` and `sk = s` are returned. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, LPR}; - /// let lpr = LPR::default(); - /// - /// let (pk, sk) = lpr.gen(); - /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { - // A <- Z_q^{n x n} - let mat_a = MatZq::sample_uniform(&self.n, &self.n, &self.q); - // s <- χ^n - let vec_s = MatZq::sample_discrete_gauss( - &self.n, - 1, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); - // e <- χ^n - let vec_e_t = MatZq::sample_discrete_gauss( - 1, - &self.n, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); - - // b^t = s^t * A + e^t - let vec_b_t = vec_s.transpose() * &mat_a + vec_e_t; - - // A = [A^t | b]^t - let mat_a = mat_a.concat_vertical(&vec_b_t).unwrap(); - - // pk = A, sk = s - (mat_a, vec_s) - } - - /// Generates an encryption of `message mod 2` for the provided public key by following these steps: - /// - r <- χ^n - /// - e <- χ^{n+1} - /// - c = A * r + e + [0^{1 x n} | msg * ⌊q/2⌋]^t - /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. - /// - /// Then, cipher `c` as a vector of type [`MatZq`] is returned. - /// - /// Parameters: - /// - `pk`: specifies the public key `pk = A` - /// - `message`: specifies the message that should be encrypted - /// - /// Returns a cipher `c` of type [`MatZq`]. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, LPR}; - /// let lpr = LPR::default(); - /// let (pk, sk) = lpr.gen(); - /// - /// let cipher = lpr.enc(&pk, 1); - /// ``` - fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - // generate message = message mod 2 - let message: Z = message.into() % 2; - - // x <- χ^n - let vec_r = MatZq::sample_discrete_gauss( - &self.n, - 1, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); - // e <- χ^{n+1} - let vec_e = MatZq::sample_discrete_gauss( - &(&self.n + 1), - 1, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); - - // c = A * r + e + [0^{1xn} | msg * ⌊q/2⌋]^t - let mut c = pk * vec_r + vec_e; - - // hide message in last entry - // compute msg * ⌊q/2⌋ - let msg_q_half = message * Z::from(&self.q).div_floor(2); - // set last entry of c = last_entry + msg * ⌊q/2⌋ - let last_entry: Zq = c.get_entry(-1, 0).unwrap(); - c.set_entry(-1, 0, last_entry + msg_q_half).unwrap(); - - c - } - - /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: - /// - x = [-sk^t | 1] * c - /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. - /// - /// Parameters: - /// - `sk`: specifies the secret key `sk = s` - /// - `cipher`: specifies the cipher containing `cipher = c` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, LPR}; - /// use qfall_math::integer::Z; - /// let lpr = LPR::default(); - /// let (pk, sk) = lpr.gen(); - /// let cipher = lpr.enc(&pk, 1); - /// - /// let m = lpr.dec(&sk, &cipher); - /// - /// assert_eq!(Z::ONE, m); - /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { - let result = (Z::MINUS_ONE * sk.transpose()) - .concat_horizontal(&MatZq::identity(1, 1, &self.q)) - .unwrap() - .dot_product(cipher) - .unwrap(); - let result: Z = result.get_representative_least_absolute_residue().abs(); - - let q_half = Z::from(&self.q).div_floor(2); - - if result.distance(Z::ZERO) > result.distance(q_half) { - Z::ONE - } else { - Z::ZERO - } - } -} - -// adds generic multi-bit encryption to this scheme -impl GenericMultiBitEncryption for LPR {} - -#[cfg(test)] -mod test_pp_generation { - use super::LPR; - use super::Z; - - /// Checks whether `new` is available for types implementing [`Into`]. - #[test] - fn new_availability() { - let _ = LPR::new(2u8, 2u32, 2u64); - let _ = LPR::new(2u16, 2i32, 2i64); - let _ = LPR::new(2i16, 2u32, 2u8); - let _ = LPR::new(Z::from(2), 2u8, 2i8); - } - - /// Checks whether `new_from_n` works properly for different choices of n. - #[test] - fn suitable_security_params() { - let n_choices = [ - 10, 11, 12, 13, 14, 25, 50, 100, 250, 500, 1000, 2500, 5000, 5001, 10000, - ]; - - for n in n_choices { - let _ = LPR::new_from_n(n); - } - } - - /// Checks whether the [`Default`] parameter choice is suitable. - #[test] - fn default_suitable() { - let lpr = LPR::default(); - - assert!(lpr.check_correctness().is_ok()); - assert!(lpr.check_security().is_ok()); - } - - /// Checks whether the generated public parameters from `new_from_n` are - /// valid choices according to security and correctness of the scheme. - #[test] - fn choice_valid() { - let n_choices = [10, 14, 25, 50, 125, 300, 600, 1200, 4000, 6000]; - - for n in n_choices { - let lpr = LPR::new_from_n(n); - - assert!(lpr.check_correctness().is_ok()); - assert!(lpr.check_security().is_ok()); - } - } - - /// Ensures that `new_from_n` is available for types implementing [`Into`]. - #[test] - #[allow(clippy::needless_borrows_for_generic_args)] - fn availability() { - let _ = LPR::new_from_n(10u8); - let _ = LPR::new_from_n(10u16); - let _ = LPR::new_from_n(10u32); - let _ = LPR::new_from_n(10u64); - let _ = LPR::new_from_n(10i8); - let _ = LPR::new_from_n(10i16); - let _ = LPR::new_from_n(10i32); - let _ = LPR::new_from_n(10i64); - let _ = LPR::new_from_n(Z::from(10)); - let _ = LPR::new_from_n(&Z::from(10)); - } - - /// Checks whether `new_from_n` returns an error for invalid input n. - #[test] - #[should_panic] - fn invalid_n() { - LPR::new_from_n(9); - } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let lpr = LPR::secure128(); - - assert!(lpr.check_correctness().is_ok()); - assert!(lpr.check_security().is_ok()); - } -} - -#[cfg(test)] -mod test_lpr { - use super::LPR; - use crate::construction::pk_encryption::PKEncryptionScheme; - use qfall_math::integer::Z; - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and small n. - #[test] - fn cycle_zero_small_n() { - let msg = Z::ZERO; - let lpr = LPR::default(); - - let (pk, sk) = lpr.gen(); - let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and small n. - #[test] - fn cycle_one_small_n() { - let msg = Z::ONE; - let lpr = LPR::default(); - - let (pk, sk) = lpr.gen(); - let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and larger n. - #[test] - fn cycle_zero_large_n() { - let msg = Z::ZERO; - let lpr = LPR::new_from_n(50); - - let (pk, sk) = lpr.gen(); - let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and larger n. - #[test] - fn cycle_one_large_n() { - let msg = Z::ONE; - let lpr = LPR::new_from_n(50); - - let (pk, sk) = lpr.gen(); - let cipher = lpr.enc(&pk, &msg); - let m = lpr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks that modulus 2 is applied correctly. - #[test] - fn modulus_application() { - let messages = [2, 3, i64::MAX, i64::MIN]; - let dr = LPR::default(); - let (pk, sk) = dr.gen(); - - for msg in messages { - let msg_mod = Z::from(msg.rem_euclid(2)); - - let cipher = dr.enc(&pk, msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg_mod, m); - } - } -} - -#[cfg(test)] -mod test_multi_bits { - use super::{GenericMultiBitEncryption, PKEncryptionScheme, LPR}; - use qfall_math::integer::Z; - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large positive values. - #[test] - fn positive() { - let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; - - for value in values { - let msg = Z::from(value); - let scheme = LPR::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for zero. - #[test] - fn zero() { - let msg = Z::ZERO; - let scheme = LPR::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large negative values, which are not encrypted itself, - /// but their absolute value. - #[test] - fn negative() { - let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; - - for value in values { - let msg = Z::from(value); - let scheme = LPR::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg.abs(), m); - } - } -} diff --git a/src/construction/pk_encryption/regev.rs b/src/construction/pk_encryption/regev.rs deleted file mode 100644 index fdcd7d7..0000000 --- a/src/construction/pk_encryption/regev.rs +++ /dev/null @@ -1,672 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains an implementation of the IND-CPA secure -//! public key Regev encryption scheme. - -use super::{GenericMultiBitEncryption, PKEncryptionScheme}; -use qfall_math::{ - error::MathError, - integer::{MatZ, Z}, - integer_mod_q::{MatZq, Modulus, Zq}, - rational::Q, - traits::{Concatenate, Distance, MatrixGetEntry, MatrixSetEntry, Pow}, -}; -use serde::{Deserialize, Serialize}; - -/// This struct manages and stores the public parameters of a [`Regev`] -/// public key encryption instance. -/// -/// Attributes: -/// - `n`: specifies the security parameter, which is not equal to the bit-security level -/// - `m`: defines the dimension of the underlying lattice -/// - `q`: specifies the modulus over which the encryption is computed -/// - `alpha`: specifies the Gaussian parameter used for independent -/// sampling from the discrete Gaussian distribution -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::pk_encryption::{Regev, PKEncryptionScheme}; -/// use qfall_math::integer::Z; -/// // setup public parameters and key pair -/// let regev = Regev::default(); -/// let (pk, sk) = regev.gen(); -/// -/// // encrypt a bit -/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 -/// let cipher = regev.enc(&pk, &msg); -/// -/// // decrypt -/// let m = regev.dec(&sk, &cipher); -/// -/// assert_eq!(msg, m); -/// ``` -#[derive(Debug, Serialize, Deserialize)] -pub struct Regev { - n: Z, // security parameter - m: Z, // number of rows of matrix A - q: Modulus, // modulus - alpha: Q, // Gaussian parameter for sampleZ -} - -impl Regev { - /// Instantiates a [`Regev`] PK encryption instance with the - /// specified parameters. - /// - /// **WARNING:** The given parameters are not checked for security nor - /// correctness of the scheme. - /// If you want to check your parameters for provable security and correctness, - /// use [`Regev::check_correctness`] and [`Regev::check_security`]. - /// Or use [`Regev::new_from_n`] for generating secure and correct - /// public parameters for [`Regev`] according to your choice of `n`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - `m`: specifies the number of columns of matrix `A` - /// - `q`: specifies the modulus - /// - `alpha`: specifies the Gaussian parameter used for independent - /// sampling from the discrete Gaussian distribution - /// - /// Returns a [`Regev`] PK encryption instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::Regev; - /// - /// let regev = Regev::new(3, 16, 13, 2); - /// ``` - /// - /// # Panics ... - /// - if the given modulus `q <= 1`. - pub fn new( - n: impl Into, - m: impl Into, - q: impl Into, - alpha: impl Into, - ) -> Self { - let n: Z = n.into(); - let m: Z = m.into(); - let q: Modulus = q.into(); - let alpha: Q = alpha.into(); - - Self { n, m, q, alpha } - } - - /// Generates a new [`Regev`] instance, i.e. a new set of suitable - /// (provably secure and correct) public parameters, - /// given the security parameter `n` for `n >= 10`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a correct and secure [`Regev`] PK encryption instance or - /// a [`MathError`] if the given `n < 10`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::Regev; - /// - /// let regev = Regev::new_from_n(15); - /// ``` - /// - /// Panics... - /// - if `n < 10`. - /// - if `n` does not fit into an [`i64`]. - pub fn new_from_n(n: impl Into) -> Self { - let n = n.into(); - if n < 10 { - panic!("Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise."); - } - - let mut m: Z; - let mut q: Modulus; - let mut alpha: Q; - (m, q, alpha) = Self::gen_new_public_parameters(&n); - let mut out = Self { - n: n.clone(), - m, - q, - alpha, - }; - while out.check_correctness().is_err() || out.check_security().is_err() { - (m, q, alpha) = Self::gen_new_public_parameters(&n); - out = Self { - n: n.clone(), - m, - q, - alpha, - }; - } - - out - } - - /// Generates new public parameters, which must not be secure or correct - /// depending on the random choice of `q`. At least every fifth execution - /// of this function should output a valid set of public parameters, - /// ensuring a secure and correct PK encryption scheme. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a set of public parameters `(m, q, alpha)` chosen according to - /// the provided `n`. - /// - /// # Examples - /// ```compile_fail - /// use qfall_crypto::construction::pk_encryption::Regev; - /// use qfall_math::integer::Z; - /// let n = Z::from(2); - /// - /// let (m, q, alpha) = Regev::gen_new_public_parameters(&n); - /// ``` - /// - /// Panics... - /// - if `n` does not fit into an [`i64`]. - fn gen_new_public_parameters(n: &Z) -> (Z, Modulus, Q) { - let n_i64 = i64::try_from(n).unwrap(); - // these powers are chosen according to experience s.t. at least every - // fifth generation of public parameters outputs a valid pair - let power = match n_i64 { - 2..=4 => 5, - 5 => 4, - _ => 3, - }; - - // generate prime q in [n^power / 2, n^power] - let upper_bound: Z = n.pow(power).unwrap(); - let lower_bound = upper_bound.div_ceil(2); - // prime used due to guide from GPV08 after Proposition 8.1 - // on how to choose appropriate parameters, but prime is not - // necessarily needed for this scheme to be correct or secure - let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); - - // choose m = (n+1) log q - let m = (n + Z::ONE) * q.log(2).unwrap().ceil(); - - // α = 1/(2 * sqrt(n) * log^2 n) - let alpha = 1 / (2 * n.sqrt() * n.log(2).unwrap().pow(2).unwrap()); - - let q = Modulus::from(q); - - (m, q, alpha) - } - - /// Checks the public parameters for - /// correctness according to Lemma 5.1 of [\[3\]](). - /// - /// The required properties are: - /// - α = o (1 / ( sqrt(n) * log n ) ) - /// - concentration bound with r=5: r * sqrt(m) * α > q/4 - /// - /// **WARNING:** Some requirements are missing to ensure - /// overwhelming correctness of the scheme for small `n`. - /// - /// Returns an empty result if the public parameters guarantee correctness - /// with overwhelming probability or a [`MathError`] if the instance would - /// not be correct. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::Regev; - /// let regev = Regev::default(); - /// - /// let is_valid = regev.check_correctness().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// correct Regev public key encryption instance. - pub fn check_correctness(&self) -> Result<(), MathError> { - let q = Z::from(&self.q); - - if self.n <= Z::ONE { - return Err(MathError::InvalidIntegerInput(String::from( - "n must be chosen bigger than 1.", - ))); - } - - // Correctness requirements - // α = o (1 / ( sqrt(n) * log n ) ) - if self.alpha > 1 / (self.n.sqrt() * self.n.log(2).unwrap()) { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log n), but α < 1 / (sqrt(n) * log n) is required." - ))); - } - // concentration bound with r=5 -> r * sqrt(m) * α > q/4 - if 20 * self.m.sqrt() * &self.alpha > q { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as 5 * sqrt(m) * α > q/4, but 5 * sqrt(m) * α <= q/4 is required." - ))); - } - - Ok(()) - } - - /// Checks the public parameters for security according to Theorem 1.1 - /// and Lemma 5.4 of [\[3\]](). - /// - /// The required properties are: - /// - q * α >= 2 sqrt(n) - /// - m > (n + 1) log q - /// - /// Returns an empty result if the public parameters guarantees security w.r.t. `n` - /// or a [`MathError`] if the instance would not be secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::Regev; - /// let regev = Regev::default(); - /// - /// let is_valid = regev.check_security().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// secure Regev public key encryption instance. - pub fn check_security(&self) -> Result<(), MathError> { - let q = Z::from(&self.q); - - // Security requirements - // q * α >= 2 sqrt(n) - if &q * &self.alpha < 2 * self.n.sqrt() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.", - ))); - } - // m > (n + 1) log q - if self.m <= ((&self.n + Z::ONE) * q.log(2).unwrap()).ceil() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as m <= (n + 1) log q, but m > (n + 1) log q is required.", - ))); - } - - Ok(()) - } - - /// This function instantiates a 128-bit secure [`Regev`] scheme. - /// - /// The public parameters used for this scheme were generated via `Regev::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(230, 5313, 7764299, 0.0011) - } -} - -impl Default for Regev { - /// Initializes a [`Regev`] struct with parameters generated by `Regev::new_from_n(13)`. - /// This parameter choice is not secure as the dimension of the lattice is too small, - /// but it provides an efficient working example. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::Regev; - /// - /// let regev = Regev::default(); - /// ``` - fn default() -> Self { - let n = Z::from(13); - let m = Z::from(154); - let q = Modulus::from(1427); - let alpha = Q::from(0.01); - - Self { n, m, q, alpha } - } -} - -impl PKEncryptionScheme for Regev { - type Cipher = MatZq; - type PublicKey = MatZq; - type SecretKey = MatZq; - - /// Generates a (pk, sk) pair for the Regev public key encryption scheme - /// by following these steps: - /// - A <- Z_q^{n x m} - /// - s <- Z_q^n - /// - e^t <- χ^m - /// - b^t = s^t * A + e^t - /// - A = [A^t | b]^t - /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. - /// - /// Then, `pk = A` and `sk = s` of type [`MatZq`] are returned. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, Regev}; - /// let regev = Regev::default(); - /// - /// let (pk, sk) = regev.gen(); - /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { - // A <- Z_q^{n x m} - let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); - // s <- Z_q^n - let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); - // e^t <- χ^m - let vec_e_t = MatZq::sample_discrete_gauss( - 1, - &self.m, - &self.q, - &self.n, - 0, - &self.alpha * Z::from(&self.q), - ) - .unwrap(); - - // b^t = s^t * A + e^t - let vec_b_t = vec_s.transpose() * &mat_a + vec_e_t; - - // A = [A^t | b]^t - let mat_a = mat_a.concat_vertical(&vec_b_t).unwrap(); - - // pk = A, sk = s - (mat_a, vec_s) - } - - /// Generates an encryption of `message mod 2` for the provided public key by following these steps: - /// - x <- Z_2^m - /// - c = A * x + [0^{1 x n} | msg * ⌊q/2⌋]^t - /// - /// Then, the ciphertext `c` is returned as a vector of type [`MatZq`]. - /// - /// Parameters: - /// - `pk`: specifies the public key `pk = A` - /// - `message`: specifies the message that should be encrypted - /// - /// Returns a cipher `c` of type [`MatZq`]. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, Regev}; - /// let regev = Regev::default(); - /// let (pk, sk) = regev.gen(); - /// - /// let cipher = regev.enc(&pk, 1); - /// ``` - fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - // generate message = message mod 2 - let message: Z = message.into() % 2; - - // x <- Z_2^m - let vec_x = MatZ::sample_uniform(&self.m, 1, 0, 2).unwrap(); - - // c = A * x + [0^{1xn} | msg * ⌊q/2⌋]^t - let mut c = pk * vec_x; - - // hide message in last entry - // compute msg * ⌊q/2⌋ - let msg_q_half = message * Z::from(&self.q).div_floor(2); - // set last entry of c = last_entry + msg * ⌊q/2⌋ - let last_entry: Zq = c.get_entry(-1, 0).unwrap(); - c.set_entry(-1, 0, last_entry + msg_q_half).unwrap(); - - c - } - - /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: - /// - x = [-sk^t | 1] * c - /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. - /// - /// Parameters: - /// - `sk`: specifies the secret key `sk = s` - /// - `cipher`: specifies the cipher containing `cipher = c` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, Regev}; - /// use qfall_math::integer::Z; - /// let regev = Regev::default(); - /// let (pk, sk) = regev.gen(); - /// let cipher = regev.enc(&pk, 1); - /// - /// let m = regev.dec(&sk, &cipher); - /// - /// assert_eq!(Z::ONE, m); - /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { - let result = (Z::MINUS_ONE * sk) - .concat_vertical(&MatZq::identity(1, 1, &self.q)) - .unwrap() - .dot_product(cipher) - .unwrap(); - let result: Z = result.get_representative_least_absolute_residue().abs(); - - let q_half = Z::from(&self.q).div_floor(2); - - if result.distance(Z::ZERO) > result.distance(q_half) { - Z::ONE - } else { - Z::ZERO - } - } -} - -// adds generic multi-bit encryption to this scheme -impl GenericMultiBitEncryption for Regev {} - -#[cfg(test)] -mod test_pp_generation { - use super::Regev; - use super::Z; - - /// Checks whether `new` is available for types implementing [`Into`]. - #[test] - fn new_availability() { - let _ = Regev::new(2u8, 2u16, 2u32, 2u64); - let _ = Regev::new(2u16, 2u64, 2i32, 2i64); - let _ = Regev::new(2i16, 2i64, 2u32, 2u8); - let _ = Regev::new(Z::from(2), Z::from(2), 2u8, 2i8); - } - - /// Checks whether `new_from_n` works properly for different choices of n. - #[test] - fn suitable_security_params() { - let n_choices = [ - 10, 11, 12, 13, 14, 25, 50, 100, 250, 500, 1000, 2500, 5000, 5001, 10000, - ]; - - for n in n_choices { - let _ = Regev::new_from_n(n); - } - } - - /// Checks whether the [`Default`] parameter choice is suitable. - #[test] - fn default_suitable() { - let regev = Regev::default(); - - assert!(regev.check_correctness().is_ok()); - assert!(regev.check_security().is_ok()); - } - - /// Checks whether the generated public parameters from `new_from_n` are - /// valid choices according to security and correctness of the scheme. - #[test] - fn choice_valid() { - let n_choices = [10, 14, 25, 50, 125, 300, 600, 1200, 4000, 6000]; - - for n in n_choices { - let regev = Regev::new_from_n(n); - assert!(regev.check_correctness().is_ok()); - assert!(regev.check_security().is_ok()); - } - } - - /// Ensures that `new_from_n` is available for types implementing [`Into`]. - #[test] - #[allow(clippy::needless_borrows_for_generic_args)] - fn availability() { - let _ = Regev::new_from_n(10u8); - let _ = Regev::new_from_n(10u16); - let _ = Regev::new_from_n(10u32); - let _ = Regev::new_from_n(10u64); - let _ = Regev::new_from_n(10i8); - let _ = Regev::new_from_n(10i16); - let _ = Regev::new_from_n(10i32); - let _ = Regev::new_from_n(10i64); - let _ = Regev::new_from_n(Z::from(10)); - let _ = Regev::new_from_n(&Z::from(10)); - } - - /// Checks whether `new_from_n` returns an error for invalid input n. - #[test] - #[should_panic] - fn invalid_n() { - Regev::new_from_n(9); - } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let regev = Regev::secure128(); - - assert!(regev.check_correctness().is_ok()); - assert!(regev.check_security().is_ok()); - } -} - -#[cfg(test)] -mod test_regev { - use super::Regev; - use crate::construction::pk_encryption::PKEncryptionScheme; - use qfall_math::integer::Z; - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and small n. - #[test] - fn cycle_zero_small_n() { - let msg = Z::ZERO; - let regev = Regev::default(); - - let (pk, sk) = regev.gen(); - let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and small n. - #[test] - fn cycle_one_small_n() { - let msg = Z::ONE; - let regev = Regev::default(); - - let (pk, sk) = regev.gen(); - let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and larger n. - #[test] - fn cycle_zero_large_n() { - let msg = Z::ZERO; - let regev = Regev::new_from_n(50); - - let (pk, sk) = regev.gen(); - let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and larger n. - #[test] - fn cycle_one_large_n() { - let msg = Z::ONE; - let regev = Regev::new_from_n(50); - - let (pk, sk) = regev.gen(); - let cipher = regev.enc(&pk, &msg); - let m = regev.dec(&sk, &cipher); - assert_eq!(msg, m); - } - - /// Checks that modulus 2 is applied correctly. - #[test] - fn modulus_application() { - let messages = [2, 3, i64::MAX, i64::MIN]; - let regev = Regev::default(); - let (pk, sk) = regev.gen(); - - for msg in messages { - let msg_mod = Z::from(msg.rem_euclid(2)); - - let cipher = regev.enc(&pk, msg); - let m = regev.dec(&sk, &cipher); - - assert_eq!(msg_mod, m); - } - } -} - -#[cfg(test)] -mod test_multi_bits { - use super::{GenericMultiBitEncryption, PKEncryptionScheme, Regev}; - use qfall_math::integer::Z; - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large positive values. - #[test] - fn positive() { - let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; - - for value in values { - let msg = Z::from(value); - let scheme = Regev::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for zero. - #[test] - fn zero() { - let msg = Z::ZERO; - let scheme = Regev::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large negative values, which are not encrypted itself, - /// but their absolute value. - #[test] - fn negative() { - let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; - - for value in values { - let msg = Z::from(value); - let scheme = Regev::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg.abs(), m); - } - } -} diff --git a/src/construction/pk_encryption/regev_discrete_gauss.rs b/src/construction/pk_encryption/regev_discrete_gauss.rs deleted file mode 100644 index f4142ea..0000000 --- a/src/construction/pk_encryption/regev_discrete_gauss.rs +++ /dev/null @@ -1,692 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains an implementation of the IND-CPA secure -//! public key Regev encryption scheme with an instantiation of the regularity lemma -//! via a discrete Gaussian distribution. - -use super::{GenericMultiBitEncryption, PKEncryptionScheme}; -use qfall_math::{ - error::MathError, - integer::Z, - integer_mod_q::{MatZq, Modulus, Zq}, - rational::Q, - traits::{Distance, Pow}, -}; -use serde::{Deserialize, Serialize}; - -/// This struct manages and stores the public parameters of a [`RegevWithDiscreteGaussianRegularity`] -/// public key encryption instance. -/// -/// Attributes: -/// - `n`: specifies the security parameter, which is not equal to the bit-security level -/// - `m`: defines the dimension of the underlying lattice -/// - `q`: specifies the modulus over which the encryption is computed -/// - `r`: specifies the Gaussian parameter used for SampleD, -/// i.e. used for encryption -/// - `alpha`: specifies the Gaussian parameter used for independent -/// sampling from the discrete Gaussian distribution -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::pk_encryption::{RegevWithDiscreteGaussianRegularity, PKEncryptionScheme}; -/// use qfall_math::integer::Z; -/// // setup public parameters and key pair -/// let regev = RegevWithDiscreteGaussianRegularity::default(); -/// let (pk, sk) = regev.gen(); -/// -/// // encrypt a bit -/// let msg = Z::ZERO; // must be a bit, i.e. msg = 0 or 1 -/// let cipher = regev.enc(&pk, &msg); -/// -/// // decrypt -/// let m = regev.dec(&sk, &cipher); -/// -/// assert_eq!(msg, m); -/// ``` -#[derive(Debug, Serialize, Deserialize)] -pub struct RegevWithDiscreteGaussianRegularity { - n: Z, // security parameter - m: Z, // number of rows of matrix A - q: Modulus, // modulus - r: Q, // Gaussian parameter for sampleD - alpha: Q, // Gaussian parameter for sampleZ -} - -impl RegevWithDiscreteGaussianRegularity { - /// Instantiates a [`RegevWithDiscreteGaussianRegularity`] PK encryption instance with the - /// specified parameters. - /// - /// **WARNING:** The given parameters are not checked for security nor - /// correctness of the scheme. - /// If you want to check your parameters for provable security and correctness, - /// use [`RegevWithDiscreteGaussianRegularity::check_correctness`] and [`RegevWithDiscreteGaussianRegularity::check_security`]. - /// Or use [`RegevWithDiscreteGaussianRegularity::new_from_n`] for generating secure and correct - /// public parameters for [`RegevWithDiscreteGaussianRegularity`] according to your choice of `n`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - `m`: specifies the number of columns of matrix `A` - /// - `q`: specifies the modulus - /// - `r`: specifies the Gaussian parameter used for SampleD, - /// i.e. used for encryption - /// - `alpha`: specifies the Gaussian parameter used for independent - /// sampling from the discrete Gaussian distribution - /// - /// Returns a [`RegevWithDiscreteGaussianRegularity`] PK encryption instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RegevWithDiscreteGaussianRegularity; - /// - /// let regev = RegevWithDiscreteGaussianRegularity::new(2, 16, 443, 4, 0.15625); - /// ``` - /// - /// # Panics ... - /// - if the given modulus `q <= 1`. - pub fn new( - n: impl Into, - m: impl Into, - q: impl Into, - r: impl Into, - alpha: impl Into, - ) -> Self { - let n: Z = n.into(); - let m: Z = m.into(); - let q: Modulus = q.into(); - let r: Q = r.into(); - let alpha: Q = alpha.into(); - - Self { n, m, q, r, alpha } - } - - /// Generates a new [`RegevWithDiscreteGaussianRegularity`] instance, i.e. a new set of suitable - /// (provably secure and correct) public parameters, - /// given the security parameter `n`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a correct and secure [`RegevWithDiscreteGaussianRegularity`] PK encryption instance or - /// a [`MathError`] if the given `n <= 1`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RegevWithDiscreteGaussianRegularity; - /// - /// let regev = RegevWithDiscreteGaussianRegularity::new_from_n(2); - /// ``` - /// - /// # Panics ... - /// - if `n <= 1`. - pub fn new_from_n(n: impl Into) -> Self { - let n = n.into(); - if n <= Z::ONE { - panic!("n must be chosen bigger than 1."); - } - - let mut m: Z; - let mut q: Modulus; - let mut r: Q; - let mut alpha: Q; - (m, q, r, alpha) = Self::gen_new_public_parameters(&n); - let mut out = Self { - n: n.clone(), - m, - q, - r, - alpha, - }; - while out.check_correctness().is_err() || out.check_security().is_err() { - (m, q, r, alpha) = Self::gen_new_public_parameters(&n); - out = Self { - n: n.clone(), - m, - q, - r, - alpha, - }; - } - - out - } - - /// Generates new public parameters, which must not be secure or correct - /// depending on the random choice of `q`. At least every fifth execution - /// of this function should output a valid set of public parameters, - /// ensuring a secure and correct PK encryption scheme. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a set of public parameters `(m, q, r, alpha)` chosen according to - /// the provided `n`. - /// - /// # Examples - /// ```compile_fail - /// use qfall_crypto::construction::pk_encryption::RegevWithDiscreteGaussianRegularity; - /// use qfall_math::integer::Z; - /// let n = Z::from(2); - /// - /// let (m, q, r, alpha) = RegevWithDiscreteGaussianRegularity::gen_new_public_parameters(&n); - /// ``` - fn gen_new_public_parameters(n: &Z) -> (Z, Modulus, Q, Q) { - let n_i64 = i64::try_from(n).unwrap(); - // these powers are chosen according to experience s.t. at least every - // fifth generation of public parameters outputs a valid pair - let power = match n_i64 { - 2 => 9, - 3 => 8, - 4..=5 => 7, - 6..=8 => 6, - 9..=12 => 5, - 13..=30 => 4, - _ => 3, - }; - - // generate prime q in [n^power / 2, n^power] - let upper_bound: Z = n.pow(power).unwrap(); - let lower_bound = upper_bound.div_ceil(2); - // prime used due to guide from GPV08 after Proposition 8.1 - // on how to choose appropriate parameters, but prime is not - // necessarily needed for this scheme to be correct or secure - let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); - - // choose m = 2 (n+1) lg q - let m = (Z::from(2) * (n + Z::ONE) * q.log(10).unwrap()).ceil(); - - // choose r = log m - let r = m.log(2).unwrap(); - - // alpha = 1/(sqrt(m) * log^2 m) - let alpha = 1 / (m.sqrt() * m.log(2).unwrap().pow(2).unwrap()); - - let q = Modulus::from(&q); - - (m, q, r, alpha) - } - - /// Checks the public parameters for correctness according to - /// Lemma 8.2 of [\[2\]](). - /// - /// The required properties are: - /// - n >= 1 - /// - q >= 5 * r * m - /// - α <= 1/(r * sqrt(m) * ω(sqrt(log n)) - /// - /// Returns an empty result if the public parameters guarantee correctness - /// with overwhelming probability or a [`MathError`] if the instance would - /// not be correct. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RegevWithDiscreteGaussianRegularity; - /// let dr = RegevWithDiscreteGaussianRegularity::default(); - /// - /// let is_valid = dr.check_correctness().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// correct RegevWithDiscreteGaussianRegularity public key encryption instance. - pub fn check_correctness(&self) -> Result<(), MathError> { - let q: Z = Z::from(&self.q); - - if self.n <= Z::ONE { - return Err(MathError::InvalidIntegerInput(String::from( - "n must be chosen bigger than 1.", - ))); - } - - // Correctness requirements - // q >= 5 * r * m - if q < 5 * &self.r * &self.m { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as q < 5rm, but q >= 5rm is required.", - ))); - } - // α <= 1/(r * sqrt(m) * ω(sqrt(log n)) - if self.alpha > 1 / (&self.r * self.m.sqrt() * self.n.log(2).unwrap().sqrt()) { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α > 1/(r*sqrt(m)*ω(sqrt(log n)), but α <= 1/(r*sqrt(m)*ω(sqrt(log n)) is required.", - ))); - } - - Ok(()) - } - - /// Checks the public parameters for security according to - /// Lemma 8.4 of [\[2\]](). - /// - /// The required properties are: - /// - q * α >= n - /// - m >= 2(n + 1) lg (q) - /// - r >= ω( sqrt( log m ) ) - /// - /// Returns an empty result if the public parameters guarantee security w.r.t. `n` - /// or a [`MathError`] if the instance would not be secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RegevWithDiscreteGaussianRegularity; - /// let dr = RegevWithDiscreteGaussianRegularity::default(); - /// - /// let is_valid = dr.check_security().is_ok(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// secure RegevWithDiscreteGaussianRegularity public key encryption instance. - pub fn check_security(&self) -> Result<(), MathError> { - let q: Z = Z::from(&self.q); - - // Security requirements - // q * α >= n - if &q * &self.alpha < self.n { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as q * α < n, but q * α >= n is required.", - ))); - } - // m >= 2(n + 1) lg (q) - if self.m < 2 * (&self.n + 1) * q.log(10).unwrap() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as m < 2(n + 1) lg (q), but m >= 2(n + 1) lg (q) is required.", - ))); - } - // r >= ω( sqrt( log m ) ) - if self.r < self.m.log(2).unwrap().sqrt() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as r < sqrt( log m ) and r >= ω(sqrt(log m)) is required." - ))); - } - - Ok(()) - } - - /// This function instantiates a 128-bit secure [`RegevWithDiscreteGaussianRegularity`] scheme. - /// - /// The public parameters used for this scheme were generated via `RegevWithDiscreteGaussianRegularity::new_from_n(350)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(350, 5248, 29892991, 12.357, 0.00009) - } -} - -impl Default for RegevWithDiscreteGaussianRegularity { - /// Initializes a [`RegevWithDiscreteGaussianRegularity`] struct with parameters generated by `RegevWithDiscreteGaussianRegularity::new_from_n(2)`. - /// This parameter choice is not secure as the dimension of the lattice is too small, - /// but it provides an efficient working example. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RegevWithDiscreteGaussianRegularity; - /// - /// let regev = RegevWithDiscreteGaussianRegularity::default(); - /// ``` - fn default() -> Self { - let n = Z::from(2); - let m = Z::from(16); - let q = Modulus::from(443); - let r = Q::from(4); - let alpha = Q::from((1, 64)); - - Self { n, m, q, r, alpha } - } -} - -impl PKEncryptionScheme for RegevWithDiscreteGaussianRegularity { - type Cipher = (MatZq, Zq); - type PublicKey = (MatZq, MatZq); - type SecretKey = MatZq; - - /// Generates a (pk, sk) pair for the Regev public key encryption scheme - /// by following these steps: - /// - s <- Z_q^n - /// - A <- Z_q^{n x m} - /// - x <- χ^m - /// - p = A^t * s + x - /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. - /// - /// Then, `pk = (A, p)` and `sk = s` are returned. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; - /// let regev = RegevWithDiscreteGaussianRegularity::default(); - /// - /// let (pk, sk) = regev.gen(); - /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { - // s <- Z_q^n - let vec_s = MatZq::sample_uniform(&self.n, 1, &self.q); - - // A <- Z_q^{n x m} - let mat_a = MatZq::sample_uniform(&self.n, &self.m, &self.q); - // x <- χ^m - let vec_x = MatZq::sample_discrete_gauss( - &self.m, - 1, - &self.q, - &self.n, - 0, - &(&self.alpha * Z::from(&self.q)), - ) - .unwrap(); - // p = A^t * s + x - let vec_p = mat_a.transpose() * &vec_s + vec_x; - - // pk = (A, p), sk = s - ((mat_a, vec_p), vec_s) - } - - /// Generates an encryption of `message mod 2` for the provided public key by following these steps: - /// e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r - /// - u = A * e - /// - c = p^t * e + message * ⌊q/2⌋ - /// - /// Then, `cipher = (u, c)` is returned. - /// - /// Parameters: - /// - `pk`: specifies the public key, which contains two matrices `pk = (A, p)` - /// - `message`: specifies the message that should be encrypted - /// - /// Returns a cipher of the form `cipher = (u, c)` for [`MatZq`] `u` and [`Zq`] `c`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; - /// let regev = RegevWithDiscreteGaussianRegularity::default(); - /// let (pk, sk) = regev.gen(); - /// - /// let cipher = regev.enc(&pk, 1); - /// ``` - fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - // generate message = message mod 2 - let message: Z = message.into() % 2; - - // e <- SampleD over lattice Z^m, center 0 with Gaussian parameter r - let vec_e = MatZq::sample_d_common(&self.m, &self.q, &self.n, &self.r).unwrap(); - - // u = A * e - let vec_u = &pk.0 * &vec_e; - // c = p^t * e + msg * ⌊q/2⌋ - let q_half = Z::from(&self.q).div_floor(2); - let c = pk.1.dot_product(&vec_e).unwrap() + message * q_half; - - (vec_u, c) - } - - /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: - /// - x = c - s^t * u - /// - if x mod q is closer to ⌊q/2⌋ than to 0, output 1. Otherwise, output 0. - /// - /// Parameters: - /// - `sk`: specifies the secret key `sk = s` - /// - `cipher`: specifies the cipher containing `cipher = (u, c)` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, RegevWithDiscreteGaussianRegularity}; - /// use qfall_math::integer::Z; - /// let regev = RegevWithDiscreteGaussianRegularity::default(); - /// let (pk, sk) = regev.gen(); - /// let cipher = regev.enc(&pk, 1); - /// - /// let m = regev.dec(&sk, &cipher); - /// - /// assert_eq!(Z::ONE, m); - /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { - let result = &cipher.1 - sk.dot_product(&cipher.0).unwrap(); - let result: Z = result.get_representative_least_absolute_residue().abs(); - - let q_half = Z::from(&self.q).div_floor(2); - - if result.distance(Z::ZERO) > result.distance(q_half) { - Z::ONE - } else { - Z::ZERO - } - } -} - -// adds generic multi-bit encryption to this scheme -impl GenericMultiBitEncryption for RegevWithDiscreteGaussianRegularity {} - -#[cfg(test)] -mod test_pp_generation { - use super::RegevWithDiscreteGaussianRegularity; - use super::Z; - - /// Checks whether `new` is available for types implementing [`Into`]. - #[test] - fn new_availability() { - let _ = RegevWithDiscreteGaussianRegularity::new(2u8, 2u16, 2u32, 2u64, 2i8); - let _ = RegevWithDiscreteGaussianRegularity::new(2u16, 2u64, 2i32, 2i64, 2i16); - let _ = RegevWithDiscreteGaussianRegularity::new(2i16, 2i64, 2u32, 2u8, 2u16); - let _ = RegevWithDiscreteGaussianRegularity::new(Z::from(2), Z::from(2), 2u8, 2i8, 2u32); - } - - /// Checks whether `new_from_n` works properly for different choices of n. - #[test] - fn suitable_security_params() { - let n_choices = [ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 5001, - 10000, - ]; - - for n in n_choices { - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(n); - } - } - - /// Checks whether the [`Default`] parameter choice is suitable. - #[test] - fn default_suitable() { - let dr = RegevWithDiscreteGaussianRegularity::default(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } - - /// Checks whether the generated public parameters from `new_from_n` are - /// valid choices according to security and correctness of the scheme. - #[test] - fn choice_valid() { - let n_choices = [ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 5001, - 10000, - ]; - - for n in n_choices { - let dr = RegevWithDiscreteGaussianRegularity::new_from_n(n); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } - } - - /// Ensures that `new_from_n` is available for types implementing [`Into`]. - #[test] - #[allow(clippy::needless_borrows_for_generic_args)] - fn new_from_n_availability() { - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2u8); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2u16); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2u32); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2u64); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2i8); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2i16); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2i32); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(2i64); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(Z::from(2)); - let _ = RegevWithDiscreteGaussianRegularity::new_from_n(&Z::from(2)); - } - - /// Checks whether `new_from_n` returns an error for invalid input n. - #[test] - #[should_panic] - fn invalid_n() { - RegevWithDiscreteGaussianRegularity::new_from_n(1); - } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let dr = RegevWithDiscreteGaussianRegularity::secure128(); - - assert!(dr.check_correctness().is_ok()); - assert!(dr.check_security().is_ok()); - } -} - -#[cfg(test)] -mod test_regev { - use super::RegevWithDiscreteGaussianRegularity; - use crate::construction::pk_encryption::PKEncryptionScheme; - use qfall_math::integer::Z; - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and small n. - #[test] - fn cycle_zero_small_n() { - let msg = Z::ZERO; - let dr = RegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and small n. - #[test] - fn cycle_one_small_n() { - let msg = Z::ONE; - let dr = RegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 0 and larger n. - #[test] - fn cycle_zero_large_n() { - let msg = Z::ZERO; - let dr = RegevWithDiscreteGaussianRegularity::new_from_n(30); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for message 1 and larger n. - #[test] - fn cycle_one_large_n() { - let msg = Z::ONE; - let dr = RegevWithDiscreteGaussianRegularity::new_from_n(30); - - let (pk, sk) = dr.gen(); - let cipher = dr.enc(&pk, &msg); - let m = dr.dec(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks that modulus 2 is applied correctly. - #[test] - fn modulus_application() { - let messages = [2, 3, i64::MAX, i64::MIN]; - let regev = RegevWithDiscreteGaussianRegularity::default(); - let (pk, sk) = regev.gen(); - - for msg in messages { - let msg_mod = Z::from(msg.rem_euclid(2)); - - let cipher = regev.enc(&pk, msg); - let m = regev.dec(&sk, &cipher); - - assert_eq!(msg_mod, m); - } - } -} - -#[cfg(test)] -mod test_multi_bits { - use super::{ - GenericMultiBitEncryption, PKEncryptionScheme, RegevWithDiscreteGaussianRegularity, - }; - use qfall_math::integer::Z; - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large positive values. - #[test] - fn positive() { - let values = [3, 13, 23, 230, 501, 1024, i64::MAX]; - - for value in values { - let msg = Z::from(value); - let scheme = RegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for zero. - #[test] - fn zero() { - let msg = Z::ZERO; - let scheme = RegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg, m); - } - - /// Checks whether the multi-bit encryption cycle works properly - /// for small and large negative values, which are not encrypted itself, - /// but their absolute value. - #[test] - fn negative() { - let values = [-3, -13, -23, -230, -501, -1024, i64::MIN]; - - for value in values { - let msg = Z::from(value); - let scheme = RegevWithDiscreteGaussianRegularity::default(); - - let (pk, sk) = scheme.gen(); - let cipher = scheme.enc_multiple_bits(&pk, &msg); - let m = scheme.dec_multiple_bits(&sk, &cipher); - - assert_eq!(msg.abs(), m); - } - } -} diff --git a/src/construction/pk_encryption/ring_lpr.rs b/src/construction/pk_encryption/ring_lpr.rs deleted file mode 100644 index 1ff38ee..0000000 --- a/src/construction/pk_encryption/ring_lpr.rs +++ /dev/null @@ -1,645 +0,0 @@ -// Copyright © 2023 Niklas Siemer -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module contains an implementation of the IND-CPA secure -//! public key Ring-LPR encryption scheme. - -use super::PKEncryptionScheme; -use crate::utils::{ - common_encodings::{ - decode_z_bitwise_from_polynomialringzq, encode_z_bitwise_in_polynomialringzq, - }, - common_moduli::new_anticyclic, -}; -use qfall_math::{ - error::MathError, - integer::Z, - integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolynomialRingZq}, - rational::Q, - traits::Pow, -}; -use serde::{Deserialize, Serialize}; - -/// This struct manages and stores the public parameters of a [`RingLPR`] -/// public key encryption instance. -/// -/// This encryption scheme is implemented according to the description in [\[1\]](). -/// -/// Attributes: -/// - `n`: specifies the security parameter, which is not equal to the bit-security level -/// - `q`: specifies the modulus over which the encryption is computed -/// - `alpha`: specifies the Gaussian parameter used for independent -/// sampling from the discrete Gaussian distribution -/// -/// # Examples -/// ``` -/// use qfall_crypto::construction::pk_encryption::{RingLPR, PKEncryptionScheme}; -/// use qfall_math::integer::Z; -/// // setup public parameters and key pair -/// let lpr = RingLPR::default(); -/// let (pk, sk) = lpr.gen(); -/// -/// // encrypt a bit -/// let msg = Z::from(15); // must be at most n bits, i.e. for default 2^16 - 1 -/// let cipher = lpr.enc(&pk, &msg); -/// -/// // decrypt -/// let m = lpr.dec(&sk, &cipher); -/// -/// assert_eq!(msg, m); -/// ``` -#[derive(Debug, Serialize, Deserialize)] -pub struct RingLPR { - n: Z, // security parameter - q: ModulusPolynomialRingZq, // modulus - alpha: Q, // Gaussian parameter for sampleZ -} - -impl RingLPR { - /// Instantiates a [`RingLPR`] PK encryption instance with the - /// specified parameters. - /// - /// **WARNING:** The given parameters are not checked for security nor - /// correctness of the scheme. - /// If you want to check your parameters for provable security and correctness, - /// use [`RingLPR::check_correctness`] and [`RingLPR::check_security`]. - /// Or use [`RingLPR::new_from_n`] for generating secure and correct - /// public parameters for [`RingLPR`] according to your choice of `n`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - `q`: specifies the modulus - /// - `alpha`: specifies the Gaussian parameter used for independent - /// sampling from the discrete Gaussian distribution - /// - /// Returns a correct and secure [`RingLPR`] PK encryption instance or - /// a [`MathError`] if the instance would not be correct or secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RingLPR; - /// - /// let lpr = RingLPR::new(3, 13, 2); - /// ``` - /// - /// # Panics ... - /// - if the given modulus `q <= 1`. - /// - if `n < 0`. - pub fn new(n: impl Into, q: impl Into, alpha: impl Into) -> Self { - let n: Z = n.into(); - - // mod = (X^n + 1) mod q - let q = new_anticyclic(&n, q).unwrap(); - - let alpha: Q = alpha.into(); - - Self { n, q, alpha } - } - - /// Generates a new [`RingLPR`] instance, i.e. a new set of suitable - /// (provably secure and correct) public parameters, - /// given the security parameter `n` for `n >= 10`. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a correct and secure [`RingLPR`] PK encryption instance or - /// a [`MathError`] if the given `n < 10`. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RingLPR; - /// - /// let lpr = RingLPR::new_from_n(16); - /// ``` - /// - /// Panics... - /// - if `n < 10` - /// - if `n` does not fit into an [`i64`]. - pub fn new_from_n(n: impl Into) -> Self { - let n = n.into(); - assert!( - n >= 10, - "Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise." - ); - - let (mut q, mut alpha) = Self::gen_new_public_parameters(&n); - let mut out = Self { - n: n.clone(), - q, - alpha, - }; - while out.check_correctness().is_err() || out.check_security().is_err() { - (q, alpha) = Self::gen_new_public_parameters(&n); - out = Self { - n: n.clone(), - q, - alpha, - }; - } - - out - } - - /// Generates new public parameters, which must not be secure or correct - /// depending on the random choice of `q`. At least every fifth execution - /// of this function should output a valid set of public parameters, - /// ensuring a secure and correct PK encryption scheme. - /// - /// Parameters: - /// - `n`: specifies the security parameter and number of rows - /// of the uniform at random instantiated matrix `A` - /// - /// Returns a set of public parameters `(q, alpha)` chosen according to - /// the provided `n`. - /// - /// # Examples - /// ```compile_fail - /// use qfall_crypto::construction::pk_encryption::RingLPR; - /// use qfall_math::integer::Z; - /// let n = Z::from(2); - /// - /// let (q, alpha) = RingLPR::gen_new_public_parameters(&n); - /// ``` - /// - /// Panics... - /// - if `n` does not fit into an [`i64`]. - fn gen_new_public_parameters(n: &Z) -> (ModulusPolynomialRingZq, Q) { - let n_i64 = i64::try_from(n).unwrap(); - - // generate prime q in [n^3 / 2, n^3] - let upper_bound: Z = n.pow(3).unwrap(); - let lower_bound = upper_bound.div_ceil(2); - // prime used due to guide from GPV08 after Proposition 8.1 - // on how to choose appropriate parameters, but prime is not - // necessarily needed for this scheme to be correct or secure - let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap(); - - // Found out by experience as the bound is not tight enough to ensure correctness for large n. - // Hence, a small factor roughly of max(log n - 4, 1) has to be applied. - // Checked for 100 parameter sets with 10 cycles each and no mismatching decryptions occurred. - let factor = match n_i64 { - 1..=20 => 1, - 21..=40 => 2, - 41..=80 => 3, - 81..=160 => 4, - _ => 5, - }; - // α = 1/(sqrt(n) * log^2 n) - let alpha = 1 / (factor * n.sqrt() * n.log(2).unwrap().pow(3).unwrap()); - - // mod = (X^n + 1) mod q - let q = new_anticyclic(n, q).unwrap(); - - (q, alpha) - } - - /// Checks the public parameters for - /// correctness according to Lemma 3.1 of [\[4\]](). - /// - /// The required properties are: - /// - α = o (1 / (sqrt(n) * log^3 n)) - /// - n = 2^d for some d ∈ N_0 - /// - /// **WARNING**: This bound is not tight. Hence, we added a small factor - /// loosely corresponding to max(log n - 4, 1) below to ensure correctness - /// with overwhelming probability. - /// - /// Returns an empty result if the public parameters guarantee correctness - /// with overwhelming probability or a [`MathError`] if the instance would - /// not be correct. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RingLPR; - /// let lpr = RingLPR::default(); - /// - /// assert!(lpr.check_correctness().is_ok()); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// correct RingLPR public key encryption instance. - /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError) - /// if the value does not fit into an [`i64`]. - pub fn check_correctness(&self) -> Result<(), MathError> { - let n_i64 = i64::try_from(&self.n)?; - - if self.n <= Z::ONE { - return Err(MathError::InvalidIntegerInput(String::from( - "n must be chosen bigger than 1.", - ))); - } - - // ensure n = 2^d for some d ∈ N_0 - let result = self.n.is_perfect_power(); - let err_msg = String::from( - "n is not a perfect power of 2, \ - which is required for the correctness of this scheme.", - ); - if let Some((root, _)) = result { - if root != 2 { - return Err(MathError::InvalidIntegerInput(err_msg)); - } - } else { - return Err(MathError::InvalidIntegerInput(err_msg)); - } - - // Found out by experience as the bound is not tight enough to ensure correctness for large n. - // Hence, a small factor roughly of max(log n - 4, 1) has to be applied. - // Checked for 100 parameter sets with 10 cycles each and no mismatching decryptions occurred. - let factor = match n_i64 { - 1..=20 => 1, - 21..=40 => 2, - 41..=80 => 3, - 81..=160 => 4, - _ => 5, - }; - // α = o (1 / sqrt(n) * log^3 n )) - if self.alpha > 1 / (factor * self.n.sqrt() * self.n.log(2).unwrap().pow(3).unwrap()) { - return Err(MathError::InvalidIntegerInput(String::from( - "Correctness is not guaranteed as α >= 1 / (sqrt(n) * log^3 n), \ - but α < 1 / (sqrt(n) * log^3 n) is required. Please check the documentation!", - ))); - } - - Ok(()) - } - - /// Checks the public parameters for security according to Section 2.2 - /// and Lemma 3.2 of [\[4\]](). - /// - /// The required properties are: - /// - q * α >= 2 sqrt(n) - /// - /// Returns an empty result if the public parameters guarantee security - /// w.r.t. `n` or a [`MathError`] if the instance would - /// not be secure. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RingLPR; - /// let lpr = RingLPR::default(); - /// - /// assert!(lpr.check_security().is_ok()); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput) - /// if at least one parameter was not chosen appropriately for a - /// secure RingLPR public key encryption instance. - pub fn check_security(&self) -> Result<(), MathError> { - let q = Z::from(&self.q.get_q()); - - // Security requirements - // q * α >= 2 sqrt(n) - if &q * &self.alpha < 2 * self.n.sqrt() { - return Err(MathError::InvalidIntegerInput(String::from( - "Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.", - ))); - } - - Ok(()) - } - - /// This function instantiates a 128-bit secure [`RingLPR`] scheme. - /// - /// The public parameters used for this scheme were generated via `RingLPR::new_from_n(512)` - /// and its bit-security determined via the [lattice estimator](https://github.com/malb/lattice-estimator). - pub fn secure128() -> Self { - Self::new(512, 92897729, 0.000005) - } -} - -impl Default for RingLPR { - /// Initializes a [`RingLPR`] struct with parameters generated by `RingLPR::new_from_n(3)`. - /// This parameter choice is not secure as the dimension of the lattice is too small, - /// but it provides an efficient working example. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::RingLPR; - /// - /// let lpr = RingLPR::default(); - /// ``` - fn default() -> Self { - Self::new(16, 2399, 0.0039) - } -} - -impl PKEncryptionScheme for RingLPR { - type Cipher = (PolynomialRingZq, PolynomialRingZq); - type PublicKey = (PolynomialRingZq, PolynomialRingZq); - type SecretKey = PolynomialRingZq; - - /// Generates a (pk, sk) pair for the RingLPR public key encryption scheme - /// by following these steps: - /// - a <- R_q - /// - s <- χ - /// - e <- χ - /// - b = s * a + e - /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. - /// - /// Then, `pk = (a, b)` and `sk = s` are returned. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, RingLPR}; - /// let lpr = RingLPR::default(); - /// - /// let (pk, sk) = lpr.gen(); - /// ``` - fn gen(&self) -> (Self::PublicKey, Self::SecretKey) { - // a <- R_q - let a = PolynomialRingZq::sample_uniform(&self.q); - // s <- χ - let s = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); - // e <- χ - let e = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); - - // b = s * a + e - let b = &a * &s + e; - - // pk = (a, b), sk = s - ((a, b), s) - } - - /// Generates an encryption of `message mod 2^n` for the provided public key by following these steps: - /// - r <- χ - /// - e1 <- χ - /// - e2 <- χ - /// - u = a * r + e1 - /// - v = b * r + e2 + mu * q/2 - /// - c = (u, v) - /// where χ is discrete Gaussian distributed with center 0 and Gaussian parameter q * α. - /// - /// Then, cipher `c = (u, v)` as a polynomial of type [`PolynomialRingZq`] is returned. - /// - /// Parameters: - /// - `pk`: specifies the public key `pk = (a, b)` - /// - `message`: specifies the message that should be encrypted - /// - /// Returns a cipher `c = (u, v)` of types [`PolynomialRingZq`]. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, RingLPR}; - /// let lpr = RingLPR::default(); - /// let (pk, sk) = lpr.gen(); - /// - /// let cipher = lpr.enc(&pk, 15); - /// ``` - fn enc(&self, pk: &Self::PublicKey, message: impl Into) -> Self::Cipher { - // ensure mu has at most n bits - let message: Z = message.into().abs(); - let mu = message % Z::from(2).pow(&self.n).unwrap(); - // set mu_q_half to polynomial with n {0,1} coefficients - let mu_q_half = encode_z_bitwise_in_polynomialringzq(&self.q, &mu); - - // r <- χ - let r = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); - // e1 <- χ - let e1 = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); - // e2 <- χ - let e2 = PolynomialRingZq::sample_discrete_gauss( - &self.q, - &self.n, - 0, - &self.alpha * &self.q.get_q(), - ) - .unwrap(); - - // u = a * r + e1 - let u = &pk.0 * &r + e1; - // v = b * r + e2 + mu * q/2 - let v = &pk.1 * &r + e2 + mu_q_half; - - // c = (u, v) - (u, v) - } - - /// Decrypts the provided `cipher` using the secret key `sk` by following these steps: - /// - v - s * u - /// - result = 0 - /// - for each coefficient of v - s * u: - /// - check whether the coefficient mod q is closer to ⌊q/2⌋ than to 0. - /// If so, add 2^coefficient to result. - /// - return result - /// - /// Parameters: - /// - `sk`: specifies the secret key `sk = s` - /// - `cipher`: specifies the cipher containing `cipher = (u, v)` - /// - /// Returns the decryption of `cipher` as a [`Z`] instance. - /// - /// # Examples - /// ``` - /// use qfall_crypto::construction::pk_encryption::{PKEncryptionScheme, RingLPR}; - /// use qfall_math::integer::Z; - /// let lpr = RingLPR::default(); - /// let (pk, sk) = lpr.gen(); - /// let cipher = lpr.enc(&pk, 212); - /// - /// let m = lpr.dec(&sk, &cipher); - /// - /// assert_eq!(Z::from(212), m); - /// ``` - fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z { - // res = v - s * u - let result = &cipher.1 - sk * &cipher.0; - - decode_z_bitwise_from_polynomialringzq(self.q.get_q(), &result) - } -} - -#[cfg(test)] -mod test_pp_generation { - use super::RingLPR; - use super::Z; - - /// Checks whether `new` is available for types implementing [`Into`]. - #[test] - fn new_availability() { - let _ = RingLPR::new(2u8, 2u32, 2u64); - let _ = RingLPR::new(2u16, 2i32, 2i64); - let _ = RingLPR::new(2i16, 2u32, 2u8); - let _ = RingLPR::new(Z::from(2), 2u8, 2i8); - } - - /// Checks whether `new_from_n` works properly for different choices of n. - #[test] - fn suitable_security_params() { - let n_choices = [16, 32, 64, 128, 256, 512, 1024]; - - for n in n_choices { - let _ = RingLPR::new_from_n(n); - } - } - - /// Checks whether the [`Default`] parameter choice is suitable. - #[test] - fn default_suitable() { - let scheme = RingLPR::default(); - - assert!(scheme.check_correctness().is_ok()); - assert!(scheme.check_security().is_ok()); - } - - /// Checks whether the generated public parameters from `new_from_n` are - /// valid choices according to security and correctness of the scheme. - #[test] - fn choice_valid() { - let n_choices = [16, 32, 64, 128, 256, 512, 1024]; - - for n in n_choices { - let scheme = RingLPR::new_from_n(n); - - assert!(scheme.check_correctness().is_ok()); - assert!(scheme.check_security().is_ok()); - } - } - - /// Ensure that `n` chosen as a non-power of two does not result in a provably - /// correct scheme. - #[test] - fn non_power_of_2_n() { - let scheme = RingLPR::new(7, 17, 0.01); - - assert!(scheme.check_correctness().is_err()) - } - - /// Ensures that `new_from_n` is available for types implementing [`Into`]. - #[test] - #[allow(clippy::needless_borrows_for_generic_args)] - fn availability() { - let _ = RingLPR::new_from_n(16u8); - let _ = RingLPR::new_from_n(16u16); - let _ = RingLPR::new_from_n(16u32); - let _ = RingLPR::new_from_n(16u64); - let _ = RingLPR::new_from_n(16i8); - let _ = RingLPR::new_from_n(16i16); - let _ = RingLPR::new_from_n(16i32); - let _ = RingLPR::new_from_n(16i64); - let _ = RingLPR::new_from_n(Z::from(16)); - let _ = RingLPR::new_from_n(&Z::from(16)); - } - - /// Checks whether `new_from_n` returns an error for invalid input n. - #[test] - #[should_panic] - fn invalid_n() { - RingLPR::new_from_n(9); - } - - /// Checks whether `secure128` outputs a new instance with correct and secure - /// parameters. - #[test] - fn secure128_validity() { - let scheme = RingLPR::secure128(); - - assert!(scheme.check_correctness().is_ok()); - assert!(scheme.check_security().is_ok()); - } -} - -#[cfg(test)] -mod test_ring_lpr { - use super::RingLPR; - use crate::construction::pk_encryption::PKEncryptionScheme; - use qfall_math::integer::Z; - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for several messages and small n. - #[test] - fn cycle_small_n() { - let scheme = RingLPR::default(); - let (pk, sk) = scheme.gen(); - let messages = [0, 1, 2, 15, 70, 256, 580, 1000, 4000, 8000, 65535]; - - for message in messages { - let cipher = scheme.enc(&pk, message); - let m = scheme.dec(&sk, &cipher); - - assert_eq!(Z::from(message), m); - } - } - - /// Checks whether the full-cycle of gen, enc, dec works properly - /// for several messages and larger n. - #[test] - fn cycle_large_n() { - let scheme = RingLPR::new_from_n(64); - let (pk, sk) = scheme.gen(); - let messages = [ - 0, - 1, - 2, - 15, - 70, - 256, - 580, - 1_000, - 4_000, - 8_000, - 20_000, - 80_000, - 240_000, - 4_000_000, - 100_000_000, - ]; - - for message in messages { - let cipher = scheme.enc(&pk, message); - let m = scheme.dec(&sk, &cipher); - - assert_eq!(Z::from(message), m); - } - } - - /// Checks that modulus 2^n is applied correctly. - #[test] - fn modulus_application() { - let messages = [65536]; - let scheme = RingLPR::default(); - let (pk, sk) = scheme.gen(); - - for msg in messages { - let cipher = scheme.enc(&pk, msg); - let m = scheme.dec(&sk, &cipher); - - assert_eq!(Z::ZERO, m); - } - } -} diff --git a/src/construction/signature.rs b/src/construction/signature.rs deleted file mode 100644 index 6dcbb3a..0000000 --- a/src/construction/signature.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2023 Marcel Luca Schmidt, Marvin Beckmann -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This module provides the trait a struct should implement if it is an -//! instance of a signature scheme. Furthermore, it contains cryptographic signatures -//! implementing the [`SignatureScheme`] trait. -//! -//! - \[1\] Gentry, Craig, Chris Peikert, and Vinod Vaikuntanathan. -//! "Trapdoors for hard lattices and new cryptographic constructions." -//! Proceedings of the fortieth annual ACM symposium on Theory of computing. 2008. -//! - -pub mod fdh; -pub mod pfdh; - -/// This trait should be implemented by every signature scheme. -/// It captures the essential functionalities each signature scheme has to support. -/// -/// Note: The gen does not take in the parameter `1^n`, as this is a public parameter, -/// which shall be defined by the struct implementing this trait. -pub trait SignatureScheme { - /// The type of the secret key. - type SecretKey; - /// The type of the public key. - type PublicKey; - /// The type of the signature. - type Signature; - - /// Generates a public key and a secret key from the attributes the - /// struct has, which implements this trait. - /// - /// Returns the public key and the secret key. - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey); - - /// Signs a message using the secret key (and potentially the public key). - /// - /// Returns the resulting signature. - fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature; - - /// Verifies that a signature is valid for a message by using the public key. - /// - /// Returns the result of the verification as a boolean. - fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool; -} diff --git a/src/construction/signature/fdh.rs b/src/construction/signature/fdh.rs deleted file mode 100644 index 7f40e39..0000000 --- a/src/construction/signature/fdh.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © 2023 Marvin Beckmann -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This Module contains a implementations of the full domain hash signature scheme, -//! which only has to be instantiated with a corresponding PSF, a storage and -//! a corresponding hash function. -//! -//! The constructions follow the general definition of a hash-then-sign signature scheme -//! that uses a hash function as in [\[1\]]() and a PSF. -//! This signature scheme uses a storage, so it is stateful. -//! -//! Requirements -//! - `psf`: The PSF which has to implement the [`PSF`](crate::primitive::psf::PSF) trait -//! and must also be (de-)serializable. -//! - `storage`: A Hashmap that safes all previously signed messages and their signature -//! - `hash`: The hash-function which has to map a string into the correct domain -//! -//! # Example -//! ## Signature Scheme from [`PSFGPV`](crate::primitive::psf::PSFGPV) -//! ``` -//! use qfall_crypto::construction::signature::{fdh::FDHGPV, SignatureScheme}; -//! -//! let mut fdh = FDHGPV::setup(4, 113, 17); -//! -//! let m = "Hello World!"; -//! -//! let (pk, sk) = fdh.gen(); -//! let sigma = fdh.sign(m.to_owned(), &sk, &pk); -//! -//! assert!(fdh.vfy(m.to_owned(), &sigma, &pk)); -//! ``` - -mod gpv; -mod gpv_ring; - -pub use gpv::FDHGPV; -pub use gpv_ring::FDHGPVRing; diff --git a/src/construction/signature/fdh/gpv.rs b/src/construction/signature/fdh/gpv.rs deleted file mode 100644 index c59914f..0000000 --- a/src/construction/signature/fdh/gpv.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright © 2023 Marvin Beckmann -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! A classical implementation of the FDH signature scheme using the [`PSFGPV`] -//! according to [\[1\]](<../index.html#:~:text=[1]>). - -use crate::{ - construction::{ - hash::{sha256::HashMatZq, HashInto}, - signature::SignatureScheme, - }, - primitive::psf::{PSF, PSFGPV}, - sample::g_trapdoor::gadget_parameters::GadgetParameters, -}; -use qfall_math::{ - integer::{MatZ, Z}, - integer_mod_q::{MatZq, Modulus}, - rational::{MatQ, Q}, -}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// Initializes an FDH signature scheme from a [`PSFGPV`]. -/// -/// This function corresponds to an implementation of an FDH-signature -/// scheme with the explicit PSF [`PSFGPV`] which is generated using -/// the default of [`GadgetParameters`]. -/// -/// Attributes: -/// - `psf`: Defines the PSF needed for preimage sampling. -/// - `storage`: Stores all previously constructed signatures. -/// - `hash`: Defines the hash function going from Strings to the PSFs range. -/// -/// Returns an explicit implementation of a FDH-signature scheme. -/// -/// # Example -/// ``` -/// use qfall_crypto::construction::signature::{fdh::FDHGPV, SignatureScheme}; -/// -/// let m = "Hello World!"; -/// -/// let mut fdh = FDHGPV::setup(4, 113, 17); -/// let (pk, sk) = fdh.gen(); -/// -/// let sigma = fdh.sign(m.to_string(), &sk, &pk); -/// -/// assert!(fdh.vfy(m.to_string(), &sigma, &pk)); -/// ``` -#[derive(Serialize, Deserialize)] -pub struct FDHGPV { - pub psf: PSFGPV, - pub storage: HashMap, - pub hash: HashMatZq, -} - -impl FDHGPV { - /// Initializes the [`FDHGPV`] with default parameters. - /// The setup function takes in the security parameter, the modulus, a length bound - /// for the signatures, and the length of randomness for this construction. - /// Then, the [`PSFGPV`] is instantiated with the default [`GadgetParameters`]. - /// This PSF with an additional storage and hash function are secured in the struct. - /// - /// Parameters: - /// - `n`: The security parameter - /// - `q`: The modulus used for the G-Trapdoors - /// - `s`: The Gaussian parameter with which is sampled - /// - /// Returns an explicit instantiation of a FDH signature scheme using the default - /// parameters. - /// - /// # Example - /// ``` - /// use qfall_crypto::construction::signature::{fdh::FDHGPV, SignatureScheme}; - /// - /// let mut fdh = FDHGPV::setup(4, 113, 17); - /// ``` - /// - /// # Panics ... - /// - if the security parameter n is not in [1, i64::MAX]. - /// - if `q <= 1`. - pub fn setup(n: impl Into, q: impl Into, s: impl Into) -> Self { - let (n, q, s) = (n.into(), q.into(), s.into()); - let psf = PSFGPV { - gp: GadgetParameters::init_default(&n, &q), - s, - }; - Self { - psf, - storage: HashMap::new(), - hash: HashMatZq { - modulus: q, - rows: i64::try_from(n).unwrap(), - cols: 1, - }, - } - } -} - -impl SignatureScheme for FDHGPV { - /// The trapdoor and a precomputed short basis that speeds up preimage sampling. - type SecretKey = (MatZ, MatQ); - /// The public matrix defining the PSF, for which the secret key defines a trapdoor - type PublicKey = MatZq; - /// Defined by the domain of the PSF. - type Signature = MatZ; - - /// Generates a trapdoor by calling the `trap_gen` of the psf - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { - self.psf.trap_gen() - } - /// Firstly checks if the message has been signed before, and if, return that - /// signature, else it continues. - /// It hashes the message into the domain and then computes a signature using - /// `samp_p` from the psf with the trapdoor. - fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature { - // check if it is in the HashMap - if let Some(sigma) = self.storage.get(&m) { - return sigma.clone(); - } - - let u = (self.hash).hash(&m); - let signature = self.psf.samp_p(pk, sk, &u); - - // insert signature in HashMap - self.storage.insert(m, signature.clone()); - signature - } - - /// Checks if a signature is firstly within D_n, and then checks if - /// the signature is actually a valid preimage under `fa` of `hash(m)`. - fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool { - if !self.psf.check_domain(sigma) { - return false; - } - - let u = (self.hash).hash(&m); - - self.psf.f_a(pk, sigma) == u - } -} - -#[cfg(test)] -mod test_fdh { - use crate::construction::signature::{fdh::gpv::FDHGPV, SignatureScheme}; - use qfall_math::{integer::Z, rational::Q, traits::Pow}; - - /// Ensure that the generated signature is valid. - #[test] - fn ensure_valid_signature_is_generated() { - let n = Z::from(4); - let k = Z::from(6); - // `s >= ||\tilde short_base|| * omega(sqrt{log m})`, - // here `log(2*n*k) = omega(sqrt{log m}))` (Theorem 4.1 - GPV08) - let s: Q = ((&n * &k).sqrt() + 1) * Q::from(2) * (Z::from(2) * &n * &k).log(2).unwrap(); - let q = Z::from(2).pow(&k).unwrap(); - - let mut fdh = FDHGPV::setup(n, &q, &s); - let (pk, sk) = fdh.gen(); - - for i in 0..10 { - let m = format!("Hello World! {i}"); - - let sigma = fdh.sign(m.to_owned(), &sk, &pk); - - assert_eq!(&sigma, &fdh.sign(m.to_owned(), &sk, &pk)); - assert!(fdh.vfy(m.to_owned(), &sigma, &pk)) - } - } - - /// Ensure that an entry is actually added to the local storage. - #[test] - fn storage_filled() { - let mut fdh = FDHGPV::setup(5, 1024, 10); - - let m = "Hello World!"; - let (pk, sk) = fdh.gen(); - let _ = fdh.sign(m.to_owned(), &sk, &pk); - - assert!(fdh.storage.contains_key(m)) - } - - /// Ensure that after deserialization the HashMap still contains all entries. - #[test] - fn reload_hashmap() { - let mut fdh = FDHGPV::setup(5, 1024, 10); - - // fill one entry in the HashMap - let m = "Hello World!"; - let (pk, sk) = fdh.gen(); - let _ = fdh.sign(m.to_owned(), &sk, &pk); - - let fdh_string = serde_json::to_string(&fdh).expect("Unable to create a json object"); - let fdh_2: FDHGPV = serde_json::from_str(&fdh_string).unwrap(); - - assert_eq!(fdh.storage, fdh_2.storage); - } -} diff --git a/src/construction/signature/fdh/gpv_ring.rs b/src/construction/signature/fdh/gpv_ring.rs deleted file mode 100644 index 9b88579..0000000 --- a/src/construction/signature/fdh/gpv_ring.rs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright © 2023 Marvin Beckmann -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! A ring implementation of the FDH signature scheme using the [`PSFGPVRing`] -//! according to [\[1\]](<../index.html#:~:text=[1]>). - -use crate::{ - construction::{ - hash::{sha256::HashMatPolynomialRingZq, HashInto}, - signature::SignatureScheme, - }, - primitive::psf::{PSFGPVRing, PSF}, - sample::g_trapdoor::gadget_parameters::GadgetParametersRing, -}; -use qfall_math::{ - integer::{MatPolyOverZ, Z}, - integer_mod_q::{MatPolynomialRingZq, Modulus}, - rational::Q, -}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// Initializes an FDH signature scheme from a [`PSFGPVRing`]. -/// The trapdoor is sampled with a Gaussian parameter of 1.005 -/// as done in [\[3\]]() who derived it from -/// [\[5\]](). -/// -/// This function corresponds to an implementation of an FDH-signature -/// scheme with the explicit PSF [`PSFGPVRing`] which is generated using -/// the default of [`GadgetParametersRing`]. -/// -/// Attributes: -/// - `psf`: Defines the PSF needed for preimage sampling. -/// - `storage`: Stores all previously constructed signatures. -/// - `hash`: Defines the hash function going from Strings to the PSFs range. -/// -/// Returns an explicit implementation of an FDH-signature scheme. -/// -/// # Example -/// ``` -/// use qfall_crypto::construction::signature::fdh::FDHGPVRing; -/// use crate::qfall_crypto::construction::signature::SignatureScheme; -/// use qfall_math::rational::Q; -/// -/// const MODULUS: i64 = 512; -/// const N: i64 = 8; -/// fn compute_s() -> Q { -/// ((2 * 2 * Q::from(1.005_f64) * Q::from(N).sqrt() + 1) * 2) * 4 -/// } -/// -/// let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); -/// let (pk, sk) = fdh.gen(); -/// let m = &format!("Hello World!"); -/// let sigma = fdh.sign(m.to_owned(), &sk, &pk); -/// assert!( -/// fdh.vfy(m.to_owned(), &sigma, &pk), -/// ); -/// ``` -#[derive(Serialize, Deserialize)] -pub struct FDHGPVRing { - pub psf: PSFGPVRing, - pub storage: HashMap, - pub hash: HashMatPolynomialRingZq, -} - -impl FDHGPVRing { - /// Initializes the [`FDHGPVRing`] with default parameters. - /// The setup function takes in the security parameter, the modulus, a length bound - /// for the signatures, and the length of randomness for this construction. - /// Then, the [`PSFGPVRing`] is instantiated with the default [`GadgetParametersRing`]. - /// This PSF with an additional storage and hash function are secured in the struct. - /// - /// Parameters: - /// - `n`: The security parameter - /// - `q`: The modulus used for the G-Trapdoors - /// - `s`: The Gaussian parameter with which is sampled - /// - /// Returns an explicit instantiation of a FDH signature scheme using the default - /// parameters. - /// - /// # Example - /// ``` - /// use qfall_crypto::construction::signature::fdh::FDHGPVRing; - /// use crate::qfall_crypto::construction::signature::SignatureScheme; - /// use qfall_math::rational::Q; - /// - /// const MODULUS: i64 = 512; - /// const N: i64 = 8; - /// fn compute_s() -> Q { - /// ((2 * 2 * Q::from(1.005_f64) * Q::from(N).sqrt() + 1) * 2) * 4 - /// } - /// - /// let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); - /// ``` - /// - /// # Panics ... - /// - if the security parameter n is not in [1, i64::MAX]. - /// - if `q <= 1`. - pub fn setup(n: impl Into, q: impl Into, s: impl Into) -> Self { - let (n, q, s) = (n.into(), q.into(), s.into()); - let psf = PSFGPVRing { - gp: GadgetParametersRing::init_default(&n, &q), - s, - s_td: Q::from(1.005_f64), - }; - let modulus = psf.gp.modulus.clone(); - Self { - psf, - storage: HashMap::new(), - hash: HashMatPolynomialRingZq { - modulus, - rows: 1, - cols: 1, - }, - } - } -} - -impl SignatureScheme for FDHGPVRing { - /// The trapdoor and a precomputed short basis that speeds up preimage sampling. - type SecretKey = (MatPolyOverZ, MatPolyOverZ); - /// The public matrix defining the PSF, for which the secret key defines a trapdoor - type PublicKey = MatPolynomialRingZq; - /// Defined by the domain of the PSF. - type Signature = MatPolyOverZ; - - /// Generates a trapdoor by calling the `trap_gen` of the psf - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { - self.psf.trap_gen() - } - - /// Firstly checks if the message has been signed before, and if, return that - /// signature, else it continues. - /// It hashes the message into the domain and then computes a signature using - /// `samp_p` from the psf with the trapdoor. - fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature { - // check if it is in the HashMap - if let Some(sigma) = self.storage.get(&m) { - return sigma.clone(); - } - - let u = (self.hash).hash(&m); - let signature = self.psf.samp_p(pk, sk, &u); - - // insert signature in HashMap - self.storage.insert(m, signature.clone()); - signature - } - - /// Checks if a signature is firstly within D_n, and then checks if - /// the signature is actually a valid preimage under `fa` of `hash(m)`. - fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool { - if !self.psf.check_domain(sigma) { - return false; - } - - let u = (self.hash).hash(&m); - - self.psf.f_a(pk, sigma) == u - } -} - -#[cfg(test)] -mod test_fdh { - use crate::construction::signature::{fdh::gpv_ring::FDHGPVRing, SignatureScheme}; - use qfall_math::rational::Q; - - const MODULUS: i64 = 512; - const N: i64 = 8; - fn compute_s() -> Q { - ((2 * 2 * Q::from(1.005_f64) * Q::from(N).sqrt() + 1) * 2) * 4 - } - - /// Ensure that the generated signature is valid. - #[test] - fn ensure_valid_signature_is_generated() { - let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); - let (pk, sk) = fdh.gen(); - - for i in 0..10 { - let m = &format!("Hello World! {i}"); - - let sigma = fdh.sign(m.to_owned(), &sk, &pk); - - assert!( - fdh.vfy(m.to_owned(), &sigma, &pk), - "This is a probabilistic test and may fail with negligible probability. \ - As n is rather small here, try to rerun the test and check whether the \ - test fails again." - ) - } - } - - /// Ensure that an entry is actually added to the local storage. - #[test] - fn storage_filled() { - let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); - - let m = "Hello World!"; - let (pk, sk) = fdh.gen(); - let sign_1 = fdh.sign(m.to_owned(), &sk, &pk); - let sign_2 = fdh.sign(m.to_owned(), &sk, &pk); - - assert!(fdh.storage.contains_key(m)); - assert_eq!(sign_1, sign_2); - } - - /// Ensure that after deserialization the HashMap still contains all entries. - #[test] - fn reload_hashmap() { - let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s()); - - // fill one entry in the HashMap - let m = "Hello World!"; - let (pk, sk) = fdh.gen(); - let _ = fdh.sign(m.to_owned(), &sk, &pk); - - let fdh_string = serde_json::to_string(&fdh).expect("Unable to create a json object"); - - let fdh_2: FDHGPVRing = serde_json::from_str(&fdh_string).unwrap(); - - assert_eq!(fdh.storage, fdh_2.storage); - } -} diff --git a/src/construction/signature/pfdh.rs b/src/construction/signature/pfdh.rs deleted file mode 100644 index 4f8b6c5..0000000 --- a/src/construction/signature/pfdh.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © 2023 Phil Milewski -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! This Module contains a general implementation of the probabilistic full domain -//! hash signature scheme. -//! -//! The constructions follow the general definition of a hash-then-sign signature scheme -//! that uses a hash function as in [\[1\]]() and a PSF. -//! -//! These signature schemes also include randomness into the hashed strings rather than -//! using a storage, so it is stateless. -//! -//! Requirements -//! - `psf`: The PSF which has to implement the [`PSF`](crate::primitive::psf::PSF) trait -//! and must also be (de-)serializable. -//! - `hash`: The hash-function which has to map a string into the correct domain. -//! - `randomness_length`: The length of the salt that is added to the string before -//! hashing. -//! -//! # Example -//! ## Signature Scheme from [`PSFGPV`](crate::primitive::psf::PSFGPV) -//! ``` -//! use qfall_crypto::construction::signature::{pfdh::PFDHGPV, SignatureScheme}; -//! -//! let mut pfdh = PFDHGPV::setup(4, 113, 17, 128); -//! -//! let m = "Hello World!"; -//! -//! let (pk, sk) = pfdh.gen(); -//! let sigma = pfdh.sign(m.to_owned(), &sk, &pk); -//! -//! assert!(pfdh.vfy(m.to_owned(), &sigma, &pk)); -//! ``` - -mod gpv; - -pub use gpv::PFDHGPV; diff --git a/src/construction/signature/pfdh/gpv.rs b/src/construction/signature/pfdh/gpv.rs deleted file mode 100644 index 3a4fbbc..0000000 --- a/src/construction/signature/pfdh/gpv.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright © 2023 Phil Milewski -// -// This file is part of qFALL-crypto. -// -// qFALL-crypto is free software: you can redistribute it and/or modify it under -// the terms of the Mozilla Public License Version 2.0 as published by the -// Mozilla Foundation. See . - -//! A classical implementation of the PFDH signature scheme using the [`PSFGPV`] -//! according to [\[1\]](<../index.html#:~:text=[1]>). - -use crate::{ - construction::{ - hash::{sha256::HashMatZq, HashInto}, - signature::SignatureScheme, - }, - primitive::psf::{PSF, PSFGPV}, - sample::g_trapdoor::gadget_parameters::GadgetParameters, -}; -use qfall_math::{ - integer::{MatZ, Z}, - integer_mod_q::{MatZq, Modulus}, - rational::{MatQ, Q}, - traits::Pow, -}; - -/// Initializes an PFDH signature scheme from a [`PSFGPV`]. -/// -/// This function corresponds to an implementation of an PFDH-signature -/// scheme with the explicit PSF [`PSFGPV`] which is generated using -/// the default of [`GadgetParameters`]. -/// -/// Attributes: -/// - `psf`: Defines the PSF needed for preimage sampling. -/// - `hash`: Defines the hash function going from Strings to the PSFs range. -/// - `randomness_length`: Defines the number of bits used for the randomness. -/// -/// Returns an explicit implementation of a PFDH-signature scheme. -/// -/// # Example -/// ``` -/// use qfall_crypto::construction::signature::{pfdh::PFDHGPV, SignatureScheme}; -/// -/// let mut pfdh = PFDHGPV::setup(4, 113, 17, 128); -/// -/// let m = "Hello World!"; -/// -/// let (pk, sk) = pfdh.gen(); -/// let sigma = pfdh.sign(m.to_owned(), &sk, &pk); -/// -/// assert!(pfdh.vfy(m.to_owned(), &sigma, &pk)); -/// ``` -pub struct PFDHGPV { - pub psf: PSFGPV, - pub hash: HashMatZq, - pub randomness_length: Z, -} - -impl PFDHGPV { - /// Initializes the [`PFDHGPV`] with default parameters. - /// The setup function takes in the security parameter, the modulus, a length bound - /// for the signatures, and the length of randomness for this construction. - /// Then, the [`PSFGPV`] is instantiated with the default [`GadgetParameters`]. - /// This PSF with an additional storage and hash function are secured in the struct. - /// - /// Parameters: - /// - `n`: The security parameter - /// - `q`: The modulus used for the G-Trapdoors - /// - `s`: The Gaussian parameter with which is sampled - /// - `randomness_length`: The length of the randomness. - /// - /// Returns an explicit instantiation of a PFDH signature scheme using the default - /// parameters. - /// - /// # Example - /// ``` - /// use qfall_crypto::construction::signature::{pfdh::PFDHGPV, SignatureScheme}; - /// - /// let mut pfdh = PFDHGPV::setup(4, 113, 17, 128); - /// ``` - /// - /// # Panics ... - /// - if the security parameter n is not in [1, i64::MAX]. - /// - if `q <= 1`. - pub fn setup( - n: impl Into, - q: impl Into, - s: impl Into, - randomness_length: impl Into, - ) -> Self { - let (n, q, s, randomness_length) = (n.into(), q.into(), s.into(), randomness_length.into()); - let psf = PSFGPV { - gp: GadgetParameters::init_default(&n, &q), - s, - }; - let n = i64::try_from(&n).unwrap(); - Self { - psf, - hash: HashMatZq { - modulus: q, - rows: n, - cols: 1, - }, - randomness_length, - } - } -} - -impl SignatureScheme for PFDHGPV { - /// The trapdoor and a precomputed short basis that speeds up preimage sampling. - type SecretKey = (MatZ, MatQ); - /// The public matrix defining the PSF, for which the secret key defines a trapdoor - type PublicKey = MatZq; - /// Defined by the domain of the PSF and additional randomness. - type Signature = (MatZ, Z); - - /// Generates a trapdoor by calling the `trap_gen` of the psf - fn gen(&mut self) -> (Self::PublicKey, Self::SecretKey) { - self.psf.trap_gen() - } - - /// Firstly generate randomness - /// It hashes the message and randomness into the domain and then computes a signature using - /// `samp_p` from the psf with the trapdoor. - fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature { - let randomness = - Z::sample_uniform(0, Z::from(2).pow(&self.randomness_length).unwrap()).unwrap(); - let u = (self.hash).hash(&format!("{m} {randomness} {}", &self.randomness_length)); - let signature_part1 = self.psf.samp_p(pk, sk, &u); - - (signature_part1, randomness) - } - - /// Checks if a signature is firstly within D_n, and then checks if - /// the signature is actually a valid preimage under `fa` of `hash(m||r)`. - fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool { - if !self.psf.check_domain(&sigma.0) { - return false; - } - - let u = (self.hash).hash(&format!("{m} {} {}", sigma.1, &self.randomness_length)); - - self.psf.f_a(pk, &sigma.0) == u - } -} - -#[cfg(test)] -mod test_pfdh { - use crate::construction::signature::{pfdh::gpv::PFDHGPV, SignatureScheme}; - use qfall_math::{integer::Z, rational::Q, traits::Pow}; - - /// Ensure that the generated signature is valid. - #[test] - fn ensure_valid_signature_is_generated() { - let n = Z::from(4); - let k = Z::from(6); - // `s >= ||\tilde short_base|| * omega(sqrt{log m})`, - // here `log(2*n*k) = omega(sqrt{log m}))` (Theorem 4.1 - GPV08) - let s: Q = ((&n * &k).sqrt() + 1) * Q::from(2) * (Z::from(2) * &n * &k).log(2).unwrap(); - let q = Z::from(2).pow(&k).unwrap(); - - let mut pfdh = PFDHGPV::setup(n, &q, &s, 128); - let (pk, sk) = pfdh.gen(); - - for i in 0..10 { - let m = format!("Hello World! {i}"); - - let sigma = pfdh.sign(m.to_owned(), &sk, &pk); - - assert!(pfdh.vfy(m.to_owned(), &sigma, &pk)) - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 7028930..c3c0546 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,6 @@ //! qFALL-crypto is co-developed together with qFALL-math which provides the basic //! foundation that is used to implement the cryptographic constructions. -pub mod construction; pub mod primitive; pub mod sample; pub mod utils; From 2443a5c8938ef9fd7a25d1a64e9d82f7bda2f77b Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 12:13:35 +0200 Subject: [PATCH 2/6] switch to dev branch in qfall math for now --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index baf1479..c4df329 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-math = { git = "https://github.com/qfall/math", rev="cac834c705ed05ce96262ebca72ec0dbe36720d9" } +qfall-math = { git = "https://github.com/qfall/math", branch="dev" } sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} serde_json = "1.0" From 63a72da88bf887000fd4202006d478c95811bbe0 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 12:38:21 +0200 Subject: [PATCH 3/6] fix latest commit of qfall-math --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c4df329..c30d784 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ autobenches = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -qfall-math = { git = "https://github.com/qfall/math", branch="dev" } +qfall-math = { git = "https://github.com/qfall/math", rev="5f50c9cd31c869462d959774fb4b51fcd1727dbe" } sha2 = "0.10.6" serde = {version="1.0", features=["derive"]} serde_json = "1.0" From 2b3b3f01d06e5ebec2faa2c7ea249bfc788de052 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 13:06:01 +0200 Subject: [PATCH 4/6] fix links in lib.rs file, as the constructions themselves were moved to a different repository --- src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c3c0546..aeb53ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,14 +11,8 @@ //! fundamental lattice-based cryptographic constructions, and samplable distributions/ //! possibilities to sample instances of lattice problems to prototype //! lattice-based cryptographic constructions and more. +//! Actual constructions can be found in [qfall-schemes](https://github.com/qfall/schemes) //! -//! Currently qFALL-crypto supports 3 main construction types: -//! - [Identity-Based Encryptions](construction::identity_based_encryption::IBEScheme) -//! - [Public-Key Encryptions](construction::pk_encryption::PKEncryptionScheme) -//! - [Signatures](construction::signature::SignatureScheme) -//! -//! These are identified by traits and then implemented for specific constructions, e.g. -//! [`RingLPR`](construction::pk_encryption::RingLPR). //! Our library has further primitives useful for prototyping such as //! [`PSFs`](primitive::psf::PSF) that can be used to implement constructions. //! From 3e6b3176d15f8daea4303f867cfea7a0d56da605 Mon Sep 17 00:00:00 2001 From: Marvin Beckmann Date: Fri, 17 Oct 2025 13:22:23 +0200 Subject: [PATCH 5/6] rename qfall-crypto to qfall-tools --- .github/ISSUE_TEMPLATE/bug_report.md | 25 +++--- CITATION.cff | 6 +- Cargo.toml | 2 +- README.md | 39 +++----- benches/README.md | 88 +++++++++++-------- benches/benchmarks.rs | 4 +- benches/psf.rs | 8 +- src/lib.rs | 12 +-- src/primitive.rs | 4 +- src/primitive/psf.rs | 4 +- src/primitive/psf/gpv.rs | 40 ++++----- src/primitive/psf/gpv_ring.rs | 40 ++++----- src/primitive/psf/mp_perturbation.rs | 54 ++++++------ src/sample.rs | 4 +- src/sample/g_trapdoor.rs | 4 +- src/sample/g_trapdoor/gadget_classical.rs | 20 ++--- src/sample/g_trapdoor/gadget_default.rs | 8 +- src/sample/g_trapdoor/gadget_parameters.rs | 12 +-- src/sample/g_trapdoor/gadget_ring.rs | 14 +-- .../g_trapdoor/short_basis_classical.rs | 8 +- src/sample/g_trapdoor/short_basis_ring.rs | 10 +-- .../g_trapdoor/trapdoor_distribution.rs | 8 +- src/utils.rs | 4 +- src/utils/common_encodings.rs | 4 +- src/utils/common_moduli.rs | 8 +- src/utils/rotation_matrix.rs | 8 +- 26 files changed, 221 insertions(+), 217 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index fd1cc88..02bfb83 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,46 +1,51 @@ --- name: Bug report about: Create a report to help us improve -title: '' +title: "" labels: bug -assignees: '' - +assignees: "" --- **Describe the bug** + **To Reproduce** + + ```rust // write your code here ``` **Expected behavior** - + **Screenshots** - + **Desktop (please complete the following information):** - - OS: - - Version of qFALL-crypto: + +- OS: +- Version of qFALL-tools: **Additional context** + **Solution** + diff --git a/CITATION.cff b/CITATION.cff index f49cb60..c478c65 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,8 +2,8 @@ # Visit https://bit.ly/cffinit to generate yours today! cff-version: 1.2.0 -title: qFALL-crypto -message: 'University Paderborn, Codes and Cryptography' +title: qFALL-tools +message: "University Paderborn, Codes and Cryptography" type: software authors: - given-names: Laurens @@ -20,5 +20,5 @@ authors: family-names: Schmidt - given-names: Niklas family-names: Siemer -repository-code: 'https://github.com/qfall/crypto' +repository-code: "https://github.com/qfall/crypto" license: MPL-2.0 diff --git a/Cargo.toml b/Cargo.toml index c30d784..4ac6218 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "qfall-crypto" +name = "qfall-tools" version = "0.1.0" edition = "2021" autobenches = false diff --git a/README.md b/README.md index 46cd72c..2048d09 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# qFALL-crypto +# qFALL-tools + [![made-with-rust](https://img.shields.io/badge/Made%20with-Rust-1f425f.svg)](https://www.rust-lang.org/) [![CI](https://github.com/qfall/crypto/actions/workflows/push.yml/badge.svg?branch=dev)](https://github.com/qfall/crypto/actions/workflows/pull_request.yml) [![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0) @@ -8,6 +9,7 @@ This repository is currently being developed by the project group [qFALL - quant The main objective of this project is to provide researchers and students with the possibility to easily and quickly prototype (lattice-based) cryptography. ## Disclaimer + Currently, we are in the development phase and interfaces might change. Feel free to check out the current progress, but be aware, that the content will change in the upcoming weeks and months. An official release will most likely be published in the second half of 2023. @@ -19,47 +21,31 @@ Please refer to [our website](https://qfall.github.io/) as central information p To install and add our library to your project, please refer to [our tutorial](https://qfall.github.io/book/index.html). It provides a step-by-step guide to install the required libraries and gives further insights in the usage of our crates. -## What does qFALL-crypto offer? +## What does qFALL-tools offer? -qFALL-crypto offers a variety of implementations of cryptographic schemes, constructions, and primitives. +qFALL-tools offers a variety of implementations of cryptographic primitives. We provide a brief overview in the following list. For a more detailed description, please refer to [our tutorial section](https://qfall.github.io/book/crypto/features.html). - -Full-fledged Cryptographic Features -- [Public Key Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption.rs) - - [LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/regev.rs) - - [Dual LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/dual_regev.rs) - - [LPR Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/lpr.rs) - - [Ring-based LPR Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/ring_lpr.rs) - - [CCA-secure Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/pk_encryption/ccs_from_ibe.rs) -- [Signatures](https://github.com/qfall/crypto/blob/dev/src/construction/signature.rs) - - [Full-Domain Hash (FDH)](https://github.com/qfall/crypto/blob/dev/src/construction/signature/fdh.rs) - - [Probabilistic FDH (PFDH)](https://github.com/qfall/crypto/blob/dev/src/construction/signature/pfdh.rs) - - [Ring-based FDH](https://github.com/qfall/crypto/blob/dev/src/construction/signature/fdh/gpv_ring.rs) -- [Identity Based Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/identity_based_encryption.rs) - - [From Dual LWE Encryption](https://github.com/qfall/crypto/blob/dev/src/construction/identity_based_encryption/dual_regev_ibe.rs) -- [Hash Functions](https://github.com/qfall/crypto/blob/dev/src/construction/hash.rs) - - [SIS-Hash Function](https://github.com/qfall/crypto/blob/dev/src/construction/hash/sis.rs) - - [SHA-256-based Hash](https://github.com/qfall/crypto/blob/dev/src/construction/hash/sha256.rs) - Building Blocks and Primitives + - [Preimage Samplable Functions (PSF)](https://github.com/qfall/crypto/blob/dev/src/primitive/psf.rs) - [Trapdoors](https://github.com/qfall/crypto/blob/dev/src/sample/g_trapdoor.rs) - - [G-trapdoor incl. short basis](https://github.com/qfall/crypto/blob/dev/src/sample/g_trapdoor/gadget_classical.rs) - - [Ring-based G-trapdoor incl. short basis](https://github.com/qfall/crypto/blob/dev/src/sample/g_trapdoor/gadget_ring.rs) + - [G-trapdoor incl. short basis](https://github.com/qfall/crypto/blob/dev/src/sample/g_trapdoor/gadget_classical.rs) + - [Ring-based G-trapdoor incl. short basis](https://github.com/qfall/crypto/blob/dev/src/sample/g_trapdoor/gadget_ring.rs) ## License + This library is distributed under the **Mozilla Public License Version 2.0** which can be found here [License](https://github.com/qfall/crypto/blob/dev/LICENSE). Permissions of this weak copyleft license are conditioned on making available source code of licensed files and modifications of those files under the same license (or in certain cases, one of the GNU licenses). Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. However, a larger work using the licensed work may be distributed under different terms and without source code for files added in the larger work. ## Citing -Please use the following bibtex entry to cite [qFALL-crypto](https://github.com/qfall/crypto): +Please use the following bibtex entry to cite [qFALL-tools](https://github.com/qfall/crypto): ```text -@misc{qFALL-crypto, +@misc{qFALL-tools, author = {Porzenheim, Laurens and Beckmann, Marvin and Kramer, Paul and Milewski, Phil and Moog, Sven and Schmidt, Marcel and Siemer, Niklas}, - title = {qFALL-crypto v0.0}, + title = {qFALL-tools v0.0}, howpublished = {Online: \url{https://github.com/qfall/crypto}}, month = Mar, year = 2023, @@ -68,4 +54,5 @@ Please use the following bibtex entry to cite [qFALL-crypto](https://github.com/ ``` ## Get in Touch + One can contact the members of the project group with our mailing list `pg-qfall(at)lists.upb.de`. diff --git a/benches/README.md b/benches/README.md index 3c192d4..a54d62d 100644 --- a/benches/README.md +++ b/benches/README.md @@ -1,82 +1,94 @@ # How to run benchmarks: + ## Criterion -We use criterion for statistical analysis. A plotting library has to be installed to generate graphs. You can find more information and help here: + +We use criterion for statistical analysis. A plotting library has to be installed to generate graphs. You can find more information and help here: + - [Criterion-rs GitHub](https://github.com/bheisler/criterion.rs) - [Cargo-criterion GitHub](https://github.com/bheisler/cargo-criterion) - [Criterion Book](https://bheisler.github.io/criterion.rs/book/criterion_rs.html) (!Watchout for the criterion version, as of writing this the book is not on the latest version!) - ### Commands -a) ```cargo criterion ``` + +a) `cargo criterion ` Has to be installed with `cargo install cargo-criterion`. Pros: + - You can remove `features = ["html_reports"]` from the `Cargo.toml` leading to a (slightly) faster compile times. - Criterion aims to move to just using cargo criterion - The large Probability Density Function graph shows the samples and marks the outlier categorization boarders. - Can use either [gnuplot](http://www.gnuplot.info/) or [plotters](https://crates.io/crates/plotters) -b) ```cargo bench ``` +b) `cargo bench ` Pros: + - Can visualize the change in performance compared to previous run or other baseline -Cons: + Cons: - Can only use [gnuplot](http://www.gnuplot.info/) ## Flamegraph + You can also run the benchmarks using the profiler flamegraph. Details can be found here: + - [Flamegraph GitHub](https://github.com/flamegraph-rs/flamegraph). -This provides insights on the execution time of the executed functions and their subroutines. + This provides insights on the execution time of the executed functions and their subroutines. Note: Flamegraph does not work in WSL ### Command -```cargo flamegraph --freq 63300 --bench benchmarks -- --bench --profile-time 5 ``` -Generates a flamegraph that allows to approximate how long each function executes. The accuracy of the approximation is better the more samples are produced. This can be improved by + +`cargo flamegraph --freq 63300 --bench benchmarks -- --bench --profile-time 5 ` +Generates a flamegraph that allows to approximate how long each function executes. The accuracy of the approximation is better the more samples are produced. This can be improved by + - increasing the sample frequency (`--freq 63300`), This frequency is throttled to the highest possible frequency which depends on the cpu, cpu-temperature, power settings and much more... - increasing `profile-time` (in seconds). This is how long the benchmark code will be executed. -This parameter also disables the statistical analysis of criterion which prevents it from showing up in the graph. -This parameter is optional, but suggested. + This parameter also disables the statistical analysis of criterion which prevents it from showing up in the graph. + This parameter is optional, but suggested. The flamegraph can be overwhelming since it exposes a lot of internal workings of rust, criterion, and more. The easiest way to find the function you are looking for is to search for it with `Ctrl + F`. You have to enter a part of the rust function name or regex (not the benchmark name). # How to create benchmarks - + ## No appropriate file exists so far: - 1. create the file - 2. Insert in new file: - ``` rust - use criterion::*; - - criterion_group!(benches); - ``` - 3. Insert in [benchmarks.rs](/benches/benchmarks.rs): - ``` rust - pub mod ; - ``` - and `::benches` in the `criterion_main!` macro. + +1. create the file +2. Insert in new file: + + ```rust + use criterion::*; + + criterion_group!(benches); + ``` + +3. Insert in [benchmarks.rs](/benches/benchmarks.rs): + ```rust + pub mod ; + ``` + and `::benches` in the `criterion_main!` macro. ## Appropriately named benchmark file exists in `/benches` (e.g. `integer.rs`) - 1. Create a function that performs the functionality that should be benchmarked (called `do_stuff` below). - 2. Add a function to handle the interaction with criterion. - e.g.: - ``` rust - /// Add Comment describing the benchmark here - pub fn bench_do_stuff(c: &mut Criterion) { - c.bench_function("", |b| b.iter(|| do_stuff())); - } - ``` - The benchmark name specified here is later used to select which benchmark to run and also displayed in the output. - This function can also look differently, for example, because it uses [criterion groups](https://docs.rs/criterion/latest/criterion/struct.BenchmarkGroup.html). - 3. Add function created in step 2 in the `criterion_group!` macro (bottom of file). - + +1. Create a function that performs the functionality that should be benchmarked (called `do_stuff` below). +2. Add a function to handle the interaction with criterion. + e.g.: + ```rust + /// Add Comment describing the benchmark here + pub fn bench_do_stuff(c: &mut Criterion) { + c.bench_function("", |b| b.iter(|| do_stuff())); + } + ``` + The benchmark name specified here is later used to select which benchmark to run and also displayed in the output. + This function can also look differently, for example, because it uses [criterion groups](https://docs.rs/criterion/latest/criterion/struct.BenchmarkGroup.html). +3. Add function created in step 2 in the `criterion_group!` macro (bottom of file). diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index b40c2c7..3b49155 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Sven Moog // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . //! This file collects the benchmarks from other files. diff --git a/benches/psf.rs b/benches/psf.rs index bfe09cd..4a44b09 100644 --- a/benches/psf.rs +++ b/benches/psf.rs @@ -1,17 +1,17 @@ // Copyright © 2025 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . use criterion::{criterion_group, Criterion}; -use qfall_crypto::{ +use qfall_math::{integer_mod_q::MatZq, rational::Q}; +use qfall_tools::{ primitive::psf::{PSFPerturbation, PSF, PSFGPV}, sample::g_trapdoor::gadget_parameters::GadgetParameters, }; -use qfall_math::{integer_mod_q::MatZq, rational::Q}; /// Benchmark [bench_psf] with `n = 8`. /// diff --git a/src/lib.rs b/src/lib.rs index aeb53ce..7c2838d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,12 @@ // Copyright © 2023 Niklas Siemer, Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! # What is qFALL-crypto? +//! # What is qFALL-tools? //! qFall-crypto provides cryptographic basics such as mathematical primitives, //! fundamental lattice-based cryptographic constructions, and samplable distributions/ //! possibilities to sample instances of lattice problems to prototype @@ -16,15 +16,15 @@ //! Our library has further primitives useful for prototyping such as //! [`PSFs`](primitive::psf::PSF) that can be used to implement constructions. //! -//! qFALL-crypto is free software: you can redistribute it and/or modify it under +//! qFALL-tools is free software: you can redistribute it and/or modify it under //! the terms of the Mozilla Public License Version 2.0 as published by the //! Mozilla Foundation. See . //! //! ## Tutorial + Website -//! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-crypto on our [website](https://qfall.github.io/). +//! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-tools on our [website](https://qfall.github.io/). //! The tutorial explains the basic steps starting from installation and //! continues with basic usage. -//! qFALL-crypto is co-developed together with qFALL-math which provides the basic +//! qFALL-tools is co-developed together with qFALL-math which provides the basic //! foundation that is used to implement the cryptographic constructions. pub mod primitive; diff --git a/src/primitive.rs b/src/primitive.rs index e776262..fed18a4 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/primitive/psf.rs b/src/primitive/psf.rs index 33cdce4..0894c6f 100644 --- a/src/primitive/psf.rs +++ b/src/primitive/psf.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marcel Luca Schmidt, Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/primitive/psf/gpv.rs b/src/primitive/psf/gpv.rs index 58ace65..74fba71 100644 --- a/src/primitive/psf/gpv.rs +++ b/src/primitive/psf/gpv.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -33,10 +33,10 @@ use serde::{Deserialize, Serialize}; /// /// # Examples /// ``` -/// use qfall_crypto::primitive::psf::PSFGPV; -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; +/// use qfall_tools::primitive::psf::PSFGPV; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; -/// use qfall_crypto::primitive::psf::PSF; +/// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPV { /// gp: GadgetParameters::init_default(8, 64), @@ -68,10 +68,10 @@ impl PSF for PSFGPV { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPV; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFGPV; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPV { /// gp: GadgetParameters::init_default(8, 64), @@ -97,10 +97,10 @@ impl PSF for PSFGPV { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPV; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFGPV; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPV { /// gp: GadgetParameters::init_default(8, 64), @@ -133,10 +133,10 @@ impl PSF for PSFGPV { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPV; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFGPV; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPV { /// gp: GadgetParameters::init_default(8, 64), @@ -178,10 +178,10 @@ impl PSF for PSFGPV { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPV; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFGPV; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPV { /// gp: GadgetParameters::init_default(8, 64), @@ -208,9 +208,9 @@ impl PSF for PSFGPV { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSF; - /// use qfall_crypto::primitive::psf::PSFGPV; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSFGPV; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; /// /// let psf = PSFGPV { diff --git a/src/primitive/psf/gpv_ring.rs b/src/primitive/psf/gpv_ring.rs index 143369d..435ada4 100644 --- a/src/primitive/psf/gpv_ring.rs +++ b/src/primitive/psf/gpv_ring.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -41,10 +41,10 @@ use serde::{Deserialize, Serialize}; /// /// # Examples /// ``` -/// use qfall_crypto::primitive::psf::PSFGPVRing; -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; +/// use qfall_tools::primitive::psf::PSFGPVRing; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// use qfall_math::rational::Q; -/// use qfall_crypto::primitive::psf::PSF; +/// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPVRing { /// gp: GadgetParametersRing::init_default(8, 512), @@ -76,10 +76,10 @@ impl PSF for PSFGPVRing { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPVRing; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + /// use qfall_tools::primitive::psf::PSFGPVRing; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPVRing { /// gp: GadgetParametersRing::init_default(8, 512), @@ -101,10 +101,10 @@ impl PSF for PSFGPVRing { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPVRing; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + /// use qfall_tools::primitive::psf::PSFGPVRing; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPVRing { /// gp: GadgetParametersRing::init_default(8, 512), @@ -139,10 +139,10 @@ impl PSF for PSFGPVRing { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPVRing; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + /// use qfall_tools::primitive::psf::PSFGPVRing; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPVRing { /// gp: GadgetParametersRing::init_default(8, 512), @@ -223,10 +223,10 @@ impl PSF for PSFGPVRing { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPVRing; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + /// use qfall_tools::primitive::psf::PSFGPVRing; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPVRing { /// gp: GadgetParametersRing::init_default(8, 512), @@ -256,10 +256,10 @@ impl PSF for PSFGPVRing { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFGPVRing; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + /// use qfall_tools::primitive::psf::PSFGPVRing; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFGPVRing { /// gp: GadgetParametersRing::init_default(8, 512), diff --git a/src/primitive/psf/mp_perturbation.rs b/src/primitive/psf/mp_perturbation.rs index ac99c94..b902309 100644 --- a/src/primitive/psf/mp_perturbation.rs +++ b/src/primitive/psf/mp_perturbation.rs @@ -1,8 +1,8 @@ // Copyright © 2025 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -35,10 +35,10 @@ use serde::{Deserialize, Serialize}; /// /// # Examples /// ``` -/// use qfall_crypto::primitive::psf::PSFPerturbation; -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; +/// use qfall_tools::primitive::psf::PSFPerturbation; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; -/// use qfall_crypto::primitive::psf::PSF; +/// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFPerturbation { /// gp: GadgetParameters::init_default(8, 64), @@ -80,10 +80,10 @@ impl PSFPerturbation { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFPerturbation; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFPerturbation; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::{Q, MatQ}; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// use qfall_math::traits::*; /// /// let psf = PSFPerturbation { @@ -152,11 +152,11 @@ impl PSFPerturbation { /// /// # Examples /// ```compile_fail -/// use qfall_crypto::primitive::psf::PSFPerturbation; -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; -/// use qfall_crypto::sample::g_trapdoor::gadget_classical::short_basis_gadget; +/// use qfall_tools::primitive::psf::PSFPerturbation; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; +/// use qfall_tools::sample::g_trapdoor::gadget_classical::short_basis_gadget; /// use qfall_math::rational::Q; -/// use qfall_crypto::primitive::psf::PSF; +/// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFPerturbation { /// gp: GadgetParameters::init_default(8, 64), @@ -211,10 +211,10 @@ impl PSF for PSFPerturbation { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFPerturbation; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFPerturbation; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFPerturbation { /// gp: GadgetParameters::init_default(8, 64), @@ -253,10 +253,10 @@ impl PSF for PSFPerturbation { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFPerturbation; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFPerturbation; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFPerturbation { /// gp: GadgetParameters::init_default(8, 64), @@ -290,10 +290,10 @@ impl PSF for PSFPerturbation { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFPerturbation; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFPerturbation; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFPerturbation { /// gp: GadgetParameters::init_default(8, 64), @@ -353,10 +353,10 @@ impl PSF for PSFPerturbation { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSFPerturbation; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSFPerturbation; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; - /// use qfall_crypto::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSF; /// /// let psf = PSFPerturbation { /// gp: GadgetParameters::init_default(8, 64), @@ -384,9 +384,9 @@ impl PSF for PSFPerturbation { /// /// # Examples /// ``` - /// use qfall_crypto::primitive::psf::PSF; - /// use qfall_crypto::primitive::psf::PSFPerturbation; - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::primitive::psf::PSF; + /// use qfall_tools::primitive::psf::PSFPerturbation; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// use qfall_math::rational::Q; /// /// let psf = PSFPerturbation { diff --git a/src/sample.rs b/src/sample.rs index f5088c9..7050e78 100644 --- a/src/sample.rs +++ b/src/sample.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/sample/g_trapdoor.rs b/src/sample/g_trapdoor.rs index 5346125..a5fd540 100644 --- a/src/sample/g_trapdoor.rs +++ b/src/sample/g_trapdoor.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/sample/g_trapdoor/gadget_classical.rs b/src/sample/g_trapdoor/gadget_classical.rs index 94dcc6a..9dccebe 100644 --- a/src/sample/g_trapdoor/gadget_classical.rs +++ b/src/sample/g_trapdoor/gadget_classical.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -34,7 +34,7 @@ use std::fmt::Display; /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::{gadget_parameters::GadgetParameters, gadget_classical::gen_trapdoor}; +/// use qfall_tools::sample::g_trapdoor::{gadget_parameters::GadgetParameters, gadget_classical::gen_trapdoor}; /// use qfall_math::integer_mod_q::MatZq; /// /// let params = GadgetParameters::init_default(42, 42); @@ -79,7 +79,7 @@ pub fn gen_trapdoor( /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::gadget_classical::gen_gadget_mat; +/// use qfall_tools::sample::g_trapdoor::gadget_classical::gen_gadget_mat; /// use qfall_math::integer::Z; /// /// let g = gen_gadget_mat(4, 4, &Z::from(2)); @@ -117,7 +117,7 @@ pub fn gen_gadget_mat(n: i64, k: impl TryInto + Display, base: &Z) -> MatZ /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::gadget_classical::gen_gadget_vec; +/// use qfall_tools::sample::g_trapdoor::gadget_classical::gen_gadget_vec; /// use qfall_math::integer::Z; /// /// let g = gen_gadget_vec(4, &Z::from(2)); @@ -147,7 +147,7 @@ pub fn gen_gadget_vec(k: impl TryInto + Display, base: &Z) -> MatZ { /// # Examples /// ``` /// use qfall_math::{integer::{Z, MatZ}, integer_mod_q::Zq, traits::MatrixGetEntry}; -/// use qfall_crypto::sample::g_trapdoor::gadget_classical::{find_solution_gadget_vec, gen_gadget_vec}; +/// use qfall_tools::sample::g_trapdoor::gadget_classical::{find_solution_gadget_vec, gen_gadget_vec}; /// use std::str::FromStr; /// /// let k = Z::from(5); @@ -198,8 +198,8 @@ pub fn find_solution_gadget_vec(value: &Zq, k: &Z, base: &Z) -> MatZ { /// use qfall_math::integer::Z; /// use qfall_math::integer::MatZ; /// use qfall_math::integer_mod_q::MatZq; -/// use qfall_crypto::sample::g_trapdoor::gadget_classical::find_solution_gadget_mat; -/// use qfall_crypto::sample::g_trapdoor::gadget_classical::gen_gadget_mat; +/// use qfall_tools::sample::g_trapdoor::gadget_classical::find_solution_gadget_mat; +/// use qfall_tools::sample::g_trapdoor::gadget_classical::gen_gadget_mat; /// use std::str::FromStr; /// /// let k = Z::from(5); @@ -237,9 +237,9 @@ pub fn find_solution_gadget_mat(value: &MatZq, k: &Z, base: &Z) -> MatZ { /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::{gadget_parameters::GadgetParameters, +/// use qfall_tools::sample::g_trapdoor::{gadget_parameters::GadgetParameters, /// gadget_default::gen_trapdoor_default}; -/// use qfall_crypto::sample::g_trapdoor::gadget_classical::short_basis_gadget; +/// use qfall_tools::sample::g_trapdoor::gadget_classical::short_basis_gadget; /// /// let params = GadgetParameters::init_default(10, 127); /// diff --git a/src/sample/g_trapdoor/gadget_default.rs b/src/sample/g_trapdoor/gadget_default.rs index 5e1648e..565f7ac 100644 --- a/src/sample/g_trapdoor/gadget_default.rs +++ b/src/sample/g_trapdoor/gadget_default.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -32,7 +32,7 @@ use qfall_math::{ /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::gadget_default::gen_trapdoor_default; +/// use qfall_tools::sample::g_trapdoor::gadget_default::gen_trapdoor_default; /// /// let (a,r) = gen_trapdoor_default(42, 101); /// ``` @@ -71,7 +71,7 @@ pub fn gen_trapdoor_default(n: impl Into, q: impl Into) -> (MatZq, M /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::gadget_default::gen_trapdoor_ring_default; +/// use qfall_tools::sample::g_trapdoor::gadget_default::gen_trapdoor_ring_default; /// /// let (a,r, e) = gen_trapdoor_ring_default(100, 29, 10);; /// ``` diff --git a/src/sample/g_trapdoor/gadget_parameters.rs b/src/sample/g_trapdoor/gadget_parameters.rs index deab5a1..c01d21e 100644 --- a/src/sample/g_trapdoor/gadget_parameters.rs +++ b/src/sample/g_trapdoor/gadget_parameters.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize}; /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// /// let params = GadgetParameters::init_default(42, 42); /// ``` @@ -66,7 +66,7 @@ pub struct GadgetParameters { /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// /// let params = GadgetParametersRing::init_default(42, 42); /// ``` @@ -102,7 +102,7 @@ impl GadgetParameters { /// /// # Examples /// ``` - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParameters; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParameters; /// /// let params = GadgetParameters::init_default(42, 42); /// ``` @@ -154,7 +154,7 @@ impl GadgetParametersRing { /// /// # Examples /// ``` - /// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; + /// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// /// let params = GadgetParametersRing::init_default(42, 42); /// ``` diff --git a/src/sample/g_trapdoor/gadget_ring.rs b/src/sample/g_trapdoor/gadget_ring.rs index 74c2951..a5eee10 100644 --- a/src/sample/g_trapdoor/gadget_ring.rs +++ b/src/sample/g_trapdoor/gadget_ring.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -41,7 +41,7 @@ use std::fmt::Display; /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::{gadget_parameters::GadgetParametersRing, gadget_ring::gen_trapdoor_ring_lwe}; +/// use qfall_tools::sample::g_trapdoor::{gadget_parameters::GadgetParametersRing, gadget_ring::gen_trapdoor_ring_lwe}; /// use qfall_math::integer::PolyOverZ; /// /// let params = GadgetParametersRing::init_default(8, 17); @@ -92,7 +92,7 @@ pub fn gen_trapdoor_ring_lwe( /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::gadget_ring::gen_gadget_ring; +/// use qfall_tools::sample::g_trapdoor::gadget_ring::gen_gadget_ring; /// use qfall_math::integer::Z; /// /// let g = gen_gadget_ring(4, &Z::from(2)); @@ -121,9 +121,9 @@ pub fn gen_gadget_ring(k: impl TryInto + Display, base: &Z) -> MatPolyOverZ /// ``` /// use qfall_math::integer::PolyOverZ; /// use qfall_math::integer_mod_q::PolynomialRingZq; -/// use qfall_crypto::sample::g_trapdoor::gadget_ring::gen_gadget_ring; -/// use qfall_crypto::sample::g_trapdoor::gadget_ring::find_solution_gadget_ring; -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; +/// use qfall_tools::sample::g_trapdoor::gadget_ring::gen_gadget_ring; +/// use qfall_tools::sample::g_trapdoor::gadget_ring::find_solution_gadget_ring; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; /// use qfall_math::integer_mod_q::MatPolynomialRingZq; /// use std::str::FromStr; /// diff --git a/src/sample/g_trapdoor/short_basis_classical.rs b/src/sample/g_trapdoor/short_basis_classical.rs index ff66b3d..26ffb9d 100644 --- a/src/sample/g_trapdoor/short_basis_classical.rs +++ b/src/sample/g_trapdoor/short_basis_classical.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -39,9 +39,9 @@ use qfall_math::{ /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::{gadget_parameters::GadgetParameters, +/// use qfall_tools::sample::g_trapdoor::{gadget_parameters::GadgetParameters, /// gadget_default::gen_trapdoor_default}; -/// use qfall_crypto::sample::g_trapdoor::short_basis_classical::gen_short_basis_for_trapdoor; +/// use qfall_tools::sample::g_trapdoor::short_basis_classical::gen_short_basis_for_trapdoor; /// use qfall_math::integer_mod_q::MatZq; /// /// let params = GadgetParameters::init_default(10, 127); diff --git a/src/sample/g_trapdoor/short_basis_ring.rs b/src/sample/g_trapdoor/short_basis_ring.rs index 29d2596..07fc7bd 100644 --- a/src/sample/g_trapdoor/short_basis_ring.rs +++ b/src/sample/g_trapdoor/short_basis_ring.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -45,9 +45,9 @@ use qfall_math::{ /// /// # Examples /// ``` -/// use qfall_crypto::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; -/// use qfall_crypto::sample::g_trapdoor::gadget_ring::gen_trapdoor_ring_lwe; -/// use qfall_crypto::sample::g_trapdoor::short_basis_ring::gen_short_basis_for_trapdoor_ring; +/// use qfall_tools::sample::g_trapdoor::gadget_parameters::GadgetParametersRing; +/// use qfall_tools::sample::g_trapdoor::gadget_ring::gen_trapdoor_ring_lwe; +/// use qfall_tools::sample::g_trapdoor::short_basis_ring::gen_short_basis_for_trapdoor_ring; /// use qfall_math::integer::PolyOverZ; /// /// let params = GadgetParametersRing::init_default(8, 16); diff --git a/src/sample/g_trapdoor/trapdoor_distribution.rs b/src/sample/g_trapdoor/trapdoor_distribution.rs index e2fcd6e..039022b 100644 --- a/src/sample/g_trapdoor/trapdoor_distribution.rs +++ b/src/sample/g_trapdoor/trapdoor_distribution.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -72,7 +72,7 @@ impl TrapdoorDistribution for PlusMinusOneZero { /// /// # Examples /// ``` - /// use qfall_crypto::sample::g_trapdoor::trapdoor_distribution::{PlusMinusOneZero, TrapdoorDistribution}; + /// use qfall_tools::sample::g_trapdoor::trapdoor_distribution::{PlusMinusOneZero, TrapdoorDistribution}; /// /// let mat = PlusMinusOneZero.sample(&42.into(), &24.into()); /// ``` @@ -101,7 +101,7 @@ impl TrapdoorDistributionRing for SampleZ { /// /// # Examples /// ``` - /// use qfall_crypto::sample::g_trapdoor::trapdoor_distribution::{SampleZ, TrapdoorDistributionRing}; + /// use qfall_tools::sample::g_trapdoor::trapdoor_distribution::{SampleZ, TrapdoorDistributionRing}; /// /// let mat = SampleZ.sample(&42.into(), &24.into(), &3.into()); /// ``` diff --git a/src/utils.rs b/src/utils.rs index 4bfed21..8df1a87 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/utils/common_encodings.rs b/src/utils/common_encodings.rs index 63c80d8..ca3e6e8 100644 --- a/src/utils/common_encodings.rs +++ b/src/utils/common_encodings.rs @@ -1,8 +1,8 @@ // Copyright © 2025 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . diff --git a/src/utils/common_moduli.rs b/src/utils/common_moduli.rs index 8ef5f74..9d6ede9 100644 --- a/src/utils/common_moduli.rs +++ b/src/utils/common_moduli.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Niklas Siemer // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -27,7 +27,7 @@ use std::fmt::Display; /// /// # Examples /// ``` -/// use qfall_crypto::utils::common_moduli::new_anticyclic; +/// use qfall_tools::utils::common_moduli::new_anticyclic; /// /// let poly_mod = new_anticyclic(8, 17); /// ``` @@ -58,7 +58,7 @@ pub fn new_anticyclic( /// /// # Examples /// ``` -/// use qfall_crypto::utils::common_moduli::new_cyclic; +/// use qfall_tools::utils::common_moduli::new_cyclic; /// /// let poly_mod = new_cyclic(8, 17); /// ``` diff --git a/src/utils/rotation_matrix.rs b/src/utils/rotation_matrix.rs index e8f98e9..4a6f617 100644 --- a/src/utils/rotation_matrix.rs +++ b/src/utils/rotation_matrix.rs @@ -1,8 +1,8 @@ // Copyright © 2023 Marvin Beckmann // -// This file is part of qFALL-crypto. +// This file is part of qFALL-tools. // -// qFALL-crypto is free software: you can redistribute it and/or modify it under +// qFALL-tools is free software: you can redistribute it and/or modify it under // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . @@ -26,7 +26,7 @@ use qfall_math::{integer::MatZ, traits::*}; /// /// # Examples /// ``` -/// use qfall_crypto::utils::rotation_matrix::rot_minus; +/// use qfall_tools::utils::rotation_matrix::rot_minus; /// use qfall_math::integer::MatZ; /// use std::str::FromStr; /// @@ -75,7 +75,7 @@ pub fn rot_minus(vec: &MatZ) -> MatZ { /// /// # Examples /// ``` -/// use qfall_crypto::utils::rotation_matrix::rot_minus_matrix; +/// use qfall_tools::utils::rotation_matrix::rot_minus_matrix; /// use qfall_math::integer::MatZ; /// use std::str::FromStr; /// From 23cd8319e2a68fb47c1752311516624f96bfb211 Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Fri, 17 Oct 2025 13:28:52 +0100 Subject: [PATCH 6/6] Revise descriptions of crate --- README.md | 23 +++++++++++++---------- src/lib.rs | 10 ++++------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 2048d09..df38823 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # qFALL-tools [![made-with-rust](https://img.shields.io/badge/Made%20with-Rust-1f425f.svg)](https://www.rust-lang.org/) -[![CI](https://github.com/qfall/crypto/actions/workflows/push.yml/badge.svg?branch=dev)](https://github.com/qfall/crypto/actions/workflows/pull_request.yml) +[![CI](https://github.com/qfall/tools/actions/workflows/push.yml/badge.svg?branch=dev)](https://github.com/qfall/tools/actions/workflows/pull_request.yml) [![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0) This repository is currently being developed by the project group [qFALL - quantum resistant fast lattice library](https://cs.uni-paderborn.de/cuk/lehre/veranstaltungen/ws-2022-23/project-group-qfall) in the winter term 2022 and summer term 2023 by the Codes and Cryptography research group in Paderborn. @@ -23,30 +23,33 @@ It provides a step-by-step guide to install the required libraries and gives fur ## What does qFALL-tools offer? -qFALL-tools offers a variety of implementations of cryptographic primitives. +qFALL-tools offers a variety of implementations of commonly used tools in lattice-based cryptography. We provide a brief overview in the following list. For a more detailed description, please refer to [our tutorial section](https://qfall.github.io/book/crypto/features.html). -Building Blocks and Primitives -- [Preimage Samplable Functions (PSF)](https://github.com/qfall/crypto/blob/dev/src/primitive/psf.rs) -- [Trapdoors](https://github.com/qfall/crypto/blob/dev/src/sample/g_trapdoor.rs) - - [G-trapdoor incl. short basis](https://github.com/qfall/crypto/blob/dev/src/sample/g_trapdoor/gadget_classical.rs) - - [Ring-based G-trapdoor incl. short basis](https://github.com/qfall/crypto/blob/dev/src/sample/g_trapdoor/gadget_ring.rs) +- [Preimage Samplable Functions (PSF)](https://github.com/qfall/tools/blob/dev/src/primitive/psf.rs) +- [Trapdoors](https://github.com/qfall/tools/blob/dev/src/sample/g_trapdoor.rs) + - [G-trapdoor incl. short basis](https://github.com/qfall/tools/blob/dev/src/sample/g_trapdoor/gadget_classical.rs) + - [Ring-based G-trapdoor incl. short basis](https://github.com/qfall/tools/blob/dev/src/sample/g_trapdoor/gadget_ring.rs) +- [Utility functions for quick instantiations](https://github.com/qfall/tools/blob/dev/src/utils/) + - [Common moduli](https://github.com/qfall/tools/blob/dev/src/utils/common_moduli.rs) + - [Rotation matrices](https://github.com/qfall/tools/blob/dev/src/utils/rotation_matrix.rs) + - [Common encodings](https://github.com/qfall/tools/blob/dev/src/utils/common_encodings.rs) ## License -This library is distributed under the **Mozilla Public License Version 2.0** which can be found here [License](https://github.com/qfall/crypto/blob/dev/LICENSE). +This library is distributed under the **Mozilla Public License Version 2.0** which can be found here [License](https://github.com/qfall/tools/blob/dev/LICENSE). Permissions of this weak copyleft license are conditioned on making available source code of licensed files and modifications of those files under the same license (or in certain cases, one of the GNU licenses). Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. However, a larger work using the licensed work may be distributed under different terms and without source code for files added in the larger work. ## Citing -Please use the following bibtex entry to cite [qFALL-tools](https://github.com/qfall/crypto): +Please use the following bibtex entry to cite [qFALL-tools](https://github.com/qfall/tools): ```text @misc{qFALL-tools, author = {Porzenheim, Laurens and Beckmann, Marvin and Kramer, Paul and Milewski, Phil and Moog, Sven and Schmidt, Marcel and Siemer, Niklas}, title = {qFALL-tools v0.0}, - howpublished = {Online: \url{https://github.com/qfall/crypto}}, + howpublished = {Online: \url{https://github.com/qfall/tools}}, month = Mar, year = 2023, note = {University Paderborn, Codes and Cryptography} diff --git a/src/lib.rs b/src/lib.rs index 7c2838d..76f5a71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,13 +7,12 @@ // Mozilla Foundation. See . //! # What is qFALL-tools? -//! qFall-crypto provides cryptographic basics such as mathematical primitives, -//! fundamental lattice-based cryptographic constructions, and samplable distributions/ -//! possibilities to sample instances of lattice problems to prototype +//! qFall-tools provides cryptographic basics such as commonly used tools in lattice-based cryptography, +//! mathematical primitives, and samplable distributions to prototype //! lattice-based cryptographic constructions and more. //! Actual constructions can be found in [qfall-schemes](https://github.com/qfall/schemes) //! -//! Our library has further primitives useful for prototyping such as +//! Our library provides further primitives useful for prototyping such as //! [`PSFs`](primitive::psf::PSF) that can be used to implement constructions. //! //! qFALL-tools is free software: you can redistribute it and/or modify it under @@ -24,8 +23,7 @@ //! You can find a dedicated [tutorial](https://qfall.github.io/book/index.html) to qFALL-tools on our [website](https://qfall.github.io/). //! The tutorial explains the basic steps starting from installation and //! continues with basic usage. -//! qFALL-tools is co-developed together with qFALL-math which provides the basic -//! foundation that is used to implement the cryptographic constructions. +//! qFALL-tools is co-developed together with qFALL-math and qFALL-schemes. pub mod primitive; pub mod sample;