4 releases
new 0.1.1-rc5 | Oct 22, 2024 |
---|---|
0.1.1-rc4 | Oct 21, 2024 |
0.1.1-rc3 | Oct 18, 2024 |
0.1.1-rc1 | Oct 14, 2024 |
0.1.0 |
|
#346 in Cryptography
766 downloads per month
33KB
631 lines
Clavis
Clavis is a Rust library that provides secure, encrypted communication over asynchronous streams. It implements a robust protocol using XChaCha20Poly1305 for encryption and X25519 for key exchange, with optional pre-shared key (PSK) authentication. Built on top of the Tokio runtime, Clavis offers a high-level abstraction for building secure network applications.
Installation
Add Clavis to your Cargo.toml
:
[dependencies]
clavis = { git = "https://github.com/pyrohost/clavis.git" }
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
Quick Start
1. Define Your Packets
Use the define_packets!
macro to create your packet types:
use clavis::define_packets;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct StructPacket {
content: String,
}
define_packets! {
pub enum MyPacket {
VoidMessage,
Message(String),
StructPacket(MyPacket),
StructuredMessage { content: String },
}
}
2. Establish an Encrypted Connection
Client Example
use clavis::{EncryptedStream, Role};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to the server
let stream = TcpStream::connect("127.0.0.1:8080").await?;
// Optional: Use a pre-shared key for additional security
let psk = b"my_secret_key";
// Create an encrypted stream
let mut client = EncryptedStream::new(stream, Role::Client, Some(psk)).await?;
// Send an encrypted message
client.write_packet(&MyPacket::Message("Hello, server!".to_string())).await?;
// Receive the response
let response: MyPacket = client.read_packet().await?;
println!("Server response: {:?}", response);
Ok(())
}
Server Example
use clavis::{EncryptedStream, Role};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("Server listening on :8080");
while let Ok((stream, addr)) = listener.accept().await {
println!("New connection from {}", addr);
let psk = b"my_secret_key";
tokio::spawn(async move {
match EncryptedStream::new(stream, Role::Server, Some(psk)).await {
Ok(mut server) => {
if let Ok(packet) = server.read_packet::<MyPacket>().await {
println!("Received: {:?}", packet);
// Send a response
let _ = server
.write_packet(&MyPacket::Message("Hello, client!".to_string()))
.await;
}
}
Err(e) => eprintln!("Connection error: {}", e),
}
});
}
Ok(())
}
Advanced Usage
Splitting Streams
You can split an EncryptedStream
into separate reader and writer halves for concurrent operations:
let (mut reader, mut writer) = encrypted_stream.split();
// Use reader and writer independently
tokio::spawn(async move {
while let Ok(packet) = reader.read_packet::<MyPacket>().await {
println!("Received: {:?}", packet);
}
});
// Write packets from another task
writer.write_packet(&MyPacket::Message("Hello!".to_string())).await?;
License
This project is licensed under the MIT License - see the LICENSE file for details.
Dependencies
~6–13MB
~151K SLoC