Rust bindings for the ObscuraProto C++ library — end-to-end encrypted WebSocket communication using hybrid encryption (Ed25519 + X25519 + ChaCha20-Poly1305).
rustObscuraProto uses cxx to provide safe Rust bindings over the C++ ObscuraProto library. Key design: thin wrappers (1:1 with C++ API).
- Key generation (Ed25519, X25519)
- Digital signatures (sign/verify)
- ECDH session key computation
- ChaCha20-Poly1305 encryption/decryption
- Noise-like session handshake
- Opcode-based payload serialization
- Protocol version negotiation
- WebSocket server (run, stop, send, sync_request, streams)
- WebSocket client (connect, disconnect, send, sync_request, streams)
- Handler API (set_on_open, set_on_ready, set_default_payload_handler, register_op_handler, register_request_handler, register_incoming_stream_handler, set_client_identity_handler, etc.)
- Client identity (set_identity, get_client_identity)
- Anonymous sessions (send_anonymous, send_to_identity)
- Incoming streams (IncomingStream event + StreamHandle)
- Op-code routed streams (
register_stream_handlerfor server/client/anon,start_stream_with_op_code) - Full configuration (ConfigBuilder with all sub-configs, incl.
request_ms) - StreamHandle::read() (read buffered stream data)
- Async request-response: non-blocking
async_requestreturning aPayloadFuture(is_ready/get/wait), blockingsync_requestwith a C++-owned deadline (configrequest_ms, 30 s default),sync_request_to_identity,async_request_to_identity - Native request timeouts:
sync_request_with_timeout/async_request_with_timeout(client + server), C++TimeoutErrormapped toObscuraError::Timeout - Seed key API:
Crypto::keypair_from_seed(deterministic Ed25519) +Crypto::derive_public_key - Client
send_responsefor server-initiated requests - Protocol v1.1 (
V1_1,SUPPORTED_VERSIONS = [V1_1, V1_0]) - Stream callbacks (set_on_end, set_on_cancel, set_on_data)
- Typed payloads (PayloadBuilder/PayloadReader with add_u8..i64/bool/f32/f64/bytes)
- secure_wipe (sodium_memzero) for scrubbing sensitive buffers
use obscura_proto::*;
// Generate keys
let alice = Crypto::generate_kx_keypair()?;
let bob = Crypto::generate_kx_keypair()?;
// Compute shared session keys
let alice_keys = Crypto::client_compute_session_keys(&alice, &bob.public_key()?)?;
let bob_keys = Crypto::server_compute_session_keys(&bob, &alice.public_key()?)?;
// Encrypt/decrypt
let encrypted = Crypto::encrypt(&alice_keys.tx, 0, &alice_keys.tx)?;
let decrypted = Crypto::decrypt(&encrypted, &bob_keys.rx)?;See docs/API.md for full API reference.
Request-response flows require an established client identity — call set_identity() (Ed25519 key) before making requests. The server routes requests only from authenticated sessions to register_request_handler handlers; anonymous sessions are served exclusively by register_anon_request_handler.
use obscura_proto::*;
use std::time::Duration;
// Client side
let identity = Crypto::generate_sign_keypair()?;
client.set_identity(&identity.to_bytes()?)?; // required before any request
let future = client.async_request(200, b"ping")?; // non-blocking: PayloadFuture
let response = future.wait(Duration::from_secs(30))?; // blocks (polls every 10 ms)
// Server side: serve op 200 for authenticated sessions
server.register_request_handler(200, |_ctx, _hdl, params| {
params.to_vec() // response bytes
});Notes:
async_request/sync_requestare non-blocking / blocking with a C++-owned deadline respectively. The deadline comes from the configrequest_ms(30 s default;request_ms = 0disables it — the call blocks until the response arrives) or from the explicittimeout_msof the*_with_timeoutconstructors (timeout_ms = 0means "use the config default"). On expiry the C++ library raisesObscuraError::Timeout(both sync calls andPayloadFutureget/wait).PayloadFutureis single-use and consuming:get/waittake ownership; a timeout burns the future (a late response is ignored). Dropping an unfulfilled future is safe.connect()is non-blocking too —Ok(())only means the attempt started.is_connected()becomestrueonly after the handshake completes (on_ready); connect failures are reported throughon_disconnect.- From a handler/callback never call synchronous blocking operations (
sync_request,wait,get) — they would stall the single C++ asio thread.sync_request/waitcalled from the ws-thread return an immediateErr(no freeze) andgetis rejected by the C++ side;async_requestdispatch is safe.stop/disconnect/ctx.stop/ctx.disconnectare also safe from a handler (delegated to a detached thread). Never panic in a callback — a panic crossing FFI aborts.
Run the echo example to see basic server/client communication using the new handler API:
cargo run --example echo| Example | Description | Command |
|---|---|---|
crypto_basics.rs |
Demonstrates key generation, signing, verification, encryption/decryption, key export/import, and payload serialization -- all without network. | cargo run --example crypto_basics |
echo.rs |
Demonstrates: find_free_port, server run (non-blocking), client connect with identity, handler-based echo. | cargo run --example echo |
authenticated_connection.rs |
Demonstrates: client set_identity, server on_open + identity handler, server get_client_identity(), server send_to_identity(). Handler-based. | cargo run --example authenticated_connection |
anonymous_messaging.rs |
Demonstrates: client WITHOUT identity, Server::send_anonymous() to send to anonymous client. Handler-based. | cargo run --example anonymous_messaging |
streaming.rs |
Demonstrates: Client::start_stream(), stream_write(), stream_end(), Server::get_stream(), StreamHandle::read(). Also demonstrates a reverse stream (server -> client). Handler-based. | cargo run --example streaming |
custom_config.rs |
Demonstrates ConfigBuilder with all sub-configs: rate_limit, timeouts, message_limits, connection_limits. Server/Client with custom config, then verify echo works. | cargo run --example custom_config |
request_response.rs |
Demonstrates new async API: Client::async_request(), Server::async_request(), Server::sync_request_to_identity(), Server::async_request_to_identity(), and StreamHandle callbacks (set_on_end, set_on_cancel). | cargo run --example request_response |
cargo build- CMake 3.11+
- C++17 compiler
- Rust 1.70+