diff --git a/Cargo.lock b/Cargo.lock index 1478f7b..a57be16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -875,9 +875,11 @@ name = "lox-distributor" version = "0.1.0" dependencies = [ "base64 0.13.1", + "chrono", "futures", "hex_fmt", "hyper", + "julianday", "lox", "lox_utils", "rand 0.8.5", @@ -885,7 +887,9 @@ dependencies = [ "serde", "serde_json", "serde_with", + "time 0.3.21", "tokio", + "zkp", ] [[package]] diff --git a/crates/lox-distributor/Cargo.toml b/crates/lox-distributor/Cargo.toml index 43a6623..1565662 100644 --- a/crates/lox-distributor/Cargo.toml +++ b/crates/lox-distributor/Cargo.toml @@ -11,16 +11,23 @@ keywords = ["tor", "lox", "bridges"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +julianday = "1.2.0" base64 = "0.13.1" hyper = { version = "0.14.24", features = ["server"] } hex_fmt = "0.3" futures = "0.3.26" +time = "0.3.21" tokio = { version = "1", features = ["full", "macros", "signal"] } rand = "0.8.5" serde = { version = "1.0", features = ["derive"] } serde_with = "3.0.0" serde_json = "1.0.87" +zkp = "0.8.0" lox = { path = "../lox-library", version = "0.1.0"} lox_utils = { path = "../lox-utils", version = "0.1.0"} rdsys_backend = { path = "../rdsys-backend-api", version = "0.1.0"} + +[dependencies.chrono] +version = "0.4.19" +features = ["serde"] \ No newline at end of file diff --git a/crates/lox-distributor/src/lox_context.rs b/crates/lox-distributor/src/lox_context.rs index 6c476e2..38fa80a 100644 --- a/crates/lox-distributor/src/lox_context.rs +++ b/crates/lox-distributor/src/lox_context.rs @@ -6,27 +6,12 @@ use lox::{ blockage_migration, check_blockage, issue_invite, level_up, migration, open_invite, redeem_invite, trust_promotion, }, - BridgeAuth, BridgeDb, IssuerPubKey, OPENINV_LENGTH, + BridgeAuth, BridgeDb, IssuerPubKey, }; +use lox_utils; use rand::RngCore; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; use std::sync::{Arc, Mutex}; -#[serde_as] -#[derive(Serialize, Deserialize)] -pub struct Invite { - #[serde_as(as = "[_; OPENINV_LENGTH]")] - invite: [u8; OPENINV_LENGTH], -} - -#[serde_as] -#[derive(Serialize, Deserialize)] -pub struct EncBridgeTable { - #[serde_as(as = "Vec<[_; ENC_BUCKET_BYTES]>")] - etable: Vec<[u8; ENC_BUCKET_BYTES]>, -} - /// Create a random BridgeLine for testing ONLY. Do not use in production! /// This was copied directly from lox/src/bridge_table.rs in order /// to easily initialize a bridgedb/lox_auth with structurally @@ -141,7 +126,10 @@ impl LoxServerContext { ba_obj.bridge_update(&bridgeline) } - fn advance_days_TEST(&self, num: u16) { + #[cfg(test)] + /// For testing only: manually advance the day by the given number + /// of days. + pub fn advance_days_TEST(&self, num: u16) { let mut ba_obj = self.ba.lock().unwrap(); ba_obj.advance_days(num); // FOR TESTING ONLY println!("Today's date according to server: {}", ba_obj.today()); @@ -164,9 +152,9 @@ impl LoxServerContext { ] } - fn gen_invite(&self) -> Invite { + fn gen_invite(&self) -> lox_utils::Invite { let obj = self.db.lock().unwrap(); - Invite { + lox_utils::Invite { invite: obj.invite(), } } @@ -241,9 +229,8 @@ pub fn generate_invite(context: LoxServerContext) -> Response { // Return the serialized encrypted bridge table pub fn send_reachability_cred(context: LoxServerContext) -> Response { - context.advance_days_TEST(85); // FOR TESTING ONLY let enc_table = context.encrypt_table(); - let etable = EncBridgeTable { etable: enc_table }; + let etable = lox_utils::EncBridgeTable { etable: enc_table }; prepare_header(serde_json::to_string(&etable).unwrap()) } @@ -262,7 +249,6 @@ pub fn verify_and_send_open_cred(request: Bytes, context: LoxServerContext) -> R pub fn verify_and_send_trust_promo(request: Bytes, context: LoxServerContext) -> Response { let req: trust_promotion::Request = serde_json::from_slice(&request).unwrap(); - context.advance_days_TEST(31); // FOR TESTING ONLY let response = context.trust_promo(req); let trust_promo_resp_str = serde_json::to_string(&response).unwrap(); prepare_header(trust_promo_resp_str) diff --git a/crates/lox-distributor/src/request_handler.rs b/crates/lox-distributor/src/request_handler.rs index 1aedaf5..bf8c62b 100644 --- a/crates/lox-distributor/src/request_handler.rs +++ b/crates/lox-distributor/src/request_handler.rs @@ -1,9 +1,7 @@ -use hyper::{body, header::HeaderValue, Body, Method, Request, Response, StatusCode}; - -use std::convert::Infallible; - use crate::lox_context; use crate::lox_context::LoxServerContext; +use hyper::{body, header::HeaderValue, Body, Method, Request, Response, StatusCode}; +use std::convert::Infallible; // Lox Request handling logic for each Lox request/protocol pub async fn handle( @@ -77,13 +75,22 @@ pub async fn handle( mod tests { use super::*; - use lox::{BridgeAuth, BridgeDb}; + use chrono::{Duration, Utc}; + use julianday::JulianDay; + use lox::{cred::BucketReachability, proto, BridgeAuth, BridgeDb}; + use lox_utils; use std::sync::{Arc, Mutex}; trait LoxClient { fn invite(&self) -> Request; fn reachability(&self) -> Request; fn pubkeys(&self) -> Request; + fn openinvite(&self, request: proto::open_invite::Request) -> Request; + fn trustpromo(&self, request: proto::trust_promotion::Request) -> Request; + fn trustmigration(&self, request: proto::migration::Request) -> Request; + fn levelup(&self, request: proto::level_up::Request) -> Request; + fn issueinvite(&self, request: proto::issue_invite::Request) -> Request; + fn redeeminvite(&self, request: proto::redeem_invite::Request) -> Request; } struct LoxClientMock {} @@ -114,7 +121,75 @@ mod tests { .unwrap(); req } + + fn openinvite(&self, request: proto::open_invite::Request) -> Request { + let req_str = serde_json::to_string(&request).unwrap(); + let req = Request::builder() + .header("Content-Type", "application/json") + .method("POST") + .uri("http://localhost/openreq") + .body(Body::from(req_str)) + .unwrap(); + req + } + + fn trustpromo(&self, request: proto::trust_promotion::Request) -> Request { + let req_str = serde_json::to_string(&request).unwrap(); + let req = Request::builder() + .header("Content-Type", "application/json") + .method("POST") + .uri("http://localhost/trustpromo") + .body(Body::from(req_str)) + .unwrap(); + req + } + + fn trustmigration(&self, request: proto::migration::Request) -> Request { + let req_str = serde_json::to_string(&request).unwrap(); + let req = Request::builder() + .header("Content-Type", "application/json") + .method("POST") + .uri("http://localhost/trustmig") + .body(Body::from(req_str)) + .unwrap(); + req + } + + fn levelup(&self, request: proto::level_up::Request) -> Request { + let req_str = serde_json::to_string(&request).unwrap(); + let req = Request::builder() + .header("Content-Type", "application/json") + .method("POST") + .uri("http://localhost/levelup") + .body(Body::from(req_str)) + .unwrap(); + req + } + + + fn issueinvite(&self, request: proto::issue_invite::Request) -> Request { + let req_str = serde_json::to_string(&request).unwrap(); + let req = Request::builder() + .header("Content-Type", "application/json") + .method("POST") + .uri("http://localhost/issueinvite") + .body(Body::from(req_str)) + .unwrap(); + req + } + + fn redeeminvite(&self, request: proto::redeem_invite::Request) -> Request { + let req_str = serde_json::to_string(&request).unwrap(); + let req = Request::builder() + .header("Content-Type", "application/json") + .method("POST") + .uri("http://localhost/redeem") + .body(Body::from(req_str)) + .unwrap(); + req + } } + struct TestHarness { context: LoxServerContext, } @@ -153,28 +228,246 @@ mod tests { }; Self { context } } + + fn advance_days(&mut self, days: u16) { + self.context.advance_days_TEST(days) + } } - + + // This should only be used for testing, use today in production + fn test_today(days: i64) -> u32 { + let naive_now_plus = (Utc::now() + Duration::days(days)).date_naive(); + JulianDay::from(naive_now_plus).inner().try_into().unwrap() + } + + async fn body_to_string(res: Response) -> String { + let body_bytes = hyper::body::to_bytes(res.into_body()).await.unwrap(); + String::from_utf8(body_bytes.to_vec()).unwrap() + } + #[tokio::test] async fn test_handle() { - let th = TestHarness::new(); + let mut th = TestHarness::new(); let lc = LoxClientMock {}; + + // Test Random page + let four_oh_four_req = Request::builder() + .header("Content-Type", "application/json") + .method("POST") + .uri("http://localhost/givemecreds") + .body(Body::empty()) + .unwrap(); + let not_found_response = handle(th.context.clone(), four_oh_four_req).await.unwrap(); + assert_eq!(not_found_response.status(), StatusCode::NOT_FOUND); + // Test Invite let invite_request = lc.invite(); - let response = handle(th.context.clone(), invite_request).await.unwrap(); - println!("Server response?: {:?}", response); - assert_eq!(response.status(), StatusCode::OK); + let invite_response = handle(th.context.clone(), invite_request).await.unwrap(); + assert_eq!(invite_response.status(), StatusCode::OK); + // Test Reachability let reachability_request = lc.reachability(); let reachability_response = handle(th.context.clone(), reachability_request) .await .unwrap(); - println!("Server response?: {:?}", reachability_response); assert_eq!(reachability_response.status(), StatusCode::OK); + // Test Pubkeys let pubkey_request = lc.pubkeys(); let pubkey_response = handle(th.context.clone(), pubkey_request).await.unwrap(); - println!("Server response?: {:?}", pubkey_response); assert_eq!(pubkey_response.status(), StatusCode::OK); + let pubkeys = body_to_string(pubkey_response).await; + let pubkeys_obj: lox_utils::PubKeys = serde_json::from_str(&pubkeys).unwrap(); + + // Test Open Invite + let invite_response_str = body_to_string(invite_response).await; + let response_data: lox_utils::Invite = serde_json::from_str(&invite_response_str).unwrap(); + let token = match lox_utils::validate(&response_data.invite) { + Ok(token) => token, + Err(e) => panic!("Error: Invitation token error {:?}", e.to_string()), + }; + let (request, state) = lox::proto::open_invite::request(&token); + let open_request = lc.openinvite(request); + let open_response = handle(th.context.clone(), open_request).await.unwrap(); + assert_eq!(open_response.status(), StatusCode::OK); + + // Test Trust Promotion + let open_resp = body_to_string(open_response).await; + let open_response_obj = serde_json::from_str(&open_resp).unwrap(); + let lox_cred = match lox::proto::open_invite::handle_response( + state, + open_response_obj, + &pubkeys_obj.lox_pub, + ) { + Ok(lox_cred) => lox_cred, + Err(e) => panic!("Error: Lox credential error {:?}", e.to_string()), + }; + let lox_cred: lox_utils::LoxCredential = lox_utils::LoxCredential { + lox_credential: lox_cred.0, + bridgeline: Some(lox_cred.1), + invitation: None, + }; + // Advance the context to a day after the credential becomes eligible to upgrade + th.advance_days(31); + let trust_result = match proto::trust_promotion::request( + &lox_cred.lox_credential, + &pubkeys_obj.lox_pub, + test_today(31), + ) { + Ok(trust_result) => trust_result, + Err(e) => panic!( + "Error: Proof error from trust promotion {:?}", + e.to_string() + ), + }; + let trustpromo_request = lc.trustpromo(trust_result.0); + let trustpromo_response = handle(th.context.clone(), trustpromo_request) + .await + .unwrap(); + assert_eq!(trustpromo_response.status(), StatusCode::OK); + + // Test Trust Migration + let trustpromo_resp = body_to_string(trustpromo_response).await; + let trustpromo_response_obj = serde_json::from_str(&trustpromo_resp).unwrap(); + let mig_cred = match lox::proto::trust_promotion::handle_response( + trust_result.1, + trustpromo_response_obj, + ) { + Ok(mig_cred) => mig_cred, + Err(e) => panic!("Error: Migration token error {:?}", e.to_string()), + }; + let migration_result = match proto::migration::request( + &lox_cred.lox_credential, + &mig_cred, + &pubkeys_obj.lox_pub, + &pubkeys_obj.migration_pub, + ) { + Ok(migration_result) => migration_result, + Err(e) => panic!( + "Error: Proof error from trust migration {:?}", + e.to_string() + ), + }; + let trustmig_request = lc.trustmigration(migration_result.0); + let trustmig_response = handle(th.context.clone(), trustmig_request).await.unwrap(); + assert_eq!(trustmig_response.status(), StatusCode::OK); + + // Test Level up + let trustmig_resp = body_to_string(trustmig_response).await; + let trustmig_response_obj = serde_json::from_str(&trustmig_resp).unwrap(); + let level_one_cred = match lox::proto::migration::handle_response( + migration_result.1, + trustmig_response_obj, + &pubkeys_obj.lox_pub, + ) { + Ok(level_one_cred) => level_one_cred, + Err(e) => panic!("Error: Level one credential error {:?}", e.to_string()), + }; + th.advance_days(14); + let new_reachability_request = lc.reachability(); + let new_reachability_response = handle(th.context.clone(), new_reachability_request) + .await + .unwrap(); + let encrypted_table = body_to_string(new_reachability_response).await; + let reachability_cred: BucketReachability = + lox_utils::generate_reachability_cred(&level_one_cred, encrypted_table); + let level_up_result = match proto::level_up::request( + &level_one_cred, + &reachability_cred, + &pubkeys_obj.lox_pub, + &pubkeys_obj.reachability_pub, + test_today(31 + 14), + ) { + Ok(level_up_result) => level_up_result, + Err(e) => panic!( + "Error: Proof error from level up {:?}", + e.to_string() + ), + }; + let level_up_request = lc.levelup(level_up_result.0); + let level_up_response = handle(th.context.clone(), level_up_request).await.unwrap(); + assert_eq!(level_up_response.status(), StatusCode::OK); + + // Test Issue Invite + let levelup_resp = body_to_string(level_up_response).await; + let levelup_response_obj = serde_json::from_str(&levelup_resp).unwrap(); + let level_two_cred = match lox::proto::level_up::handle_response( + level_up_result.1, + levelup_response_obj, + &pubkeys_obj.lox_pub, + ) { + Ok(level_two_cred) => level_two_cred, + Err(e) => panic!("Error: Level two credential error {:?}", e.to_string()), + }; + let new_reachability_request = lc.reachability(); + let new_reachability_response = handle(th.context.clone(), new_reachability_request) + .await + .unwrap(); + let encrypted_table = body_to_string(new_reachability_response).await; + let reachability_cred: BucketReachability = + lox_utils::generate_reachability_cred(&level_two_cred, encrypted_table); + + let issue_invite_result = match proto::issue_invite::request( + &level_two_cred, + &reachability_cred, + &pubkeys_obj.lox_pub, + &pubkeys_obj.reachability_pub, + test_today(31 + 14), + ) { + Ok(issue_invite_result) => issue_invite_result, + Err(e) => panic!( + "Error: Proof error from issue invitation {:?}", + e.to_string() + ), + }; + let issue_invite_request = lc.issueinvite(issue_invite_result.0); + let issue_invite_response = handle(th.context.clone(), issue_invite_request).await.unwrap(); + println!("Server response?: {:?}", issue_invite_response); + assert_eq!(issue_invite_response.status(), StatusCode::OK); + + // Test Redeem Invite + let invite_resp = body_to_string(issue_invite_response).await; + let invite_response_obj = serde_json::from_str(&invite_resp).unwrap(); + let issue_invite_cred = match lox::proto::issue_invite::handle_response( + issue_invite_result.1, + invite_response_obj, + &pubkeys_obj.lox_pub, + &pubkeys_obj.invitation_pub, + ) { + Ok(issue_invite_cred) => issue_invite_cred, + Err(e) => panic!("Error: Issue invite credential error {:?}", e.to_string()), + }; + let new_invite = match proto::redeem_invite::request( + &issue_invite_cred.1, + &pubkeys_obj.invitation_pub, + test_today(31 + 14), + ) { + Ok(new_invite) => new_invite, + Err(e) => panic!( + "Error: Proof error from level up {:?}", + e.to_string() + ), + }; + let new_redeem_invite_request = lc.redeeminvite(new_invite.0); + let new_redeem_invite_response = handle(th.context.clone(), new_redeem_invite_request) + .await + .unwrap(); + println!("Server response?: {:?}", new_redeem_invite_response); + assert_eq!(new_redeem_invite_response.status(), StatusCode::OK); + let redeemed_cred_resp = body_to_string(new_redeem_invite_response).await; + let redeemed_cred_resp_obj = serde_json::from_str(&redeemed_cred_resp).unwrap(); + + let redeemed_cred_result = match proto::redeem_invite::handle_response( + new_invite.1, + redeemed_cred_resp_obj, + &pubkeys_obj.lox_pub, + ) { + Ok(redeemed_cred_result) => redeemed_cred_result, + Err(e) => panic!( + "Error: Proof error from issue invitation {:?}", + e.to_string() + ), + }; + } } diff --git a/crates/lox-utils/Cargo.toml b/crates/lox-utils/Cargo.toml index a286bed..095cb22 100644 --- a/crates/lox-utils/Cargo.toml +++ b/crates/lox-utils/Cargo.toml @@ -23,4 +23,4 @@ full = [] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] \ No newline at end of file +rustdoc-args = ["--cfg", "docsrs"] diff --git a/crates/lox-utils/src/lib.rs b/crates/lox-utils/src/lib.rs index acfa7b3..870bf13 100644 --- a/crates/lox-utils/src/lib.rs +++ b/crates/lox-utils/src/lib.rs @@ -6,6 +6,13 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use std::array::TryFromSliceError; +#[serde_as] +#[derive(Serialize, Deserialize)] +pub struct Invite { + #[serde_as(as = "[_; OPENINV_LENGTH]")] + pub invite: [u8; OPENINV_LENGTH], +} + #[derive(Deserialize, Serialize)] pub struct OpenReqState { pub request: proto::open_invite::Request,