From 589b2cc7f7007158ec03a7266194fc8e642cce9f Mon Sep 17 00:00:00 2001 From: Vecna Date: Sun, 5 Mar 2023 19:38:04 -0500 Subject: [PATCH] Begin client using hyper to work with onyinyang's server --- Cargo.toml | 6 +++- src/bin/lox_client_2.rs | 65 +++++++++++++++++++++++++++++++++++++++++ src/client_lib.rs | 65 +++++++++++++++++++++++++++++++++++++++++ src/hyper_client_net.rs | 25 ++++++++++++++++ 4 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 src/bin/lox_client_2.rs create mode 100644 src/client_lib.rs create mode 100644 src/hyper_client_net.rs diff --git a/Cargo.toml b/Cargo.toml index e88ec6d..3f537de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lox = { git = "https://git-crysp.uwaterloo.ca/iang/lox.git", branch = "vvecna/lox_test" } +#lox = { git = "https://git-crysp.uwaterloo.ca/iang/lox.git", branch = "vvecna/lox_test" } +lox = { git = "https://gitlab.torproject.org/onyinyang/lox.git" } ed25519-dalek = { version = "1", features = ["serde"] } serde = "1" bincode = "1" serde_json = "1.0" +serde_with = "1.9.1" +time = "0.2" # TODO: reduce feature set to just the ones needed tokio = { version = "1.20", features = ["full"] } +hyper = { version = "0.14", features = ["full"] } diff --git a/src/bin/lox_client_2.rs b/src/bin/lox_client_2.rs new file mode 100644 index 0000000..5968034 --- /dev/null +++ b/src/bin/lox_client_2.rs @@ -0,0 +1,65 @@ +// During a later cleanup, this file will replace lox_client.rs. + +// This seems like probably not the best way to do this, but it works. +#[path = "../client_lib.rs"] +mod client_lib; +use client_lib::*; + +use lox::IssuerPubKey; +use std::env::args; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +#[tokio::main] +async fn main() { + // TODO: Do proper argument handling + let server_addr = args().nth(1).unwrap(); // must include http:// + + + // Get Lox Authority public keys + // TODO: Make this filename configurable + let lox_auth_pubkeys_filename = "lox_auth_pubkeys.json"; + + let lox_auth_pubkeys: Vec = if Path::new(lox_auth_pubkeys_filename).exists() { + // read in file + let lox_auth_pubkeys_infile = File::open(lox_auth_pubkeys_filename).unwrap(); + serde_json::from_reader(lox_auth_pubkeys_infile).unwrap() + } else { + // download from Lox Auth + let pubkeys = get_lox_auth_keys(server_addr.clone()).await; + // save to file for next time + let mut lox_auth_pubkeys_outfile = File::create(lox_auth_pubkeys_filename).expect("Failed to create lox_auth pubkeys file"); + write!( + lox_auth_pubkeys_outfile, + "{}", + serde_json::to_string(&pubkeys).unwrap() + ).expect("Failed to write to lox_auth pubkeys file"); + pubkeys + }; + let lox_pub = lox_auth_pubkeys[0].clone(); + let migration_pub = lox_auth_pubkeys[1].clone(); + let migrationkey_pub = lox_auth_pubkeys[2].clone(); + let reachability_pub = lox_auth_pubkeys[3].clone(); + let invitation_pub = lox_auth_pubkeys[4].clone(); + + // Get Lox Credential + // TODO: Make this filename configurable + let lox_cred_filename = "lox_cred.json"; + let lox_cred = if Path::new(lox_cred_filename).exists() { + let lox_cred_infile = File::open(lox_cred_filename).unwrap(); + serde_json::from_reader(lox_cred_infile).unwrap() + } else { + // get new credential based on an open invite + let open_invite = get_open_invitation(server_addr.clone()).await; + let cred = get_lox_credential(server_addr.clone(), open_invite, lox_pub).await; + // save to file for next time + let mut lox_cred_outfile = File::create(lox_cred_filename).expect("Failed to create lox credential file"); + write!( + lox_cred_outfile, + "{}", + serde_json::to_string(&cred).unwrap() + ).expect("Failed to write to lox credential file"); + cred + }; +} diff --git a/src/client_lib.rs b/src/client_lib.rs new file mode 100644 index 0000000..d8aff1f --- /dev/null +++ b/src/client_lib.rs @@ -0,0 +1,65 @@ +mod hyper_client_net; +use hyper_client_net::net_request; + +use lox::IssuerPubKey; +use lox::OPENINV_LENGTH; +use lox::proto::*; +use serde::{Serialize, Deserialize}; +use serde_with::serde_as; +use std::time::Duration; + +// From https://gitlab.torproject.org/onyinyang/lox-server/-/blob/main/src/main.rs +// TODO: Move this to main Lox library? +#[serde_as] +#[derive(Serialize, Deserialize)] +pub struct Invite { + #[serde_as(as = "[_; OPENINV_LENGTH]")] + invite: [u8; OPENINV_LENGTH], +} + +/// Get today's (real or simulated) date +/// +/// This function is modified from the lox lib.rs +fn today(time_offset: Duration) -> u32 { + // We will not encounter negative Julian dates (~6700 years ago) + // or ones larger than 32 bits + (time::OffsetDateTime::now_utc().date() + time_offset) + .julian_day() + .try_into() + .unwrap() +} + +// Download Lox Auth pubkeys +pub async fn get_lox_auth_keys(server_addr: String) -> Vec { + let lox_auth_pubkeys_resp = net_request(format!("{}/pubkeys", server_addr), [].to_vec()).await; + let lox_auth_pubkeys: Vec = serde_json::from_slice(&lox_auth_pubkeys_resp).unwrap(); + lox_auth_pubkeys +} + +// Get an open invitation +pub async fn get_open_invitation(server_addr: String) -> [u8; OPENINV_LENGTH] { + let open_invite_resp = net_request(format!("{}/invite", server_addr), [].to_vec()).await; + let open_invite: [u8; OPENINV_LENGTH] = serde_json::from_slice::(&open_invite_resp).unwrap().invite; + open_invite +} + +// Get a Lox Credential from an open invitation +pub async fn get_lox_credential(server_addr: String, open_invite: [u8; OPENINV_LENGTH], lox_pub: IssuerPubKey) -> lox::cred::Lox { + let (open_inv_req, state) = open_invite::request(&open_invite); + let encoded_open_inv_req: Vec = serde_json::to_vec(&open_inv_req).unwrap(); + let encoded_open_inv_resp = net_request(format!("{}/openreq", server_addr), encoded_open_inv_req).await; + let decoded_open_inv_resp: open_invite::Response = serde_json::from_slice(&encoded_open_inv_resp).unwrap(); + let (cred, bridgeline) = open_invite::handle_response(state, decoded_open_inv_resp, &lox_pub).unwrap(); + cred + // TODO: Also return the bridgeline +} + +// Get a migration credential to migrate to higher trust +pub async fn trust_promotion(server_addr: String, lox_cred: lox::cred::Lox, lox_pub: IssuerPubKey) -> lox::cred::Migration { + let (prom_req, state) = trust_promotion::request(&lox_cred, &lox_pub, today(Duration::ZERO)).unwrap(); + let encoded_prom_req: Vec = serde_json::to_vec(&prom_req).unwrap(); + let encoded_prom_resp = net_request(format!("{}/promreq", server_addr), encoded_prom_req).await; + let decoded_prom_resp: trust_promotion::Response = serde_json::from_slice(&encoded_prom_resp).unwrap(); + let migration_cred = trust_promotion::handle_response(state, decoded_prom_resp).unwrap(); + migration_cred +} diff --git a/src/hyper_client_net.rs b/src/hyper_client_net.rs new file mode 100644 index 0000000..c4c02f2 --- /dev/null +++ b/src/hyper_client_net.rs @@ -0,0 +1,25 @@ +// This file provides networking using hyper (which +// https://gitlab.torproject.org/onyinyang/lox-server uses). +// During a later cleanup, this will replace client_net.rs. + +use hyper::{Body, Client, Method, Request}; + +pub async fn net_request(url: String, body: Vec) -> Vec { + let client = Client::new(); + + let uri = url.parse().expect("Failed to parse URL"); + + let resp = if body.len() > 0 { + // make a POST with a body + let req = Request::builder().method(Method::POST).uri(uri).body(Body::from(body)).expect("Failed to create POST request"); + client.request(req).await.expect("Failed to POST") + } else { + // make a GET request + client.get(uri).await.expect("Failed to GET") + }; + + println!("Response: {}", resp.status()); + + let buf = hyper::body::to_bytes(resp).await.expect("Failed to concat bytes"); + buf.to_vec() +}