109 lines
3.9 KiB
Rust
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),
|
|
}
|
|
}
|
|
}
|