Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor enclave server #18

Merged
merged 1 commit into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/server/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[env]
INFURAKEY = "default val"
PRIVATEKEY = "default val"
11 changes: 11 additions & 0 deletions packages/server/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

358 changes: 3 additions & 355 deletions packages/server/src/bin/cli.rs
Original file line number Diff line number Diff line change
@@ -1,357 +1,5 @@
mod util;
use rfv::cli::run_cli;

use dialoguer::{theme::ColorfulTheme, Input, FuzzySelect};
use std::{thread, time, env};
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::Read;

use fhe::{
bfv::{BfvParametersBuilder, Encoding, Plaintext, PublicKey},
};
use fhe_traits::{FheEncoder, FheEncrypter, Serialize as FheSerialize, DeserializeParametrized};
use rand::{thread_rng};
use util::timeit::{timeit};

use hyper::Request;
use hyper::Method;
use hyper_tls::HttpsConnector;
use hyper_util::{client::legacy::Client as HyperClient, rt::TokioExecutor};
use bytes::Bytes;

use http_body_util::Empty;
use http_body_util::BodyExt;
use tokio::io::{AsyncWriteExt as _, self};

use hmac::{Hmac, Mac};
use jwt::SignWithKey;
use sha2::Sha256;
use std::collections::BTreeMap;


#[derive(Debug, Deserialize, Serialize)]
struct JsonResponse {
response: String
}

#[derive(Debug, Deserialize, Serialize)]
struct JsonResponseTxHash {
response: String,
tx_hash: String,
}

#[derive(Debug, Deserialize, Serialize)]
struct JsonRequestGetRounds {
response: String,
}

#[derive(Debug, Deserialize, Serialize)]
struct RoundCount {
round_count: u32,
}

#[derive(Debug, Deserialize, Serialize)]
struct JsonRequest {
response: String,
pk_share: u32,
id: u32,
round_id: u32,
}

#[derive(Debug, Deserialize, Serialize)]
struct CrispConfig {
round_id: u32,
poll_length: u32,
chain_id: u32,
voting_address: String,
ciphernode_count: u32,
enclave_address: String,
authentication_id: String,
}

#[derive(Debug, Deserialize, Serialize)]
struct PKRequest {
round_id: u32,
pk_bytes: Vec<u8>,
}

#[derive(Debug, Deserialize, Serialize)]
struct EncryptedVote {
round_id: u32,
enc_vote_bytes: Vec<u8>,
postId: String,
}

#[derive(Debug, Deserialize, Serialize)]
struct AuthenticationLogin {
postId: String,
}

