Chris Pollett >
Students >
Molina
( Print View)
[Bio]
[Blog]
[CS 297 Proposal]
[Deliverable 1]
[Deliverable 2]
[Deliverable 3]
[Deliverable 4]
[McEliece System]
[Regev System]
[RSA System]
[CS 297 Report-PDF]
[CS 298 Proposal]
[CS298 Slides-PDF]
[CS298 Report-PDF]
|
Implementing the Regev Cryptosystem
Michaela Molina (michaela.molina@sjsu.edu)
Purpose:
The purpose of this deliverable was to implement the public key system from [Regev2009]. The system is a candidate for post-quantum cryptography.
Code:
main.rs
// main.rs
mod text;
use rand_distr::{Normal, Distribution};
use rand::{thread_rng, Rng};
use std::process;
use rand::prelude::SliceRandom;
use std::time::Instant;
use std::time::Duration;
use std::collections::HashMap;
fn main() {
// Example use
let (public_key, private_key) = create_system(5, 0.5);
let ciphertext = encrypt(String::from("one two three four five"), &public_key);
let plaintext = decrypt(&ciphertext, &private_key);
println!("plaintext = {}", plaintext);
}
// Takes in the security parameter n and the error E
// Sets m and p according to the parameter requirements
// and returns the resulting private and public key
fn create_system(n:u64, E:f64) -> (public_key, private_key, Vec<u64>) {
let mut lower = n*n;
let mut upper = 2*lower;
let mut p = rand::thread_rng().gen_range(lower..upper) as u64;
if p < 2 {
println!("Error: p must be >= 2");
process::exit(1);
}
let m = ((1.0 + E) * (n as f64 + 1.0) * (p as f64).log2());
let m = m.round() as u64;
let mut B = alpha(n as f64);
let private_key = private_key {
p: p,
s: draw_vector_from_Znp(n, p)
};
let (lst, e_vector) = get_list(m, n, p, B, &private_key.s);
let public_key = public_key {
m: m,
n: n,
p: p,
list: lst
};
return (public_key, private_key, e_vector);
}
// Holds the tuple used in the public key and the encrypted message
#[derive(Clone)]
pub struct pair {
pub a: Vec<u64>,
pub b: u64
}
// Holds the private key
pub struct private_key {
pub s: Vec<u64>,
pub p: u64
}
// Holds the public key
pub struct public_key {
pub m: u64,
pub n: u64,
pub p: u64,
pub list: Vec<pair>
}
// Takes in a String and returns the ciphertext as a vector of tuples
pub fn encrypt(message:String, public_key:&public_key) -> Vec<pair> {
let mut plaintext = string_to_binary_vec(message);
return encrypt_vector(&plaintext, &public_key);
}
// Takes in ciphertext in the form of a vector of tuples
// Returns the plaintext as a String
pub fn decrypt(ciphertext:&Vec<pair>, private_key:&private_key) -> String {
let mut plaintext = decrypt_vector(ciphertext, &private_key);
let mut plaintext = binary_vec_to_string(&plaintext);
plaintext
}
// Takes in a vector of zeros and ones and encrypts each bit at a time
// Returns a vector of tuples representing the ciphertext
fn encrypt_vector(message:&Vec<u64>, public_key:&public_key) -> Vec<pair> {
let mut encrypted_bits = Vec::new();
for bit in message {
let (pair, error_sum) = encrypt_bit(*bit, &public_key);
encrypted_bits.push(pair);
}
encrypted_bits
}
// Takes in a vector of tuples
// Returns a vector of zeros and ones representing the plaintext
fn decrypt_vector(encrypted_bits:&Vec<pair>, private_key:&private_key) -> Vec<u64> {
let mut decrypted_bits = Vec::new();
for bit in encrypted_bits {
let mut decrypted_bit = decrypt_bit(bit, &private_key);
decrypted_bits.push(decrypted_bit);
}
decrypted_bits
}
// Decrypts a single bit
fn decrypt_bit(encrypted_bit:&pair, private_key:&private_key) -> u64 {
let s = private_key.s.clone();
let p = private_key.p;
let mut a = encrypted_bit.a.clone();
let mut b = encrypted_bit.b.clone();
let mut inner_product = inner_product(&a, &s, p);
let mut point = (b as i64 - inner_product as i64).rem_euclid(p as i64) as u64;
let mut q = (p as f64 / 2 as f64).floor() as u64;
let mut circular_distance_to_0 = circular_distance(point as u64, 0, p);
let mut circular_distance_to_q = circular_distance(point as u64, q, p);
if circular_distance_to_0 < circular_distance_to_q {
return 0;
} else {
return 1;
}
}
// This method is used in the decryption of a bit
// https://stackoverflow.com/questions/6192825/c-calculating-the-distance-between-2-floats-modulo-12
fn circular_distance(point:u64, target:u64, modulus:u64) -> u64 {
let diff = (point as i64 - target as i64).abs() as u64;
return std::cmp::min(diff, modulus-diff);
}
// Encrypts a single bit and returns the result
fn encrypt_bit(bit:u64, public_key:&public_key) -> (pair, Vec<u64>) {
let m = public_key.m;
let n = public_key.n;
let p = public_key.p;
let list = public_key.list.clone();
let mut subset = choose_subset(m);
let mut x = vec![0; n as usize];
let mut y = 0 as u64;
for index in subset.clone() {
let mut a = list[index as usize].a.clone();
let mut b = list[index as usize].b.clone();
x = add_vector(&x, &a, p);
y += b;
y = y.rem_euclid(p);
}
if bit == 0 {
let pair = pair {
a: x,
b: y
};
return (pair, subset);
} else {
y += (p as f64 / 2 as f64).floor() as u64;
y = y.rem_euclid(p);
let pair = pair {
a: x,
b: y
};
return (pair, subset);
}
}
// Adds two vectors modulo p and returns the result
fn add_vector(a:&Vec<u64>, b:&Vec<u64>, p:u64) -> Vec<u64> {
let mut sum = Vec::new();
for i in 0..a.len() {
sum.push((a[i] + b[i]).rem_euclid(p));
}
sum
}
// This method chooses a subset for the encryption of a single bit
// and returns a vector of indices
fn choose_subset(m:u64) -> Vec<u64> {
let mut set = Vec::new();
let mut num = Vec::new();
while num.len() < m as usize { // create length m vector of 1s and 0s
num.push(rand::thread_rng().gen_range(0..2) as u8); // 0 or 1
}
for index in 0..num.len() {
if num[index]==1 {
set.push((index) as u64);
}
}
set
}
// Returns a list of tuples used in the public key
fn get_list(m:u64, n:u64, p:u64, B:f64, private_key:&Vec<u64>) -> (Vec<pair>, Vec<u64>) {
let mut s = private_key.clone();
let mut public_key = Vec::new();
let mut e_vector = Vec::new();
for i in 0..m {
let mut a = draw_vector_from_Znp(n, p);
let mut e = draw_element_from_X(p, B);
e_vector.push(e);
let mut b = (inner_product(&a, &s, p) + e).rem_euclid(p);
let mut pair = pair {
a: a,
b: b
};
public_key.push(pair);
}
(public_key, e_vector)
}
// Computes the dot product of two vectors modulo p and returns the result
fn inner_product(a:&Vec<u64>, b:&Vec<u64>, p:u64) -> u64 {
let mut sum = 0;
if a.len() != b.len() {
println!("Error in inner product - lengths were different");
process::exit(1);
}
for i in 0..a.len() {
sum += a[i] * b[i];
}
return (sum).rem_euclid(p);
}
// Draws a vector of length n with elements
// in {0,...,p-1} for use in the public and private keys
fn draw_vector_from_Znp(n:u64, p:u64) -> Vec<u64> {
let mut vec = Vec::new();
for i in 0..n {
vec.push(rand::thread_rng().gen_range(0..p));
}
vec
}
// Draws an error from the specified error distribution
fn draw_element_from_X(p:u64, B:f64) -> u64 {
let mut mean = 0.0;
// mean 0, standard deviation B/sqrt(2pi)
let mut std_dev = B/(2 as f64*std::f64::consts::PI).sqrt();
// create normal distribution
let normal = Normal::new(mean, std_dev).unwrap();
// sample from normal distribution
let mut v = normal.sample(&mut rand::thread_rng());
// reduce result modulo 1
let mut w = (v).rem_euclid(1.0);
// multiply by p
let mut x = w * p as f64;
// round to nearest integer modulo p
let mut y = (x.round()).rem_euclid(p as f64);
return y as u64;
}
// Returns B E R+, a parameters used in the error distribution
fn alpha(n:f64) -> f64 {
return 1 as f64/(n.sqrt() * n.log2() * n.log2());
}
// Returns vector of all possible binary vectors of width width
pub fn all_possible_messages(width:u64) -> Vec<Vec<u64>> {
let mut vectors = Vec::new();
for i in 0..2_u64.pow(width as u32) {
let mut vec = vec(i, width);
vectors.push(vec);
}
vectors
}
// Converts a u64 to a binary vector
pub fn vec(u:u64, length:u64) -> Vec<u64> {
let mut vec = vec![0 as u64; length as usize];
let mut u = u.clone();
for i in (0..vec.len()).rev() {
if u%2 == 0 {
vec[i] = 0 as u64;
} else {
vec[i] = 1 as u64;
}
u = u >> 1;
}
vec
}
// Checks if two vectors are equal
pub fn is_equal(a:&Vec<u64>, b:&Vec<u64>, width:u64) -> bool {
for i in 0..width as usize {
if a[i] != b[i] {
return false;
}
}
return true;
}
// Converts a String to a binary vector for encrypting
pub fn string_to_binary_vec(s:String) -> Vec<u64> {
let mut binary_string = String::from("");
for character in s.into_bytes() {
if character == 32 {
binary_string += &format!("00{:b}", character);
} else {
binary_string += &format!("0{:b}", character);
}
}
let mut binary_vector = Vec::new();
for ch in binary_string.chars() {
if ch == '1' {
binary_vector.push(1);
} else {
binary_vector.push(0);
}
}
binary_vector
}
// Converts a binary vector to a String for decrypting
pub fn binary_vec_to_string(v:&Vec<u64>) -> String {
let mut vec_of_chars = Vec::new();
let mut binary_string = String::from("");
for i in 0..v.len() {
if v[i] == 0 {
binary_string += "0";
} else {
binary_string += "1";
}
if binary_string.len() == 8 {
vec_of_chars.push(binary_string.clone());
binary_string = String::from("");
}
}
let mut w = Vec::new();
for i in 0..vec_of_chars.len() {
let bin = vec_of_chars[i].clone();
w.push(isize::from_str_radix(&vec_of_chars[i].clone(), 2).unwrap() as u8);
}
while w[w.len()-1] == 0 {
w.pop();
}
let mut w = &w[..];
let st = String::from("");
st
}
Cargo.toml
[package]
name = "regev"
version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8.5"
rand_distr = "0.4.3"
|