diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..d5135e9 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["-C", "target-cpu=native"] \ No newline at end of file diff --git a/.gitignore b/.gitignore index ab2b470..75faea8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ target .code +req +req1 diff --git a/Cargo.lock b/Cargo.lock index 01104a6..492e8bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -977,6 +977,7 @@ dependencies = [ "bit-vec", "bitvec 0.22.0", "bls12_381", + "futures", "group", "lazy_static", "rand", @@ -1550,6 +1551,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ + "base64", "serde", "serde_with_macros", ] diff --git a/Cargo.toml b/Cargo.toml index 03164ce..86bcfe5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ time = "*" rocket = "0.5.0-rc.1" rayon = "*" base64 = "*" -serde_with = "*" +serde_with = { version = "*", features = ["base64","default"] } +futures = "*" reqwest ={ version = "*", features = ["json"]} rocket_okapi = { version = "0.8.0-alpha-1", features = ["swagger", "rapidoc"] } diff --git a/src/core.rs b/src/core.rs index 2c61ca7..0fbd199 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,7 +1,7 @@ use bls12_381::*; use crate::utils; use crate::proofs; -use std::collections::HashMap; +use crate::shamir; use std::time::Instant; use rayon::prelude::*; use serde::{Deserialize, Serialize}; @@ -12,25 +12,22 @@ use crate::serializers; use serde_with::serde_as; #[serde_as] -#[derive(Deserialize, Serialize,JsonSchema)] +#[derive(Deserialize, Serialize,JsonSchema,Clone,Copy)] pub struct PublicParameters{ - #[serde_as(as = "Vec")] - #[schemars(with = "Vec::")] - pub space: Vec, - pub k: usize, #[serde_as(as = "serializers::SerializeGt")] #[schemars(with = "String")] pub gt_gen: Gt, - #[serde_as(as = "Vec")] - #[schemars(with = "Vec::")] - pub ratelimiter_public_keys: Vec + #[serde_as(as = "serializers::SerializeGt")] + #[schemars(with = "String")] + pub ratelimiter_public_key: Gt } pub struct ServerState{ r: Scalar, pub n: Scalar, - p: G2Projective + p: G2Projective, + pw: String, } impl ServerState { @@ -40,9 +37,9 @@ impl ServerState { let username = username.to_owned(); let to_be_hashed = username + password; - let h2 = G2Affine::generator() * utils::hash_string_to_scalar(to_be_hashed); - let p = h2 * r; - ServerState{r,n,p} + let tempr = utils::hash_string_to_scalar(to_be_hashed); + let p = G2Affine::generator() * tempr * r; + ServerState{r,n,p,pw: password.to_owned()} } fn from_nonce(username: &String, password: &String, nonce: &Scalar) -> ServerState{ @@ -50,14 +47,15 @@ impl ServerState { let username = username.to_owned(); let to_be_hashed = username + password; - let h2 = G2Affine::generator() * utils::hash_string_to_scalar(to_be_hashed); - let p = h2 * r; - ServerState{r,n: nonce.clone(),p} + let tempr = utils::hash_string_to_scalar(to_be_hashed); + let p = G2Affine::generator() * tempr * r; + + ServerState{r,n: nonce.clone(),p,pw: password.to_owned()} } } #[serde_as] -#[derive(Serialize, Deserialize, JsonSchema)] +#[derive(Serialize, Deserialize, JsonSchema,Clone,Copy,Debug)] pub struct RatelimiterRequest{ #[serde_as(as = "serializers::SerializeScalar")] #[schemars(with = "String")] @@ -70,18 +68,36 @@ pub struct RatelimiterRequest{ #[serde_as] #[derive(Serialize, Deserialize, JsonSchema)] pub struct RatelimiterResponse{ - elements: Vec + proof: proofs::SameDLogProof, + #[serde_as(as = "serializers::SerializeGt")] + #[schemars(with = "String")] + u: Gt, +} + + +#[serde_as] +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct EncryptedMessage{ + #[serde_as(as = "serde_with::base64::Base64")] + #[schemars(with = "String")] + pub c1: [u8; 64], + #[serde_as(as = "serde_with::base64::Base64")] + #[schemars(with = "String")] + pub c2: [u8; 64], + #[serde_as(as = "serializers::SerializeScalar")] + #[schemars(with = "String")] + pub n: Scalar, } #[serde_as] #[derive(Serialize, Deserialize, JsonSchema)] -pub struct RatelimiterResponseElement{ - proof: proofs::SameDLogProof, - #[serde_as(as = "serializers::SerializeGt")] +pub struct SetKeyHelper{ + #[serde_as(as = "serializers::SerializeScalar")] #[schemars(with = "String")] - p: Gt + pub key: Scalar, } + pub fn phe_init(username: &String, password: &String) -> (ServerState,RatelimiterRequest){ let ss = ServerState::new(username,password); let rr = RatelimiterRequest{n:ss.n,p:ss.p}; @@ -96,71 +112,184 @@ pub fn phe_init_decrypt(username: &String, password: &String,n: &Scalar) -> (Ser return (ss,rr) } -pub fn phe_ratelimiter(private_keys: &Vec, request: &RatelimiterRequest,pp: &PublicParameters) -> RatelimiterResponse { - let u = pairing(&G1Affine::from(G1Affine::generator() * request.n) , &G2Affine::from(request.p)); - let results = private_keys.par_iter().zip(&pp.ratelimiter_public_keys).map(|(private_key,public_key)| { - let value = u*private_key; - let proof = proofs::SameDLogProofPublic{g:pp.gt_gen,h:u,y1:*public_key,y2:value}.proof(&private_key); - RatelimiterResponseElement{proof,p:value} - }).collect::>(); - RatelimiterResponse{elements:results} + + +pub fn phe_ratelimiter(private_key: &Scalar, request: &RatelimiterRequest,pp: &PublicParameters) -> RatelimiterResponse { + let n = request.n; + let u = pairing(&G1Affine::from(G1Affine::generator() * n) , &G2Affine::from(request.p)); + let value = u*private_key; + let proof = proofs::SameDLogProofPublic{g:pp.gt_gen,h:u,y1:pp.ratelimiter_public_key,y2:value}.proof(&private_key); + RatelimiterResponse{proof,u:value} } -pub fn phe_enc_finish(msg: &Vec,pp: &PublicParameters,response: &RatelimiterResponse,ss:&ServerState) -> Result,String>{ - let split_message = utils::split_message_into_space(msg,&pp.space,pp.k); - assert_eq!(split_message.len(),pp.ratelimiter_public_keys.len(),"key length {} and msglength {} do not match",pp.ratelimiter_public_keys.len(),split_message.len()); - let u = pairing(&G1Affine::from(G1Affine::generator() * ss.n) , &G2Affine::from(ss.p)); +pub fn phe_enc_finish_t(msg: &Vec,pp: &Vec,response: &Vec,ss:&ServerState,n: i64) -> Result{ + let h = pairing(&G1Affine::from(G1Affine::generator() * ss.n), &G2Affine::from(ss.p)); let r_inv = ss.r.invert().unwrap(); - let values = pp.ratelimiter_public_keys.par_iter().zip(&response.elements).zip(&split_message).map(|((public_key,x),msg)|{ - if !x.proof.verify(&proofs::SameDLogProofPublic{g:pp.gt_gen,h:u,y1:*public_key,y2:x.p}){ - Err("Invalid Proof".to_string()) - }else{ - Ok(x.p * r_inv * msg) - } - }).collect::,String>>(); - values + let proofok = response.par_iter().zip(pp).map(|(r,p)| r.proof.verify(&proofs::SameDLogProofPublic{g:p.gt_gen,h:h ,y1:p.ratelimiter_public_key,y2:r.u})).collect::>().iter().all(|x| *x); + if !proofok{ + return Err("Proofs not ok".to_string()); + } + + + let split_message = utils::vec_to_msg(msg); + let uvec = response.iter().map(|r| r.u).collect::>(); + + let u = shamir::recover_shares(&uvec, n); + + let ut = u * r_inv; + + let c1 = utils::hash_data( // H3([u]t 1/r, pw, id, ns, nr) + M //id is username, ns is nonce + &[ + &&"3".to_string(), + &&ut, + &&ss.pw, + &&ss.n, + //&response.iter().map(|r| &r.n).collect::>(), + ]); + let c2 =// H3([u]t 1/r, M, pw, id, ns, nr) + utils::hash_data( + &[ + &&"3".to_string(), + &&ut, + &&split_message, + &&ss.pw, + &&ss.n, + //&response.iter().map(|r| &r.n).collect::>(), + ], + ); + let c1 = utils::xor(&c1, &split_message); + Ok(EncryptedMessage{c1,c2,n:ss.n}) } -pub fn phe_dec_finish_simple(ciphertext: &Vec,pp: &PublicParameters,response: &RatelimiterResponse,ss:&ServerState) -> Result,String>{ +pub fn phe_dec_finish_t(ciphertext: &EncryptedMessage,pp: &Vec,response: &Vec,ss:&ServerState,n: i64) -> Result,String>{ + let h = pairing(&G1Affine::from(G1Affine::generator() * ss.n), &G2Affine::from(ss.p)); + let r_inv = ss.r.invert().unwrap(); + + let proofok = response.par_iter().zip(pp).map(|(r,p)| r.proof.verify(&proofs::SameDLogProofPublic{g:p.gt_gen,h:h,y1:p.ratelimiter_public_key,y2:r.u})).collect::>().iter().all(|x| *x); + if !proofok{ + return Err("Proofs not ok".to_string()); + } + let uvec = response.iter().map(|r| r.u).collect::>(); + let u = shamir::recover_shares(&uvec, n); + + let ut = u * r_inv; + + let c1 = utils::hash_data( // H3([u]t 1/r, pw, id, ns, nr) + M //id is username, ns is nonce + &[ + &&"3".to_string(), + &&ut, + &&ss.pw, + &&ss.n, + //&response.iter().map(|r| &r.n).collect::>(), + ]); + let c1 = utils::xor(&c1, &ciphertext.c1); + let c2 =// H3([u]t 1/r, M, pw, id, ns, nr) + utils::hash_data( + &[ + &&"3".to_string(), + &&ut, + &&c1, + &&ss.pw, + &&ss.n, + //&response.iter().map(|r| &r.n).collect::>(), + ], + ); + + if c2 == ciphertext.c2{ + Ok(c1.to_vec()) + }else{ + Err("Invalid ciphertext".to_string()) + } +} + + + +pub fn phe_enc_finish(msg: &Vec,pp: &PublicParameters,response: &RatelimiterResponse,ss:&ServerState) -> Result{ + let split_message = utils::vec_to_msg(msg); let u = pairing(&G1Affine::from(G1Affine::generator() * ss.n) , &G2Affine::from(ss.p)); let r_inv = ss.r.invert().unwrap(); - let values = pp.ratelimiter_public_keys.par_iter().zip(&response.elements).map(|(public_key,x)|{ - if !x.proof.verify(&proofs::SameDLogProofPublic{g:pp.gt_gen,h:u,y1:*public_key,y2:x.p}){ - Err("Invalid Proof".to_string()) - }else{ - Ok(x.p * r_inv) - } - }).collect::,String>>()?; - - return Ok(utils::solve_dlog(&values, ciphertext, &pp.space, pp.k)?); + if !response.proof.verify(&proofs::SameDLogProofPublic{g:pp.gt_gen,h:u,y1:pp.ratelimiter_public_key,y2:response.u}){ + Err("Invalid Proof ENC".to_string()) + }else{ + let ut = response.u * r_inv; + let c1 = utils::hash_data( // H3([u]t 1/r, pw, id, ns, nr) + M //id is username, ns is nonce + &[ + &&"3".to_string(), + &&ut, + &&ss.pw, + &&ss.n + ] + ); + let c1 = utils::xor(&c1,&split_message); + let c2 = // H3([u]t 1/r, M, pw, id, ns, nr) + utils::hash_data( + &[ + &&"3".to_string(), + &&ut, + &&split_message, + &&ss.pw, + &&ss.n + ] + ); + Ok(EncryptedMessage{c1,c2,n:ss.n}) + } } -fn phe_dec_finish_preparedspace(pp: &PublicParameters,response: &RatelimiterResponse,ss:&ServerState,preparedspace: &Vec>) -> Result,String>{ +pub fn phe_dec_finish_simple(ciphertext: &EncryptedMessage,pp: &PublicParameters,response: &RatelimiterResponse,ss:&ServerState) -> Result,String>{ let u = pairing(&G1Affine::from(G1Affine::generator() * ss.n) , &G2Affine::from(ss.p)); let r_inv = ss.r.invert().unwrap(); + if !response.proof.verify(&proofs::SameDLogProofPublic{g:pp.gt_gen,h:u,y1:pp.ratelimiter_public_key,y2:response.u}){ + Err("Invalid Proof".to_string()) + }else{ - let values = pp.ratelimiter_public_keys.par_iter().zip(&response.elements).map(|(public_key,x)|{ - if !x.proof.verify(&proofs::SameDLogProofPublic{g:pp.gt_gen,h:u,y1:*public_key,y2:x.p}){ - Err("Invalid Proof".to_string()) + let ut = response.u * r_inv; + let c1 = utils::hash_data( // H3([u]t 1/r, pw, id, ns, nr) + M //id is username, ns is nonce + &[ + &&"3".to_string(), + &&ut, + &&ss.pw, + &&ss.n + ] + ); + let split_message = utils::xor(&ciphertext.c1,&c1); + let c2 = // H3([u]t 1/r, M, pw, id, ns, nr) + utils::hash_data( + &[ + &&"3".to_string(), + &&ut, + &&split_message, + &&ss.pw, + &&ss.n + ] + ); + if ciphertext.c2 == c2 { + Ok(split_message.to_vec()) }else{ - Ok(x.p * r_inv) + Err("Invalid C2".to_string()) } - }).collect::,String>>()?; - - return Ok(utils::find_in_precomputed_space(&values, &pp.space,preparedspace, pp.k)); + } } #[test] fn test_core(){ - rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap(); - test_core_k(32,2); + //rayon::ThreadPoolBuilder::new().num_threads(8).build_global().unwrap(); + test_core_k(); //test_core_k(32,4); //test_core_k(32,8); } +#[test] +fn t1est_core_test() +{ + rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap(); + test_core_t(10,10); +} + + + #[test] fn test_speed_operations() { let mut s1 = utils::random_scalar(); @@ -238,42 +367,91 @@ fn test_speed_operations() { println!(" {:.2?} gt exp ",start.elapsed()/1000); } +fn test_core_t(n:i64,t:i64){ + let msg = (0..64).map(|x|(x) as u8).collect::>(); + let generator = pairing(&G1Affine::generator(),&G2Affine::generator()); + let realkey = utils::random_scalar(); + + let rlkeys = shamir::gen_shares_scalar(realkey,n,t); + let pps = rlkeys.iter().map(|x|PublicParameters{ + gt_gen: generator, + ratelimiter_public_key: generator*x + }).collect::>(); -fn test_core_k(bytes:usize, k:usize) { + let pp_keys = pps.iter().map(|x|{x.ratelimiter_public_key}).collect::>(); - let keysize = bytes*8/k; //in bytes - let msg = (0..keysize/8*k).map(|x|(255*x/(keysize/8*k-1)) as u8).collect::>(); - let generator = utils::random_gt(); - let keys = (0..keysize).map(|_|utils::random_scalar()).collect::>(); - let public_keys = keys.iter().map(|x|generator*x).collect::>(); - let space = utils::prepare_messages_to_space(k); - let pp = PublicParameters{space, ratelimiter_public_keys:public_keys,gt_gen:generator,k}; + let pp_key = shamir::recover_shares(&pp_keys, n as i64); + assert_eq!(pp_key,generator*realkey,"Public keys do not match"); let start = Instant::now(); let (ss,request) = phe_init(&"test".to_string(),&"test".to_string()); - println!("k: {} {:.2?} phe_init", k,start.elapsed()); + println!("{:.2?} phe_init",start.elapsed()); + + let responses = (0..t as usize).into_par_iter().map(|i|{ + let response = phe_ratelimiter(&rlkeys[i],&request,&pps[i]); + response + }).collect::>(); + let start = Instant::now(); - let response = phe_ratelimiter(&keys,&request,&pp); - println!("k: {} {:.2?} phe_ratelimiter", k,start.elapsed()); + let res = phe_enc_finish_t(&msg,&pps,&responses,&ss,n).unwrap(); + println!("{:.2?} phe_enc_finish_t",start.elapsed()); + + let (ss,request) = phe_init_decrypt(&"test".to_string(),&"test".to_string(),&request.n); + + let responses = (0..t as usize).into_par_iter().map(|i|{ + let response = phe_ratelimiter(&rlkeys[i],&request,&pps[i]); + response + }).collect::>(); + let start = Instant::now(); + let res = phe_dec_finish_t(&res,&pps,&responses,&ss,n); + println!("{:.2?} phe_dec_finish_t",start.elapsed()); + + assert_eq!(res.unwrap(),msg); +} + + +fn test_core_k() { + let msg = (0..64).map(|x|(x) as u8).collect::>(); + let generator = utils::random_gt(); + let key = utils::random_scalar(); + let public_key = generator*key; + let pp = PublicParameters{ + gt_gen: generator, + ratelimiter_public_key: public_key + }; + + let start = Instant::now(); + + let (ss,request) = phe_init(&"test".to_string(),&"test".to_string()); + println!("{:.2?} phe_init",start.elapsed()); + let response = phe_ratelimiter(&key,&request,&pp); + let start = Instant::now(); + for _i in 0..100{ + let response = phe_ratelimiter(&key,&request,&pp); + } + println!("{:.2?} phe_ratelimiter",start.elapsed()/1000); + let ciphertext = phe_enc_finish(&msg,&pp,&response,&ss).unwrap(); - println!("k: {} {:.2?} phe_enc_finish", k,start.elapsed()); - let start = Instant::now(); + for _i in 0..100{ + let ciphertext = phe_enc_finish(&msg,&pp,&response,&ss).unwrap(); + } + println!("{:.2?} phe_enc_finish",start.elapsed()/1000); + + let (ss,request) = phe_init_decrypt(&"test".to_string(),&"test".to_string(),&ciphertext.n); + + let response = phe_ratelimiter(&key,&request,&pp); + let expected = phe_dec_finish_simple(&ciphertext,&pp,&response,&ss).unwrap(); - println!("k: {} {:.2?} phe_dec_finish_simple", k,start.elapsed()); - assert_eq!(expected,msg); - let start = Instant::now(); - let prepared_space = ciphertext.iter().map(|x|utils::prepare_messages_to_precomputed_space(&pp.space, x)).collect::>(); - println!("k: {} {:.2?} prepare_messages_to_precomputed_space", k,start.elapsed()); + for _i in 0..100{ + let expected = phe_dec_finish_simple(&ciphertext,&pp,&response,&ss).unwrap(); + } - let start = Instant::now(); - - let expected = phe_dec_finish_preparedspace(&pp, &response, &ss, &prepared_space).unwrap(); - println!("k: {} {:.2?} phe_dec_finish_preparedspace", k,start.elapsed()); + println!("{:.2?} phe_dec_finish_simple", start.elapsed()/1000); assert_eq!(expected,msg); } \ No newline at end of file diff --git a/src/cryptoservice.rs b/src/cryptoservice.rs index afddfdb..9d078f4 100644 --- a/src/cryptoservice.rs +++ b/src/cryptoservice.rs @@ -3,40 +3,50 @@ extern crate bls12_381; extern crate rand; +use std::sync::RwLock; +use bls12_381::pairing; +use bls12_381::G2Affine; +use std::sync::Mutex; +use std::sync::Arc; use rocket_okapi::settings::UrlObject; use rocket_okapi::{openapi, openapi_get_routes, rapidoc::*, swagger_ui::*}; use rocket::serde::json::Json; use rocket::Request; -use ophe::core::{RatelimiterRequest,RatelimiterResponse}; +use ophe::core::{RatelimiterRequest,RatelimiterResponse,SetKeyHelper}; use ophe::core; use ophe::utils; use rocket::State; use bls12_381::Scalar; +use bls12_381::G1Affine; -fn make_public_parameters() -> (Vec,core::PublicParameters){ - let bytes = 32; - let k = 2; - let keysize = bytes*8/k; - let generator = utils::random_gt(); - let keys = (0..keysize).map(|_|utils::random_scalar()).collect::>(); - let public_keys = keys.iter().map(|x|generator*x).collect::>(); - let space = utils::prepare_messages_to_space(k); +fn make_public_parameters(key: Scalar) -> CryptoserviceState{ + let generator = pairing(&G1Affine::generator(), &G2Affine::generator()); + let public_key = generator * key; - (keys,core::PublicParameters{space, ratelimiter_public_keys:public_keys,gt_gen:generator,k}) + let pp = core::PublicParameters{ratelimiter_public_key:public_key,gt_gen:generator}; + CryptoserviceState{key,pp} } +struct CryptoserviceState { + key: Scalar, + pp: core::PublicParameters, +} + +type CryptoserviceStatePointer = Arc>; + + #[rocket::main] async fn main() { - let (keys,pp) = make_public_parameters(); + let c = make_public_parameters(utils::random_scalar()); + let c_p:CryptoserviceStatePointer = Arc::new(RwLock::new(c)); let launch_result = rocket::build() - .manage(pp) - .manage(keys) - .mount("/", openapi_get_routes![phe_help,get_public_parameters]) + .manage(c_p) + .mount("/", openapi_get_routes![phe_help,get_public_parameters,set_key]) .mount( "/swagger-ui/", make_swagger_ui(&SwaggerUIConfig { @@ -69,16 +79,38 @@ async fn main() { #[openapi()] #[post("/phe_help",format = "json", data = "")] -fn phe_help(request: Json,pp: &State,keys: &State>) -> Json { - Json(core::phe_ratelimiter(keys,&request,pp)) +fn phe_help(request: Json,c_state: &State) -> Json { + + let (key,pp) = { + let c_state = c_state.read().unwrap(); + (c_state.key.clone(),c_state.pp.clone()) + }; + + + Json(core::phe_ratelimiter(&key,&request,&pp)) } #[openapi()] #[get("/get_public_parameters")] -fn get_public_parameters(pp: &State) -> Json<&core::PublicParameters>{ +fn get_public_parameters(c_state: &State) -> Json{ + let pp = c_state.read().unwrap().pp.clone(); Json(pp) } + +#[openapi()] +#[post("/set_key",format = "json", data = "")] +fn set_key(request: Json,c_state: &State) -> Json { + let new_c = make_public_parameters(request.key); + + let mut c_state = c_state.write().unwrap(); + c_state.key = new_c.key; + c_state.pp = new_c.pp; + + + Json(new_c.pp) +} + #[catch(422)] fn serialize_failed(_req: &Request) -> String { format!("Malformed Request") diff --git a/src/lib.rs b/src/lib.rs index b6c3c88..e1a3a5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] mod proofs; pub mod utils; -mod shamir; +pub mod shamir; pub mod core; pub mod serializers; \ No newline at end of file diff --git a/src/ophe.rs b/src/ophe.rs index 9749a11..8c50683 100644 --- a/src/ophe.rs +++ b/src/ophe.rs @@ -3,40 +3,99 @@ extern crate bls12_381; extern crate rand; +use bls12_381::G2Affine; +use bls12_381::pairing; +use bls12_381::G1Affine; +use std::time::Duration; +use crate::core::EncryptedMessage; use rocket_okapi::settings::UrlObject; use rocket_okapi::{openapi, openapi_get_routes, rapidoc::*, swagger_ui::*}; use rocket::serde::json::Json; use rocket::Request; -use ophe::core::{RatelimiterResponse,PublicParameters}; +use ophe::core::{PublicParameters,RatelimiterRequest,RatelimiterResponse}; use ophe::core; use rocket::State; use serde::{Deserialize, Serialize}; use rocket_okapi::okapi::schemars; use rocket_okapi::okapi::schemars::JsonSchema; -use bls12_381::Scalar; -use bls12_381::Gt; -use ophe::serializers; use serde_with::serde_as; - +use futures::future::join_all; use rocket::http::Status; +struct RlState { + pp: PublicParameters, + url: String, + client: reqwest::Client +} + +struct OpheState { + pps: Vec, + n: i64, + t: i64, +} + +#[derive(Serialize,Deserialize,JsonSchema)] +struct EncryptRequest { + username: String, + password: String, + /// # data + /// data to be encrypted + data:String +} + +#[serde_as] +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct DecryptRequest{ + username: String, + password: String, + ciphertext: EncryptedMessage +} + + +#[catch(422)] +fn serialize_failed(_req: &Request) -> String { + format!("Malformed Request") +} + #[rocket::main] async fn main() { - let client = reqwest::Client::new(); - let res = client.get("http://localhost:9999/get_public_parameters").send() - .await.map_err(|x|{format!("Cryptoservice not reachable: {}",x)}).unwrap().text().await.unwrap(); + let cryptoservice_urls = vec!["http://localhost:9001","http://localhost:9002","http://localhost:9003"]; + let n = cryptoservice_urls.len(); + let t = n; + let rl_key = ophe::utils::random_scalar(); + let rl_public_key = pairing(&G1Affine::generator(), &G2Affine::generator()) * rl_key; + println!("Generated public key: {:?}",rl_public_key); + let keys = ophe::shamir::gen_shares_scalar(rl_key, n as i64,t as i64); + + let pps = keys.iter().zip(cryptoservice_urls).map( |(k,url)| async move{ + let client = reqwest::ClientBuilder::new().tcp_keepalive(Some(Duration::from_secs(60))).build().unwrap(); + let set_key_request = core::SetKeyHelper{key:k.clone()}; + let res = client.post(&format!("{}/set_key",url)).json(&set_key_request).send() + .await.map_err(|x|{format!("Cryptoservice not reachable: {}",x)}).unwrap().text().await.unwrap(); + let pp: PublicParameters = serde_json::from_str(&res).map_err(|x|{format!("Cryptoservice not reachable: {}",x)}).unwrap(); + RlState{pp,url:url.to_string(),client} + }).collect::>(); + + let pps = join_all(pps).await; + + let pp_keys = pps.iter().map(|x|{x.pp.ratelimiter_public_key}).collect::>(); + + let pp_key = ophe::shamir::recover_shares(&pp_keys, n as i64); + + assert_eq!(pp_key,rl_public_key,"Public keys do not match"); + + let o_state = OpheState{pps,n: n as i64,t: t as i64}; - let pp: PublicParameters = serde_json::from_str(&res).map_err(|x|{format!("Cryptoservice not reachable: {}",x)}).unwrap(); println!("Received public parameters from crytoservice"); - let launch_result = rocket::build().mount("/", openapi_get_routes![make_request,decrypt]) - .manage(pp) + let launch_result = rocket::build().mount("/", openapi_get_routes![encrypt,decrypt]) + .manage(o_state) .mount( "/swagger-ui/", make_swagger_ui(&SwaggerUIConfig { @@ -61,99 +120,60 @@ async fn main() { .register("/",catchers![serialize_failed]) .launch() .await; -match launch_result { - Ok(_) => println!("Rocket shut down gracefully."), - Err(err) => println!("Rocket had an error: {}", err), -}; -} - -#[derive(Serialize,Deserialize,JsonSchema)] -struct UsernamePw{ - username: String, - password: String -} - -#[catch(422)] -fn serialize_failed(_req: &Request) -> String { - format!("Malformed Request") -} - -#[serde_as] -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct EnrollResponse{ - #[serde_as(as = "serializers::SerializeScalar")] - #[schemars(with = "String")] - n: Scalar, - #[serde_as(as = "Vec")] - #[schemars(with = "Vec")] - ciphertext: Vec + + match launch_result { + Ok(_) => println!("Rocket shut down gracefully."), + Err(err) => println!("Rocket had an error: {}", err), + }; } #[openapi()] -#[post("/make_request",format = "json", data = "")] -async fn make_request(request: Json,pp: &State) -> Result,(Status, Json)> { +#[post("/encrypt",format = "json", data = "")] +async fn encrypt(request: Json,o_state: &State) -> Result,(Status, Json)> { let (ss,request1) = core::phe_init(&request.username,&request.password); - let client = reqwest::Client::new(); + let mut msg = request.data.clone().into_bytes(); - let res = client.post("http://localhost:9999/phe_help") - .json(&request1) - .send() - .await.map_err(|_x| {(Status::InternalServerError,Json("Cryptoserice unreachable.".to_string()))})?.text().await.map_err(|_x| {(Status::InternalServerError,Json("Cryptoserice unreachable.".to_string()))})?; - - let response: RatelimiterResponse = serde_json::from_str(&res).map_err(|_x| {(Status::InternalServerError,Json("Invalid cryptoservice response.".to_string()))})?; - - let mut msg = Vec::new(); - for _x in 0..32{ - msg.push(0u8); + if msg.len() > 64 { + return Err((Status::BadRequest,Json("Data too long.".to_string()))) } - let ciphertext = core::phe_enc_finish(&msg,&pp,&response,&ss).map_err(|x| {(Status::InternalServerError,Json("Decryption failed.".to_string()+&x))})?; + // pad to 64 bytes + msg.resize(64,0); - Ok(Json(EnrollResponse{n:ss.n,ciphertext})) -} + let pps = o_state.pps.iter().map(|x| x.pp).collect::>(); + let responses = get_ratelimiter_reponses(&request1,&o_state.pps).await.map_err(|x| {(Status::InternalServerError,Json("Decryption failed: ".to_string()+&x))})?; -#[serde_as] -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct DecryptRequest{ - #[serde_as(as = "serializers::SerializeScalar")] - #[schemars(with = "String")] - n: Scalar, - username: String, - password: String, - #[serde_as(as = "Vec")] - #[schemars(with = "Vec")] - ciphertext: Vec + let ciphertext = core::phe_enc_finish_t(&msg,&pps,&responses,&ss,o_state.n).map_err(|x| {(Status::InternalServerError,Json("Decryption failed.".to_string()+&x))})?; + + Ok(Json(ciphertext)) } #[openapi()] #[post("/decrypt",format = "json", data = "")] -async fn decrypt(request: Json,pp: &State) -> Result,(Status, Json)>{ +async fn decrypt(request: Json,o_state: &State) -> Result,(Status, Json)> { + let (ss,request1) = core::phe_init_decrypt(&request.username,&request.password,&request.ciphertext.n); - let res = decrypt_err(request,pp).await; + let responses = get_ratelimiter_reponses(&request1,&o_state.pps).await.map_err(|x| {(Status::InternalServerError,Json("Decryption failed: ".to_string()+&x))})?; - res + let pps = o_state.pps.iter().map(|x| x.pp).collect::>(); + + let res = core::phe_dec_finish_t(&request.ciphertext,&pps,&responses,&ss,o_state.n).map_err(|x| {(Status::InternalServerError,Json("Decryption failed: ".to_string()+&x))})?; + + Ok(Json(String::from_utf8(res).map_err(|_x| {(Status::InternalServerError,Json("Decryption failed. Utf8".to_string()))})?.trim_end_matches(char::from(0)).to_string())) } +async fn get_ratelimiter_reponses(request: &RatelimiterRequest, urls: &Vec) -> Result,String> { + let responses = urls.iter().map(|x| async move { + let res = x.client.post(&format!("{}/phe_help",x.url)) + .json(&request) + .send() + .await.map_err(|_x| {format!("Cryptoserice unreachable.")})?.text().await.map_err(|_x| {format!("Cryptoserice unreachable.")})?; + let rs = serde_json::from_str(&res).map_err(|_x| {format!("Invalid cryptoservice response.")})?; + Ok::<_,String>(rs) + + }); -async fn decrypt_err(request: Json,pp: &State) -> Result,(Status, Json)> { - let (ss,request1) = core::phe_init_decrypt(&request.username,&request.password,&request.n); - - let client = reqwest::Client::new(); - - let res = client.post("http://localhost:9999/phe_help") - .json(&request1) - .send() - .await.map_err(|_x| {(Status::InternalServerError,Json("Cryptoserice unreachable.".to_string()))})?.text().await.map_err(|_x| {(Status::InternalServerError,Json("Internal Error.".to_string()))})?; - - let response: RatelimiterResponse = serde_json::from_str(&res).map_err(|_x| {(Status::InternalServerError,Json("Invalid cryptoservice response.".to_string()))})?; - - let mut msg = Vec::new(); - for _x in 0..32{ - msg.push(0u8); - } - let expected = core::phe_dec_finish_simple(&request.ciphertext,&pp,&response,&ss).map_err(|x| {(Status::InternalServerError,Json("Decryption failed.".to_string()+&x))})?; - println!("{:?}",expected); - Ok(Json(true)) + join_all(responses).await.into_iter().flatten().collect() } diff --git a/src/shamir.rs b/src/shamir.rs index 1cfd268..9b25094 100644 --- a/src/shamir.rs +++ b/src/shamir.rs @@ -1,4 +1,5 @@ use bls12_381::*; +use rayon::prelude::*; use crate::utils; fn lambda(i: i64, shares: i64) -> (Scalar, bool) { @@ -19,17 +20,15 @@ fn lambda(i: i64, shares: i64) -> (Scalar, bool) { } } -fn recover_shares(shares: &Vec, n: i64) -> Gt { - let mut target = Gt::identity(); - for i in 0..shares.len() { +pub fn recover_shares(shares: &Vec, n: i64) -> Gt { + shares.par_iter().enumerate().map(|(i, share)| { let (l, b) = lambda((i + 1) as i64, n); - let mut temp = shares[i] * l; + let mut temp = share * l; if b { temp = -temp } - target = target + temp; - } - return target; + temp + }).reduce(|| Gt::identity(), |a, b| a + b) } fn recover_shares_scalar(shares: &Vec, n: i64) -> Scalar { @@ -61,7 +60,7 @@ fn eval_at_scalar(poly: &Vec, x: i64) -> Scalar { return y; } -fn gen_shares(secret: Gt, n: i64, t: i64) -> Vec { +pub fn gen_shares(secret: Gt, n: i64, t: i64) -> Vec { assert!(t > 0); assert!(n >= t); assert!(t <= n); @@ -77,7 +76,7 @@ fn gen_shares(secret: Gt, n: i64, t: i64) -> Vec { return shares; } -fn gen_shares_scalar(secret: Scalar, n: i64, t: i64) -> Vec { +pub fn gen_shares_scalar(secret: Scalar, n: i64, t: i64) -> Vec { assert!(t > 0); assert!(n >= t); assert!(t <= n); diff --git a/src/utils.rs b/src/utils.rs index f442b00..3aeac35 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -4,11 +4,6 @@ use sha2::{Digest, Sha512}; use std::convert::TryInto; use bls12_381::*; use rand_core::{OsRng,RngCore}; -use std::collections::HashMap; -use rayon::prelude::*; -#[cfg(test)] -use std::time::Instant; - pub fn random_scalar() -> Scalar { let mut buf = [0u8; 64]; @@ -20,6 +15,71 @@ pub fn random_gt() -> Gt{ Gt::random(OsRng) } +pub trait HashData { + fn hash_data(&self) -> Vec; +} + +impl HashData for &Gt { + fn hash_data(&self) -> Vec { + self.to_compressed().to_vec() + } +} + +impl HashData for &G2Affine { + fn hash_data(&self) -> Vec { + self.to_compressed().to_vec() + } +} + +impl HashData for &G1Affine { + fn hash_data(&self) -> Vec { + self.to_compressed().to_vec() + } +} + +impl HashData for &Scalar { + fn hash_data(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl HashData for &String { + fn hash_data(&self) -> Vec { + self.as_bytes().to_vec() + } +} + +impl HashData for &[u8; 64] { + fn hash_data(&self) -> Vec { + self.to_vec() + } +} + +impl HashData for &Vec<&dyn HashData> { + fn hash_data(&self) -> Vec { + self.iter().map(|x| x.hash_data()).flatten().collect() + } +} + + +pub fn hash_data(elements: &[&dyn HashData]) -> [u8; 64] { + let buf:[u8; 64]; + let mut hasher = Sha512::new(); + for x in elements { + hasher.update(x.hash_data()); + } + let result = hasher.finalize(); + buf = result.as_slice().try_into().expect("Wrong length"); + + buf +} + +pub fn hash_to_scalar(elements: &[&dyn HashData]) -> Scalar { + let buf = hash_data(elements); + Scalar::from_bytes_wide(&buf) +} + + pub fn hash_string_to_scalar(element: String) -> Scalar { let buf:[u8; 64]; let mut hasher = Sha512::new(); @@ -44,144 +104,25 @@ pub fn hash_gt_to_scalar(elements: &[&Gt]) -> Scalar { Scalar::from_bytes_wide(&buf) } -pub fn prepare_messages_to_space(k: usize) -> Vec{ - let mut m = Vec::new(); - for _j in 0..1 << k { - //let current_m = Scalar::from_raw([(j + 1) as u64, 0, 0, 0]); - let current_m = random_scalar(); - m.push(current_m); +pub fn convert_64_bytes_to_scalar(bytes: &[u8]) -> Result { + if bytes.len() != 64 { + return Err("Wrong length"); } - m + let mut buf = [0u8; 64]; + buf.copy_from_slice(bytes); + Ok(Scalar::from_bytes_wide(&buf)) } - -pub fn prepare_messages_to_precomputed_space(space: &Vec,cipher: &Gt) -> HashMap<[u8;288],usize>{ - let mapped = space.par_iter().map(|x|(cipher * x.invert().unwrap()).to_compressed()).collect::>(); - let mut m = HashMap::new(); - (0..space.len()).for_each(|i| { - m.insert( mapped[i],i); - }); - m +pub fn vec_to_msg(vec: &Vec) -> [u8; 64] { + let mut buf = [0u8; 64]; + buf.copy_from_slice(vec); + buf } -pub fn find_in_precomputed_space(ciphertext: &Vec,_space: &Vec,maps: &Vec>,k:usize) -> Vec{ - let mut prep = Vec::new(); - for i in 0..ciphertext.len() { - let index = maps[i][&ciphertext[i].to_compressed()]; - for z in 0..k{ - prep.push((index>>z)&1); - } +pub fn xor(a: &[u8], b: &[u8]) -> [u8; 64] { + let mut buf = [0u8; 64]; + for i in 0..64 { + buf[i] = a[i] ^ b[i]; } - let mut result = Vec::new(); - for i in 0..prep.len()/8{ - let mut x = 0u8; - for j in 0..8{ - x = x + (prep[i*8+j]<,space: &Vec,k: usize) -> Vec { - let mut preparedmsg = Vec::new(); - for i in 0..msg.len() { - for j in 0..8{ - preparedmsg.push((msg[i]&(1<>j); - } - } - let msgsize = preparedmsg.len(); // size of msg in bits - assert!(msgsize%k==0,"Msg must be aligned to space"); //TODO extend msg with size and padding - let steps = msgsize / k; - let mut result = Vec::new(); - for i in 0..steps{ - let mut x = 0; - for j in 0..k{ - x = x + ((preparedmsg[i*k+j] as usize)<,ciphertext: &Vec,space:& Vec,k: usize) -> Result,String>{ - let mut prep = Vec::new(); - let spacesize = space.len(); - - let t = target.par_iter().map(|x|space.iter().map(|y|x*y).collect::>()).collect::>(); - - for i in 0..target.len() { - let mut found = false; - for j in 0..spacesize{ - if t[i][j] == ciphertext[i]{ - found = true; - for z in 0..k{ - prep.push((j>>z)&1); - } - } - } - if !found { - return Err("Decryption failed.".to_string()) - } - } - let mut result = Vec::new(); - for i in 0..prep.len()/8{ - let mut x = 0u8; - for j in 0..8{ - x = x | (prep[i*8+j]<>(); - let test = (&split).into_iter().map(|x|gt*x).collect::>(); - - let start = Instant::now(); - let solved = solve_dlog(>s, &test, &space, k).unwrap(); - - println!("k: {} {:.2?} solve dlog ", k,start.elapsed()); - - assert_eq!(msg.to_vec(),solved); - let start = Instant::now(); - let prepared_space= (&test).into_iter().map(|x|prepare_messages_to_precomputed_space(&space, x)).collect::>>(); - - - println!("k: {} {:.2?} precomputing message space", k,start.elapsed()); - - let start = Instant::now(); - let prepared_solved = find_in_precomputed_space(>s,&space,&prepared_space, k); - - println!("k: {} {:.2?} lookup in prepared message space", k,start.elapsed()); - - assert_eq!(msg.to_vec(),prepared_solved); - -} - - -#[test] -fn test_msg_space() { - //rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap(); - test_msg_space_k(2); - test_msg_space_k(4); - test_msg_space_k(8); - //test_msg_space_k(16); + buf } \ No newline at end of file