226 lines
7.6 KiB
Rust
226 lines
7.6 KiB
Rust
mod client_lib;
|
|
use client_lib::*;
|
|
|
|
mod client_net;
|
|
use client_net::HyperNet;
|
|
|
|
use curve25519_dalek::scalar::Scalar;
|
|
use getopts::Options;
|
|
use lox_library::bridge_table::BridgeLine;
|
|
use lox_library::bridge_table::MAX_BRIDGES_PER_BUCKET;
|
|
use lox_library::scalar_u32;
|
|
use lox_library::IssuerPubKey;
|
|
use serde::Serialize;
|
|
use std::env::args;
|
|
use std::fs::File;
|
|
use std::io::Write;
|
|
use std::path::Path;
|
|
use std::str::FromStr;
|
|
|
|
// Prints the argument details for this program
|
|
fn print_usage(program: &str, opts: Options) {
|
|
let brief = format!("Usage: {} [options]", program);
|
|
print!("{}", opts.usage(&brief));
|
|
}
|
|
|
|
// Helper function to save serializable objects to files
|
|
fn save_object<T: Serialize>(obj: T, filename: &str) {
|
|
let mut outfile = File::create(filename).expect(&("Failed to create ".to_string() + filename));
|
|
write!(outfile, "{}", serde_json::to_string(&obj).unwrap())
|
|
.expect(&("Failed to write to ".to_string() + filename));
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let args: Vec<String> = args().collect();
|
|
|
|
// files used to store various data
|
|
let bucket_filename = "bucket.json";
|
|
let invite_filename = "invite.json";
|
|
let lox_auth_pubkeys_filename = "lox_auth_pubkeys.json";
|
|
let lox_cred_filename = "lox_cred.json";
|
|
|
|
let mut opts = Options::new();
|
|
opts.optflag("h", "help", "print this help menu");
|
|
//#[cfg(test)]
|
|
opts.optopt(
|
|
"A",
|
|
"advance-days",
|
|
"increase server days by NUM_DAYS",
|
|
"NUM_DAYS",
|
|
);
|
|
opts.optflag("I", "invite", "generate invitation for a friend");
|
|
opts.optflag("L", "level-up", "increase trust level");
|
|
opts.optflag("N", "new-lox-cred", "get a new Lox Credential");
|
|
opts.optopt("R", "redeem", "redeem invitation", "INVITE_FILE");
|
|
opts.optopt(
|
|
"",
|
|
"server",
|
|
"Lox Auth server address [http://localhost:8001]",
|
|
"ADDR",
|
|
);
|
|
|
|
let matches = match opts.parse(&args[1..]) {
|
|
Ok(m) => m,
|
|
Err(f) => {
|
|
panic!("{}", f.to_string())
|
|
}
|
|
};
|
|
|
|
if matches.opt_present("h") {
|
|
print_usage(&args[0], opts);
|
|
return;
|
|
}
|
|
|
|
let net = if matches.opt_present("server") {
|
|
HyperNet {
|
|
hostname: matches.opt_str("server").unwrap(),
|
|
}
|
|
} else {
|
|
HyperNet {
|
|
hostname: "http://localhost:8001".to_string(),
|
|
}
|
|
};
|
|
|
|
// Advance days on server (TESTING ONLY)
|
|
//#[cfg(test)]
|
|
if matches.opt_present("A") {
|
|
let days: u16 = u16::from_str(matches.opt_str("A").unwrap().as_str()).unwrap();
|
|
let today: u32 = advance_days(&net, days).await;
|
|
println!("Today's date according to the server: {}", today);
|
|
}
|
|
|
|
// Get Lox Authority public keys
|
|
let lox_auth_pubkeys: Vec<IssuerPubKey> = 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(&net).await;
|
|
// save to file for next time
|
|
save_object(&pubkeys, &lox_auth_pubkeys_filename);
|
|
pubkeys
|
|
};
|
|
|
|
// Get Lox Credential and Bucket
|
|
let (lox_cred, bucket) = if matches.opt_present("R") && Path::new(invite_filename).exists() {
|
|
// redeem invite
|
|
let invite_infile = File::open(invite_filename).unwrap();
|
|
let invite = serde_json::from_reader(invite_infile).unwrap();
|
|
let (cred, bucket) = redeem_invite(
|
|
&net,
|
|
&invite,
|
|
get_lox_pub(&lox_auth_pubkeys),
|
|
get_invitation_pub(&lox_auth_pubkeys),
|
|
)
|
|
.await;
|
|
|
|
// save to files for future use
|
|
save_object(&cred, &lox_cred_filename);
|
|
save_object(&bucket, &bucket_filename);
|
|
(cred, bucket)
|
|
} else if matches.opt_present("N")
|
|
|| !Path::new(lox_cred_filename).exists()
|
|
|| !Path::new(bucket_filename).exists()
|
|
{
|
|
// get new Lox Credential
|
|
let open_invite = get_open_invitation(&net).await;
|
|
let (cred, bl) =
|
|
get_lox_credential(&net, &open_invite, get_lox_pub(&lox_auth_pubkeys)).await;
|
|
let mut bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET];
|
|
// note: this is a bucket with one real bridgeline and n-1
|
|
// default (zeroed out) bridgelines
|
|
bucket[0] = bl;
|
|
|
|
// save to files for future use
|
|
save_object(&cred, &lox_cred_filename);
|
|
save_object(&bucket, &bucket_filename);
|
|
(cred, bucket)
|
|
} else {
|
|
// Read existing Lox Credential and BridgeLine from files
|
|
let cred = serde_json::from_reader(File::open(lox_cred_filename).unwrap()).unwrap();
|
|
let bucket = serde_json::from_reader(File::open(bucket_filename).unwrap()).unwrap();
|
|
(cred, bucket)
|
|
};
|
|
|
|
let (lox_cred, bucket) = if matches.opt_present("L") {
|
|
let old_level = get_cred_trust_level(&lox_cred);
|
|
|
|
// If trust level is 0, do trust promotion, otherwise level up.
|
|
let (cred, bucket) = if old_level == 0 {
|
|
if eligible_for_trust_promotion(&net, &lox_cred).await {
|
|
let migration_cred =
|
|
trust_promotion(&net, &lox_cred, get_lox_pub(&lox_auth_pubkeys)).await;
|
|
let cred = trust_migration(
|
|
&net,
|
|
&lox_cred,
|
|
&migration_cred,
|
|
get_lox_pub(&lox_auth_pubkeys),
|
|
get_migration_pub(&lox_auth_pubkeys),
|
|
)
|
|
.await;
|
|
let bucket = get_bucket(&net, &cred).await;
|
|
(cred, bucket)
|
|
} else {
|
|
(lox_cred, bucket)
|
|
}
|
|
} else {
|
|
if eligible_for_level_up(&net, &lox_cred).await {
|
|
let encbuckets = get_reachability_credential(&net).await;
|
|
let (cred, bucket) = level_up(
|
|
&net,
|
|
&lox_cred,
|
|
&encbuckets,
|
|
get_lox_pub(&lox_auth_pubkeys),
|
|
get_reachability_pub(&lox_auth_pubkeys),
|
|
)
|
|
.await;
|
|
(cred, bucket)
|
|
} else {
|
|
(lox_cred, bucket)
|
|
}
|
|
};
|
|
save_object(&cred, &lox_cred_filename);
|
|
save_object(&bucket, &bucket_filename);
|
|
let new_level = get_cred_trust_level(&cred);
|
|
if new_level > old_level {
|
|
println!("Old level: {}\nNew level: {}", old_level, new_level);
|
|
} else if new_level == old_level {
|
|
println!("Unable to level up. Current level: {}", new_level);
|
|
}
|
|
(cred, bucket)
|
|
} else {
|
|
(lox_cred, bucket)
|
|
};
|
|
|
|
// Invite a friend
|
|
let lox_cred = if matches.opt_present("I") {
|
|
if scalar_u32(&lox_cred.invites_remaining).unwrap() > 0 {
|
|
let (cred, invite) = issue_invite(
|
|
&net,
|
|
&lox_cred,
|
|
&get_reachability_credential(&net).await,
|
|
get_lox_pub(&lox_auth_pubkeys),
|
|
get_reachability_pub(&lox_auth_pubkeys),
|
|
get_invitation_pub(&lox_auth_pubkeys),
|
|
)
|
|
.await;
|
|
// TODO: Make this unique per-run (e.g., add timestamp)
|
|
save_object(&invite, &invite_filename);
|
|
save_object(&cred, &lox_cred_filename);
|
|
println!("Invite saved in {}", &invite_filename);
|
|
println!(
|
|
"Invites left: {}",
|
|
scalar_u32(&cred.invites_remaining).unwrap()
|
|
);
|
|
cred
|
|
} else {
|
|
println!("No invites left");
|
|
lox_cred
|
|
}
|
|
} else {
|
|
lox_cred
|
|
};
|
|
}
|