20 Commits

Author SHA1 Message Date
TIBERGHIEN corentin
c852c5bb4a root request 2026-01-13 17:13:35 +01:00
98fcc1a0b2 wip handling root request 2026-01-13 02:32:48 +01:00
8e279d9e24 wip datum 2026-01-11 22:12:08 +01:00
TIBERGHIEN corentin
92f38c9c12 fix name length issue 2026-01-10 20:34:51 +01:00
TIBERGHIEN corentin
489669b93d wip messages creation & handling 2026-01-09 20:09:39 +01:00
9fc33804d0 manage handshake wiip 2026-01-09 01:03:40 +01:00
TIBERGHIEN corentin
cd2f87cb81 wip 2026-01-08 19:12:07 +01:00
dc1767abe4 messages rewrite 2026-01-07 23:34:44 +01:00
f51b8e999c server handshake handling 2026-01-05 02:48:58 +01:00
c748dfa71d retry sending messages 2026-01-04 01:35:51 +01:00
74f30f2c7f server handshake gui 2026-01-02 23:21:50 +01:00
1914c68c9f registration put 2026-01-02 22:27:26 +01:00
1d72d7500a graphical stuff 2026-01-02 17:23:33 +01:00
c804695725 signature verification 2025-12-31 19:40:25 +01:00
cc09fab16d message handling and serv registration 2025-12-30 20:18:18 +01:00
ced0c992e7 wip messages handling 2025-12-23 17:05:29 +01:00
1844037488 code cleanup and documentation 2025-12-19 23:08:02 +01:00
3fa81e9ee3 signature extension 2025-12-18 14:06:02 +01:00
6ac06ccfe5 wip registering ip addresses 2025-12-18 00:34:57 +01:00
e902070c82 wip structures and message signature 2025-12-17 14:09:51 +01:00
15 changed files with 1948 additions and 228 deletions

BIN
README.md

Binary file not shown.

View File

@@ -1,22 +1,26 @@
use client_network::{ use client_network::{
MerkleNode, MerkleTree, NetworkCommand, NetworkEvent, NodeHash, filename_to_string, ChunkNode, MerkleNode, MerkleTree, NetworkCommand, NetworkEvent, NodeHash, filename_to_string,
node_hash_to_hex_string, node_hash_to_hex_string,
}; };
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
use egui::{ use egui::{
Align, Align2, Button, CentralPanel, CollapsingHeader, Context, Id, LayerId, Layout, Order, Align, Align2, Button, CentralPanel, CollapsingHeader, Context, Id, LayerId, Layout, Order,
Popup, ScrollArea, SidePanel, TopBottomPanel, Ui, ViewportCommand, Popup, ScrollArea, SidePanel, TextStyle, TopBottomPanel, Ui, ViewportCommand,
}; };
use std::collections::HashMap; use std::{collections::HashMap, fmt::format};
enum ServerStatus { enum ServerStatus {
Loading, Loading,
NotConnected, NotConnected,
Connected, Connected,
ConnectedHandshake,
} }
// --- Main Application Struct --- // --- Main Application Struct ---
pub struct P2PClientApp { pub struct P2PClientApp {
remaining: std::time::Duration, // temps restant
last_update: std::time::Instant, // pour calculer delta
timer_started: bool,
// Communication channels // Communication channels
network_cmd_tx: Sender<NetworkCommand>, network_cmd_tx: Sender<NetworkCommand>,
network_event_rx: Receiver<NetworkEvent>, network_event_rx: Receiver<NetworkEvent>,
@@ -25,6 +29,8 @@ pub struct P2PClientApp {
status_message: String, status_message: String,
known_peers: Vec<String>, known_peers: Vec<String>,
connect_address_input: String, connect_address_input: String,
connected_address: String,
connect_name_input: String,
// Key: Parent Directory Hash (String), Value: List of children FileNode // Key: Parent Directory Hash (String), Value: List of children FileNode
loaded_fs: HashMap<String, MerkleTree>, loaded_fs: HashMap<String, MerkleTree>,
@@ -33,33 +39,70 @@ pub struct P2PClientApp {
active_peer: Option<String>, active_peer: Option<String>,
server_status: ServerStatus, server_status: ServerStatus,
show_network_popup: bool, // gérer selon besoin
error_message: Option<String>, // Some(message) -> afficher, None -> rien
//
active_server: String,
} }
impl P2PClientApp { impl P2PClientApp {
pub fn new(cmd_tx: Sender<NetworkCommand>, event_rx: Receiver<NetworkEvent>) -> Self { pub fn new(cmd_tx: Sender<NetworkCommand>, event_rx: Receiver<NetworkEvent>) -> Self {
let (root_hash, tree_content) = MerkleNode::generate_base_tree(); //let (root_hash, tree_content) = MerkleNode::generate_base_tree();
let mut loaded_fs = HashMap::new(); let mut loaded_fs = HashMap::new();
let tree = MerkleTree::new(tree_content, root_hash); //let tree = MerkleTree::new(tree_content, root_hash);
loaded_fs.insert("bob".to_string(), tree); //loaded_fs.insert("bob".to_string(), tree);
Self { Self {
remaining: std::time::Duration::from_secs(0),
timer_started: false,
last_update: std::time::Instant::now(),
network_cmd_tx: cmd_tx, network_cmd_tx: cmd_tx,
network_event_rx: event_rx, network_event_rx: event_rx,
status_message: "Client Initialized. Awaiting network status...".to_string(), status_message: "Client Initialized. Awaiting network status...".to_string(),
known_peers: vec!["bob".to_string()], known_peers: vec!["bob".to_string()],
connect_address_input: "127.0.0.1:8080".to_string(), connect_address_input: "https://jch.irif.fr:8443".to_string(),
connected_address: "".to_string(),
loaded_fs, loaded_fs,
active_peer: None, active_peer: None,
server_status: ServerStatus::Loading, server_status: ServerStatus::NotConnected,
show_network_popup: false,
error_message: None,
connect_name_input: "bob".to_string(),
active_server: "".to_string(),
} }
} }
pub fn show_error(&mut self, msg: impl Into<String>) {
self.error_message = Some(msg.into());
}
pub fn clear_error(&mut self) {
self.error_message = None;
}
} }
// --- eframe::App Trait Implementation --- // --- eframe::App Trait Implementation ---
impl eframe::App for P2PClientApp { impl eframe::App for P2PClientApp {
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) { fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
if matches!(self.server_status, ServerStatus::Connected) && !self.timer_started {
self.remaining = std::time::Duration::from_secs(30 * 60);
self.last_update = std::time::Instant::now();
self.timer_started = true;
}
// in update (every frame)
let now = std::time::Instant::now();
let delta = now.saturating_duration_since(self.last_update);
self.last_update = now;
if matches!(self.server_status, ServerStatus::Connected)
&& self.remaining > std::time::Duration::ZERO
{
self.remaining = self.remaining.saturating_sub(delta);
}
// 1. Process incoming Network Events // 1. Process incoming Network Events
// We poll the channel and update the GUI state for every event received. // We poll the channel and update the GUI state for every event received.
while let Ok(event) = self.network_event_rx.try_recv() { while let Ok(event) = self.network_event_rx.try_recv() {
@@ -73,37 +116,77 @@ impl eframe::App for P2PClientApp {
} }
} }
NetworkEvent::PeerListUpdated(peers) => { NetworkEvent::PeerListUpdated(peers) => {
todo!(); //todo!();
self.known_peers = peers; self.known_peers = peers;
} }
NetworkEvent::FileTreeReceived(_peer_id, _) => { NetworkEvent::FileTreeReceived(_peer_id, _) => {
todo!(); todo!();
// self.loaded_tree_nodes.insert(_peer_id, tree); //self.loaded_tree_nodes.insert(_peer_id, tree);
self.status_message = "🔄 File tree updated successfully.".to_string(); //self.status_message = "🔄 File tree updated successfully.".to_string();
} }
NetworkEvent::FileTreeRootReceived(peer_id, root_hash) => { NetworkEvent::FileTreeRootReceived(peer_id, root_hash) => {
todo!(); // todo!();
// self.status_message = format!("🔄 Received Merkle Root from {}: {}", peer_id, &root_hash[..8]); /*self.status_message = format!(
// "🔄 Received Merkle Root from {}: {}",
// peer_id,
// self.active_peer_id = Some(peer_id.clone()); &root_hash[..8]
// );*/
//
// // Request the content of the root directory immediately if let Ok(chunknode) = ChunkNode::new(Vec::new()) {
// let _ = self.network_cmd_tx.send(NetworkCommand::RequestDirectoryContent( let mut data_map: HashMap<NodeHash, MerkleNode> = HashMap::new();
// peer_id, data_map.insert(root_hash, MerkleNode::Chunk(chunknode));
// root_hash, let tree = MerkleTree {
// )); data: data_map,
root: root_hash,
};
match &self.active_peer {
Some(activepeer) => {
self.loaded_fs.insert(activepeer.clone(), tree);
} }
NetworkEvent::Connected() => { None => {}
}
println!("tree created");
}
//self.active_peer_id = Some(peer_id.clone());
// Request the content of the root directory immediately
/*let _ = self
.network_cmd_tx
.send(NetworkCommand::RequestDirectoryContent(peer_id, root_hash));*/
}
NetworkEvent::Connected(ip) => {
self.server_status = ServerStatus::Connected; self.server_status = ServerStatus::Connected;
self.connected_address = ip.clone();
let _ = self.network_cmd_tx.send(NetworkCommand::FetchPeerList(
self.connected_address.clone(),
));
}
NetworkEvent::ConnectedHandshake() => {
self.server_status = ServerStatus::ConnectedHandshake;
}
NetworkEvent::Disconnected() => {
self.active_server = "".to_string();
self.connected_address = "".to_string();
self.known_peers.clear();
self.server_status = ServerStatus::NotConnected;
}
NetworkEvent::Error(err) => {
self.show_error(err);
} }
NetworkEvent::Disconnected() => todo!(),
NetworkEvent::Error() => todo!(),
NetworkEvent::DataReceived(_, merkle_node) => todo!(), NetworkEvent::DataReceived(_, merkle_node) => todo!(),
NetworkEvent::HandshakeFailed() => {}
NetworkEvent::ServerHandshakeFailed(err) => {
self.active_server = "".to_string();
self.server_status = ServerStatus::NotConnected;
let err_msg = format!("Failed to connect to the server: {}", err);
self.show_error(err_msg);
let res = self.network_cmd_tx.send(NetworkCommand::ResetServerPeer());
}
} }
} }
@@ -122,19 +205,104 @@ impl eframe::App for P2PClientApp {
}); });
ui.menu_button("Network", |ui| { ui.menu_button("Network", |ui| {
match self.server_status {
ServerStatus::Connected | ServerStatus::ConnectedHandshake => {
let desired = egui::vec2(300.0, 0.0); // width 300, auto-height if 0
ui.set_min_size(desired);
ui.vertical(|ui| {
if ui.button("Disconnect").clicked() {
println!("Disconnecting...");
let _ = self.network_cmd_tx.send(NetworkCommand::Disconnect());
self.server_status = ServerStatus::NotConnected;
self.remaining = std::time::Duration::from_secs(0);
self.timer_started = false;
ui.close();
}
});
}
ServerStatus::NotConnected => {
let desired = egui::vec2(0.0, 0.0); // width 300, auto-height if 0
ui.set_min_size(desired);
ui.vertical(|ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Server IP:"); ui.label("Server IP:");
ui.text_edit_singleline(&mut self.connect_address_input); ui.text_edit_singleline(&mut self.connect_address_input);
});
ui.horizontal(|ui| {
ui.label("Name:");
ui.text_edit_singleline(&mut self.connect_name_input);
});
if ui.button("Connect").clicked() { if ui.button("Connect").clicked() {
let addr = self.connect_address_input.clone(); let addr = self.connect_address_input.clone();
let name = self.connect_name_input.clone();
let _ = self let _ = self
.network_cmd_tx .network_cmd_tx
.send(NetworkCommand::ConnectToServer(addr)); .send(NetworkCommand::ConnectToServerPut(addr, name));
self.server_status = ServerStatus::Loading; self.server_status = ServerStatus::Loading;
ui.close(); ui.close();
} }
}); });
}
_ => {}
}
/* ui.horizontal(|ui| {
ui.label("Server peer name:");
ui.text_edit_singleline(&mut self.connect_server_name_input);
if ui.button("Connect").clicked() {
let addr = self.connect_address_input.clone();
let serv_name = self.connect_server_name_input.clone();
let _ = self
.network_cmd_tx
.send(NetworkCommand::ConnectToServer(addr, serv_name));
self.server_status = ServerStatus::Loading;
ui.close();
}
});*/
}); });
// état
/*if ui.button("Network").clicked() {
self.show_network_popup = true;
}*/
/*if self.show_network_popup {
egui::Window::new("Network")
.collapsible(false)
.resizable(false)
.show(ctx, |ui| {
ui.horizontal_wrapped(|ui| {
ui.with_layout(
egui::Layout::right_to_left(egui::Align::TOP),
|ui| {
if ui.button("✕").clicked() {
self.show_network_popup = false;
}
},
);
});
ui.horizontal(|ui| {
ui.label("Server IP:");
ui.text_edit_singleline(&mut self.connect_address_input);
});
ui.horizontal(|ui| {
ui.label("Server peer name:");
ui.text_edit_singleline(&mut self.connect_server_name_input);
if ui.button("Connect").clicked() {
// envoyer commande...
let addr = self.connect_address_input.clone();
let serv_name = self.connect_server_name_input.clone();
let _ = self
.network_cmd_tx
.send(NetworkCommand::ConnectToServer(addr, serv_name));
self.server_status = ServerStatus::Loading;
self.show_network_popup = false;
}
});
});
}*/
}); });
}); });
@@ -145,14 +313,22 @@ impl eframe::App for P2PClientApp {
ui.spinner(); ui.spinner();
} }
ServerStatus::Connected => { ServerStatus::Connected => {
ui.label("📡"); ui.label("Registered but no server peer chosen...");
} }
ServerStatus::NotConnected => { ServerStatus::NotConnected => {
ui.label("No connection.."); ui.label("No connection..");
} }
ServerStatus::ConnectedHandshake => {
let str = format!("📡");
ui.label(str);
}
} }
ui.add_space(ui.available_width() - 30.0); ui.add_space(ui.available_width() - 30.0);
ui.label("30:00"); // formater mm:ss
let secs = self.remaining.as_secs();
let minutes = secs / 60;
let seconds = secs % 60;
ui.label(format!("{:02}:{:02}", minutes, seconds));
}); });
}); });
@@ -164,8 +340,9 @@ impl eframe::App for P2PClientApp {
ui.heading("🌐 Known Peers"); ui.heading("🌐 Known Peers");
ui.add_space(20.0); ui.add_space(20.0);
if ui.button("🔄").clicked() { if ui.button("🔄").clicked() {
println!("addr:{}", self.connected_address.clone());
let res = self.network_cmd_tx.send(NetworkCommand::FetchPeerList( let res = self.network_cmd_tx.send(NetworkCommand::FetchPeerList(
self.connect_address_input.clone(), self.connected_address.clone(),
)); ));
if let Some(error) = res.err() { if let Some(error) = res.err() {
println!( println!(
@@ -186,10 +363,14 @@ impl eframe::App for P2PClientApp {
let is_active = let is_active =
self.active_peer.as_ref().map_or(false, |id| id == peer); // if peer.id == self.active_peer_id self.active_peer.as_ref().map_or(false, |id| id == peer); // if peer.id == self.active_peer_id
if ui let selectable;
.selectable_label(is_active, format!("{}", peer)) if &self.active_server == peer {
.clicked() selectable =
{ ui.selectable_label(is_active, format!("{} 📡 🌀", peer))
} else {
selectable = ui.selectable_label(is_active, format!("{}", peer));
}
if selectable.clicked() {
// switch to displaying this peer's tree // switch to displaying this peer's tree
self.active_peer = Some(peer.clone()); self.active_peer = Some(peer.clone());
// Request root content if not loaded // Request root content if not loaded
@@ -197,13 +378,39 @@ impl eframe::App for P2PClientApp {
.loaded_fs .loaded_fs
.contains_key(self.active_peer.as_ref().unwrap()) .contains_key(self.active_peer.as_ref().unwrap())
{ {
todo!(); //todo!();
// let _ = self.network_cmd_tx.send(NetworkCommand::RequestDirectoryContent( let _ = self.network_cmd_tx.send(NetworkCommand::Discover(
// peer.clone(), peer.clone(),
// peer.clone(), "root".to_string(),
// )); self.connected_address.clone(),
));
} }
} }
selectable.context_menu(|ui| {
// ... action
match self.server_status {
ServerStatus::Connected => {
if ui
.button("Utiliser le peer en tant que serveur")
.clicked()
{
self.active_server = peer.to_string();
let res = self.network_cmd_tx.send(
NetworkCommand::ServerHandshake(
peer.to_string(),
self.connected_address.clone(),
),
);
}
}
_ => {}
}
if ui.button("Infos").clicked() {
// action 3
ui.close();
}
// ... autres boutons
});
} }
} }
}); });
@@ -238,6 +445,21 @@ impl eframe::App for P2PClientApp {
// ui.label(format!("Status: {}", self.status_message)); // ui.label(format!("Status: {}", self.status_message));
}); });
if let Some(msg) = &self.error_message {
let msg = msg.clone();
egui::Window::new("Error")
.collapsible(false)
.resizable(false)
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
.show(ctx, |ui| {
ui.label(&msg);
if ui.button("OK").clicked() {
self.clear_error();
}
});
ctx.request_repaint();
}
ctx.request_repaint_after(std::time::Duration::from_millis(10)); ctx.request_repaint_after(std::time::Duration::from_millis(10));
} }
} }
@@ -305,7 +527,13 @@ impl P2PClientApp {
entry.content_hash, entry.content_hash,
tree, tree,
depth + 1, depth + 1,
Some(entry.filename), Some(
entry
.filename
.as_slice()
.try_into()
.expect("incorrect size"),
),
); );
} }
}); });
@@ -326,7 +554,7 @@ impl P2PClientApp {
.enabled(true) .enabled(true)
.show(ui, |ui| { .show(ui, |ui| {
for child in &node.children_hashes { for child in &node.children_hashes {
self.draw_file_node(ui, child.clone(), tree, depth + 1, None); self.draw_file_node(ui, child.content_hash, tree, depth + 1, None);
} }
}); });
} }

