diff --git a/Cargo.toml b/Cargo.toml index 2146aaf..ab162ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,4 +29,5 @@ byteorder = "1.3.4" socks = "0.3.3" integer-encoding = "2.1.1" secretbox = "0.1.2" -subtle = "2.3.0" \ No newline at end of file +subtle = "2.3.0" +hashbrown = "0.9.1" \ No newline at end of file diff --git a/examples/simple_client.rs b/examples/simple_client.rs new file mode 100644 index 0000000..d96a4ca --- /dev/null +++ b/examples/simple_client.rs @@ -0,0 +1,48 @@ +use tapir::acns::tor::authentication::HashedPassword; +use ed25519_dalek::SecretKey; +use tapir::primitives::identity::Identity; +use tapir::connections::{OutboundConnection, Connection, InboundConnection, ConnectionInterface}; +use tapir::primitives::transcript::Transcript; +use tapir::applications::authentication_app::{AuthenicationApp, AuthenticationCapability}; +use tapir::connections::service::Service; +use rand::rngs::OsRng; +use tapir::acns::tor::TorProcess; + +fn main() { + let mut auth_control_port = TorProcess::connect(9051) + .unwrap() + .authenticate(Box::new(HashedPassword::new(String::from("examplehashedpassword")))) + .unwrap(); + let mut csprng = OsRng {}; + let keypair = ed25519_dalek::Keypair::generate(&mut csprng); + match auth_control_port.add_onion_v3(SecretKey::from_bytes(&keypair.secret.to_bytes()).unwrap(), 9878, 10029) { + Ok(service_id) => { + // we authenticated! + let identity = Identity::initialize(keypair); + println!("Service Id: {}", service_id); + println!("Setup: {}", identity.hostname()); + + let mut service = Service::init(identity.clone()); + + let identity = identity.clone(); + let outbound_identity = identity.clone(); + let outbound_service = |mut conn: Connection| { + let mut transcript = Transcript::new_transcript("tapir-transcript"); + let mut auth_app = AuthenicationApp::new(outbound_identity); + match auth_app.run_outbound(conn, &mut transcript) { + Ok(conn) => { + println!("Authenticated {} {}", conn.hostname(), conn.has_capability(&AuthenticationCapability)); + } + Err(err) => { + println!("Error: {:?}", err); + } + } + }; + match service.connect("lzry2bfjkdzlx64aksxw5jpz6qdnvjoekpkzjqclpd4wz25dbv2pasid", outbound_service.clone()) { + _ => {} + } + loop {} + } + Err(err) => println!("{:?}", err), + } +} \ No newline at end of file diff --git a/examples/simple_server.rs b/examples/simple_server.rs new file mode 100644 index 0000000..9dda11f --- /dev/null +++ b/examples/simple_server.rs @@ -0,0 +1,47 @@ +use tapir::acns::tor::authentication::HashedPassword; +use ed25519_dalek::SecretKey; +use tapir::primitives::identity::Identity; +use tapir::connections::{OutboundConnection, Connection, InboundConnection, ConnectionInterface}; +use tapir::primitives::transcript::Transcript; +use tapir::applications::authentication_app::AuthenicationApp; +use tapir::connections::service::Service; +use rand::rngs::OsRng; +use tapir::acns::tor::TorProcess; + +fn main() { + let mut auth_control_port = TorProcess::connect(9051) + .unwrap() + .authenticate(Box::new(HashedPassword::new(String::from("examplehashedpassword")))) + .unwrap(); + let mut csprng = OsRng {}; + let keypair = ed25519_dalek::Keypair::generate(&mut csprng); + match auth_control_port.add_onion_v3(SecretKey::from_bytes(&keypair.secret.to_bytes()).unwrap(), 9878, 10029) { + Ok(service_id) => { + // we authenticated! + let identity = Identity::initialize(keypair); + println!("Service Id: {}", service_id); + println!("Setup: {}", identity.hostname()); + + let service = Service::init(identity.clone()); + + let identity = identity.clone(); + let inbound_service = |mut conn: Connection| { + let mut transcript = Transcript::new_transcript("tapir-transcript"); + let mut auth_app = AuthenicationApp::new(identity); + match auth_app.run_inbound(conn, &mut transcript) { + Ok(conn) => { + println!("Authenticated Inbound Connection from {}", conn.hostname()) + } + _ => { + println!("Failed Inbound Authentication") + } + } + }; + + let mut service = service.listen(10029, inbound_service.clone()).unwrap_or_else(|_| panic!()); + + loop {} + } + Err(err) => println!("{:?}", err), + } +} \ No newline at end of file diff --git a/src/applications/authentication_app.rs b/src/applications/authentication_app.rs index 8e146b6..6b973e9 100644 --- a/src/applications/authentication_app.rs +++ b/src/applications/authentication_app.rs @@ -1,6 +1,6 @@ use crate::applications::authentication_app::AuthenticationAppError::NotAuthenticatedError; use crate::connections::utils::public_key_to_hostname; -use crate::connections::{Connection, ConnectionInterface, InboundConnection, OutboundConnection}; +use crate::connections::{Connection, ConnectionInterface, InboundConnection, OutboundConnection, Capability}; use crate::primitives::identity::Identity; use crate::primitives::transcript::Transcript; use ed25519_dalek::PublicKey; @@ -8,7 +8,6 @@ use integer_encoding::VarInt; use serde::Deserialize; use serde::Serialize; use sha3::Digest; -use std::io::Error; use std::sync::Arc; use subtle::ConstantTimeEq; @@ -21,6 +20,8 @@ pub enum AuthenticationAppError { NotAuthenticatedError, } +pub const AuthenticationCapability : Capability = Capability("AuthenticationCapability"); + struct AuthenticationSession { long_term_identity: Arc, ephemeral_identity: Identity, @@ -35,7 +36,7 @@ struct AuthenticationSession { conn: Connection, } -impl AuthenticationSession { +impl AuthenticationSession where Direction:Clone { pub fn new_outbound(mut conn: Connection, long_term_identity: Arc) -> AuthenticationSession { let ephemeral_identity = Identity::initialize_ephemeral_identity(); let mut auth_session = AuthenticationSession { @@ -96,7 +97,7 @@ impl AuthenticationSession { } /// check the challenge from the remote - fn check_remote_challenge(&mut self) -> Result<(), AuthenticationAppError> { + fn check_remote_challenge(&mut self) -> Result, AuthenticationAppError> { match self.conn.send_encrypted(self.generate_challenge_message()) { Ok(()) => { let remote_challenge = self.conn.expect_encrypted(); @@ -104,7 +105,9 @@ impl AuthenticationSession { cmp_challenge.extend_from_slice(self.challenge.as_slice()); cmp_challenge.extend_from_slice(public_key_to_hostname(&self.remote_long_term_identity).as_bytes()); if remote_challenge.ct_eq(cmp_challenge.as_slice()).unwrap_u8() == 1 { - return Ok(()); + self.conn.set_hostname(&public_key_to_hostname(&self.remote_long_term_identity)); + self.conn.set_capability(&AuthenticationCapability); + return Ok(self.conn.try_clone()); } self.conn.shutdown(); Err(NotAuthenticatedError) @@ -115,7 +118,7 @@ impl AuthenticationSession { } impl AuthenticationSession { - pub fn generate_challenge(&mut self, transcript: &mut Transcript) -> Result<(), AuthenticationAppError> { + pub fn generate_challenge(&mut self, transcript: &mut Transcript) -> Result, AuthenticationAppError> { let l2e = self.long_term_identity.edh(self.remote_ephemeral_identity); let e2l = self.ephemeral_identity.edh(self.remote_long_term_identity); let e2e = self.ephemeral_identity.edh(self.remote_ephemeral_identity); @@ -140,7 +143,7 @@ impl AuthenticationSession { } impl AuthenticationSession { - pub fn generate_challenge(&mut self, transcript: &mut Transcript) -> Result<(), AuthenticationAppError> { + pub fn generate_challenge(&mut self, transcript: &mut Transcript) -> Result, AuthenticationAppError> { let l2e = self.long_term_identity.edh(self.remote_ephemeral_identity); let e2l = self.ephemeral_identity.edh(self.remote_long_term_identity); let e2e = self.ephemeral_identity.edh(self.remote_ephemeral_identity); @@ -177,13 +180,13 @@ impl AuthenicationApp { AuthenicationApp { identity } } - pub fn run_outbound(&mut self, conn: &mut Connection, transcript: &mut Transcript) -> Result<(), AuthenticationAppError> { - let mut auth_session = AuthenticationSession::::new_outbound(conn.try_clone(), self.identity.clone()); + pub fn run_outbound(&mut self, conn: Connection, transcript: &mut Transcript) -> Result, AuthenticationAppError> { + let mut auth_session = AuthenticationSession::::new_outbound(conn, self.identity.clone()); auth_session.generate_challenge(transcript) } - pub fn run_inbound(&mut self, conn: &mut Connection, transcript: &mut Transcript) -> Result<(), AuthenticationAppError> { - let mut auth_session = AuthenticationSession::::new_inbound(conn.try_clone(), self.identity.clone()); + pub fn run_inbound(&mut self, conn: Connection, transcript: &mut Transcript) -> Result, AuthenticationAppError> { + let mut auth_session = AuthenticationSession::::new_inbound(conn, self.identity.clone()); auth_session.generate_challenge(transcript) } } diff --git a/src/connections/mod.rs b/src/connections/mod.rs index 340c36f..77f499a 100644 --- a/src/connections/mod.rs +++ b/src/connections/mod.rs @@ -3,6 +3,7 @@ use secretbox::CipherType::Salsa20; use secretbox::SecretBox; use std::io::{Error, Read, Write}; use std::net::{Shutdown, TcpStream}; +use hashbrown::HashSet; /// Connections provides an interface for manage sets of connections on top of a particular /// ACN. @@ -21,22 +22,41 @@ pub struct InboundConnection(()); #[derive(Clone)] pub struct OutboundConnection(()); +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct Capability(pub &'static str); + + pub struct Connection { conn: TcpStream, direction: Direction, key: Vec, + hostname: String, + capabilities: HashSet } pub trait ConnectionInterface { + fn set_hostname(&mut self, hostname: &String); + fn hostname(&self) -> String; fn enable_encryption(&mut self, key: Vec); fn send(&mut self, amsg: &String) -> Result<(), Error>; fn send_encrypted(&mut self, msg: Vec) -> Result<(), Error>; fn expect_encrypted(&mut self) -> Vec; fn expect(&mut self) -> Result, Error>; fn shutdown(&mut self); + + fn set_capability(&mut self, capability: &Capability); + fn has_capability(&self, capability: &Capability) -> bool; } impl ConnectionInterface for Connection { + fn set_hostname(&mut self, hostname: &String) { + self.hostname = hostname.clone() + } + + fn hostname(&self) -> String { + self.hostname.clone() + } + fn enable_encryption(&mut self, key: Vec) { self.key = key } @@ -111,6 +131,14 @@ impl ConnectionInterface for Connection { _ => {} // If anything bad happens we will know soon enough... } } + + fn set_capability(&mut self, capability: &Capability) { + self.capabilities.insert(capability.clone()); + } + + fn has_capability(&self, capability: &Capability) -> bool { + self.capabilities.contains(capability) + } } impl Connection @@ -122,6 +150,8 @@ where conn, direction: InboundConnection(()), key: vec![], + hostname: String::new(), + capabilities: HashSet::new() } } @@ -130,6 +160,8 @@ where conn, direction: OutboundConnection(()), key: vec![], + hostname: String::new(), + capabilities: HashSet::new() } } @@ -138,6 +170,8 @@ where conn: self.conn.try_clone().unwrap(), direction: self.direction.clone(), key: self.key.clone(), + hostname: self.hostname.clone(), + capabilities: self.capabilities.clone() } } }