tapir-rs/src/acns/tor/mod.rs

109 lines
3.9 KiB
Rust

use crate::acns::tor::authentication::TorAuthenticationMethod;
use crate::acns::ACNError;
use crate::acns::ACNError::{AuthenticationError, ServiceSetupError};
use ed25519_dalek::ExpandedSecretKey;
use std::io::Write;
use std::io::{BufRead, BufReader, Error};
use std::net::TcpStream;
pub mod authentication;
pub mod run;
pub mod torrc;
pub mod validation;
#[derive(Debug)]
pub struct TorDisconnected(());
#[derive(Debug)]
pub struct TorConnected(());
#[derive(Debug)]
pub struct TorAuthenticated(());
#[derive(Debug)]
pub struct TorProcess<ConnectionStatus> {
conn: TcpStream,
status: ConnectionStatus,
}
impl TorProcess<TorDisconnected> {
pub fn connect(control_port: u16) -> Result<TorProcess<TorConnected>, Error> {
match TcpStream::connect(&format!("127.0.0.1:{}", control_port)) {
Ok(conn) => Ok(TorProcess { conn, status: TorConnected(()) }),
Err(err) => Err(err),
}
}
}
/// From the control port spec...
/// Before the client has authenticated, no command other than
/// PROTOCOLINFO, AUTHCHALLENGE, AUTHENTICATE, or QUIT is valid. If the
/// controller sends any other command, or sends a malformed command, or
/// sends an unsuccessful AUTHENTICATE command, or sends PROTOCOLINFO or
/// AUTHCHALLENGE more than once, Tor sends an error reply and closes
/// the connection.
/// We are more strict as we only allow the caller to authenticate
impl TorProcess<TorConnected> {
pub fn authenticate(mut self, auth_method: Box<dyn TorAuthenticationMethod>) -> Result<TorProcess<TorAuthenticated>, ACNError> {
auth_method.authenticate(&mut self.conn);
let mut reader = BufReader::new(self.conn.try_clone().unwrap());
let mut result = String::new();
match reader.read_line(&mut result) {
Ok(_n) => match result.as_str() {
"250 OK\r\n" => Ok(TorProcess {
conn: self.conn,
status: TorAuthenticated(()),
}),
"515 Bad authentication\r\n" => Err(AuthenticationError(String::from("515 Bad authentication"))),
_ => Err(AuthenticationError(String::from(result))),
},
Err(err) => Err(AuthenticationError(err.to_string())),
}
}
}
/// We are now authenticated!
impl TorProcess<TorAuthenticated> {
/// Tell the control port to create a new Onion V3 service given the ed25519 secret key...
pub fn add_onion_v3(&mut self, secret_key: ed25519_dalek::SecretKey, virtual_port: u16, target_port: u16) -> Result<String, ACNError> {
let esk = ExpandedSecretKey::from(&secret_key);
match write!(self.conn, "ADD_ONION ED25519-V3:{} Port={},{}\r\n", base64::encode(esk.to_bytes()), virtual_port, target_port) {
_ => {}
}
let mut reader = BufReader::new(self.conn.try_clone().unwrap());
let mut result = String::new();
match reader.read_line(&mut result) {
Ok(_n) => {
if result.starts_with("250-ServiceID=") {
// We authenticated!
return Ok(result);
}
Err(ServiceSetupError(result))
}
Err(err) => Err(ServiceSetupError(err.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use crate::acns::tor::authentication::HashedPassword;
use crate::acns::tor::TorProcess;
use rand::rngs::OsRng;
#[test]
fn connect_tor() {
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(keypair.secret, 9878, 10029) {
Ok(_) => {
// we authenticated!
}
Err(err) => println!("{:?}", err),
}
}
}