View File

@@ -1,5 +1,5 @@
use crate::gui_app::P2PClientApp; use crate::gui_app::P2PClientApp;
use client_network::{NetworkCommand, NetworkEvent, start_p2p_executor}; use client_network::{NetworkCommand, NetworkEvent, P2PSharedData, start_p2p_executor};
mod gui_app; mod gui_app;
@@ -11,7 +11,10 @@ async fn main() -> eframe::Result<()> {
// 2. Start the P2P Network Executor in a separate Tokio task // 2. Start the P2P Network Executor in a separate Tokio task
// The executor runs in the background of our main async runtime. // The executor runs in the background of our main async runtime.
let _network_handle = start_p2p_executor(network_cmd_rx, network_event_tx);
let shared_data: Option<P2PSharedData> = None;
let _network_handle = start_p2p_executor(network_cmd_rx, network_event_tx, shared_data);
// 3. Configure and Run the Eframe/Egui GUI // 3. Configure and Run the Eframe/Egui GUI
let options = eframe::NativeOptions { let options = eframe::NativeOptions {
@@ -34,4 +37,6 @@ async fn main() -> eframe::Result<()> {
Ok(Box::new(app)) Ok(Box::new(app))
}), }),
) )
// Starts the protocol
} }

View File

@@ -1,12 +1,18 @@
use std::io::Read;
use bytes::Bytes;
use p256::EncodedPoint;
use p256::ecdsa::{ use p256::ecdsa::{
Signature, SigningKey, VerifyingKey, Signature, SigningKey, VerifyingKey,
signature::{Signer, Verifier}, signature::{Signer, Verifier},
}; };
use rand_core::OsRng; use rand_core::OsRng;
use sha2::{Digest, Sha256};
/// ///
/// contains the ecdsa private key, the ecdsa public key and the username /// contains the ecdsa private key, the ecdsa public key and the username
/// ///
///
pub struct CryptographicSignature { pub struct CryptographicSignature {
priv_key: SigningKey, priv_key: SigningKey,
pub pub_key: VerifyingKey, pub pub_key: VerifyingKey,
@@ -40,16 +46,114 @@ pub fn formatPubKey(crypto_pair: CryptographicSignature) -> String {
hex::encode(pubkey_bytes) hex::encode(pubkey_bytes)
} }
#[cfg(test)] pub async fn get_peer_key(username: &String) -> Result<VerifyingKey, reqwest::Error> {
mod tests { let client = reqwest::Client::new();
// Note this useful idiom: importing names from outer (for mod tests) scope. let uri = format!("https://jch.irif.fr:8443/peers/{}/key", username);
use super::*; let res = client.get(uri).send().await?;
if res.status().is_success() {
println!("Successfully retreived the peers key.");
} else {
eprintln!(
"Failed to get the peers key from the server. Status: {}",
res.status()
);
}
let body: Bytes = res.bytes().await?;
let slice: &[u8] = body.as_ref();
let body_bytes: &[u8; 64] = slice.try_into().expect("size error");
let received_key = convert_verifyingkey(body_bytes);
Ok(received_key)
}
#[test] fn convert_verifyingkey(raw_xy: &[u8; 64]) -> VerifyingKey {
fn creating_cryptographic_signature() { let mut sec1 = [0u8; 65];
let username = String::from("quoicoubeh"); sec1[0] = 0x04;
let crypto_pair = CryptographicSignature::new(username); sec1[1..].copy_from_slice(raw_xy);
let formatted_pubkey =formatPubKey(crypto_pair);
println!("pubkey : {}",formatted_pubkey); let ep = EncodedPoint::from_bytes(&sec1).expect("invalid point bytes");
let pk = VerifyingKey::from_encoded_point(&ep).expect("invalid encoded point");
VerifyingKey::from(pk)
}
pub fn verify_signature(pubkey: VerifyingKey, message: &Vec<u8>) -> bool {
let length_bytes: [u8; 2] = message[5..7].try_into().expect("Taille incorrecte");
let length = u16::from_be_bytes(length_bytes);
println!("message length: {}", length);
let msg_to_hash = &message[..length as usize + 7];
let signature_bytes = &message[length as usize + 7..length as usize + 7 + 64];
println!("conversion start");
let sig = match Signature::from_bytes(signature_bytes.try_into().expect("conversion error")) {
Ok(s) => s,
Err(_) => return false,
};
println!("conversion done");
match pubkey.verify(&msg_to_hash, &sig) {
Ok(()) => true,
Err(_) => false,
} }
} }
///
/// takes a serialized message and adds the signature using the private key
///
pub fn sign_message(crypto_pair: &CryptographicSignature, message: &Vec<u8>) -> Vec<u8> {
let length_bytes: [u8; 2] = message[5..7]
.try_into()
.expect("slice with incorrect length");
let msg_length = u16::from_be_bytes(length_bytes);
println!(
"message to serialize: {:?}",
&message[..7 + msg_length as usize]
);
let digest = Sha256::digest(&message[..7 + msg_length as usize]);
let signature = crypto_pair.priv_key.sign_prehash_recoverable(&digest);
let message_length = 7 + msg_length as usize + 64;
let mut signed_message = Vec::with_capacity(message_length);
println!("{}", message_length);
signed_message.extend_from_slice(&message[..7 + msg_length as usize]);
println!("signed_tmp:{:?}", signed_message);
match signature {
Ok(signature) => {
let r = signature.0.r();
let s = signature.0.s();
let r_bytes = r.to_bytes(); // Returns a GenericArray/bytes object
let s_bytes = s.to_bytes();
signed_message.extend_from_slice(&r_bytes[..32]);
signed_message.extend_from_slice(&s_bytes[..32]);
println!("signed:{:?}, len: {}", signed_message, signed_message.len());
signed_message
}
Err(e) => {
panic!("error");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
///
/// creates a cryptographic signature
///
#[test]
fn creating_cryptographic_signature() {
let username = String::from("gamixtreize");
let crypto_pair = CryptographicSignature::new(username);
let formatted_pubkey = formatPubKey(crypto_pair);
println!("pubkey : {}", formatted_pubkey);
}
/*#[test]
fn signing_message() {
let username = String::from("gamixtreize");
let crypto_pair = CryptographicSignature::new(username.clone());
let handshake = HandshakeMessage::hello(0, 12, username);
let ser = handshake.serialize();
let signed_message = sign_message(&crypto_pair, &ser);
println!("unsigned_message: {:?}", ser);
println!("signed_message: {:?}", signed_message);
}*/
}

View File

@@ -80,7 +80,7 @@ impl MerkleTree {
} }
} }
fn generate_random_file_node( /*fn generate_random_file_node(
storage: &mut HashMap<NodeHash, MerkleNode>, storage: &mut HashMap<NodeHash, MerkleNode>,
) -> Result<NodeHash, String> { ) -> Result<NodeHash, String> {
let mut rng = rng(); let mut rng = rng();
@@ -110,9 +110,9 @@ fn generate_random_file_node(
storage.insert(hash, node); storage.insert(hash, node);
Ok(hash) Ok(hash)
} }
} }*/
fn generate_random_directory_node( /*fn generate_random_directory_node(
depth: u32, depth: u32,
max_depth: u32, max_depth: u32,
storage: &mut HashMap<NodeHash, MerkleNode>, storage: &mut HashMap<NodeHash, MerkleNode>,
@@ -172,7 +172,7 @@ fn generate_random_directory_node(
storage.insert(hash, node); storage.insert(hash, node);
Ok(hash) Ok(hash)
} }
} }*/
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ChunkNode { pub struct ChunkNode {
@@ -208,7 +208,7 @@ impl ChunkNode {
// Helper struct // Helper struct
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DirectoryEntry { pub struct DirectoryEntry {
pub filename: [u8; FILENAME_HASH_SIZE], pub filename: Vec<u8>,
pub content_hash: NodeHash, pub content_hash: NodeHash,
} }
@@ -240,7 +240,7 @@ pub struct BigNode {
} }
impl BigNode { impl BigNode {
pub fn new(children_hashes: Vec<NodeHash>) -> Result<Self, String> { /*pub fn new(children_hashes: Vec<NodeHash>) -> Result<Self, String> {
let n = children_hashes.len(); let n = children_hashes.len();
if n < MIN_BIG_CHILDREN || n > MAX_BIG_CHILDREN { if n < MIN_BIG_CHILDREN || n > MAX_BIG_CHILDREN {
return Err(format!( return Err(format!(
@@ -249,16 +249,17 @@ impl BigNode {
)); ));
} }
Ok(BigNode { children_hashes }) Ok(BigNode { children_hashes })
} }*/
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct BigDirectoryNode { pub struct BigDirectoryNode {
pub children_hashes: Vec<NodeHash>, //pub children_hashes: Vec<NodeHash>,
pub children_hashes: Vec<DirectoryEntry>,
} }
impl BigDirectoryNode { impl BigDirectoryNode {
pub fn new(children_hashes: Vec<NodeHash>) -> Result<Self, String> { /*pub fn new(children_hashes: Vec<NodeHash>) -> Result<Self, String> {
let n = children_hashes.len(); let n = children_hashes.len();
if n < MIN_BIG_CHILDREN || n > MAX_BIG_CHILDREN { if n < MIN_BIG_CHILDREN || n > MAX_BIG_CHILDREN {
return Err(format!( return Err(format!(
@@ -267,6 +268,14 @@ impl BigDirectoryNode {
)); ));
} }
Ok(BigDirectoryNode { children_hashes }) Ok(BigDirectoryNode { children_hashes })
}*/
pub fn new(entries: Vec<DirectoryEntry>) -> Result<Self, String> {
if entries.len() > MAX_DIRECTORY_ENTRIES {
return Err(format!("Directory exceeds {} bytes", entries.len()));
}
Ok(BigDirectoryNode {
children_hashes: entries,
})
} }
} }
@@ -301,14 +310,14 @@ impl MerkleNode {
} }
MerkleNode::BigDirectory(node) => { MerkleNode::BigDirectory(node) => {
for hash in &node.children_hashes { for hash in &node.children_hashes {
bytes.extend_from_slice(hash); bytes.extend_from_slice(&hash.content_hash);
} }
} }
} }
bytes bytes
} }
pub fn generate_random_tree( /*pub fn generate_random_tree(
max_depth: u32, max_depth: u32,
) -> Result<(NodeHash, HashMap<NodeHash, MerkleNode>), String> { ) -> Result<(NodeHash, HashMap<NodeHash, MerkleNode>), String> {
let mut storage = HashMap::new(); let mut storage = HashMap::new();
@@ -317,9 +326,9 @@ impl MerkleNode {
let root_hash = generate_random_directory_node(0, max_depth, &mut storage)?; let root_hash = generate_random_directory_node(0, max_depth, &mut storage)?;
Ok((root_hash, storage)) Ok((root_hash, storage))
} }*/
pub fn generate_base_tree() -> (NodeHash, HashMap<NodeHash, MerkleNode>) { /*pub fn generate_base_tree() -> (NodeHash, HashMap<NodeHash, MerkleNode>) {
let mut res = HashMap::new(); let mut res = HashMap::new();
let node1 = MerkleNode::Chunk(ChunkNode::new_random()); let node1 = MerkleNode::Chunk(ChunkNode::new_random());
@@ -369,5 +378,5 @@ impl MerkleNode {
res.insert(root_hash, root); res.insert(root_hash, root);
(root_hash, res) (root_hash, res)
} }*/
} }

View File

@@ -0,0 +1,96 @@
use crate::{BigDirectoryNode, DirectoryEntry, DirectoryNode, MerkleNode, MerkleTree, NodeHash};
use sha2::{Digest, Sha256};
const CHUNK: u8 = 0;
const DIRECTORY: u8 = 1;
const BIG: u8 = 2;
const BIGDIRECTORY: u8 = 3;
fn parse_received_datum(recevied_datum: Vec<u8>, datum_length: usize, mut tree: MerkleTree) {
if datum_length > recevied_datum.len() {
return;
}
if datum_length < 32 + 64 {
return;
}
let hash_name: [u8; 32] = recevied_datum[..32].try_into().expect("error");
let sigstart = datum_length - 64;
let value = &recevied_datum[32..sigstart];
let value_slice = value.to_vec();
let signature: [u8; 32] = recevied_datum[sigstart..datum_length]
.try_into()
.expect("Taille incorrecte");
let datum_type = value_slice[0];
match datum_type {
CHUNK => {
tree.data.insert(
hash_name,
MerkleNode::Chunk(crate::ChunkNode { data: value_slice }),
);
}
DIRECTORY => {
let nb_entries = value_slice[1];
let mut dir_entries = Vec::new();
let mut offset = 1 as usize;
for i in 0..nb_entries {
offset = (offset as u8 + 64 * i) as usize;
let name = &recevied_datum[offset..offset + 32];
let mut hash = [0u8; 32];
hash.copy_from_slice(&recevied_datum[offset + 32..offset + 64]);
// envoyer un datum request
dir_entries.push(DirectoryEntry {
filename: name.to_vec(),
content_hash: hash,
});
}
let current = DirectoryNode::new(dir_entries);
match current {
Ok(current_node) => {
tree.data
.insert(hash_name, MerkleNode::Directory(current_node));
}
Err(e) => {
println!("{}", e);
}
}
}
BIG => {
let chlidren: Vec<NodeHash> = Vec::new();
tree.data.insert(
hash_name,
MerkleNode::Big(crate::BigNode {
children_hashes: chlidren,
}),
);
}
BIGDIRECTORY => {
let nb_entries = value_slice[1];
let mut dir_entries = Vec::new();
let mut offset = 1 as usize;
for i in 0..nb_entries {
offset = (offset as u8 + 64 * i) as usize;
let name = &recevied_datum[offset..offset + 32];
let mut hash = [0u8; 32];
hash.copy_from_slice(&recevied_datum[offset + 32..offset + 64]);
// envoyer un datum request
dir_entries.push(DirectoryEntry {
filename: name.to_vec(),
content_hash: hash,
});
}
let current = BigDirectoryNode::new(dir_entries);
match current {
Ok(current_node) => {
tree.data
.insert(hash_name, MerkleNode::BigDirectory(current_node));
}
Err(e) => {
println!("{}", e);
}
}
}
_ => {}
}
}

View File

@@ -1,12 +1,119 @@
mod data;
mod protocol;
mod cryptographic_signature; mod cryptographic_signature;
mod registration; mod data;
mod datum_parsing;
mod message_handling;
mod messages_channels;
mod messages_structure; mod messages_structure;
mod peers_refresh;
mod registration;
mod server_communication;
use crate::{
cryptographic_signature::CryptographicSignature,
message_handling::EventType,
messages_channels::{MultipleSenders, start_receving_thread},
messages_structure::{ROOTREQUEST, construct_message},
peers_refresh::HandshakeHistory,
registration::{
get_socket_address, parse_addresses, perform_handshake, register_with_the_server,
},
server_communication::{generate_id, get_peer_list},
};
use std::{
fmt,
sync::{Arc, Mutex},
};
use std::{
io::Error,
net::{SocketAddr, UdpSocket},
str::FromStr,
};
pub struct P2PSharedData {
shared_socket: Arc<UdpSocket>,
shared_cryptopair: Arc<CryptographicSignature>,
shared_messageslist: Arc<Mutex<HashMap<i32, EventType>>>,
shared_senders: Arc<MultipleSenders>,
server_name: Arc<Mutex<String>>,
handshake_peers: Arc<HandshakeHistory>,
}
impl P2PSharedData {
pub fn new(
username: String,
cmd_tx: crossbeam_channel::Sender<NetworkEvent>,
) -> Result<P2PSharedData, Error> {
let messages_list = HashMap::<i32, EventType>::new();
let username = String::from(username);
let crypto_pair = CryptographicSignature::new(username);
let socket = UdpSocket::bind("0.0.0.0:0")?;
let shared_socket = Arc::new(socket);
let shared_cryptopair = Arc::new(crypto_pair);
let shared_messageslist = Arc::new(Mutex::new(messages_list));
let senders = MultipleSenders::new(1, &shared_socket, cmd_tx);
let shared_senders = Arc::new(senders);
let server_name = Arc::new(Mutex::new("".to_string()));
let handhsake_peers = Arc::new(HandshakeHistory::new());
Ok(P2PSharedData {
shared_socket: shared_socket,
shared_cryptopair: shared_cryptopair,
shared_messageslist: shared_messageslist,
shared_senders: shared_senders,
server_name: server_name,
handshake_peers: handhsake_peers,
})
}
pub fn socket(&self) -> Arc<UdpSocket> {
self.shared_socket.clone()
}
pub fn cryptopair(&self) -> Arc<CryptographicSignature> {
self.shared_cryptopair.clone()
}
pub fn messages_list(&self) -> Arc<Mutex<HashMap<i32, EventType>>> {
self.shared_messageslist.clone()
}
pub fn servername(&self) -> String {
let guard = self.server_name.lock().unwrap();
guard.to_string()
}
pub fn set_servername(&self, new: String) {
let mut guard = self.server_name.lock().unwrap();
*guard = new
}
pub fn senders(&self) -> Arc<MultipleSenders> {
self.shared_senders.clone()
}
pub fn socket_ref(&self) -> &UdpSocket {
&*self.shared_socket
}
pub fn cryptopair_ref(&self) -> &CryptographicSignature {
&*self.shared_cryptopair
}
pub fn handshake_ref(&self) -> &HandshakeHistory {
&*self.handshake_peers
}
pub fn messages_list_ref(&self) -> &Mutex<HashMap<i32, EventType>> {
&*self.shared_messageslist
}
pub fn senders_ref(&self) -> &MultipleSenders {
&*self.shared_senders
}
pub fn add_message(&self, id: i32, evt: EventType) {
let mut map = self.shared_messageslist.lock().unwrap();
map.insert(id, evt);
}
}
/// Messages sent to the Network thread by the GUI. /// Messages sent to the Network thread by the GUI.
pub enum NetworkCommand { pub enum NetworkCommand {
ConnectToServer(String), // ServerIP ConnectToServerPut(String, String), // ServerIP
ServerHandshake(String, String), // ServerName
FetchPeerList(String), // ServerIP FetchPeerList(String), // ServerIP
RegisterAsPeer(String), RegisterAsPeer(String),
Ping(), Ping(),
@@ -14,22 +121,31 @@ pub enum NetworkCommand {
RequestFileTree(String), // peer_id RequestFileTree(String), // peer_id
RequestDirectoryContent(String, String), RequestDirectoryContent(String, String),
RequestChunk(String, String), RequestChunk(String, String),
Disconnect(),
ResetServerPeer(),
Discover(String, String, String),
GetChildren(String, String),
// ... // ...
} }
/// Messages sent to the GUI by the Network thread. /// Messages sent to the GUI by the Network thread.
pub enum NetworkEvent { pub enum NetworkEvent {
Connected(), Connected(String),
ConnectedHandshake(),
Disconnected(), Disconnected(),
Error(), Error(String),
PeerConnected(String), PeerConnected(String),
PeerListUpdated(Vec<String>), PeerListUpdated(Vec<String>),
FileTreeReceived(String, Vec<MerkleNode>), // peer_id, content FileTreeReceived(String, Vec<MerkleNode>), // peer_id, content
DataReceived(String, MerkleNode), DataReceived(String, MerkleNode),
FileTreeRootReceived(String, String), FileTreeRootReceived(String, NodeHash),
HandshakeFailed(),
ServerHandshakeFailed(String),
// ... // ...
} }
use std::collections::HashMap;
pub use crate::data::*; pub use crate::data::*;
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
@@ -52,10 +168,13 @@ pub fn calculate_chunk_id(data: &[u8]) -> String {
pub fn start_p2p_executor( pub fn start_p2p_executor(
cmd_rx: Receiver<NetworkCommand>, cmd_rx: Receiver<NetworkCommand>,
event_tx: Sender<NetworkEvent>, event_tx: Sender<NetworkEvent>,
mut shared_data: Option<P2PSharedData>,
) -> tokio::task::JoinHandle<()> { ) -> tokio::task::JoinHandle<()> {
// Use tokio to spawn the asynchronous networking logic // Use tokio to spawn the asynchronous networking logic
tokio::task::spawn(async move { tokio::task::spawn(async move {
// P2P/Networking Setup goes here // P2P/Networking Setup goes here
let handshake_history = Arc::new(Mutex::new(HandshakeHistory::new()));
let handshake_clone = handshake_history.clone();
println!("Network executor started."); println!("Network executor started.");
@@ -64,6 +183,16 @@ pub fn start_p2p_executor(
// Check for commands from the GUI // Check for commands from the GUI
if let Ok(cmd) = cmd_rx.try_recv() { if let Ok(cmd) = cmd_rx.try_recv() {
match cmd { match cmd {
NetworkCommand::ServerHandshake(username, ip) => {
println!("server handshake called");
if let Some(sd) = shared_data.as_ref() {
start_receving_thread(sd, event_tx.clone(), &handshake_clone);
let res =
perform_handshake(&sd, username, ip, event_tx.clone(), true).await;
} else {
println!("no shared data");
}
}
NetworkCommand::ConnectPeer(addr) => { NetworkCommand::ConnectPeer(addr) => {
println!("[Network] ConnectPeer() called"); println!("[Network] ConnectPeer() called");
println!("[Network] Attempting to connect to: {}", addr); println!("[Network] Attempting to connect to: {}", addr);
@@ -74,28 +203,131 @@ pub fn start_p2p_executor(
NetworkCommand::RequestFileTree(_) => { NetworkCommand::RequestFileTree(_) => {
println!("[Network] RequestFileTree() called"); println!("[Network] RequestFileTree() called");
} }
NetworkCommand::Discover(username, hash, ip) => {
// envoie un handshake au peer, puis un root request
if let Some(sd) = shared_data.as_ref() {
let res = {
let m = handshake_clone.lock().unwrap();
m.get_peer_info_username(username.clone()).cloned()
};
match res {
Some(peerinfo) => {
// envoyer un root request
let rootrequest = construct_message(
ROOTREQUEST,
Vec::new(),
generate_id(),
sd.cryptopair_ref(),
);
match rootrequest {
None => {}
Some(resp_msg) => {
println!("msg_sent:{:?}", resp_msg);
sd.senders_ref().send_via(
0,
resp_msg,
peerinfo.ip.to_string(),
false,
sd.messages_list_ref(),
);
}
}
}
None => {
// envoyer un handshake
let res = perform_handshake(
&sd,
username,
ip,
event_tx.clone(),
false,
)
.await;
}
}
} else {
println!("no shared data");
}
}
NetworkCommand::GetChildren(username, hash) => {
// envoie un datum request au peer
}
NetworkCommand::RequestDirectoryContent(_, _) => { NetworkCommand::RequestDirectoryContent(_, _) => {
println!("[Network] RequestDirectoryContent() called"); println!("[Network] RequestDirectoryContent() called");
} }
NetworkCommand::RequestChunk(_, _) => { NetworkCommand::RequestChunk(_, _) => {
println!("[Network] RequestChunk() called"); println!("[Network] RequestChunk() called");
} }
NetworkCommand::ConnectToServer(ip) => { NetworkCommand::ConnectToServerPut(ip, name) => {
println!("[Network] ConnectToServer() called"); println!("[Network] ConnectToServer() called");
// Actual server connection // Actual server connection
tokio::time::sleep(std::time::Duration::from_millis(5000)).await; shared_data = match P2PSharedData::new(name.clone(), event_tx.clone()) {
Ok(sd) => Some(sd),
Err(e) => {
let mut err_msg = String::from("failed to initialize socket: ");
err_msg += &e.to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg));
let res = event_tx.send(NetworkEvent::Disconnected());
None
}
};
let res = event_tx.send(NetworkEvent::Connected()); if let Some(sd) = shared_data.as_ref() {
if let Err(e) = register_with_the_server(&sd.cryptopair(), &ip).await {
let mut err_msg = String::from("request failed: ");
err_msg += &e.to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg));
let res = event_tx.send(NetworkEvent::Disconnected());
} else {
let res = event_tx.send(NetworkEvent::Connected(ip));
println!("username created: {}", sd.cryptopair().username);
}
//println!("ip: {}", ip);
}
//tokio::time::sleep(std::time::Duration::from_millis(5000)).await;
/*let res = event_tx.send(NetworkEvent::Connected());
if let Some(error) = res.err() { if let Some(error) = res.err() {
println!( println!(
"[Network] Couldn't send crossbeam message to GUI: {}", "[Network] Couldn't send crossbeam message to GUI: {}",
error.to_string() error.to_string()
); );
} }*/
} }
NetworkCommand::FetchPeerList(ip) => { NetworkCommand::FetchPeerList(ip) => {
if ip == "" {
let res = event_tx.send(NetworkEvent::Error(
"Not registered to any server".to_string(),
));
} else {
println!("cc");
match get_peer_list(ip).await {
Ok(body) => match String::from_utf8(body.to_vec()) {
Ok(peers_list) => {
let mut peers: Vec<String> = Vec::new();
let mut current = String::new();
for i in peers_list.chars() {
if i == '\n' {
peers.push(current.clone());
current.clear();
} else {
current.push(i);
}
}
let res =
event_tx.send(NetworkEvent::PeerListUpdated(peers));
}
Err(e) => {
eprintln!("invalid UTF-8 in socket address bytes: {}", e);
}
},
Err(e) => println!("error"),
}
}
println!("[Network] FetchPeerList() called"); println!("[Network] FetchPeerList() called");
} }
NetworkCommand::RegisterAsPeer(_) => { NetworkCommand::RegisterAsPeer(_) => {
@@ -104,6 +336,22 @@ pub fn start_p2p_executor(
NetworkCommand::Ping() => { NetworkCommand::Ping() => {
println!("[Network] Ping() called"); println!("[Network] Ping() called");
} }
NetworkCommand::Disconnect() => {
if let Some(sd) = shared_data.as_ref() {
println!("Disconnecting: {}", &sd.cryptopair().username);
shared_data = None;
let res = event_tx.send(NetworkEvent::Disconnected());
} else {
println!("no p2p data");
}
}
NetworkCommand::ResetServerPeer() => {
if let Some(sd) = shared_data.as_ref() {
sd.set_servername("".to_string());
} else {
println!("no p2p data");
}
}
} }
} }

View File

@@ -0,0 +1,428 @@
use crate::{
NetworkEvent, NodeHash,
cryptographic_signature::{
CryptographicSignature, get_peer_key, sign_message, verify_signature,
},
messages_channels::MultipleSenders,
messages_structure::construct_message,
peers_refresh::HandshakeHistory,
registration,
server_communication::generate_id,
};
use std::{collections::HashMap, net::SocketAddr};
use std::{
net::IpAddr,
sync::{Arc, Mutex},
};
pub enum EventType {
SendRootRequest,
}
const ID: usize = 4;
const TYPE: usize = 5;
const LENGTH: usize = 7;
const EXTENSIONS: usize = 4;
const SIGNATURE: usize = 64;
const PING: u8 = 0;
const OK: u8 = 128;
const ERROR: u8 = 129;
const HELLO: u8 = 1;
const HELLOREPLY: u8 = 130;
const ROOTREQUEST: u8 = 2;
const ROOTREPLY: u8 = 131;
const DATUMREQUEST: u8 = 3;
const NODATUM: u8 = 133;
const DATUM: u8 = 132;
const NATTRAVERSALREQUEST: u8 = 4;
const NATTRAVERSALREQUEST2: u8 = 5;
pub fn handle_recevied_message(
messages_list: &Arc<Mutex<HashMap<i32, EventType>>>,
recevied_message: &Vec<u8>,
crypto_pair: &CryptographicSignature,
//socket_addr: &SocketAddr,
senders: &MultipleSenders,
server_name: &String,
cmd_tx: crossbeam_channel::Sender<NetworkEvent>,
ip: SocketAddr,
handhsake_history: &Arc<Mutex<HandshakeHistory>>,
) {
if recevied_message.len() < 4 {
return;
} // Basic safety check
let message_id: [u8; 4] = recevied_message[0..4].try_into().expect("size error");
let id = i32::from_be_bytes(message_id);
let mut is_resp_to_server_handshake = false;
if recevied_message[4] == HELLO {
let length_bytes: [u8; 2] = recevied_message[TYPE..LENGTH]
.try_into()
.expect("Taille incorrecte");
let msg_length = u16::from_be_bytes(length_bytes) as usize;
let ilength = u16::from_be_bytes(length_bytes);
let received_name = &recevied_message[LENGTH + EXTENSIONS..LENGTH + ilength as usize];
let name = String::from_utf8(received_name.to_vec()).expect("wrong name");
if name.clone() == server_name.clone() {
is_resp_to_server_handshake = true;
}
}
let resp = parse_message(
recevied_message.to_vec(),
id,
crypto_pair,
cmd_tx,
ip,
messages_list,
handhsake_history,
);
match resp {
None => {}
Some(resp_msg) => {
println!("msg_sent:{:?}", resp_msg);
senders.send_via(
0,
resp_msg,
ip.to_string(),
is_resp_to_server_handshake,
messages_list,
);
}
}
// Lock the mutex to access the HashMap
/*let list = messages_list.lock().unwrap();
let eventtype = list.get(&id); // Clone the enum so we can release the lock if needed
match eventtype {
Some(EventType::ServerHelloReply) => {
/*registration::register_ip_addresses(
crypto_pair,
socket_addr.to_string(),
senders,
&messages_list, // Pass the mutable reference inside the lock
546,
);*/
}
Some(_) => print!("Not implemented"),
None => {
let message_type = recevied_message[4];
// Handle handshake
if message_type == 1 {
let mut resp_to_serv = false;
println!("verify the signature");
let parsed_received_message = HandshakeMessage::parse(recevied_message.to_vec());
let received_name = String::from_utf8(parsed_received_message.name).expect("error");
let peer_pubkey = tokio::runtime::Runtime::new()
.unwrap()
.block_on(get_peer_key(&received_name))
.expect("failed to retrieve public key");
if received_name == server_name.to_string() {
resp_to_serv = true;
}
if !verify_signature(peer_pubkey, recevied_message) {
println!(
"incorrect signature from given peer: {}, ignoring message {}",
&received_name, id
);
} else {
// verify if this is a server handshake request
let username_size = crypto_pair.username.len();
let hello_handshake = HandshakeMessage::helloReply(
id as u32,
username_size as u16 + 4,
crypto_pair.username.clone(),
);
//HandshakeMessage::display(&hello_handshake);
let hello_handshake_serialized = hello_handshake.serialize();
let message_signed = sign_message(crypto_pair, &hello_handshake_serialized);
senders.send_via(0, message_signed, socket_addr.to_string(), resp_to_serv);
let mut list = messages_list.lock().expect("Failed to lock messages_list");
match list.get(&id) {
Some(_) => {
list.remove(&id);
}
None => {
list.insert(id, EventType::ServerHelloReply);
}
}
}
}
print!("Message not found for ID: {}", id)
}
}*/
}
pub fn parse_message(
received_message: Vec<u8>,
id: i32,
crypto_pair: &CryptographicSignature,
cmd_tx: crossbeam_channel::Sender<NetworkEvent>,
ip: SocketAddr,
messages_list: &Arc<Mutex<HashMap<i32, EventType>>>,
handhsake_history_mutex: &Arc<Mutex<HandshakeHistory>>,
) -> Option<Vec<u8>> {
let mut handhsake_history = handhsake_history_mutex.lock().unwrap();
let cmd_tx_clone = cmd_tx.clone();
let id_bytes: [u8; 4] = received_message[0..ID]
.try_into()
.expect("Taille incorrecte");
let msgtype = received_message[ID];
let length_bytes: [u8; 2] = received_message[TYPE..LENGTH]
.try_into()
.expect("Taille incorrecte");
let msg_length = u16::from_be_bytes(length_bytes) as usize;
// verify signature
match msgtype {
HELLO | HELLOREPLY | NODATUM | NATTRAVERSALREQUEST | NATTRAVERSALREQUEST2 => {
let ilength = u16::from_be_bytes(length_bytes);
println!("name received length: {}", ilength);
let received_name = &received_message[LENGTH + EXTENSIONS..LENGTH + ilength as usize];
let received_username = String::from_utf8(received_name.to_vec());
match received_username {
Ok(username) => {
let peer_pubkey =
match handhsake_history.get_peer_info_username(username.clone()) {
Some(peerinfo) => peerinfo.pubkey,
_ => tokio::runtime::Runtime::new()
.unwrap()
.block_on(get_peer_key(&username))
.expect("failed to retrieve public key"),
};
match msgtype {
HELLOREPLY => {
handhsake_history.add_new_handshake(peer_pubkey, "".to_string(), ip);
}
_ => {}
}
let signature: [u8; SIGNATURE] = received_message
[LENGTH + msg_length..LENGTH + msg_length + SIGNATURE]
.try_into()
.expect("Taille incorrecte");
if !verify_signature(peer_pubkey, &received_message) {
println!(
"incorrect signature from given peer: {}, ignoring message of type {} with id {}",
&username, received_message[ID], id
);
return None;
}
}
Err(e) => {
println!("incorrect name: {}", e);
return None;
}
}
}
ROOTREPLY => {
let ilength = u16::from_be_bytes(length_bytes);
println!("name received length: {}", ilength);
if let Some(peerinfo) = handhsake_history.get_peer_info_ip(ip.to_string()) {
if !verify_signature(peerinfo.pubkey, &received_message) {
println!(
"incorrect signature from given peer: {}, ignoring message of type {} with id {}",
&peerinfo.username, received_message[ID], id
);
return None;
} else {
println!("signature verified");
}
}
}
_ => {}
}
// Message handling
let mut constructed_message: Option<Vec<u8>> = None;
match msgtype {
// PING
//
// envoie un OK
PING => {
constructed_message = construct_message(OK, Vec::new(), id, crypto_pair);
}
//
// OK
//
// rien ?
// si NATTRAVERSALREQUEST alors
//
// ERROR
//
// affiche un msg d'erreur
ERROR => {
if let Ok(err_received) =
String::from_utf8(received_message[LENGTH..(msg_length + LENGTH)].to_vec())
{
let err_msg = format!("Error received from peer {} : {}", ip, err_received);
let _ = cmd_tx_clone.send(NetworkEvent::Error(err_msg));
} else {
let err_msg = format!("Error received from peer {} : N/A", ip,);
let _ = cmd_tx_clone.send(NetworkEvent::Error(err_msg));
}
}
// HELLO
//
// envoie une hello reply
//
HELLO => {
let mut payload = Vec::new();
payload.extend_from_slice(&0u32.to_be_bytes());
payload.extend_from_slice(&crypto_pair.username.clone().as_bytes());
let helloreply = construct_message(HELLOREPLY, payload, id, crypto_pair);
return helloreply;
}
// HELLOREPLY
//
//
// ajoute a la liste des peers handshake
HELLOREPLY => {
// ajoute l'username a la liste des peers handshake
let received_length = u16::from_be_bytes(
received_message[TYPE..LENGTH]
.try_into()
.expect("incorrect size"),
);
let received_username =
&received_message[LENGTH + EXTENSIONS..LENGTH + received_length as usize];
handhsake_history.update_peer_info(
ip.to_string(),
String::from_utf8(received_username.to_vec()).expect("invalid conversion"),
);
// verifie s'il faut renvoyer un root request
let guard = messages_list.lock().expect("Échec du verrouillage");
let res = guard.get(&id);
match res {
Some(ev) => {
match ev {
EventType::SendRootRequest => {
// envoyer la root request
let rootrequest = construct_message(
ROOTREQUEST,
Vec::new(),
generate_id(),
crypto_pair,
);
return rootrequest;
}
}
}
None => {}
}
}
//
// ROOTREQUEST
//
// envoie un root reply
//
// ROOTREPLY
//
ROOTREPLY => {
// recuperer le pseudo du peers ayant repondu
let peers_exist = handhsake_history.get_peer_info_ip(ip.to_string());
match peers_exist {
Some(peerinfo) => {
// envoyer le hash a la gui
let received_hash: NodeHash = received_message[LENGTH..(32 + LENGTH)]
.try_into()
.expect("incorrect size");
let res = cmd_tx_clone.send(NetworkEvent::FileTreeRootReceived(
peerinfo.username.clone(),
received_hash,
));
println!("file tree sent")
}
None => {
eprintln!("no peers found");
}
}
}
//
// DATUMREQUEST
//
// envoie le datum
//
// NODATUM
//
// affiche un msg d'erreur
//
// DATUM
//
// parcourt le directory recu ou le big directory et renvoie une DATUMREQUEST pour chaque
// directory ou big directory lu
//
// NATTRAVERSALREQUEST
//
// repond OK et envoie un NATTRAVERSALREQUEST2 au pair B
//
// NATTRAVERSALREQUEST2
//
// envoie OK à S puis envoie un ping à S
// PING
//
// envoie un OK
//
// OK
//
// si NATTRAVERSALREQUEST alors
//
// ERROR
//
// affiche un msg d'erreur
//
// HELLO
//
// envoie une hello reply
//
// HELLOREPLY
//
// envoie un root request
//
// ROOTREQUEST
//
// envoie un root reply
//
// ROOTREPLY
//
// envoie un datum request
//
// DATUMREQUEST
//
// envoie le datum
//
// NODATUM
//
// affiche un msg d'erreur
//
// DATUM
//
// parcourt le directory recu ou le big directory et renvoie une DATUMREQUEST pour chaque
// directory ou big directory lu
//
// NATTRAVERSALREQUEST
//
// repond OK et envoie un NATTRAVERSALREQUEST2 au pair B
//
// NATTRAVERSALREQUEST2
//
// envoie OK à S puis envoie un ping à S
_ => return None,
};
constructed_message
}

View File

@@ -0,0 +1,300 @@
use crate::P2PSharedData;
use crate::cryptographic_signature::CryptographicSignature;
use crate::message_handling::EventType;
use crate::message_handling::handle_recevied_message;
use crate::peers_refresh::HandshakeHistory;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::net::UdpSocket;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{self, Sender};
use std::thread;
use std::collections::VecDeque;
use std::time::{Duration, Instant};
use crate::NetworkEvent;
pub struct MultipleSenders {
senders: Vec<Sender<Message>>,
response_channel: crossbeam_channel::Sender<NetworkEvent>,
}
pub struct Message {
pub payload: Vec<u8>,
pub address: String,
pub is_resp_to_server_handshake: bool,
}
struct RetryMessage {
msg: Message,
attempts: u8,
next_try: Instant,
}
impl MultipleSenders {
/*pub fn new(num_channels: usize, socket: &Arc<UdpSocket>) -> Self {
let mut senders = Vec::new();
// Wrap the socket in an Arc so it can be shared across threads
for i in 0..num_channels {
let (tx, rx) = mpsc::channel::<Message>();
// Clone the Arc (this just bumps the reference count, it doesn't copy the socket)
let sock_clone = Arc::clone(&socket);
senders.push(tx);
thread::spawn(move || {
println!("Canal d'envoi {} prêt", i);
for msg in rx {
// Use the cloned Arc inside the thread
if let Err(e) = sock_clone.send_to(&msg.payload, &msg.address) {
eprintln!(
"Erreur d'envoi sur canal {}: {}, address: {}",
i, e, &msg.address
);
} else {
let message_id: [u8; 4] = msg.payload[0..4].try_into().expect("size error");
let id = i32::from_be_bytes(message_id);
let message_type = msg.payload[4];
println!(
"Message {0} de type {1} envoyé à {2} par le canal {3}",
id, message_type, msg.address, i
);
}
}
});
}
MultipleSenders { senders }
}*/
pub fn new(
num_channels: usize,
socket: &Arc<UdpSocket>,
cmd_tx: crossbeam_channel::Sender<NetworkEvent>,
) -> Self {
let mut senders = Vec::new();
for i in 0..num_channels {
let (tx, rx) = mpsc::channel::<Message>();
let sock_clone = Arc::clone(&socket);
let cmd_tx_clone = cmd_tx.clone();
senders.push(tx);
thread::spawn(move || {
println!("Canal d'envoi {} prêt", i);
let mut queue: VecDeque<RetryMessage> = VecDeque::new();
let max_attempts = 5;
loop {
// Priorité aux messages en attente prêts à être réessayés
if let Some(front) = queue.front() {
if front.next_try <= Instant::now() {
// On prend le message de la queue
let mut item = queue.pop_front().unwrap();
match sock_clone.send_to(&item.msg.payload, &item.msg.address) {
Ok(_) => {
if (&item).msg.is_resp_to_server_handshake {
let res =
cmd_tx_clone.send(NetworkEvent::ConnectedHandshake());
}
let message_id: [u8; 4] =
item.msg.payload[0..4].try_into().expect("size error");
let id = i32::from_be_bytes(message_id);
let message_type = item.msg.payload[4];
println!(
"Message {0} de type {1} envoyé à {2} par le canal {3} (retry {4})",
id, message_type, item.msg.address, i, item.attempts
);
}
Err(e) => {
item.attempts += 1;
if item.attempts >= max_attempts {
let str = format!(
"Abandon du message après {} tentatives sur canal {}: {}, address: {}",
item.attempts, i, e, item.msg.address
);
if (&item).msg.is_resp_to_server_handshake {
let res = cmd_tx_clone
.send(NetworkEvent::ServerHandshakeFailed(str));
}
} else {
// Backoff exponentiel simple
let backoff = Duration::from_millis(
2000u64.saturating_pow(item.attempts as u32),
);
item.next_try = Instant::now() + backoff;
eprintln!(
"Erreur d'envoi sur canal {}: {}, reprogrammation dans {:?}, tentative {}",
i, e, backoff, item.attempts
);
queue.push_front(item); // remettre en tête pour réessayer plus tôt
}
}
}
continue;
}
}
// Si aucun retry prêt, on bloque sur rx avec timeout court, pour pouvoir traiter les timers
match rx.recv_timeout(Duration::from_millis(200)) {
Ok(msg) => {
// On tente d'envoyer immédiatement
match sock_clone.send_to(&msg.payload, &msg.address) {
Ok(_) => {
if msg.is_resp_to_server_handshake {
let res =
cmd_tx_clone.send(NetworkEvent::ConnectedHandshake());
}
let message_id: [u8; 4] =
msg.payload[0..4].try_into().expect("size error");
let id = i32::from_be_bytes(message_id);
let message_type = msg.payload[4];
println!(
"Message {0} de type {1} envoyé à {2} par le canal {3}",
id, message_type, msg.address, i
);
}
Err(e) => {
eprintln!(
"Erreur d'envoi initial sur canal {}: {}, address: {} -- mise en queue pour retry",
i, e, &msg.address
);
let retry = RetryMessage {
msg,
attempts: 1,
next_try: Instant::now() + Duration::from_millis(100),
};
queue.push_back(retry);
}
}
}
Err(mpsc::RecvTimeoutError::Timeout) => {
// Permet de vérifier la queue à nouveau
continue;
}
Err(mpsc::RecvTimeoutError::Disconnected) => {
// Le sender a été fermé ; vider la queue et sortir
eprintln!(
"Sender fermé pour le canal {}, fermeture du thread d'envoi",
i
);
break;
}
}
}
});
}
MultipleSenders {
senders,
response_channel: cmd_tx.clone(),
}
}
/// Envoie un message via un canal spécifique (round-robin ou index précis)
pub fn send_via(
&self,
channel_idx: usize,
data: Vec<u8>,
remote_addr: String,
is_resp_to_server_handshake: bool,
messages_list: &Mutex<HashMap<i32, EventType>>,
) {
println!(
"is_resp_to_server_handshake {}",
is_resp_to_server_handshake
);
let msg_to_send = Message {
payload: data.clone(),
address: remote_addr,
is_resp_to_server_handshake,
};
if let Some(sender) = self.senders.get(channel_idx) {
let _ = sender.send(msg_to_send);
}
if !is_resp_to_server_handshake {
let mut guard = messages_list.lock().unwrap();
let message_id: [u8; 4] = data[0..4].try_into().expect("size error");
let id = i32::from_be_bytes(message_id);
guard.insert(id, EventType::SendRootRequest);
}
}
/*pub fn start_receving_thread(
socket: &Arc<UdpSocket>,
messages_list: &Arc<HashMap<i32, EventType>>,
crypto_pair: &Arc<CryptographicSignature>,
socket_addr: SocketAddr,
senders: &Arc<MultipleSenders>,
) {
let sock_clone = Arc::clone(socket);
let cryptopair_clone = Arc::clone(crypto_pair);
let senders_clone = Arc::clone(senders);
let messages_clone = Arc::clone(messages_list);
thread::spawn(move || {
let mut buf = [0u8; 1024];
loop {
match sock_clone.recv_from(&mut buf) {
Ok((amt, src)) => {
handle_recevied_message(
&messages_clone,
&buf.to_vec(),
&cryptopair_clone,
&socket_addr,
&senders_clone,
);
println!("Reçu {} octets de {}: {:?}", amt, src, &buf[..amt]);
}
Err(e) => eprintln!("Erreur de réception: {}", e),
}
}
});
}*/
}
pub fn start_receving_thread(
shared_data: &P2PSharedData,
cmd_tx: crossbeam_channel::Sender<NetworkEvent>,
handshake_history: &Arc<Mutex<HandshakeHistory>>,
) {
let sock_clone = shared_data.socket();
let cryptopair_clone = shared_data.cryptopair();
let senders_clone = shared_data.senders();
let messages_clone = shared_data.messages_list();
let servername_clone = shared_data.servername();
let handshake_clone = handshake_history.clone();
thread::spawn(move || {
let mut buf = [0u8; 1024];
loop {
match sock_clone.recv_from(&mut buf) {
Ok((amt, src)) => {
let received_data = buf[..amt].to_vec();
println!("Reçu {} octets de {}: {:?}", amt, src, received_data);
handle_recevied_message(
&messages_clone,
&received_data,
&cryptopair_clone,
&senders_clone,
&servername_clone,
cmd_tx.clone(),
src,
&handshake_clone,
);
}
Err(e) => eprintln!("Erreur de réception: {}", e),
}
}
});
}

View File

@@ -1,50 +1,69 @@
struct UDPMessage { use crate::{
id: [u8; 4], cryptographic_signature::{CryptographicSignature, sign_message},
msg_type: u8, server_communication::generate_id,
length: [u8; 2], };
body: [u8; 985],
signature: [u8; 32], const ID: usize = 4;
const TYPE: usize = 5;
const LENGTH: usize = 7;
const EXTENSIONS: usize = 4;
const SIGNATURE: usize = 64;
const PING: u8 = 0;
const OK: u8 = 128;
const ERROR: u8 = 129;
const HELLO: u8 = 1;
const HELLOREPLY: u8 = 130;
pub const ROOTREQUEST: u8 = 2;
const ROOTREPLY: u8 = 131;
const DATUMREQUEST: u8 = 3;
const NODATUM: u8 = 133;
const DATUM: u8 = 132;
const NATTRAVERSALREQUEST: u8 = 4;
const NATTRAVERSALREQUEST2: u8 = 5;
pub fn construct_message(
msgtype: u8,
payload: Vec<u8>,
id: i32,
crypto_pair: &CryptographicSignature,
) -> Option<Vec<u8>> {
let mut message = Vec::new();
// ID
// type
message.extend_from_slice(&id.to_be_bytes());
message.push(msgtype);
match msgtype {
HELLO | HELLOREPLY => {
// length
let a = payload.len() as u16;
println!("payload size:{}", a);
message.extend_from_slice(&a.to_be_bytes());
message.extend_from_slice(&payload);
let signature = sign_message(crypto_pair, &message);
return Some(signature);
}
PING | OK | ROOTREQUEST => {
message.extend_from_slice(&0u16.to_be_bytes());
return Some(message);
}
ERROR | DATUMREQUEST => {
message.extend_from_slice(&payload.len().to_be_bytes());
message.extend_from_slice(&payload);
return Some(message);
}
ROOTREPLY | NODATUM | DATUM | NATTRAVERSALREQUEST => {
message.extend_from_slice(&payload.len().to_be_bytes());
message.extend_from_slice(&payload);
let signature = sign_message(crypto_pair, &message);
message.extend_from_slice(&signature);
return Some(message);
}
_ => {}
}
None
} }
struct HandshakeMessage {
id: [u8; 4],
msg_type: u8,
length: [u8; 2],
extensions: [u8; 4],
name: [u8; 981],
signature: [u8; 32],
}
impl UDPMessage {
pub fn ping(id: i32) -> UDPMessage {
UDPMessage { id: id.to_ne_bytes(), msg_type: 0, length: [0; 2], body: [0; 985], signature: [0; 32]}
}
pub fn error(id: i32) -> UDPMessage {
UDPMessage { id: id.to_ne_bytes(), msg_type: 129, length: [0; 2], body: [0; 985], signature: [0; 32]}
}
pub fn hello(id: i32, length: i16, username: String) -> HandshakeMessage {
let username_bytes = username.as_bytes();
let mut body: [u8; 981] = [0; 981];
let length_to_copy = username_bytes.len().min(981);
body[..length_to_copy].copy_from_slice(&username_bytes[..length_to_copy]);
HandshakeMessage {id: id.to_ne_bytes(), msg_type: 1, length: length.to_ne_bytes(), extensions: [0;4], name: body, signature: [0;32]}
}
pub fn helloReply(id: i32, length: i16, username: String) -> HandshakeMessage {
let username_bytes = username.as_bytes();
let mut body: [u8; 981] = [0; 981];
let length_to_copy = username_bytes.len().min(981);
body[..length_to_copy].copy_from_slice(&username_bytes[..length_to_copy]);
HandshakeMessage {id: id.to_ne_bytes(), msg_type: 130, length: length.to_ne_bytes(), extensions: [0;4], name: body, signature: [0;32]}
}
}

View File

@@ -0,0 +1,160 @@
// this class consists of a thread that will re send pings every time the first element
// of the stack is at the correct unix time
use std::{
collections::{HashMap, VecDeque},
net::{AddrParseError, Ipv4Addr, SocketAddr},
ops::Add,
process::Command,
sync::{Arc, Mutex},
thread,
time::{self, Duration, SystemTime},
};
use crate::NetworkEvent;
use crate::{
P2PSharedData, construct_message, generate_id, messages_structure,
registration::perform_handshake,
};
use crossbeam_channel::{Receiver, Sender};
use p256::ecdsa::VerifyingKey;
#[derive(Debug, Clone)]
pub struct PeerInfo {
pub username: String,
pub pubkey: VerifyingKey,
pub ip: SocketAddr,
}
pub struct HandshakeHistory {
//time_k_ip_v: HashMap<u64, u64>,
username_k_peerinfo_v: HashMap<String, PeerInfo>,
ip_k_peerinfo_v: HashMap<String, PeerInfo>,
}
impl HandshakeHistory {
pub fn new() -> HandshakeHistory {
HandshakeHistory {
//time_k_ip_v: HashMap::new(),
//ip_k_peerinfo_v: HashMap::new(),
username_k_peerinfo_v: HashMap::new(),
ip_k_peerinfo_v: HashMap::new(),
}
}
/*pub fn update_handshake(&self) {
let hashmap_shared = Arc::new(self.username_k_peerinfo_v);
thread::spawn(move || {
let selfhashmap = hashmap_shared.clone();
loop {
for peer in selfhashmap.keys() {
let peer_ip = selfhashmap.get(peer);
// send ping
}
let mut child = Command::new("sleep").arg("10").spawn().unwrap();
let _result = child.wait().unwrap();
}
});
}*/
pub fn get_peer_info_username(&self, username: String) -> Option<&PeerInfo> {
self.username_k_peerinfo_v.get(&username).clone()
}
pub fn get_peer_info_ip(&self, ip: String) -> Option<&PeerInfo> {
self.ip_k_peerinfo_v.get(&ip).clone()
}
pub fn update_handshake(&self) {
// clone the map so we own it (cheap if PeerInfo is Clone)
let map_clone: Arc<HashMap<String, PeerInfo>> =
Arc::new(self.username_k_peerinfo_v.clone());
//let map_ip_clone: Arc<HashMap<String, PeerInfo>> = Arc::new(self.ip_k_peerinfo_v.clone());
let map_for_thread = Arc::clone(&map_clone);
thread::spawn(move || {
loop {
// Arc<HashMap<..>> derefs to &HashMap so these reads work
for (peer, peerinfo) in map_for_thread.iter() {
// send ping to peerinfo
}
thread::sleep(Duration::from_secs(10));
}
});
}
pub fn update_peer_info(&mut self, ip: String, username: String) {
let peerinfo = self.get_peer_info_ip(ip.clone());
match peerinfo {
Some(peer_info) => match ip.parse::<SocketAddr>() {
Ok(addr) => {
let new_peer_info = PeerInfo {
username: username.clone(),
pubkey: peer_info.pubkey,
ip: addr,
};
self.ip_k_peerinfo_v.insert(ip, new_peer_info.clone());
self.username_k_peerinfo_v.insert(username, new_peer_info);
}
Err(e) => eprintln!("parse error: {}", e),
},
None => {
eprintln!("no peer info found in hashmap")
}
}
}
pub fn add_new_handshake(&mut self, hash: VerifyingKey, username: String, ip: SocketAddr) {
let peerinfo = PeerInfo {
username: username.clone(),
pubkey: hash,
ip,
};
self.username_k_peerinfo_v
.insert(username, peerinfo.clone());
self.ip_k_peerinfo_v
.insert(ip.to_string(), peerinfo.clone());
}
}
pub fn perform_discover(
username: String,
hash: String,
sd: &P2PSharedData,
server_ip: String,
event_tx: Sender<NetworkEvent>,
) {
// first, sends handshake
if hash == "root" {
perform_handshake(sd, username, server_ip, event_tx, false);
/*if let Some(data) = construct_message(
messages_structure::ROOTREQUEST,
Vec::new(),
generate_id(),
sd.cryptopair_ref(),
) {
if let Some(peerinfo) = sd.handshake_ref() {
sd.senders_ref()
.send_via(0, data, peerinfo.ip.to_string(), false);
}
}*/
} else {
// envoyer un datum request
}
}
#[cfg(test)]
mod tests {
use std::net::{IpAddr, Ipv4Addr};
use super::*;
/*#[test]
fn creating_cryptographic_signature() {
let mut hh = HandshakeHistory::new();
hh.add_new_handshake(
20,
"putain".to_string(),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 1),
);
}*/
}

View File

@@ -1,49 +0,0 @@
use http::{Request, Response};
use p256::ecdsa::{
Signature, SigningKey, VerifyingKey,
signature::{Signer, Verifier},
};
use rand_core::OsRng;
struct KeyRegistration {
priv_key: SigningKey,
pub_key: VerifyingKey,
username: String,
}
impl KeyRegistration {
fn new(username: String) -> KeyRegistration {
let priv_key = SigningKey::random(&mut OsRng);
let pub_key = VerifyingKey::from(&priv_key);
KeyRegistration {
priv_key: priv_key,
pub_key: pub_key,
username: username,
}
}
}
async fn register_with_the_server(key: KeyRegistration) -> Result<bytes::Bytes, reqwest::Error> {
let client = reqwest::Client::new();
let pubkey_ser = key.pub_key.to_encoded_point(false);
let pubkey_str = hex::encode(pubkey_ser);
let uri = format!("https://jch.irif.fr:8443/peers/{}/key", key.username);
let resp = client.put(uri).send().await?.error_for_status()?;
resp.bytes().await
}
/*#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_genereation() {
let keys = KeyRegistration::new();
let pubkey = keys.pub_key;
let pubkey_ser = pubkey.to_encoded_point(false);
println!("string pubkey: {}", hex::encode(pubkey_ser));
println!("string privkey: {}", hex::encode(keys.priv_key.to_bytes()))
}
}*/

View File

@@ -1,36 +1,151 @@
use crate::cryptographic_signature::{CryptographicSignature, formatPubKey}; use bytes::Bytes;
use getrandom::Error;
use crate::NetworkEvent;
use crate::P2PSharedData;
use crate::cryptographic_signature::{CryptographicSignature, formatPubKey, sign_message};
use crate::message_handling::EventType;
use crate::messages_channels::{Message, MultipleSenders};
use crate::messages_structure::construct_message;
use crate::server_communication::generate_id;
use crossbeam_channel::{Receiver, Sender};
use std::collections::HashMap;
use std::net::SocketAddr;
use std::net::UdpSocket; use std::net::UdpSocket;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
/// ///
/// Registration with the server happens in two steps: first, the client /// sends the cryptographic signature to the server using a PUT request over the HTTP API.
/// sends its cryptographic signature to the server using a PUT request over the HTTP API. ///
async fn register_with_the_server(crypto_pair: CryptographicSignature) -> Result<(), reqwest::Error>{ pub async fn register_with_the_server(
crypto_pair: &Arc<CryptographicSignature>,
server_uri: &String,
) -> Result<(), reqwest::Error> {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let uri = format!("https://jch.irif.fr:8443/peers/{}/key", crypto_pair.username); let uri = format!("{0}/peers/{1}/key", server_uri, crypto_pair.username);
let encoded_point = crypto_pair.pub_key.to_encoded_point(false); let encoded_point = crypto_pair.pub_key.to_encoded_point(false);
let pubkey_bytes = encoded_point.as_ref().to_vec(); let pubkey_bytes = encoded_point.as_ref().to_vec();
let pubkey_bytes_minus = pubkey_bytes[1..].to_vec(); let pubkey_bytes_minus = pubkey_bytes[1..].to_vec();
// In order to register with the server, a peer ϕ makes a PUT request to the URL /peers/ϕ/key with its 64-byte public key in the body let res = client.put(uri).body(pubkey_bytes_minus).send().await?;
let res = client.put(uri) let res = res.error_for_status()?;
.body(pubkey_bytes_minus) println!("register ip adresses");
.send()
.await?;
if res.status().is_success() {
println!("Successfully registered with the server.");
} else {
eprintln!("Failed to register with the server. Status: {}", res.status());
let str = hex::encode(res.bytes().await?);
eprintln!("erreur : {}", str);
}
Ok(()) Ok(())
} }
/// It then ///
/// registers each of its IP addresses by sending a Hello request to the server. /// sends a get request to the server to get the socket address of the given peer
/// After the client sends a Hello request to the server, the server will verify that the client is able ///
/// to receive requests by sending a Hello request to the client. If the client doesnt reply to the Hello
/// request with a properly signed message, its address will not be published by the server. pub async fn get_socket_address(username: String, ip: String) -> Result<Bytes, reqwest::Error> {
fn register_ip_addresses(crypto_pair: CryptographicSignature) { let client = reqwest::Client::new();
let socket = UdpSocket::bind("127.0.0.1:4242"); let uri = format!("{}/peers/{}/addresses", ip, username);
let res = client.get(uri).send().await?;
if res.status().is_success() {
println!("Successfully retreived the addresses.");
} else {
eprintln!(
"Failed to get the peers addresses from the server. Status: {}",
res.status()
);
}
let body: Bytes = res.bytes().await?;
Ok(body)
}
pub fn parse_addresses(input: &String) -> Vec<SocketAddr> {
let mut addrs = Vec::new();
for line in input.lines() {
let s = line.trim();
if s.is_empty() {
continue;
}
if let Ok(sock) = SocketAddr::from_str(s) {
addrs.push(sock);
}
}
addrs
}
///
/// registers the IP addresses by sending a Hello request to the server.
///
pub async fn perform_handshake(
sd: &P2PSharedData,
username: String,
ip: String,
event_tx: Sender<NetworkEvent>,
is_server_handshake: bool,
) {
println!("username: {}, ip: {}", username.clone(), ip.clone());
let crypto_pair = sd.cryptopair_ref();
let senders = sd.senders_ref();
let messages_list = sd.messages_list_ref();
let id = generate_id();
let server_addr_query = get_socket_address(username.clone(), ip.clone());
match server_addr_query.await {
Ok(sockaddr_bytes) => {
match String::from_utf8(sockaddr_bytes.to_vec()) {
Ok(s) => {
let addresses = parse_addresses(&s);
if let Some(first) = addresses.first() {
sd.set_servername(username);
// first: &SocketAddr
let mut payload = Vec::new();
payload.extend_from_slice(&0u32.to_be_bytes());
payload.extend_from_slice(&crypto_pair.username.clone().as_bytes());
let hello_handshake = construct_message(1, payload, id, crypto_pair);
match hello_handshake {
Some(handshake_message) => {
senders.send_via(
0,
handshake_message,
first.to_string(),
is_server_handshake,
messages_list,
);
}
None => {}
}
//let res = event_tx
// .send(NetworkEvent::());
} else {
//let res = event_tx.send(NetworkEvent::Error());
let err_msg =
format!("no valid socket addresses found in: {}", s).to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg));
}
}
Err(e) => {
//let res = event_tx.send(NetworkEvent::Error());
let err_msg =
format!("invalid UTF-8 in socket address bytes: {}", e).to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg));
}
}
}
Err(e) => {
let err_msg = format!("failed to retreive socket address: {}", e).to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg));
}
}
/*let mut list = messages_list.lock().expect("Failed to lock messages_list");
match list.get(&id) {
Some(_) => {
list.remove(&id);
}
None => {
list.insert(id, EventType::ServerHelloReply);
}
}
println!("message sent: {}", &id);*/
// 3. Perform the insertion
/*let mut buf = [0u8; 1024];
socket.recv_from(&mut buf).expect("receive failed");
let hello_handshake_received = UDPMessage::parse(buf.to_vec());
hello_handshake_received.display();*/
//TODO //TODO
} }
@@ -39,12 +154,32 @@ mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope. // Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*; use super::*;
/*///
/// does the procedure to register with the server
///
#[tokio::test] #[tokio::test]
async fn creating_cryptographic_signature() { async fn registering_with_server() {
let username = String::from("charlie_kirk"); let username = String::from("gameixtreize");
let server_uri = String::from("https://jch.irif.fr:8443");
let crypto_pair = CryptographicSignature::new(username); let crypto_pair = CryptographicSignature::new(username);
if let Err(e) = register_with_the_server(crypto_pair).await { if let Err(e) = register_with_the_server(crypto_pair, server_uri).await {
eprintln!("Error during registration: {}", e); eprintln!("Error during registration: {}", e);
} }
}*/
/*///
/// retreives the socket address of a given peer
///
#[tokio::test]
async fn retreive_socket_addr() {
let username = String::from("ipjkndqfshjldfsjlbsdfjhhj");
match get_socket_address(username).await {
Ok(body) => {
println!("{:?}", body);
} }
Err(e) => {
eprintln!("Erreur HTTP: {}", e);
}
}
}*/
} }

View File

@@ -0,0 +1,22 @@
use bytes::Bytes;
use rand::Rng;
pub async fn get_peer_list(server_address: String) -> Result<Bytes, reqwest::Error> {
let client = reqwest::Client::new();
let uri = format!("{}/peers/", server_address);
let res = client.get(uri).send().await?;
if res.status().is_success() {
println!("Successfully retreived the addresses.");
} else {
eprintln!(
"Failed to get the peers addresses from the server. Status: {}",
res.status()
);
}
let body: Bytes = res.bytes().await?;
Ok(body)
}
pub fn generate_id() -> i32 {
rand::rng().random_range(0..i32::MAX)
}

43
todo.md
View File

@@ -1,16 +1,9 @@
# Todo : # Todo :
## peer discovery
- get rsquest to the uri /peers/
## registration with the server
- generation of the cryptographic key OK ## peer discovery
- put request to the uri (check if the peer is already connected) OK
- udp handshakes
- get request to the uri /peers/key to get the public key of a peer
- get request to the uri /peers/key/addresses
## handshake ## handshake
- handshake structure
- 5min timeout after handshake - 5min timeout after handshake
- matain connection every 4 min - matain connection every 4 min
@@ -22,12 +15,34 @@
- setting in gui to act as a relay - setting in gui to act as a relay
- chunk, directory, big, bigdirectory structures - chunk, directory, big, bigdirectory structures
fonctionnalités : ## fonctionnalités application :
s'enregistrer avec le serveur OK
rechercher un pair
generer une clé publique OK
rechercher les fichiers d'un pair rechercher les fichiers d'un pair
telechargement des fichiers telechargement des fichiers
choisir un dossier à partager choisir un dossier à partager
se deconnecter du réseau choisir le nombre de canaux
handshake server DOING
se deconnecter du réseau DOING
## autre :
socket ipv6
# FAIT :
- choisir un pseudo OK
- get rsquest to the uri /peers/ OK
- generation of the cryptographic key OK
- put request to the uri (check if the peer is already connected) OK
- get request to the uri /peers/key to get the public key of a peer OK
- get request to the uri /peers/key/addresses OK
- udp handshakes OK
- handshake structure OK
- s'enregistrer avec le serveur OK
- generer une clé publique OK
- verifier signature OK
- 2 channels -> un pour envoyer et un pour recevoir OK