28 Commits

Author SHA1 Message Date
Tiago Batista Cardoso
6b3cbbe557 some modifications 2026-01-16 12:33:21 +01:00
TIBERGHIEN corentin
14fa256f9c wip nattraversal 2026-01-16 11:19:20 +01:00
Tiago Batista Cardoso
29c67e340c update 2026-01-16 11:18:45 +01:00
Tiago Batista Cardoso
be7430fdc6 peer address in nat traversal 2026-01-16 11:18:44 +01:00
Tiago Batista Cardoso
60145f279a implementation 2026-01-16 11:15:56 +01:00
Tiago Batista Cardoso
003d55bd75 thing 2026-01-16 10:55:56 +01:00
b61e1b1036 Merge pull request 'tmp' (#2) from tmp into master
Reviewed-on: #2
2026-01-16 09:54:46 +00:00
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
0c601a76b8 Merge pull request 'tmp' (#1) from tmp into master
Reviewed-on: #1
2026-01-11 20:58:30 +00: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 2293 additions and 223 deletions

BIN
README.md

Binary file not shown.

View File

@@ -1,30 +1,36 @@
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>,
// GUI State // GUI State
status_message: String, status_message: String,
known_peers: Vec<String>, known_peers: Vec<(String, bool)>,
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(), true)],
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() {
@@ -68,42 +111,82 @@ impl eframe::App for P2PClientApp {
todo!(); todo!();
self.status_message = format!("✅ Peer connected: {}", addr); self.status_message = format!("✅ Peer connected: {}", addr);
if !self.known_peers.contains(&addr) { if !self.known_peers.contains(&(addr, true)) {
self.known_peers.push(addr); self.known_peers.push((addr, true));
} }
} }
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!(
@@ -184,26 +361,75 @@ impl eframe::App for P2PClientApp {
} else { } else {
for peer in &self.known_peers { for peer in &self.known_peers {
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.0); // if peer.id == self.active_peer_id
if ui let selectable;
.selectable_label(is_active, format!("{}", peer)) if &self.active_server == &peer.0 {
.clicked() selectable =
{ ui.selectable_label(is_active, format!("{} 📡 🌀", peer.0))
} else {
selectable = ui.selectable_label(is_active, format!("{}", peer.0));
}
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.0.clone());
// Request root content if not loaded // Request root content if not loaded
if !self if !self
.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.0.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.0.to_string();
let res = self.network_cmd_tx.send(
NetworkCommand::ServerHandshake(
peer.0.to_string(),
self.connected_address.clone(),
),
);
}
}
_ => {}
}
if ui.button("Send Ping").clicked() {
let res = self
.network_cmd_tx
.send(NetworkCommand::Ping(peer.0.to_string()));
}
if ui.button("Send Nat Traversal Request").clicked() {
match self.network_cmd_tx.send(NetworkCommand::NatTraversal(
peer.0.to_string(),
self.connected_address.clone(),
)) {
Ok(_) => {
print!("[+] successfully sent nat traversal request")
}
Err(_) => {
print!("[-] failed to send nat traversal request")
}
}
}
if ui.button("Infos").clicked() {
// action 3
ui.close();
}
// ... autres boutons
});
} }
} }
}); });
@@ -238,6 +464,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 +546,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 +573,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,19 @@
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 reqwest::Error;
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 +47,119 @@ 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?;
#[test] match res.error_for_status_ref() {
fn creating_cryptographic_signature() { Ok(_) => {
let username = String::from("quoicoubeh"); println!("Successfully retreived the peers key.");
let crypto_pair = CryptographicSignature::new(username); let body: Bytes = res.bytes().await?;
let formatted_pubkey =formatPubKey(crypto_pair); let slice: &[u8] = body.as_ref();
println!("pubkey : {}",formatted_pubkey); let body_bytes: &[u8; 64] = slice.try_into().expect("size error");
let received_key = convert_verifyingkey(body_bytes);
Ok(received_key)
}
Err(e) => {
eprintln!(
"Failed to get the peers key from the server. Status: {}",
res.status()
);
Err(e)
}
} }
} }
fn convert_verifyingkey(raw_xy: &[u8; 64]) -> VerifyingKey {
let mut sec1 = [0u8; 65];
sec1[0] = 0x04;
sec1[1..].copy_from_slice(raw_xy);
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,35 +1,154 @@
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::{
NATTRAVERSALREQUEST, NATTRAVERSALREQUEST2, ROOTREQUEST, construct_message,
},
peers_refresh::HandshakeHistory,
registration::{parse_addresses, perform_handshake, register_with_the_server},
server_communication::{generate_id, get_peer_list},
};
use std::{
io::Error,
net::{IpAddr, Ipv4Addr, UdpSocket},
};
use std::{
net::SocketAddr,
sync::{Arc, Mutex},
};
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>,
}
use bytes::Bytes;
use p256::pkcs8::der::pem::Base64Encoder;
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(String),
ConnectPeer(String), // IP:PORT NatTraversal(String, String),
ConnectPeer((String, bool)), // IP:PORT
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, bool)>),
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 +171,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,9 +186,19 @@ 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::ConnectPeer(addr) => { 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((username, connected)) => {
println!("[Network] ConnectPeer() called"); println!("[Network] ConnectPeer() called");
println!("[Network] Attempting to connect to: {}", addr); println!("[Network] Attempting to connect to: {}", username);
// Network logic to connect... // Network logic to connect...
// If successful, send an event back: // If successful, send an event back:
// event_tx.send(NetworkEvent::PeerConnected(addr)).unwrap(); // event_tx.send(NetworkEvent::PeerConnected(addr)).unwrap();
@@ -74,36 +206,201 @@ 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, bool)> = Vec::new();
let mut current = String::new();
for i in peers_list.chars() {
if i == '\n' {
peers.push((current.clone(), false));
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(_) => {
println!("[Network] RegisterAsPeer() called"); println!("[Network] RegisterAsPeer() called");
} }
NetworkCommand::Ping() => { NetworkCommand::Ping(String) => {
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");
}
}
NetworkCommand::NatTraversal(username, ip) => {
if let Some(sd) = shared_data.as_ref() {
println!("username:{}, ip:{}", username, ip);
// user server to send nattraversal request
let server_addr_query =
get_socket_address(sd.servername().clone(), ip.clone());
let peer_addr_query = get_socket_address(username.clone(), ip.clone());
match server_addr_query.await {
Some(server_addr) => match peer_addr_query.await {
Some(peer_addr) => {
let payload = socket_addr_to_vec(server_addr);
print!("{:?}", payload.clone());
let natreq = construct_message(
NATTRAVERSALREQUEST,
server_addr.to_string().into_bytes(),
generate_id(),
&sd.cryptopair(),
);
sd.senders_ref().send_via(
0,
natreq.expect(
"couldnt construct message nattraversalrequest2",
),
server_addr.to_string(),
false,
sd.messages_list_ref(),
);
}
None => {
let err_msg = format!("failed to retreive socket address")
.to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg));
}
},
None => {
let err_msg =
format!("failed to retreive socket address").to_string();
let res = event_tx.send(NetworkEvent::Error(err_msg));
}
}
}
}
} }
} }
@@ -117,3 +414,78 @@ pub fn start_p2p_executor(
} }
}) })
} }
fn socket_addr_to_vec(addr: SocketAddr) -> Vec<u8> {
let mut v = match addr.ip() {
IpAddr::V4(v4) => v4.octets().to_vec(),
IpAddr::V6(v6) => v6.octets().to_vec(),
};
v.extend(&addr.port().to_be_bytes());
v
}
fn parse_pack(s: &str) -> Option<[u8; 6]> {
// split into "ip" and "port"
let mut parts = s.rsplitn(2, ':');
let port_str = parts.next()?;
let ip_str = parts.next()?; // if missing, invalid
let ip: Ipv4Addr = ip_str.parse().ok()?;
let port: u16 = port_str.parse().ok()?;
let octets = ip.octets();
let port_be = port.to_be_bytes();
Some([
octets[0], octets[1], octets[2], octets[3], port_be[0], port_be[1],
])
}
///
/// sends a get request to the server to get the socket address of the given peer
///
pub async fn get_socket_address(username: String, ip: String) -> Option<SocketAddr> {
let client = reqwest::Client::new();
let uri = format!("{}/peers/{}/addresses", ip, username);
let res = client.get(uri).send().await.expect("couldnt get response");
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.expect("couldnt get bytes");
match String::from_utf8(body.to_vec()) {
Ok(s) => {
let addresses = parse_addresses(&s);
addresses.iter().copied().find(|a| a.is_ipv4())
}
Err(_) => None,
}
}
pub async fn get_possible_socket_address(username: String, ip: String) -> Vec<SocketAddr> {
let client = reqwest::Client::new();
let uri = format!("{}/peers/{}/addresses", ip, username);
let res = client.get(uri).send().await.expect("couldnt get response");
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.expect("couldnt get bytes");
match String::from_utf8(body.to_vec()) {
Ok(s) => {
let addresses = parse_addresses(&s);
addresses.iter().copied().filter(|a| a.is_ipv4()).collect()
}
Err(_) => Vec::new(),
}
}

