From 0faecae249875c9dc31ca7470be7247b5a8af66b Mon Sep 17 00:00:00 2001 From: Vecna Date: Tue, 20 Jun 2023 12:47:49 -0400 Subject: [PATCH] Add Networking trait to abstract net implementation from library --- Cargo.toml | 1 + src/bin/lox_client.rs | 27 ++++++++++++-------- src/client_lib.rs | 57 +++++++++++++++++++++++-------------------- src/client_net.rs | 49 ++++++++++++++++++++++--------------- 4 files changed, 79 insertions(+), 55 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb333fb..1f4c3e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ time = "0.2" # TODO: reduce feature set to just the ones needed tokio = { version = "1.20", features = ["full"] } hyper = { version = "0.14", features = ["full"] } +async-trait = "0.1.68" diff --git a/src/bin/lox_client.rs b/src/bin/lox_client.rs index eb7e8e3..5e5d0c6 100644 --- a/src/bin/lox_client.rs +++ b/src/bin/lox_client.rs @@ -2,6 +2,9 @@ #[path = "../client_lib.rs"] mod client_lib; use client_lib::*; +#[path = "../client_net.rs"] +mod client_net; +use client_net::HyperNet; use curve25519_dalek::scalar::Scalar; use getopts::Options; @@ -53,10 +56,14 @@ async fn main() { return; } - let server_addr = if matches.opt_present("server") { - matches.opt_str("server").unwrap() + let net = if matches.opt_present("server") { + HyperNet { + hostname: matches.opt_str("server").unwrap(), + } } else { - "http://localhost:8001".to_string() + HyperNet { + hostname: "http://localhost:8001".to_string(), + } }; // Get Lox Authority public keys @@ -69,7 +76,7 @@ async fn main() { serde_json::from_reader(lox_auth_pubkeys_infile).unwrap() } else { // download from Lox Auth - let pubkeys = get_lox_auth_keys(&server_addr).await; + let pubkeys = get_lox_auth_keys(&net).await; // save to file for next time save_object(&pubkeys, &lox_auth_pubkeys_filename); pubkeys @@ -83,9 +90,9 @@ async fn main() { || !Path::new(bridgeline_filename).exists() { // get new Lox Credential - let open_invite = get_open_invitation(&server_addr).await; + let open_invite = get_open_invitation(&net).await; let (cred, bl) = - get_lox_credential(&server_addr, &open_invite, get_lox_pub(&lox_auth_pubkeys)).await; + get_lox_credential(&net, &open_invite, get_lox_pub(&lox_auth_pubkeys)).await; // save to files for next time save_object(&cred, &lox_cred_filename); @@ -104,9 +111,9 @@ async fn main() { // If trust level is 0, do trust promotion, otherwise level up. let cred = if old_level == 0 { let migration_cred = - trust_promotion(&server_addr, &lox_cred, get_lox_pub(&lox_auth_pubkeys)).await; + trust_promotion(&net, &lox_cred, get_lox_pub(&lox_auth_pubkeys)).await; let cred = trust_migration( - &server_addr, + &net, &lox_cred, &migration_cred, get_lox_pub(&lox_auth_pubkeys), @@ -115,9 +122,9 @@ async fn main() { .await; cred } else { - let encbuckets = get_reachability_credential(&server_addr).await; + let encbuckets = get_reachability_credential(&net).await; let cred = level_up( - &server_addr, + &net, &lox_cred, &encbuckets, get_lox_pub(&lox_auth_pubkeys), diff --git a/src/client_lib.rs b/src/client_lib.rs index 5e86f55..93424b3 100644 --- a/src/client_lib.rs +++ b/src/client_lib.rs @@ -1,6 +1,4 @@ -mod client_net; -use client_net::net_request; - +use async_trait::async_trait; use curve25519_dalek::scalar::Scalar; use lox::bridge_table::BridgeLine; use lox::bridge_table::ENC_BUCKET_BYTES; @@ -14,6 +12,12 @@ use std::time::Duration; // used for testing function use std::io::Write; +// provides a generic way to make network requests +#[async_trait] +pub trait Networking { + async fn request(&self, endpoint: String, body: Vec) -> Vec; +} + // From https://gitlab.torproject.org/onyinyang/lox-server/-/blob/main/src/main.rs // TODO: Move this to main Lox library? #[serde_as] @@ -99,35 +103,35 @@ pub fn get_cred_trust_level(cred: &lox::cred::Lox) -> i8 { } // Download Lox Auth pubkeys -pub async fn get_lox_auth_keys(server_addr: &str) -> Vec { - let resp = net_request(server_addr.to_string() + "/pubkeys", [].to_vec()).await; +pub async fn get_lox_auth_keys(net: &dyn Networking) -> Vec { + let resp = net.request("/pubkeys".to_string(), [].to_vec()).await; let lox_auth_pubkeys: Vec = serde_json::from_slice(&resp).unwrap(); lox_auth_pubkeys } // Get encrypted bridge table -pub async fn get_reachability_credential(server_addr: &str) -> Vec<[u8; ENC_BUCKET_BYTES]> { - let resp = net_request(server_addr.to_string() + "/reachability", [].to_vec()).await; +pub async fn get_reachability_credential(net: &dyn Networking) -> Vec<[u8; ENC_BUCKET_BYTES]> { + let resp = net.request("/reachability".to_string(), [].to_vec()).await; let reachability_cred: EncBridgeTable = serde_json::from_slice(&resp).unwrap(); reachability_cred.etable } // Get an open invitation -pub async fn get_open_invitation(server_addr: &str) -> [u8; OPENINV_LENGTH] { - let resp = net_request(server_addr.to_string() + "/invite", [].to_vec()).await; +pub async fn get_open_invitation(net: &dyn Networking) -> [u8; OPENINV_LENGTH] { + let resp = net.request("/invite".to_string(), [].to_vec()).await; let open_invite: [u8; OPENINV_LENGTH] = serde_json::from_slice::(&resp).unwrap().invite; open_invite } // Get a Lox Credential from an open invitation pub async fn get_lox_credential( - server_addr: &str, + net: &dyn Networking, open_invite: &[u8; OPENINV_LENGTH], lox_pub: &IssuerPubKey, ) -> (lox::cred::Lox, BridgeLine) { let (req, state) = open_invite::request(&open_invite); let encoded_req: Vec = serde_json::to_vec(&req).unwrap(); - let encoded_resp = net_request(server_addr.to_string() + "/openreq", encoded_req).await; + let encoded_resp = net.request("/openreq".to_string(), encoded_req).await; let decoded_resp: open_invite::Response = serde_json::from_slice(&encoded_resp).unwrap(); let (cred, bridgeline) = open_invite::handle_response(state, decoded_resp, &lox_pub).unwrap(); (cred, bridgeline) @@ -135,7 +139,7 @@ pub async fn get_lox_credential( // Get a migration credential to migrate to higher trust pub async fn trust_promotion( - server_addr: &str, + net: &dyn Networking, lox_cred: &lox::cred::Lox, lox_pub: &IssuerPubKey, ) -> lox::cred::Migration { @@ -147,7 +151,7 @@ pub async fn trust_promotion( // trust_promotion::request(&lox_cred, &lox_pub, today(Duration::ZERO)).unwrap(); trust_promotion::request(&lox_cred, &lox_pub, today(Duration::from_secs(time_offset.into()))).unwrap(); // FOR TESTING ONLY let encoded_req: Vec = serde_json::to_vec(&req).unwrap(); - let encoded_resp = net_request(server_addr.to_string() + "/trustpromo", encoded_req).await; + let encoded_resp = net.request("/trustpromo".to_string(), encoded_req).await; let decoded_resp: trust_promotion::Response = serde_json::from_slice(&encoded_resp).unwrap(); let migration_cred = trust_promotion::handle_response(state, decoded_resp).unwrap(); migration_cred @@ -155,7 +159,7 @@ pub async fn trust_promotion( // Promote from untrusted (trust level 0) to trusted (trust level 1) pub async fn trust_migration( - server_addr: &str, + net: &dyn Networking, lox_cred: &lox::cred::Lox, migration_cred: &lox::cred::Migration, lox_pub: &IssuerPubKey, @@ -164,7 +168,7 @@ pub async fn trust_migration( let (req, state) = migration::request(lox_cred, migration_cred, lox_pub, migration_pub).unwrap(); let encoded_req: Vec = serde_json::to_vec(&req).unwrap(); - let encoded_resp = net_request(server_addr.to_string() + "/trustmig", encoded_req).await; + let encoded_resp = net.request("/trustmig".to_string(), encoded_req).await; let decoded_resp: migration::Response = serde_json::from_slice(&encoded_resp).unwrap(); let cred = migration::handle_response(state, decoded_resp, lox_pub).unwrap(); cred @@ -172,7 +176,7 @@ pub async fn trust_migration( // Increase trust from at least level 1 to higher levels pub async fn level_up( - server_addr: &str, + net: &dyn Networking, lox_cred: &lox::cred::Lox, encbuckets: &Vec<[u8; ENC_BUCKET_BYTES]>, lox_pub: &IssuerPubKey, @@ -202,7 +206,7 @@ pub async fn level_up( ) .unwrap(); let encoded_req: Vec = serde_json::to_vec(&req).unwrap(); - let encoded_resp = net_request(server_addr.to_string() + "/levelup", encoded_req).await; + let encoded_resp = net.request("/levelup".to_string(), encoded_req).await; let decoded_resp: level_up::Response = serde_json::from_slice(&encoded_resp).unwrap(); let cred = level_up::handle_response(state, decoded_resp, lox_pub).unwrap(); cred @@ -210,7 +214,7 @@ pub async fn level_up( // Request an Invitation credential to give to a friend pub async fn issue_invite( - server_addr: &str, + net: &dyn Networking, lox_cred: &lox::cred::Lox, encbuckets: &Vec<[u8; ENC_BUCKET_BYTES]>, lox_pub: &IssuerPubKey, @@ -234,7 +238,7 @@ pub async fn issue_invite( ) .unwrap(); let encoded_req: Vec = serde_json::to_vec(&req).unwrap(); - let encoded_resp = net_request(server_addr.to_string() + "/issueinvite", encoded_req).await; + let encoded_resp = net.request("/issueinvite".to_string(), encoded_req).await; let decoded_resp: issue_invite::Response = serde_json::from_slice(&encoded_resp).unwrap(); let (cred, invite) = issue_invite::handle_response(state, decoded_resp, lox_pub, invitation_pub).unwrap(); @@ -243,7 +247,7 @@ pub async fn issue_invite( // Redeem an Invitation credential to start at trust level 1 pub async fn redeem_invite( - server_addr: &str, + net: &dyn Networking, invite: &lox::cred::Invitation, lox_pub: &IssuerPubKey, invitation_pub: &IssuerPubKey, @@ -251,7 +255,7 @@ pub async fn redeem_invite( let (req, state) = redeem_invite::request(invite, invitation_pub, today(Duration::ZERO)).unwrap(); let encoded_req: Vec = serde_json::to_vec(&req).unwrap(); - let encoded_resp = net_request(server_addr.to_string() + "/redeem", encoded_req).await; + let encoded_resp = net.request("/redeem".to_string(), encoded_req).await; let decoded_resp: redeem_invite::Response = serde_json::from_slice(&encoded_resp).unwrap(); let cred = redeem_invite::handle_response(state, decoded_resp, lox_pub).unwrap(); cred @@ -259,13 +263,13 @@ pub async fn redeem_invite( // Check for a migration credential to move to a new bucket pub async fn check_blockage( - server_addr: &str, + net: &dyn Networking, lox_cred: &lox::cred::Lox, lox_pub: &IssuerPubKey, ) -> lox::cred::Migration { let (req, state) = check_blockage::request(lox_cred, lox_pub).unwrap(); let encoded_req: Vec = serde_json::to_vec(&req).unwrap(); - let encoded_resp = net_request(server_addr.to_string() + "/checkblockage", encoded_req).await; + let encoded_resp = net.request("/checkblockage".to_string(), encoded_req).await; let decoded_resp: check_blockage::Response = serde_json::from_slice(&encoded_resp).unwrap(); let migcred = check_blockage::handle_response(state, decoded_resp).unwrap(); migcred @@ -273,7 +277,7 @@ pub async fn check_blockage( // Migrate to a new bucket (must be level >= 3) pub async fn blockage_migration( - server_addr: &str, + net: &dyn Networking, lox_cred: &lox::cred::Lox, migcred: &lox::cred::Migration, lox_pub: &IssuerPubKey, @@ -282,8 +286,9 @@ pub async fn blockage_migration( let (req, state) = blockage_migration::request(lox_cred, migcred, lox_pub, migration_pub).unwrap(); let encoded_req: Vec = serde_json::to_vec(&req).unwrap(); - let encoded_resp = - net_request(server_addr.to_string() + "/blockagemigration", encoded_req).await; + let encoded_resp = net + .request("/blockagemigration".to_string(), encoded_req) + .await; let decoded_resp: blockage_migration::Response = serde_json::from_slice(&encoded_resp).unwrap(); let cred = blockage_migration::handle_response(state, decoded_resp, lox_pub).unwrap(); cred diff --git a/src/client_net.rs b/src/client_net.rs index 5879eac..f14e695 100644 --- a/src/client_net.rs +++ b/src/client_net.rs @@ -1,25 +1,36 @@ // This file provides networking using hyper (which // https://gitlab.torproject.org/onyinyang/lox-server uses). +use crate::client_lib::Networking; +use async_trait::async_trait; use hyper::{Body, Client, Method, Request}; -pub async fn net_request(url: String, body: Vec) -> Vec { - let client = Client::new(); - - let uri: hyper::Uri = url.parse().expect("Failed to parse URL"); - - // always POST even if body is empty - let req = Request::builder() - .method(Method::POST) - .uri(uri) - .body(Body::from(body)) - .expect("Failed to create POST request"); - let resp = client.request(req).await.expect("Failed to POST"); - - println!("Response: {}", resp.status()); - - let buf = hyper::body::to_bytes(resp) - .await - .expect("Failed to concat bytes"); - buf.to_vec() +pub struct HyperNet { + pub hostname: String, +} + +#[async_trait] +impl Networking for HyperNet { + async fn request(&self, endpoint: String, body: Vec) -> Vec { + let client = Client::new(); + + let url = self.hostname.to_string() + &endpoint; + + let uri: hyper::Uri = url.parse().expect("Failed to parse URL"); + + // always POST even if body is empty + let req = Request::builder() + .method(Method::POST) + .uri(uri) + .body(Body::from(body)) + .expect("Failed to create POST request"); + let resp = client.request(req).await.expect("Failed to POST"); + + println!("Response: {}", resp.status()); + + let buf = hyper::body::to_bytes(resp) + .await + .expect("Failed to concat bytes"); + buf.to_vec() + } }