generate prime number + comments
This commit is contained in:
1 parent
9346355406
commit
057ca33b6b
4 files changed
+169
-30
No files matched your search
@@ -256,8 +256,8 @@ mod tests {
|
||||
// c += c0.clone();
|
||||
c *= c1.clone();
|
||||
|
||||
let encrypted_z = encrypt_and_evaluate(&c, &bk, kappa*2);
|
||||
c = bootstrap(&c, &encrypted_z, &bk.enc_s, &pk, kappa*2);
|
||||
let encrypted_z = encrypt_and_evaluate(&c, &bk, kappa);
|
||||
c = bootstrap(&c, &encrypted_z, &bk.enc_s, &pk, kappa);
|
||||
|
||||
let m_boot = decrypt_bit(&c, &sk.p);
|
||||
assert_eq!(
|
||||
|
||||
+10
-8
@@ -1,14 +1,14 @@
|
||||
use crate::utils::{generate_random_integer, generate_random_odd_integer};
|
||||
use crate::utils::{generate_random_odd_integer, generate_random_prime};
|
||||
use rug::{Assign, Integer};
|
||||
|
||||
pub fn encrypt_bit(bit: u8, key: &Integer, gamma: u32, rho: u32) -> Integer {
|
||||
assert!(bit == 0 || bit == 1, "Only bits (0 or 1) are allowed");
|
||||
|
||||
let q: Integer = generate_random_integer(gamma);
|
||||
let q: Integer = generate_random_odd_integer(gamma);
|
||||
|
||||
// noise
|
||||
let p_div_4: Integer = key.clone() / 4;
|
||||
let r = std::iter::repeat_with(|| generate_random_integer(rho))
|
||||
let r = std::iter::repeat_with(|| generate_random_odd_integer(rho))
|
||||
.find(|r| r.clone() * 2 < p_div_4)
|
||||
.unwrap();
|
||||
|
||||
@@ -24,8 +24,9 @@ pub fn decrypt_bit(encrypted: &Integer, key: &Integer) -> u8 {
|
||||
bit.to_u8().unwrap()
|
||||
}
|
||||
|
||||
/// Secret key: a random eta-bit *prime* (Fermat-tested, see `utils::generate_random_prime`).
|
||||
pub fn generate_secret_key(eta: u32) -> Integer {
|
||||
generate_random_odd_integer(eta)
|
||||
generate_random_prime(eta)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -36,14 +37,15 @@ mod tests {
|
||||
fn test_secret_key_generation_properties() {
|
||||
let eta = 1024;
|
||||
let key = generate_secret_key(eta);
|
||||
assert!(key.significant_bits() <= eta);
|
||||
assert!(key.significant_bits() >= eta - 1);
|
||||
assert_eq!(key.significant_bits(), eta);
|
||||
assert!(key.is_odd());
|
||||
// independent primality cross-check (GMP Miller-Rabin)
|
||||
assert_ne!(key.is_probably_prime(30), rug::integer::IsPrime::No);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encrypt_decrypt_bit() {
|
||||
let eta = 10000;
|
||||
let eta = 1024;
|
||||
let key = generate_secret_key(eta);
|
||||
|
||||
let bit = 0;
|
||||
@@ -59,7 +61,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_multiple_encryptions_are_different() {
|
||||
let eta = 10000;
|
||||
let eta = 1024;
|
||||
let key = generate_secret_key(eta);
|
||||
|
||||
let bit = 1;
|
||||
|
||||
+29
-16
@@ -13,22 +13,31 @@ pub struct PrivateKey {
|
||||
pub p: Integer,
|
||||
}
|
||||
|
||||
/// Generates a key pair.
|
||||
/// gamma: bit-length of the q_i, eta: bit-length of the (prime) secret p,
|
||||
/// rho: noise bit-length, theta: total number of public elements (x_0 included, >= 2).
|
||||
///
|
||||
/// x_0 = q_0 * p exactly (no noise) and is the largest element; the other x_i = q_i*p + 2*r_i.
|
||||
/// Reducing a ciphertext modulo x_0 therefore never changes its noise, which is what makes products of ciphertexts (and the bootstrapping circuit) reducible. With a noisy x_0, reducing a product would add k*r_0 to the noise with k ~ x_0 and break decryption.
|
||||
pub fn generate_keys(gamma: u32, eta: u32, rho: u32, theta: usize) -> (PrivateKey, PublicKey) {
|
||||
assert!(theta >= 2, "need at least x_0 and one more public element");
|
||||
let p: Integer = generate_secret_key(eta);
|
||||
|
||||
let mut xs: Vec<Integer> = Vec::with_capacity(theta);
|
||||
for _ in 0..theta {
|
||||
let xi = encrypt_bit(0u8, &p, gamma, rho);
|
||||
xs.push(xi);
|
||||
}
|
||||
xs.sort_by(|a, b| b.cmp(a));
|
||||
// noisy elements x_1 .. x_{theta-1}
|
||||
let mut xs: Vec<Integer> = (1..theta)
|
||||
.map(|_| encrypt_bit(0u8, &p, gamma, rho))
|
||||
.collect();
|
||||
|
||||
while xs[0].is_even() {
|
||||
xs.remove(0);
|
||||
let xi = encrypt_bit(0u8, &p, gamma, rho);
|
||||
xs.push(xi);
|
||||
xs.sort_by(|a, b| b.cmp(a));
|
||||
}
|
||||
// q_0 = (largest q_i) + 2 is odd and strictly above every other q_i, so that
|
||||
// x_i < (q_i + 1) * p <= x_0 for all i.
|
||||
let q_max = xs
|
||||
.iter()
|
||||
.map(|x| Integer::from(x / &p))
|
||||
.max()
|
||||
.expect("theta >= 2");
|
||||
let q0 = q_max + 2u32;
|
||||
xs.push(q0 * &p);
|
||||
xs.sort_by(|a, b| b.cmp(a));
|
||||
|
||||
(PrivateKey { p }, PublicKey { xs })
|
||||
}
|
||||
@@ -65,22 +74,26 @@ pub fn encrypt_bit_asym(m: u8, pk: &PublicKey, rho: u32) -> Integer {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::decrypt_bit;
|
||||
use rug::integer::IsPrime;
|
||||
|
||||
#[test]
|
||||
fn test_key_generation_properties() {
|
||||
let eta: u32 = 10000;
|
||||
let eta: u32 = 1024;
|
||||
let theta: usize = 5;
|
||||
let rho: u32 = 100;
|
||||
let (sk, pk) = generate_keys(rho, eta, rho, theta);
|
||||
|
||||
assert_eq!(sk.p.significant_bits(), eta);
|
||||
assert!(sk.p.is_odd());
|
||||
assert_ne!(sk.p.is_probably_prime(30), IsPrime::No);
|
||||
|
||||
assert_eq!(pk.xs.len(), theta);
|
||||
assert!(pk.xs[0].is_odd());
|
||||
// x_0 is an exact multiple of p
|
||||
assert!(pk.xs[0].is_divisible(&sk.p));
|
||||
|
||||
for xi in pk.xs.iter().skip(1) {
|
||||
assert!(pk.xs[0] >= *xi);
|
||||
assert!(pk.xs[0] > *xi);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,11 +101,11 @@ mod tests {
|
||||
fn test_encrypt_decrypt_bit() {
|
||||
use rayon::prelude::*;
|
||||
|
||||
let eta: u32 = 10000;
|
||||
let eta: u32 = 1024;
|
||||
let theta: usize = 50;
|
||||
let rho: u32 = 100;
|
||||
|
||||
(0..=127).into_par_iter().for_each(|_| {
|
||||
(0..=31).into_par_iter().for_each(|_| {
|
||||
let (sk, pk) = generate_keys(rho, eta, rho, theta);
|
||||
for &m in &[0u8, 1u8] {
|
||||
let c = encrypt_bit_asym(m, &pk, rho);
|
||||
|
||||
+128
-4
@@ -1,9 +1,15 @@
|
||||
use rand::rngs::StdRng;
|
||||
use rand::Rng;
|
||||
use rand::SeedableRng;
|
||||
use rug::rand::RandState;
|
||||
use rug::integer::Order;
|
||||
use rug::Integer;
|
||||
|
||||
/// Number of random bases used by the Fermat test when generating primes.
|
||||
pub const FERMAT_ROUNDS: u32 = 10;
|
||||
|
||||
/// Trial-division bound used to discard most composites before the (expensive) Fermat test.
|
||||
const SIEVE_BOUND: u32 = 5000;
|
||||
|
||||
pub fn generate_random_odd_integer(num_bits: u32) -> Integer {
|
||||
let mut rng = StdRng::from_os_rng();
|
||||
let mut x = Integer::from(1);
|
||||
@@ -26,10 +32,128 @@ pub fn generate_random_integer(num_bits: u32) -> Integer {
|
||||
x
|
||||
}
|
||||
|
||||
/// Uniform random `finish - start` bit integer, shifted left by `start`.
|
||||
pub fn generate_random_integer_range(start: u32, finish: u32) -> Integer {
|
||||
assert!(finish > start, "finish must be greater than start");
|
||||
let k = finish - start;
|
||||
let mut rand = RandState::new();
|
||||
let i = Integer::from(Integer::random_bits(k, &mut rand)) << start;
|
||||
i
|
||||
let mut rng = StdRng::from_os_rng();
|
||||
let mut buf = vec![0u8; k.div_ceil(8) as usize];
|
||||
rng.fill(&mut buf[..]);
|
||||
let x = Integer::from_digits(&buf, Order::Lsf).keep_bits(k);
|
||||
x << start
|
||||
}
|
||||
|
||||
/// Primes below `SIEVE_BOUND`.
|
||||
fn small_primes() -> Vec<u32> {
|
||||
let mut is_composite = vec![false; SIEVE_BOUND as usize + 1];
|
||||
let mut primes = Vec::new();
|
||||
for i in 2..=SIEVE_BOUND as usize {
|
||||
if !is_composite[i] {
|
||||
primes.push(i as u32);
|
||||
let mut j = i * i;
|
||||
while j <= SIEVE_BOUND as usize {
|
||||
is_composite[j] = true;
|
||||
j += i;
|
||||
}
|
||||
}
|
||||
}
|
||||
primes
|
||||
}
|
||||
|
||||
/// Fermat primality test with `rounds` random bases.
|
||||
///
|
||||
/// `n` is declared (probably) prime iff `a^(n-1) = 1 (mod n)` for every random base `a` in `[2, n-2]`. Small numbers are settled exactly by trial division first.
|
||||
/// (Carmichael numbers fool every coprime base, but they are rare among random large candidates.)
|
||||
pub fn is_probable_prime_fermat(n: &Integer, rounds: u32) -> bool {
|
||||
if *n < 2 {
|
||||
return false;
|
||||
}
|
||||
for p in small_primes() {
|
||||
if *n == p {
|
||||
return true;
|
||||
}
|
||||
if n.is_divisible_u(p) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// n > SIEVE_BOUND and has no small factor
|
||||
let n_minus_1 = Integer::from(n - 1u32);
|
||||
let range = Integer::from(n - 3u32); // bases in [2, n-2]
|
||||
for _ in 0..rounds.max(1) {
|
||||
let a = generate_random_integer_range(0, n.significant_bits() + 1) % &range + 2u32;
|
||||
match a.pow_mod(&n_minus_1, n) {
|
||||
Ok(r) if r == 1 => {}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Random `num_bits`-bit prime (top bit set), found by rejection sampling + Fermat test.
|
||||
pub fn generate_random_prime(num_bits: u32) -> Integer {
|
||||
assert!(num_bits >= 2, "a prime needs at least 2 bits");
|
||||
loop {
|
||||
let candidate = generate_random_odd_integer(num_bits);
|
||||
if is_probable_prime_fermat(&candidate, FERMAT_ROUNDS) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rug::integer::IsPrime;
|
||||
|
||||
#[test]
|
||||
fn test_range_generator_is_random() {
|
||||
let a = generate_random_integer_range(0, 128);
|
||||
let b = generate_random_integer_range(0, 128);
|
||||
assert_ne!(a, b);
|
||||
for _ in 0..50 {
|
||||
let x = generate_random_integer_range(3, 40);
|
||||
assert!(x < (Integer::from(1) << 40u32));
|
||||
assert!(x.is_divisible_2pow(3));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fermat_small_numbers() {
|
||||
let primes = [2u32, 3, 5, 7, 11, 4999, 5003, 7919, 104729];
|
||||
for p in primes {
|
||||
assert!(is_probable_prime_fermat(&Integer::from(p), 5), "{p}");
|
||||
}
|
||||
let composites = [0u32, 1, 4, 9, 561, 1105, 1729, 4999 * 4999, 5003 * 5009];
|
||||
for c in composites {
|
||||
assert!(!is_probable_prime_fermat(&Integer::from(c), 5), "{c}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fermat_large_numbers() {
|
||||
// Mersenne prime 2^127 - 1 and 2^521 - 1
|
||||
for e in [127u32, 521] {
|
||||
let m = (Integer::from(1) << e) - 1u32;
|
||||
assert!(is_probable_prime_fermat(&m, 10));
|
||||
}
|
||||
// product of two large primes
|
||||
let p = generate_random_prime(128);
|
||||
let q = generate_random_prime(128);
|
||||
assert!(!is_probable_prime_fermat(&(p * q), 10));
|
||||
// 2^127 + 1 is divisible by 3
|
||||
let c = (Integer::from(1) << 127u32) + 1u32;
|
||||
assert!(!is_probable_prime_fermat(&c, 10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_random_prime() {
|
||||
for bits in [16u32, 64, 256, 512] {
|
||||
let p = generate_random_prime(bits);
|
||||
assert_eq!(p.significant_bits(), bits);
|
||||
assert!(p.is_odd());
|
||||
// independent cross-check with GMP's Miller-Rabin
|
||||
assert_ne!(p.is_probably_prime(30), IsPrime::No);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user