View File

@@ -0,0 +1,473 @@
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,
senders,
);
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>>,
senders: &MultipleSenders,
) -> 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
NATTRAVERSALREQUEST => {
// send ok & send nattraversalrequest2 to peer
constructed_message = construct_message(OK, Vec::new(), id, crypto_pair);
let ilength = u16::from_be_bytes(length_bytes);
let received_address =
&received_message[LENGTH + EXTENSIONS..LENGTH + ilength as usize];
let address = String::from_utf8(received_address.to_vec()).expect("wrong name");
let natreq2 = construct_message(
NATTRAVERSALREQUEST2,
ip.to_string().into_bytes(),
id,
crypto_pair,
);
senders.send_via(
0,
natreq2.expect("couldnt construct message nattraversalrequest2"),
address,
false,
&messages_list,
);
}
NATTRAVERSALREQUEST2 => {
// send ok & send ping to peer
constructed_message = construct_message(OK, Vec::new(), id, crypto_pair);
let ilength = u16::from_be_bytes(length_bytes);
let received_address = &received_message[LENGTH..LENGTH + ilength as usize];
let address = String::from_utf8(received_address.to_vec()).expect("wrong name");
let pingreq = construct_message(PING, Vec::new(), id, crypto_pair);
senders.send_via(
0,
pingreq.expect("couldnt construct message ping request"),
address,
false,
&messages_list,
);
}
//
// 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,241 @@
struct UDPMessage { use crate::cryptographic_signature::{CryptographicSignature, sign_message};
id: [u8; 4],
msg_type: u8, const ID: usize = 4;
length: [u8; 2], const TYPE: usize = 5;
body: [u8; 985], const LENGTH: usize = 7;
signature: [u8; 32], const EXTENSIONS: usize = 4;
const SIGNATURE: usize = 64;
pub(crate) const PING: u8 = 0;
pub(crate) const OK: u8 = 128;
pub(crate) const ERROR: u8 = 129;
pub(crate) const HELLO: u8 = 1;
pub(crate) const HELLOREPLY: u8 = 130;
pub(crate) const ROOTREQUEST: u8 = 2;
pub(crate) const ROOTREPLY: u8 = 131;
pub(crate) const DATUMREQUEST: u8 = 3;
pub(crate) const NODATUM: u8 = 133;
pub(crate) const DATUM: u8 = 132;
pub(crate) const NATTRAVERSALREQUEST: u8 = 4;
pub(crate) 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 => {
println!("payload:{:?}", &payload);
message.extend_from_slice(&(payload.len() as u16).to_be_bytes());
message.extend_from_slice(&payload);
println!("payload:{:?}", &message);
let signature = sign_message(crypto_pair, &message);
message.extend_from_slice(&signature);
return Some(message);
}
_ => {}
}
None
} }
struct HandshakeMessage { pub struct UDPMessage {
id: [u8; 4], id: u32,
msg_type: u8, msg_type: u8,
length: [u8; 2], length: u16,
extensions: [u8; 4], body: Vec<u8>,
name: [u8; 981], signature: Vec<u8>,
signature: [u8; 32],
} }
pub struct HandshakeMessage {
pub id: u32,
msg_type: u8,
length: u16,
extensions: u32,
pub name: Vec<u8>,
pub signature: Vec<u8>,
}
pub struct NatTraversal {}
impl UDPMessage { impl UDPMessage {
pub fn ping(id: i32) -> UDPMessage { pub fn ping(id: u32) -> UDPMessage {
UDPMessage { id: id.to_ne_bytes(), msg_type: 0, length: [0; 2], body: [0; 985], signature: [0; 32]} UDPMessage {
id: id,
msg_type: 0,
length: 0,
body: vec![0; 985],
signature: vec![0; 32],
}
} }
pub fn error(id: i32) -> UDPMessage { pub fn error(id: u32) -> UDPMessage {
UDPMessage {
UDPMessage { id: id.to_ne_bytes(), msg_type: 129, length: [0; 2], body: [0; 985], signature: [0; 32]} id: id,
msg_type: 129,
length: 0,
body: vec![0; 985],
signature: vec![0; 32],
}
} }
pub fn hello(id: i32, length: i16, username: String) -> HandshakeMessage { pub fn parse(received_message: Vec<u8>) -> UDPMessage {
let username_bytes = username.as_bytes(); let id_bytes: [u8; 4] = received_message[0..4]
.try_into()
let mut body: [u8; 981] = [0; 981]; .expect("Taille incorrecte");
let length_bytes: [u8; 2] = received_message[5..7]
let length_to_copy = username_bytes.len().min(981); .try_into()
body[..length_to_copy].copy_from_slice(&username_bytes[..length_to_copy]); .expect("Taille incorrecte");
HandshakeMessage {id: id.to_ne_bytes(), msg_type: 1, length: length.to_ne_bytes(), extensions: [0;4], name: body, signature: [0;32]} let msg_length = u16::from_be_bytes(length_bytes);
let name_bytes = &received_message[7..msg_length as usize + 8];
let signature_bytes =
&received_message[msg_length as usize + 8..msg_length as usize + 9 + 32];
UDPMessage {
id: u32::from_be_bytes(id_bytes),
msg_type: received_message[4],
length: u16::from_be_bytes(length_bytes),
body: name_bytes.to_vec(),
signature: signature_bytes.to_vec(),
}
} }
pub fn helloReply(id: i32, length: i16, username: String) -> HandshakeMessage { pub fn display(&self) {
let username_bytes = username.as_bytes(); println!("ID: {:?}", self.id);
println!("Message Type: {}", self.msg_type);
let mut body: [u8; 981] = [0; 981]; println!("Length: {:?}", self.length);
let good_length = usize::min(self.length as usize, 985);
let length_to_copy = username_bytes.len().min(981); println!("name: {:?}", &self.body[..good_length]);
body[..length_to_copy].copy_from_slice(&username_bytes[..length_to_copy]); println!("Signature: {:?}", self.signature);
HandshakeMessage {id: id.to_ne_bytes(), msg_type: 130, length: length.to_ne_bytes(), extensions: [0;4], name: body, signature: [0;32]}
} }
} }
impl HandshakeMessage {
pub fn display(&self) {
println!("ID: {:?}", self.id);
println!("Message Type: {}", self.msg_type);
println!("Length: {:?}", self.length);
println!("extensions: {:?}", self.extensions);
println!("name: {:?}", &self.name[..(self.length - 4) as usize]);
println!("Signature: {:?}", self.signature);
}
pub fn hello(id: u32, length: u16, username: String) -> HandshakeMessage {
let name_vec = username.trim_end_matches(char::from(0)).as_bytes().to_vec();
HandshakeMessage {
id: id,
msg_type: 1,
length: length,
extensions: 0,
name: name_vec,
signature: vec![0; 64],
}
}
pub fn helloReply(id: u32, length: u16, username: String) -> HandshakeMessage {
let name_vec = username.trim_end_matches(char::from(0)).as_bytes().to_vec();
HandshakeMessage {
id: id,
msg_type: 130,
length: length,
extensions: 0,
name: name_vec,
signature: vec![0; 64],
}
}
pub fn serialize(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + 1 + 2 + 4 + self.name.len() + self.signature.len());
// id: u32 little-endian
out.extend_from_slice(&self.id.to_be_bytes());
// msg_type: u8
out.push(self.msg_type);
out.extend_from_slice(&self.length.to_be_bytes());
out.extend_from_slice(&self.extensions.to_be_bytes());
out.extend_from_slice(&self.name);
out.extend_from_slice(&self.signature);
out
}
pub fn parse(received_message: Vec<u8>) -> HandshakeMessage {
let id_bytes: [u8; 4] = received_message[0..4]
.try_into()
.expect("Taille incorrecte");
let length_bytes: [u8; 2] = received_message[5..7]
.try_into()
.expect("Taille incorrecte");
let msg_length = u16::from_be_bytes(length_bytes);
let extensions_bytes: [u8; 4] = received_message[7..11]
.try_into()
.expect("Taille incorrecte");
let name_bytes = &received_message[11..(11 + msg_length - 4) as usize];
let signature_bytes =
&received_message[(11 + msg_length - 4) as usize..(11 + msg_length - 4 + 64) as usize];
HandshakeMessage {
id: u32::from_be_bytes(id_bytes),
msg_type: received_message[4],
length: u16::from_be_bytes(length_bytes),
extensions: u32::from_be_bytes(extensions_bytes),
name: name_bytes.to_vec(),
signature: signature_bytes.to_vec(),
}
}
}
#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*;
/// creates an handshake message
#[tokio::test]
async fn creating_handshake_msg() {
let username = String::from("charlie_kirk");
let handshake = HandshakeMessage::hello(0, 12, username);
handshake.display();
}
/// parses an handshake message
#[tokio::test]
async fn parse_handshakemessage() {
let username = String::from("charlie_kirk");
let handshake = HandshakeMessage::hello(0, 12, username);
let ser = handshake.serialize();
let parsed = HandshakeMessage::parse(ser);
handshake.display();
parsed.display();
}
}

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,110 @@
use crate::cryptographic_signature::{CryptographicSignature, formatPubKey}; use bytes::Bytes;
use crate::NetworkEvent;
use crate::P2PSharedData;
use crate::cryptographic_signature::{CryptographicSignature, formatPubKey, sign_message};
use crate::get_socket_address;
use crate::message_handling::EventType;
use crate::messages_channels::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 pub fn parse_addresses(input: &String) -> Vec<SocketAddr> {
/// registers each of its IP addresses by sending a Hello request to the server. let mut addrs = Vec::new();
/// After the client sends a Hello request to the server, the server will verify that the client is able for line in input.lines() {
/// to receive requests by sending a Hello request to the client. If the client doesnt reply to the Hello let s = line.trim();
/// request with a properly signed message, its address will not be published by the server. if s.is_empty() {
fn register_ip_addresses(crypto_pair: CryptographicSignature) { continue;
let socket = UdpSocket::bind("127.0.0.1:4242"); }
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 {
Some(sockaddr_bytes) => {
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,
sockaddr_bytes.to_string(),
is_server_handshake,
messages_list,
);
}
None => {}
}
}
None => {
let err_msg = format!("failed to retreive socket address:").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 +113,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)
}