#[derive(Debug, Deserialize, Serialize)]
struct AuthenticationResponse {
response: String,
jwt_token: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {

let https = HttpsConnector::new();
//let client = HyperClient::builder(TokioExecutor::new()).build::<_, Empty<Bytes>>(https);
let client_get = HyperClient::builder(TokioExecutor::new()).build::<_, Empty<Bytes>>(https.clone());
let client = HyperClient::builder(TokioExecutor::new()).build::<_, String>(https);
let mut auth_res = AuthenticationResponse {
response: "".to_string(),
jwt_token: "".to_string(),
};

print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
let selections = &[
"CRISP: Voting Protocol (ETH)",
"More Coming Soon!"
];

let selections_2 = &[
"Initialize new CRISP round.",
"Continue Existing CRISP round."
];

let selections_3 = &[
"Abstain.",
"Vote yes.",
"Vote no."
];

let selection_1 = FuzzySelect::with_theme(&ColorfulTheme::default())
.with_prompt("Enclave (EEEE): Please choose the private execution environment you would like to run!")
.default(0)
.items(&selections[..])
.interact()
.unwrap();

if selection_1 == 0 {
print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
//println!("Encrypted Protocol Selected {}!", selections[selection_1]);
let selection_2 = FuzzySelect::with_theme(&ColorfulTheme::default())
.with_prompt("Create a new CRISP round or particpate in an existing round.")
.default(0)
.items(&selections_2[..])
.interact()
.unwrap();

if selection_2 == 0 {
print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
println!("Starting new CRISP round!");
// let input_token: String = Input::with_theme(&ColorfulTheme::default())
// .with_prompt("Enter Proposal Registration Token")
// .interact_text()
// .unwrap();
print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
println!("Reading proposal details from config.");
let path = env::current_dir().unwrap();
let mut pathst = path.display().to_string();
pathst.push_str("/example_config.json");
let mut file = File::open(pathst).unwrap();
let mut data = String::new();
file.read_to_string(&mut data).unwrap();
let config: CrispConfig = serde_json::from_str(&data).expect("JSON was not well-formatted");
println!("round id: {:?}", config.round_id); // get new round id from current id in server
println!("poll length {:?}", config.poll_length);
println!("chain id: {:?}", config.chain_id);
println!("voting contract: {:?}", config.voting_address);
println!("ciphernode count: {:?}", config.ciphernode_count);

println!("Initializing Keyshare nodes...");

let response_id = JsonRequestGetRounds { response: "Test".to_string() };
let _out = serde_json::to_string(&response_id).unwrap();
let mut url_id = config.enclave_address.clone();
url_id.push_str("/get_rounds");

//let token = Authorization::bearer("some-opaque-token").unwrap();
//println!("bearer token {:?}", token.token());
//todo: add auth field to config file to get bearer token
let req = Request::builder()
.method(Method::GET)
.uri(url_id)
.body(Empty::<Bytes>::new())?;

let resp = client_get.request(req).await?;

println!("Response status: {}", resp.status());

let body_bytes = resp.collect().await?.to_bytes();
let body_str = String::from_utf8(body_bytes.to_vec()).unwrap();
let count: RoundCount = serde_json::from_str(&body_str).expect("JSON was not well-formatted");
println!("Server Round Count: {:?}", count.round_count);

// TODO: get secret from env var
// let key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret")?;
// let mut claims = BTreeMap::new();
// claims.insert("postId", config.authentication);
// let mut bearer_str = "Bearer ".to_string();
// let token_str = claims.sign_with_key(&key)?;
// bearer_str.push_str(&token_str);
// println!("{:?}", bearer_str);

let round_id = count.round_count + 1;
let response = CrispConfig {
round_id: round_id,
poll_length: config.poll_length,
chain_id: config.chain_id,
voting_address: config.voting_address,
ciphernode_count: config.ciphernode_count,
enclave_address: config.enclave_address.clone(),
authentication_id: config.authentication_id.clone(),
};
let out = serde_json::to_string(&response).unwrap();
let mut url = config.enclave_address.clone();
url.push_str("/init_crisp_round");
let req = Request::builder()
.header("authorization", "Bearer fpKL54jvWmEGVoRdCNjG")
.method(Method::POST)
.uri(url)
.body(out)?;

let mut resp = client.request(req).await?;

println!("Response status: {}", resp.status());

while let Some(next) = resp.frame().await {
let frame = next?;
if let Some(chunk) = frame.data_ref() {
io::stdout().write_all(chunk).await?;
}
}
println!("Round Initialized.");
println!("Gathering Keyshare nodes for execution environment...");
let three_seconds = time::Duration::from_millis(1000);
thread::sleep(three_seconds);
println!("\nYou can now vote Encrypted with Round ID: {:?}", round_id);

}
if selection_2 == 1 {
print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
let input_crisp_id: u32 = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Enter CRISP round ID.")
.interact_text()
.unwrap();
let path = env::current_dir().unwrap();
let mut pathst = path.display().to_string();
pathst.push_str("/example_config.json");
let mut file = File::open(pathst).unwrap();
let mut data = String::new();
file.read_to_string(&mut data).unwrap();
let config: CrispConfig = serde_json::from_str(&data).expect("JSON was not well-formatted");
println!("Voting state Initialized");

// get authentication token
let user = AuthenticationLogin {
postId: config.authentication_id.clone(),
};

let out = serde_json::to_string(&user).unwrap();
let mut url = config.enclave_address.clone();
url.push_str("/authentication_login");
let req = Request::builder()
.method(Method::POST)
.uri(url)
.body(out)?;

let mut resp = client.request(req).await?;
let body_bytes = resp.collect().await?.to_bytes();
let body_str = String::from_utf8(body_bytes.to_vec()).unwrap();
auth_res = serde_json::from_str(&body_str).expect("JSON was not well-formatted");
println!("Authentication response {:?}", auth_res);

// get public encrypt key
let v: Vec<u8> = vec! [0];
let response_pk = PKRequest { round_id: input_crisp_id, pk_bytes: v };
let out = serde_json::to_string(&response_pk).unwrap();
let mut url = config.enclave_address.clone();
url.push_str("/get_pk_by_round");
let req = Request::builder()
.method(Method::POST)
.uri(url)
.body(out)?;

let resp = client.request(req).await?;

println!("Response status: {}", resp.status());

let body_bytes = resp.collect().await?.to_bytes();
let body_str = String::from_utf8(body_bytes.to_vec()).unwrap();
let pk_res: PKRequest = serde_json::from_str(&body_str).expect("JSON was not well-formatted");
println!("Shared Public Key for CRISP round {:?} collected.", pk_res.round_id);

let degree = 4096;
let plaintext_modulus: u64 = 4096;
let moduli = vec![0xffffee001, 0xffffc4001, 0x1ffffe0001];
// Let's generate the BFV parameters structure.
let params = timeit!(
"Parameters generation",
BfvParametersBuilder::new()
.set_degree(degree)
.set_plaintext_modulus(plaintext_modulus)
.set_moduli(&moduli)
.build_arc()?
);
let pk_deserialized = PublicKey::from_bytes(&pk_res.pk_bytes, &params).unwrap();
// todo: validate that this user can vote
let selection_3 = FuzzySelect::with_theme(&ColorfulTheme::default())
.with_prompt("Please select your voting option.")
.default(0)
.items(&selections_3[..])
.interact()
.unwrap();

let mut vote_choice: u64 = 0;
if selection_3 == 0 {
println!("Exiting voting system. You may choose to vote later.");
vote_choice = 0;
}
if selection_3 == 1 {
vote_choice = 1;
}
if selection_3 == 2 {
vote_choice = 0;
}
println!("Encrypting vote.");
let votes: Vec<u64> = [vote_choice].to_vec();
let pt = Plaintext::try_encode(&[votes[0]], Encoding::poly(), &params)?;
let ct = pk_deserialized.try_encrypt(&pt, &mut thread_rng())?;
println!("Vote encrypted.");
println!("Calling voting contract with encrypted vote.");

let request_contract = EncryptedVote {
round_id: input_crisp_id,
enc_vote_bytes: ct.to_bytes(),
postId: auth_res.jwt_token,
};
let out = serde_json::to_string(&request_contract).unwrap();
let mut url = config.enclave_address.clone();
url.push_str("/broadcast_enc_vote");
let req = Request::builder()
.method(Method::POST)
.uri(url)
.body(out)?;

let resp = client.request(req).await?;

println!("Response status: {}", resp.status());

let body_bytes = resp.collect().await?.to_bytes();
let body_str = String::from_utf8(body_bytes.to_vec()).unwrap();
let contract_res: JsonResponseTxHash = serde_json::from_str(&body_str).expect("JSON was not well-formatted");
println!("Contract call: {:?}", contract_res.response);
println!("TxHash is {:?}", contract_res.tx_hash);
}

}
if selection_1 == 1 {
println!("Check back soon!");
std::process::exit(1);
}

Ok(())
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
run_cli()
}
Loading
Loading