initial commit

This commit is contained in:
Neemek 2025-10-04 12:31:58 +02:00
commit 565e400e8d
9 changed files with 796 additions and 0 deletions

159
src/lib.rs Normal file
View file

@ -0,0 +1,159 @@
use chacha::Block;
use std::io::{Error, Read, Write};
use std::net::{Shutdown, TcpStream};
use rand::{rng, RngCore};
use x25519_dalek::{EphemeralSecret, PublicKey};
#[inline]
fn u8_to_u32(to: &mut [u32], from: &[u8]) {
for i in 0..to.len() {
to[i] = u32::from_le_bytes(from[4*i..][..4].try_into().unwrap());
}
}
/*
#[repr(u8)]
enum Exchange {
DHCurve25519 = 0
}
impl TryFrom<u8> for Exchange {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Exchange::DHCurve25519),
_ => Err(Error::new(ErrorKind::InvalidData, "Invalid exchange code")),
}
}
}
#[repr(u8)]
enum Encryption {
ChaCha = 0
}
impl TryFrom<u8> for Encryption {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Encryption::ChaCha),
_ => Err(Error::new(ErrorKind::InvalidData, "Invalid encryption code")),
}
}
}
*/
#[derive(Debug)]
pub struct EncryptedStream {
socket: TcpStream,
block: Block,
}
impl EncryptedStream {
pub fn bind(address: &str) -> Result<EncryptedStream, Error> {
let secret = EphemeralSecret::random();
let public = PublicKey::from(&secret);
let mut socket = TcpStream::connect(address)?;
socket.write(public.as_bytes())?;
let mut their_public_bytes = [0u8; 32];
socket.read_exact(&mut their_public_bytes)?;
let their_public = PublicKey::from(their_public_bytes);
let shared_secret = secret.diffie_hellman(&their_public);
let mut nonce_bytes = [0u8; 12];
socket.read_exact(&mut nonce_bytes)?;
let mut nonce_u32s = [0u32; 3];
u8_to_u32(&mut nonce_u32s, &nonce_bytes);
let mut key_u32s = [0u32; 8];
u8_to_u32(&mut key_u32s, &shared_secret.to_bytes());
Ok(EncryptedStream {
socket,
block: Block::new(key_u32s, 0, nonce_u32s, 20)
})
}
pub fn shutdown(&self, how: Shutdown) -> Result<(), Error> {
self.socket.shutdown(how)
}
}
impl TryFrom<TcpStream> for EncryptedStream {
type Error = Error;
fn try_from(mut socket: TcpStream) -> Result<Self, Self::Error> {
let secret = EphemeralSecret::random();
let public = PublicKey::from(&secret);
let mut their_public_bytes = [0u8; 32];
socket.read(&mut their_public_bytes)?;
socket.write(public.as_bytes())?;
let their_public = PublicKey::from(their_public_bytes);
let shared_secret = secret.diffie_hellman(&their_public);
let mut nonce_bytes = [0u8; 12];
rng().fill_bytes(&mut nonce_bytes);
socket.write(&nonce_bytes)?;
let mut nonce_u32s = [0u32; 3];
u8_to_u32(&mut nonce_u32s, &nonce_bytes);
let mut key_u32s = [0u32; 8];
u8_to_u32(&mut key_u32s, &shared_secret.to_bytes());
Ok(EncryptedStream {
socket,
block: Block::new(key_u32s, 0, nonce_u32s, 20)
})
}
}
impl Read for EncryptedStream {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
let n = self.socket.read(buf)?;
self.block.process(&mut buf[..n]);
Ok(n)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Error> {
let n = self.socket.read_to_end(buf)?;
self.block.process(buf.as_mut_slice());
Ok(n)
}
}
impl Write for EncryptedStream {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
let mut new = Vec::from(buf);
self.block.process(new.as_mut_slice());
self.socket.write(&new)
}
fn flush(&mut self) -> std::io::Result<()> {
self.socket.flush()
}
}
#[cfg(test)]
mod tests {}

47
src/main.rs Normal file
View file

@ -0,0 +1,47 @@
use hermes::EncryptedStream;
use std::env::args;
use std::io::{Read, Write};
use std::net::{Shutdown, TcpListener};
use std::process::exit;
fn main() {
let args = args().skip(1).collect::<Vec<_>>();
if args.len() == 0 {
println!("missing arg (Client or Server?)");
exit(1)
}
match args[0].as_ref() {
"s" => {
let server = TcpListener::bind("127.0.0.1:2007").unwrap();
println!("listening");
for incoming in server.incoming() {
println!("got incoming connection");
let mut encrypted = EncryptedStream::try_from(incoming.unwrap()).unwrap();
println!("successfully encrypted stream");
encrypted.write(b"the fitness gram pacer test is a multi-stage aerobic fitness test...").unwrap();
println!("successfully wrote data");
encrypted.flush().unwrap();
encrypted.shutdown(Shutdown::Both).unwrap();
println!("closed connection");
}
}
"c" => {
let mut client = EncryptedStream::bind("127.0.0.1:2007").unwrap();
println!("successfully connected client");
let mut buf = Vec::new();
client.read_to_end(&mut buf).unwrap();
println!("successfully read data");
println!("data: {}", String::from_utf8_lossy(&buf));
}
_ => {
println!("Unknown command (not client or server)");
exit(1)
}
}
}