diff --git a/src/core.rs b/src/core.rs index 0fbd199..60b41af 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,4 +1,5 @@ use bls12_381::*; +use group::Group; use crate::utils; use crate::proofs; use crate::shamir; @@ -74,9 +75,28 @@ pub struct RatelimiterResponse{ u: Gt, } +#[serde_as] +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct RatelimiterRotateRequest{ + #[serde_as(as = "serializers::SerializeScalar")] + #[schemars(with = "String")] + pub s: Scalar, +} #[serde_as] #[derive(Serialize, Deserialize, JsonSchema)] +pub struct RatelimiterRotateResponse{ + #[serde_as(as = "serializers::SerializeScalar")] + #[schemars(with = "String")] + pub n: Scalar, + #[serde_as(as = "serializers::SerializeGt")] + #[schemars(with = "String")] + pub pk_new: Gt, +} + + +#[serde_as] +#[derive(Serialize, Deserialize, JsonSchema, Clone)] pub struct EncryptedMessage{ #[serde_as(as = "serde_with::base64::Base64")] #[schemars(with = "String")] @@ -273,6 +293,22 @@ pub fn phe_dec_finish_simple(ciphertext: &EncryptedMessage,pp: &PublicParameters } + +pub fn phe_roate_ratelimiter(private_key: &Scalar, s: &Scalar) -> (RatelimiterRotateResponse, Scalar){ + let k_new = private_key + s; + let pk_i = Gt::generator() * k_new; + let n_i = utils::random_scalar(); + + (RatelimiterRotateResponse{n: n_i, pk_new: pk_i}, k_new) +} + +pub fn find_set(public_key: &Gt, public_key_shares: &Vec, n: &i64) -> Vec{ + let test_pk = shamir::recover_shares(public_key_shares, *n); + + assert_eq!(public_key.clone(), test_pk, "Find Set: Public keys do not match"); + return public_key_shares.clone(); +} + #[test] fn test_core(){ //rayon::ThreadPoolBuilder::new().num_threads(8).build_global().unwrap(); diff --git a/src/cryptoservice.rs b/src/cryptoservice.rs index 9d078f4..bf6ac65 100644 --- a/src/cryptoservice.rs +++ b/src/cryptoservice.rs @@ -13,8 +13,9 @@ use rocket_okapi::{openapi, openapi_get_routes, rapidoc::*, swagger_ui::*}; use rocket::serde::json::Json; use rocket::Request; -use ophe::core::{RatelimiterRequest,RatelimiterResponse,SetKeyHelper}; +use ophe::core::{RatelimiterRequest,RatelimiterResponse,SetKeyHelper, RatelimiterRotateRequest, RatelimiterRotateResponse}; use ophe::core; + use ophe::utils; use rocket::State; use bls12_381::Scalar; @@ -46,7 +47,7 @@ async fn main() { let launch_result = rocket::build() .manage(c_p) - .mount("/", openapi_get_routes![phe_help,get_public_parameters,set_key]) + .mount("/", openapi_get_routes![phe_help,get_public_parameters,set_key, rotate_key]) .mount( "/swagger-ui/", make_swagger_ui(&SwaggerUIConfig { @@ -111,6 +112,17 @@ fn set_key(request: Json,c_state: &State,c_state: &State) -> Json { + let mut c_state = c_state.write().unwrap(); + let (response, k_new) = core::phe_roate_ratelimiter(&c_state.key, &request.s); + c_state.key = k_new; + + Json(response) +} + #[catch(422)] fn serialize_failed(_req: &Request) -> String { format!("Malformed Request") diff --git a/src/ophe.rs b/src/ophe.rs index 8c50683..e8e0f84 100644 --- a/src/ophe.rs +++ b/src/ophe.rs @@ -4,8 +4,11 @@ extern crate bls12_381; extern crate rand; use bls12_381::G2Affine; +use bls12_381::Gt; +use bls12_381::Scalar; use bls12_381::pairing; use bls12_381::G1Affine; +use rocket::request; use std::time::Duration; use crate::core::EncryptedMessage; use rocket_okapi::settings::UrlObject; @@ -13,8 +16,10 @@ use rocket_okapi::{openapi, openapi_get_routes, rapidoc::*, swagger_ui::*}; use rocket::serde::json::Json; use rocket::Request; -use ophe::core::{PublicParameters,RatelimiterRequest,RatelimiterResponse}; +use ophe::core::{PublicParameters,RatelimiterRequest,RatelimiterResponse,RatelimiterRotateRequest,RatelimiterRotateResponse}; use ophe::core; +use ophe::utils::random_scalar; +use ophe::shamir; use rocket::State; use serde::{Deserialize, Serialize}; @@ -25,17 +30,19 @@ use futures::future::join_all; use rocket::http::Status; - +#[derive(Clone)] struct RlState { pp: PublicParameters, url: String, - client: reqwest::Client + client: reqwest::Client, + s: Scalar, } struct OpheState { pps: Vec, n: i64, t: i64, + pk: Gt, } #[derive(Serialize,Deserialize,JsonSchema)] @@ -55,6 +62,12 @@ pub struct DecryptRequest{ ciphertext: EncryptedMessage } +#[serde_as] +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct RotateRequest{ + ciphertext: EncryptedMessage +} + #[catch(422)] fn serialize_failed(_req: &Request) -> String { @@ -78,7 +91,7 @@ async fn main() { 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} + RlState{pp,url:url.to_string(),client,s: Scalar::zero()} }).collect::>(); let pps = join_all(pps).await; @@ -89,12 +102,12 @@ async fn main() { 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 o_state = OpheState{pps,n: n as i64,t: t as i64, pk: pp_key}; println!("Received public parameters from crytoservice"); - let launch_result = rocket::build().mount("/", openapi_get_routes![encrypt,decrypt]) + let launch_result = rocket::build().mount("/", openapi_get_routes![encrypt,decrypt,rotate]) .manage(o_state) .mount( "/swagger-ui/", @@ -163,6 +176,18 @@ async fn decrypt(request: Json,o_state: &State) -> Re 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())) } + +#[openapi()] +#[post("/rotate",format = "json", data = "")] +async fn rotate(request: Json,o_state: &State) -> Result,(Status, Json)> { + let sk_new = random_scalar(); + let shares = shamir::gen_shares_scalar(Scalar::zero(), o_state.n, o_state.t); + let ct_new = request.ciphertext.clone(); + let responses = get_ratelimiter_rotate_reponses(&shares, &o_state.pps).await.map_err(|x| {(Status::InternalServerError,Json("Rotation failed: ".to_string()+&x))})?; + + Ok(Json(ct_new)) +} + 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)) @@ -177,3 +202,26 @@ async fn get_ratelimiter_reponses(request: &RatelimiterRequest, urls: &Vec, urls: &Vec) -> Result,String> { + // insert shares to ratelimiter state + let mut requests = Vec::new(); + for (index, item) in urls.iter().enumerate() { + let mut tmp_state:RlState = item.clone(); + tmp_state.s = shares[index]; + requests.push(tmp_state); + } + + let responses = requests.iter().map(|x| async move { + let request = RatelimiterRotateRequest{s:x.s}; + let res = x.client.post(&format!("{}/rotate_key",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) + }); + + join_all(responses).await.into_iter().flatten().collect() +}