use crate::{
message::Message,
server::PORT,
types::{KeyExchange, PoW, RequestPubkey, SentMessage},
utils::{
cleanup_terminal, clear_screen, derive_key, deserialize_pow, enter_alt_screen, get_enter,
get_message, get_publickey_bytes, handle_client, handle_del, header, sign_message,
status_server, waiting_menu,
},
};
use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
use colored::*;
use crossterm::{cursor::MoveTo, execute};
use dialoguer::Select;
use ed25519_dalek::{Signature, SigningKey, Verifier, VerifyingKey};
use rand::rngs::OsRng;
use std::io::{stdout, Error};
use x25519_dalek::{PublicKey, ReusableSecret};
pub async fn client(passowrd: String) -> Result<(), Error> {
let mut csprng = OsRng;
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
let secret = signing_key.to_keypair_bytes();
let mut stdout = stdout();
match enter_alt_screen(&mut stdout) {
Ok(_) => (),
Err(e) => println!("{}", e.red()),
}
let mut message_index: usize = 0;
let mut render_new_msg: bool = false;
loop {
if render_new_msg {
view_received_messages(
&mut stdout,
&mut message_index,
&mut render_new_msg,
&passowrd,
)
.await;
} else {
clear_screen(&mut stdout);
header();
let options = &[
"View received messages",
"Send a message",
"Delete a message",
"Exit",
];
let selected = match Select::new()
.default(0)
.clear(false)
.items(options)
.interact()
{
Ok(result) => result,
Err(e) => {
println!("{}", e.to_string().red());
return Ok(());
}
};
match selected {
0 => {
view_received_messages(
&mut stdout,
&mut message_index,
&mut render_new_msg,
&passowrd,
)
.await
}
1 => send_message(secret).await,
2 => {
delete_message(
&mut stdout,
&mut message_index,
&mut render_new_msg,
passowrd.clone(),
)
.await
}
3 => break,
_ => {}
}
}
}
cleanup_terminal(&mut stdout);
Ok(())
}
async fn send_message(secret: [u8; 64]) {
match execute!(stdout(), MoveTo(0, 0)) {
Ok(_) => (),
Err(e) => println!("{}", e.to_string().red()),
}
clear_screen(&mut stdout());
header();
let domain = match crate::utils::get_domain::<62>() {
Ok(domain) => domain,
Err(_) => return,
};
let message_to_send = match get_message::<{ Message::CIPHERTEXT }>(domain.clone()) {
Ok(result) => result,
Err(_) => return,
};
match execute!(stdout(), MoveTo(0, 0)) {
Ok(_) => (),
Err(e) => println!("{}", e.to_string().red()),
}
clear_screen(&mut stdout());
header();
let waiting = format!("Downloading {domain}'s public key...");
let success = format!("{domain}'s public key has been successfully obtained");
let (tx, handle) = waiting_menu("[1/5]", waiting, success);
let output = match tokio::process::Command::new("torsocks")
.arg("curl")
.arg("-X")
.arg("GET")
.arg(format!("http://{domain}:{PORT}/get_domain_pk"))
.output()
.await
{
Ok(result) => {
match tx.send(Ok(())) {
Ok(tx) => tx,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
}
}
match handle.join() {
Ok(handle) => handle,
Err(_) => {
println!("{}", "Handle process fails".red());
get_enter();
}
}
result
}
Err(e) => {
let result = Err(format!("Request failed: {}", e));
match tx.send(result) {
Ok(tx) => tx,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
}
}
match handle.join() {
Ok(handle) => handle,
Err(_) => {
println!("{}", "Handle process fails".red());
get_enter();
}
}
return;
}
};
let hex_bob_pubkey = match output.status.success() {
true => output.stdout,
false => {
println!(
"{}",
"Check if tor is running. If this is the case, the HS kkv server must be offline"
.red()
);
get_enter();
return;
}
};
let raw_bob_pubkey = match hex::decode(hex_bob_pubkey) {
Ok(bytes) => bytes,
Err(e) => {
println!("{} {}", "Failed to decode hex:".red(), e.to_string().red());
get_enter();
return;
}
};
let bob_pubkey: [u8; 32] = match raw_bob_pubkey.try_into() {
Ok(arr) => arr,
Err(_) => {
println!("{}", "Vector must have exactly 32 elements!".red());
get_enter();
return;
}
};
let alice_pubkey_bytes = get_publickey_bytes(secret);
let hex_alice = hex::encode(alice_pubkey_bytes);
let pubkey = RequestPubkey::new(hex_alice.clone());
let waiting = format!("Requesting first Proof of Work from {domain} server...");
let success = format!("Proof of Work has been successfully received from {domain} server");
let (tx, handle) = waiting_menu("[2/5]", waiting, success);
let data = match serde_urlencoded::to_string(&pubkey) {
Ok(data) => data,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
return;
}
};
let output = match tokio::process::Command::new("torsocks")
.arg("curl")
.arg("-X")
.arg("POST")
.arg("-H")
.arg("Content-Type: application/x-www-form-urlencoded")
.arg("-d")
.arg(data)
.arg(format!("http://{domain}:{PORT}/pow"))
.output()
.await
{
Ok(result) => {
match tx.send(Ok(())) {
Ok(tx) => tx,
Err(e) => println!("{}", e.to_string().red()),
}
match handle.join() {
Ok(handle) => handle,
Err(_) => println!("{}", "Handle process fails".red()),
}
result
}
Err(e) => {
let result = Err(e.to_string());
match tx.send(result) {
Ok(tx) => tx,
Err(e) => println!("{}", e.to_string().red()),
}
match handle.join() {
Ok(handle) => handle,
Err(_) => println!("{}", "Handle process fails".red()),
}
get_enter();
return;
}
};
let json_data = match output.status.success() {
true => String::from_utf8_lossy(&output.stdout),
false => return,
};
let pow_response: PoW = match serde_json::from_str(&json_data) {
Ok(pow_response) => pow_response,
Err(_) => {
println!("{}", "Deserialization process failed".red());
get_enter();
return;
}
};
let verifying_key = match VerifyingKey::from_bytes(&bob_pubkey) {
Ok(verifying_key) => verifying_key,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
return;
}
};
let bytes = match hex::decode(pow_response.get_bytes()) {
Ok(bytes) => bytes,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
return;
}
};
let decode_sig = match hex::decode(pow_response.get_signature()) {
Ok(decode_sig) => decode_sig,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
return;
}
};
let signature_bytes: [u8; 64] = decode_sig.try_into().unwrap_or([0; 64]);
let signature = Signature::from_bytes(&signature_bytes);
if verifying_key.verify(&bytes, &signature).is_err() {
println!(
"{}",
"The proof of work you have received is not valid".red()
);
get_enter();
return;
}
let fixed_array: [u8; 36] = bytes[0..36].try_into().unwrap_or([0; 36]);
let (search, cost) = deserialize_pow(fixed_array);
let waiting = format!("Solving Proof of Work sent by {domain}...");
let success = "Proof of Work has been successfully solved".to_string();
let (tx, handle) = waiting_menu("[3/5]", waiting, success);
let nonce = match blake3_pow::search(&search, cost) {
Ok(nonce) => {
match tx.send(Ok(())) {
Ok(tx) => tx,
Err(e) => println!("{}", e.to_string().red()),
}
match handle.join() {
Ok(handle) => handle,
Err(_) => println!("{}", "Handle process fails".red()),
}
nonce
}
Err(_) => {
let result = Err("PoW failed, try again".to_string());
match tx.send(result) {
Ok(tx) => tx,
Err(e) => println!("{}", e.to_string().red()),
}
match handle.join() {
Ok(handle) => handle,
Err(_) => println!("{}", "Handle process fails".red()),
}
[0; 32]
}
};
let hex_solved_pow = hex::encode(nonce);
let diffie_hellman_sk = ReusableSecret::random();
let diffie_hellman = PublicKey::from(&diffie_hellman_sk).to_bytes();
let hex_diffie_hellman = hex::encode(diffie_hellman);
let mut combined = Vec::new();
combined.extend_from_slice(&diffie_hellman);
combined.extend_from_slice(&nonce);
let diffie_hellman_sig = sign_message(&combined, secret);
let hex_diffie_hellman_sig = hex::encode(diffie_hellman_sig);
let hex_pow = hex::encode(bytes.clone());
let hex_pow_sig = hex::encode(signature_bytes);
let input = KeyExchange::new(
hex_alice.clone(),
hex_solved_pow,
hex_pow,
hex_pow_sig,
hex_diffie_hellman,
hex_diffie_hellman_sig,
);
let waiting = format!("Exchanging public keys with {domain}...");
let success = format!("PublicKeys has been successfully exchanged with {domain}");
let (tx, handle) = waiting_menu("[4/5]", waiting, success);
let data = match serde_urlencoded::to_string(&input) {
Ok(data) => data,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
return;
}
};
let output = match tokio::process::Command::new("torsocks")
.arg("curl")
.arg("-X")
.arg("POST")
.arg("-H")
.arg("Content-Type: application/x-www-form-urlencoded")
.arg("-d")
.arg(data)
.arg(format!("http://{domain}:{PORT}/exchange_keys"))
.output()
.await
{
Ok(result) => {
match tx.send(Ok(())) {
Ok(tx) => tx,
Err(e) => println!("{}", e.to_string().red()),
}
match handle.join() {
Ok(handle) => handle,
Err(_) => println!("{}", "Handle process fails".red()),
}
result
}
Err(e) => {
let result = Err(e.to_string());
match tx.send(result) {
Ok(tx) => tx,
Err(e) => println!("{}", e.to_string().red()),
}
match handle.join() {
Ok(handle) => handle,
Err(_) => println!("{}", "Handle process fails".red()),
}
get_enter();
return;
}
};
let json_data = match output.status.success() {
true => String::from_utf8_lossy(&output.stdout),
false => return,
};
let key_exchange: KeyExchange = match serde_json::from_str(&json_data) {
Ok(key) => key,
Err(_) => {
println!("{}", "Deserialization process failed".red());
get_enter();
return;
}
};
let diffie_hellman = match hex::decode(key_exchange.get_diffie_hellman()) {
Ok(diffie_hellman) => diffie_hellman,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
return;
}
};
let waiting = format!("Sending the message to {domain}...");
let success = format!("The message has been successfully sent to {domain}");
let (tx, handle) = waiting_menu("[5/5]", waiting, success);
let bob_df_pk: [u8; 32] = match diffie_hellman.try_into() {
Ok(bob_df_pk) => bob_df_pk,
Err(_) => {
println!(
"{}",
"Failed to convert bob_df_pk to fixed-size array".red()
);
get_enter();
return;
}
};
let bob_df_public = PublicKey::from(bob_df_pk);
let shared_secret = diffie_hellman_sk.diffie_hellman(&bob_df_public);
let key = derive_key(shared_secret.as_bytes());
let cipher = ChaCha20Poly1305::new(&key);
let fixed_12bytes: &[u8; 12] = &shared_secret.as_bytes()[0..12]
.try_into()
.unwrap_or([0; 12]);
let shared_nonce = Nonce::from_slice(fixed_12bytes);
let signature_mts = sign_message(message_to_send.as_bytes(), secret);
let mut message_struct = Vec::new();
message_struct.extend_from_slice(&signature_mts);
message_struct.extend_from_slice(message_to_send.as_bytes());
let ciphertext = match cipher.encrypt(shared_nonce, message_struct.as_ref()) {
Ok(ciphertext) => ciphertext,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
return;
}
};
let hex_chiphertext = hex::encode(ciphertext.clone());
let sig = sign_message(&ciphertext, secret);
let hex_sig = hex::encode(sig);
let msg = SentMessage::new(hex_alice, hex_chiphertext, hex_sig);
let data = match serde_urlencoded::to_string(&msg) {
Ok(data) => data,
Err(e) => {
println!("{}", e.to_string().red());
get_enter();
return;
}
};
match tokio::process::Command::new("torsocks")
.arg("curl")
.arg("-X")
.arg("POST")
.arg("-H")
.arg("Content-Type: application/x-www-form-urlencoded")
.arg("-d")
.arg(data)
.arg(format!("http://{domain}:{PORT}/msg"))
.output()
.await
{
Ok(result) => {
match tx.send(Ok(())) {
Ok(tx) => tx,
Err(e) => println!("{}", e.to_string().red()),
}
match handle.join() {
Ok(handle) => handle,
Err(_) => println!("{}", "Handle process fails".red()),
}
result
}
Err(e) => {
let result = Err(e.to_string());
match tx.send(result) {
Ok(tx) => tx,
Err(e) => println!("{}", e.to_string().red()),
}
match handle.join() {
Ok(handle) => handle,
Err(_) => println!("{}", "Handle process fails".red()),
}
get_enter();
return;
}
};
get_enter();
}
pub async fn view_received_messages(
stdout: &mut std::io::Stdout,
index: &mut usize,
render_new_msg: &mut bool,
passowrd: &String,
) {
clear_screen(stdout);
if status_server(passowrd.clone()).await {
handle_client(index, render_new_msg, passowrd).await;
} else {
println!("{}", "Start the kkv server".red());
get_enter();
*render_new_msg = false; }
}
pub async fn delete_message(
stdout: &mut std::io::Stdout,
index: &mut usize,
render_new_msg: &mut bool,
passowrd: String,
) {
clear_screen(stdout);
if status_server(passowrd.clone()).await {
handle_del(index, render_new_msg, passowrd).await;
} else {
println!("{}", "Start the kkv server".red());
get_enter();
*render_new_msg = false; }
}