58
todo.md
View File

@@ -1,20 +1,32 @@
# Todo : # Todo
## peer discovery ## peer discovery
## handshake
# Todo
## peer discovery
- get rsquest to the uri /peers/ - get rsquest to the uri /peers/
## registration with the server ## registration with the server
- generation of the cryptographic key OK - generation of the cryptographic key OK
- put request to the uri (check if the peer is already connected) OK - put request to the uri (check if the peer is already connected) OK
- udp handshakes - udp handshakes OK
- get request to the uri /peers/key to get the public key of a peer - get request to the uri /peers/key to get the public key of a peer OK
- get request to the uri /peers/key/addresses - get request to the uri /peers/key/addresses OK
## handshake ## handshake
- handshake structure
- handshake structure OK
- 5min timeout after handshake - 5min timeout after handshake
- matain connection every 4 min - matain connection every 4 min
## data transfer ## data transfer
- request structure - request structure
- root/root reply structure - root/root reply structure
- datum/nodatum and datum structures - datum/nodatum and datum structures
@@ -22,12 +34,40 @@
- 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 application
## nat traversal
- make hello and helloreply messages set the first extension bit to announce that peer is available for nat traversal
- implement actual nat traversal requests
- implement nat traversal :
- if hello/helloreply doesnt work with a peer, find a peer that supports nat traversal (server in priority) then begin protocol
fonctionnalités : fonctionnalités :
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