/* * Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers * All rights reserved. * Licensed under the Apache License, Version 2.0 * * or the MIT license , * at your option. All files in the project carrying such * notice may not be copied, modified, or distributed except * according to those terms. */ //static NOISE_CONFIG: &'static str = "Noise_XK_25519_ChaChaPoly_BLAKE2b"; use std::collections::HashMap; use std::fmt; use std::sync::Arc; use crate::actor::{Actor, SoS}; use crate::actors::*; use crate::broker::BROKER; use crate::errors::NetError; use crate::errors::ProtocolError; use crate::types::*; use crate::utils::*; use async_std::stream::StreamExt; use async_std::sync::Mutex; use futures::future::Either; use futures::{channel::mpsc, select, Future, FutureExt, SinkExt}; use noise_protocol::U8Array; use noise_protocol::{patterns::noise_xk, CipherState, HandshakeState}; use noise_rust_crypto::sensitive::Sensitive; use noise_rust_crypto::*; use p2p_repo::log::*; use p2p_repo::types::{PrivKey, PubKey}; use p2p_repo::utils::{sign, verify}; use serde_bare::from_slice; use unique_id::sequence::SequenceGenerator; use unique_id::Generator; use unique_id::GeneratorFromSeed; #[derive(Debug, Clone)] pub enum ConnectionCommand { Msg(ProtocolMessage), Error(NetError), ProtocolError(ProtocolError), Close, } #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] pub trait IConnect: Send + Sync { async fn open( &self, ip: IP, peer_privk: Sensitive<[u8; 32]>, peer_pubk: PubKey, remote_peer: DirectPeerId, config: StartConfig, ) -> Result; } #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] pub trait IAccept: Send + Sync { type Socket; async fn accept( &self, remote_bind_address: BindAddress, local_bind_address: BindAddress, peer_privk: Sensitive<[u8; 32]>, socket: Self::Socket, ) -> Result; } #[derive(PartialEq, Debug, Clone)] pub enum ConnectionDir { Server, Client, } impl ConnectionDir { pub fn is_server(&self) -> bool { *self == ConnectionDir::Server } } #[derive(Debug, PartialEq)] pub enum FSMstate { Local0, Noise0, Noise1, Noise2, Noise3, // unused ExtRequest, ExtResponse, ClientHello, ServerHello, ClientAuth, AuthResult, } pub struct NoiseFSM { state: FSMstate, dir: ConnectionDir, sender: Sender, bind_addresses: Option<(BindAddress, BindAddress)>, actors: Arc>>>, noise_handshake_state: Option>, noise_cipher_state_enc: Option>, noise_cipher_state_dec: Option>, from: Option>, to: Option, nonce_for_hello: Vec, config: Option, } impl fmt::Debug for NoiseFSM { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("NoiseFSM") .field("state", &self.state) .field("dir", &self.dir) .finish() } } pub enum StepReply { Responder(ProtocolMessage), Response(ProtocolMessage), NONE, } pub struct ClientConfig { pub user: PubKey, pub client: PubKey, pub client_priv: PrivKey, } pub struct ExtConfig {} pub struct CoreConfig {} pub struct AdminConfig {} pub enum StartConfig { Client(ClientConfig), Ext(ExtConfig), Core(CoreConfig), Admin(AdminConfig), } impl NoiseFSM { pub fn new( bind_addresses: Option<(BindAddress, BindAddress)>, tp: TransportProtocol, dir: ConnectionDir, actors: Arc>>>, sender: Sender, from: Sensitive<[u8; 32]>, to: Option, ) -> Self { Self { state: if tp == TransportProtocol::Local { FSMstate::Local0 } else { FSMstate::Noise0 }, dir, bind_addresses, actors, sender, noise_handshake_state: None, noise_cipher_state_enc: None, noise_cipher_state_dec: None, from: Some(from), to, nonce_for_hello: vec![], config: None, } } fn decrypt(&mut self, ciphertext: &Noise) -> Result { let ser = self .noise_cipher_state_dec .as_mut() .unwrap() .decrypt_vec(ciphertext.data()) .map_err(|e| ProtocolError::DecryptionError)?; Ok(from_slice::(&ser)?) } fn encrypt(&mut self, plaintext: ProtocolMessage) -> Result { let ser = serde_bare::to_vec(&plaintext)?; let cipher = self .noise_cipher_state_enc .as_mut() .unwrap() .encrypt_vec(&ser); Ok(Noise::V0(NoiseV0 { data: cipher })) } pub async fn remove_actor(&self, id: i64) { self.actors.lock().await.remove(&id); } pub async fn send(&mut self, msg: ProtocolMessage) -> Result<(), ProtocolError> { log_trace!("SENDING: {:?}", msg); if self.noise_cipher_state_enc.is_some() { let cipher = self.encrypt(msg)?; self.sender .send(ConnectionCommand::Msg(ProtocolMessage::Noise(cipher))) .await .map_err(|e| ProtocolError::IoError)?; return Ok(()); } else { self.sender .send(ConnectionCommand::Msg(msg)) .await .map_err(|e| ProtocolError::IoError)?; return Ok(()); } } // pub async fn receive( // &mut self, // msg: ProtocolMessage, // ) -> Result { // if self.state == FSMstate::AuthResult && self.noise_cipher_state.is_some() { // if let ProtocolMessage::Noise(noise) = msg { // let new = self.decrypt(&noise); // Ok(new) // } else { // Err(ProtocolError::MustBeEncrypted) // } // } else { // Err(ProtocolError::InvalidState) // } // } pub async fn step( &mut self, mut msg_opt: Option, ) -> Result { if self.noise_cipher_state_dec.is_some() { if let Some(ProtocolMessage::Noise(noise)) = msg_opt.as_ref() { let new = self.decrypt(noise)?; msg_opt.replace(new); } else { return Err(ProtocolError::MustBeEncrypted); } } if msg_opt.is_some() { log_trace!("RECEIVED: {:?}", msg_opt.as_ref().unwrap()); } match self.state { // TODO verify that ID is zero FSMstate::Local0 => { // CLIENT LOCAL if !self.dir.is_server() && msg_opt.is_none() { self.state = FSMstate::ClientHello; Box::new(Actor::::new(0, true)); return Ok(StepReply::NONE); } // SERVER LOCAL else if let Some(msg) = msg_opt.as_ref() { if self.dir.is_server() && msg.type_id() == ClientHello::Local.type_id() { self.state = FSMstate::ServerHello; Box::new(Actor::::new(msg.id(), false)); return Ok(StepReply::NONE); } } } FSMstate::Noise0 => { // CLIENT INITIALIZE NOISE if !self.dir.is_server() && msg_opt.is_none() { let mut handshake = HandshakeState::::new( noise_xk(), true, &[], Some(from_ed_priv_to_dh_priv(self.from.take().unwrap())), None, Some(*self.to.unwrap().to_dh_from_ed().slice()), None, ); let payload = handshake .write_message_vec(&[]) .map_err(|e| ProtocolError::NoiseHandshakeFailed)?; let noise = Noise::V0(NoiseV0 { data: payload }); self.send(noise.into()).await?; self.noise_handshake_state = Some(handshake); self.state = FSMstate::Noise1; return Ok(StepReply::NONE); } // SERVER INITIALIZE NOISE else if let Some(msg) = msg_opt.as_ref() { if self.dir.is_server() { if let ProtocolMessage::Noise(noise) = msg { let mut handshake = HandshakeState::::new( noise_xk(), false, &[], Some(from_ed_priv_to_dh_priv(self.from.take().unwrap())), None, None, None, ); let mut payload = handshake.read_message_vec(noise.data()).map_err(|e| { log_debug!("{:?}", e); ProtocolError::NoiseHandshakeFailed })?; payload = handshake.write_message_vec(&payload).map_err(|e| { log_debug!("{:?}", e); ProtocolError::NoiseHandshakeFailed })?; let noise = Noise::V0(NoiseV0 { data: payload }); self.send(noise.into()).await?; self.noise_handshake_state = Some(handshake); self.state = FSMstate::Noise2; return Ok(StepReply::NONE); } } } } FSMstate::Noise1 => { // CLIENT second round NOISE if let Some(msg) = msg_opt.as_ref() { if !self.dir.is_server() { if let ProtocolMessage::Noise(noise) = msg { let handshake = self.noise_handshake_state.as_mut().unwrap(); let mut payload = handshake .read_message_vec(noise.data()) .map_err(|e| ProtocolError::NoiseHandshakeFailed)?; payload = handshake.write_message_vec(&payload).map_err(|e| { log_debug!("{:?}", e); ProtocolError::NoiseHandshakeFailed })?; if !handshake.completed() { return Err(ProtocolError::NoiseHandshakeFailed); } let ciphers = handshake.get_ciphers(); match self.config.as_ref().unwrap() { StartConfig::Client(client_config) => { let noise3 = ClientHello::Noise3(Noise::V0(NoiseV0 { data: payload })); self.send(noise3.into()).await?; self.state = FSMstate::ClientHello; } StartConfig::Ext(ext_config) => { todo!(); } StartConfig::Core(core_config) => { todo!(); } StartConfig::Admin(admin_config) => { todo!(); } } self.noise_cipher_state_enc = Some(ciphers.0); self.noise_cipher_state_dec = Some(ciphers.1); self.noise_handshake_state = None; return Ok(StepReply::NONE); } } } } FSMstate::Noise2 => { // SERVER second round NOISE if let Some(msg) = msg_opt.as_ref() { if self.dir.is_server() { if let ProtocolMessage::Start(StartProtocol::Client(ClientHello::Noise3( noise, ))) = msg { let handshake = self.noise_handshake_state.as_mut().unwrap(); let _ = handshake .read_message_vec(noise.data()) .map_err(|e| ProtocolError::NoiseHandshakeFailed)?; if !handshake.completed() { return Err(ProtocolError::NoiseHandshakeFailed); } let peer_id = PubKey::Ed25519PubKey(handshake.get_rs().unwrap()); self.to = Some(peer_id); let (local_bind_address, remote_bind_address) = self.bind_addresses.ok_or(ProtocolError::BrokerError)?; BROKER .write() .await .attach_peer_id( remote_bind_address, local_bind_address, peer_id, None, ) .await .map_err(|_| ProtocolError::BrokerError)?; let ciphers = handshake.get_ciphers(); self.noise_cipher_state_enc = Some(ciphers.1); self.noise_cipher_state_dec = Some(ciphers.0); self.noise_handshake_state = None; let mut nonce_buf = [0u8; 32]; getrandom::getrandom(&mut nonce_buf).unwrap(); self.nonce_for_hello = nonce_buf.to_vec(); let server_hello = ServerHello::V0(ServerHelloV0 { nonce: self.nonce_for_hello.clone(), }); self.state = FSMstate::ServerHello; self.send(server_hello.into()).await?; return Ok(StepReply::NONE); } } } } FSMstate::Noise3 => {} FSMstate::ExtRequest => {} FSMstate::ExtResponse => {} FSMstate::ClientHello => { if let Some(msg) = msg_opt.as_ref() { if !self.dir.is_server() { if let ProtocolMessage::ServerHello(hello) = msg { if let StartConfig::Client(client_config) = self.config.as_ref().unwrap() { let content = ClientAuthContentV0 { user: client_config.user, client: client_config.client, /// Nonce from ServerHello nonce: hello.nonce().clone(), }; let ser = serde_bare::to_vec(&content)?; let sig = sign(client_config.client_priv, client_config.client, &ser)?; let client_auth = ClientAuth::V0(ClientAuthV0 { content, /// Signature by client key sig, }); self.state = FSMstate::ClientAuth; self.send(client_auth.into()).await?; return Ok(StepReply::NONE); } } } } } FSMstate::ServerHello => { if let Some(msg) = msg_opt.as_ref() { if self.dir.is_server() { if let ProtocolMessage::ClientAuth(client_auth) = msg { if *client_auth.nonce() != self.nonce_for_hello { return Err(ProtocolError::InvalidNonce); } let ser = serde_bare::to_vec(&client_auth.content_v0())?; let mut result = ProtocolError::NoError; let verif = verify(&ser, client_auth.sig(), client_auth.client()); if verif.is_err() { result = verif.unwrap_err().into(); } else { // TODO check that the device has been registered for this user. if not, set result = AccessDenied } let auth_result = AuthResult::V0(AuthResultV0 { result: result.clone() as u16, metadata: vec![], }); self.send(auth_result.into()).await?; if (result.is_err()) { return Err(result); } log_info!("AUTHENTICATION SUCCESSFUL ! waiting for requests on the server side"); self.state = FSMstate::AuthResult; return Ok(StepReply::NONE); } } } } FSMstate::ClientAuth => { if let Some(msg) = msg_opt.as_ref() { if !self.dir.is_server() { if let ProtocolMessage::AuthResult(auth_res) = msg { if let StartConfig::Client(client_config) = self.config.as_ref().unwrap() { if auth_res.result() != 0 { return Err(ProtocolError::AccessDenied); } self.state = FSMstate::AuthResult; log_info!("AUTHENTICATION SUCCESSFUL ! waiting for requests on the client side"); return Ok(StepReply::NONE); } } } } } FSMstate::AuthResult => { if let Some(msg) = msg_opt { let id = msg.id(); if self.dir.is_server() && id > 0 || !self.dir.is_server() && id < 0 { return Ok(StepReply::Responder(msg)); } else if id != 0 { return Ok(StepReply::Response(msg)); } } } } Err(ProtocolError::InvalidState) } } #[derive(Debug)] pub struct ConnectionBase { fsm: Option>>, sender: Option>, receiver: Option>, sender_tx: Option>, receiver_tx: Option>, shutdown: Option>>, shutdown_sender: Option>>, dir: ConnectionDir, next_request_id: SequenceGenerator, tp: TransportProtocol, actors: Arc>>>, } impl ConnectionBase { pub fn new(dir: ConnectionDir, tp: TransportProtocol) -> Self { Self { fsm: None, receiver: None, sender: None, sender_tx: None, receiver_tx: None, shutdown: None, shutdown_sender: None, next_request_id: SequenceGenerator::new(1), dir, tp, actors: Arc::new(Mutex::new(HashMap::new())), } } pub fn transport_protocol(&self) -> TransportProtocol { self.tp } pub fn take_shutdown(&mut self) -> Receiver> { self.shutdown.take().unwrap() } pub async fn join_shutdown(&mut self) -> Result<(), NetError> { match self.take_shutdown().next().await { Some(Either::Left(error)) => Err(error), Some(Either::Right(_)) => Ok(()), None => Ok(()), } } pub async fn reset_shutdown(&self, remote_peer_id: PubKey) { let _ = self .shutdown_sender .as_ref() .unwrap() .send(Either::Right(remote_peer_id)) .await; } pub fn set_shutdown(&mut self) -> Sender> { let (shutdown_sender, shutdown_receiver) = mpsc::unbounded::>(); self.shutdown = Some(shutdown_receiver); self.shutdown_sender = Some(shutdown_sender.clone()); shutdown_sender } pub fn take_sender(&mut self) -> Receiver { self.sender.take().unwrap() } pub fn take_receiver(&mut self) -> Sender { self.receiver.take().unwrap() } pub fn guard(&mut self, dir: ConnectionDir) -> Result<(), NetError> { if self.dir == dir { Ok(()) } else { Err(NetError::DirectionAlreadySet) } } async fn read_loop( mut receiver: Receiver, mut sender: Sender, actors: Arc>>>, fsm: Arc>, ) -> ResultSend<()> { while let Some(msg) = receiver.next().await { match msg { ConnectionCommand::Close | ConnectionCommand::Error(_) | ConnectionCommand::ProtocolError(_) => { log_info!("EXIT READ LOOP because : {:?}", msg); break; } ConnectionCommand::Msg(proto_msg) => { let res; { let mut locked_fsm = fsm.lock().await; res = locked_fsm.step(Some(proto_msg)).await; } match res { Err(e) => { if sender .send(ConnectionCommand::ProtocolError(e)) .await .is_err() { break; //TODO test that sending a ProtocolError effectively closes the connection (with ConnectionCommand::Close) } } Ok(StepReply::NONE) => {} Ok(StepReply::Responder(responder)) => { let r = responder .get_actor() .respond(responder, Arc::clone(&fsm)) .await; if r.is_err() { if sender .send(ConnectionCommand::ProtocolError(r.unwrap_err())) .await .is_err() { break; } } } Ok(StepReply::Response(response)) => { let mut lock = actors.lock().await; let exists = lock.get_mut(&response.id()); match exists { Some(actor_sender) => { if actor_sender .send(ConnectionCommand::Msg(response)) .await .is_err() { break; } } None => { if sender .send(ConnectionCommand::ProtocolError( ProtocolError::ActorError, )) .await .is_err() { break; } } } } } } } } log_info!("END OF READ LOOP"); Ok(()) } pub async fn request< A: Into + std::fmt::Debug + Sync + Send + 'static, B: TryFrom + std::fmt::Debug + Sync + Send + 'static, >( &self, msg: A, ) -> Result, ProtocolError> { if self.fsm.is_none() { return Err(ProtocolError::FsmNotReady); } let mut id = self.next_request_id.next_id(); if self.dir == ConnectionDir::Server { id = !id + 1; } let mut actor = Box::new(Actor::::new(id, true)); self.actors.lock().await.insert(id, actor.get_receiver_tx()); let mut proto_msg: ProtocolMessage = msg.into(); proto_msg.set_id(id); let res = actor .request(proto_msg, Arc::clone(self.fsm.as_ref().unwrap())) .await; res } pub async fn send(&mut self, cmd: ConnectionCommand) { let _ = self.sender_tx.as_mut().unwrap().send(cmd).await; } // pub async fn inject(&mut self, cmd: ConnectionCommand) { // let _ = self.receiver_tx.as_mut().unwrap().send(cmd).await; // } // pub async fn close_streams(&mut self) { // let _ = self.receiver_tx.as_mut().unwrap().close_channel(); // let _ = self.sender_tx.as_mut().unwrap().close_channel(); // } pub async fn close(&mut self) { log_info!("closing..."); self.send(ConnectionCommand::Close).await; } pub async fn start(&mut self, config: StartConfig) { // BOOTSTRAP the protocol from client-side if !self.dir.is_server() { let res; { let mut fsm = self.fsm.as_ref().unwrap().lock().await; fsm.config = Some(config); res = fsm.step(None).await; } if let Err(err) = res { self.send(ConnectionCommand::ProtocolError(err)).await; } } else { panic!("cannot call start on a server-side connection"); } } pub fn start_read_loop( &mut self, bind_addresses: Option<(BindAddress, BindAddress)>, from: Sensitive<[u8; 32]>, to: Option, ) { let (sender_tx, sender_rx) = mpsc::unbounded(); let (receiver_tx, receiver_rx) = mpsc::unbounded(); self.sender = Some(sender_rx); self.receiver = Some(receiver_tx.clone()); self.sender_tx = Some(sender_tx.clone()); self.receiver_tx = Some(receiver_tx); let fsm = Arc::new(Mutex::new(NoiseFSM::new( bind_addresses, self.tp, self.dir.clone(), Arc::clone(&self.actors), sender_tx.clone(), from, to, ))); self.fsm = Some(Arc::clone(&fsm)); spawn_and_log_error(Self::read_loop( receiver_rx, sender_tx, Arc::clone(&self.actors), fsm, )); } } mod test { use crate::actor::*; use crate::actors::*; use crate::types::*; use p2p_repo::log::*; use std::any::{Any, TypeId}; #[async_std::test] pub async fn test_connection() {} #[async_std::test] pub async fn test_typeid() { log_info!( "{:?}", ClientHello::Noise3(Noise::V0(NoiseV0 { data: vec![] })).type_id() ); let a = Noise::V0(NoiseV0 { data: [].to_vec() }); log_info!("{:?}", a.type_id()); log_info!("{:?}", TypeId::of::()); log_info!("{:?}", ClientHello::Local.type_id()); log_info!("{:?}", TypeId::of::()); } }