#communication #encryption #async #security

clavis

A Rust library for secure, encrypted communication over asynchronous streams

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 Oct 14, 2024

#346 in Cryptography

Download history 275/week @ 2024-10-11 491/week @ 2024-10-18

766 downloads per month

MIT license

33KB
631 lines

Clavis

Crates.io License: MIT

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