Crate katzenpost_thin_client

Crate katzenpost_thin_client 

Source
Expand description

A thin client for sending and receiving messages via a Katzenpost mix network.

This crate provides a thin client library for interacting with a Katzenpost mixnet, suitable for desktop and mobile applications.

A mix network is a type of anonymous communications network. What’s a thin client library? It’s code you can use as a depencency in your application so that it can interact with services on the mix network. The Katzenpost client daemon is a multiplexing client; many applications on the same device can use their thin client libraries to connect to the daemon and interact with mixnet services concurrently.

This example can be found here: https://github.com/katzenpost/thin_client/blob/main/examples/echo_ping.rs Thin client example usage::

use std::env;
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::process;

use tokio::time::{timeout, Duration};
use tokio::runtime::Runtime;

use serde_cbor::Value;

use katzenpost_thin_client::{ThinClient, Config, pretty_print_pki_doc};

struct ClientState {
    reply_message: Arc<Mutex<Option<BTreeMap<Value, Value>>>>,
    pki_received: Arc<Mutex<bool>>,
}

impl ClientState {
    fn new() -> Self {
        Self {
            reply_message: Arc::new(Mutex::new(None)),
            pki_received: Arc::new(Mutex::new(false)),
        }
    }

    fn save_reply(&self, reply: &BTreeMap<Value, Value>) {
        let mut stored_reply = self.reply_message.lock().unwrap();
        *stored_reply = Some(reply.clone());
    }

    fn set_pki_received(&self) {
        let mut pki_flag = self.pki_received.lock().unwrap();
        *pki_flag = true;
    }

    fn is_pki_received(&self) -> bool {
        *self.pki_received.lock().unwrap()
    }

    fn await_message_reply(&self) -> Option<BTreeMap<Value, Value>> {
        let stored_reply = self.reply_message.lock().unwrap();
        stored_reply.clone()
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        eprintln!("Usage: {} <config_path>", args[0]);
        process::exit(1);
    }
    let config_path = &args[1];

    let rt = Runtime::new().unwrap();
    rt.block_on(run_client(config_path)).unwrap();
}

async fn run_client(config_path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let state = Arc::new(ClientState::new());
    let state_for_reply = Arc::clone(&state);
    let state_for_pki = Arc::clone(&state);

    let mut cfg = Config::new(config_path)?;
    cfg.on_new_pki_document = Some(Arc::new(move |_pki_doc| {
        println!("✅ PKI document received.");
        state_for_pki.set_pki_received();
    }));
    cfg.on_message_reply = Some(Arc::new(move |reply| {
        println!("📩 Received a reply!");
        state_for_reply.save_reply(reply);
    }));

    println!("🚀 Initializing ThinClient...");
    let client = ThinClient::new(cfg).await?;

    println!("⏳ Waiting for PKI document...");
    let result = timeout(Duration::from_secs(5), async {
        loop {
            if state.is_pki_received() {
                break;
            }
            tokio::task::yield_now().await;
        }
    })
    .await;

    if result.is_err() {
        return Err("❌ PKI document not received in time.".into());
    }

    println!("✅ Pretty printing PKI document:");
    let doc = client.pki_document().await;
    pretty_print_pki_doc(&doc);
    println!("AFTER Pretty printing PKI document");

    let service_desc = client.get_service("echo").await?;
    println!("got service descriptor for echo service");

    let surb_id = ThinClient::new_surb_id();
    let payload = b"hello".to_vec();
    let (dest_node, dest_queue) = service_desc.to_destination();

    println!("before calling send_message");
    client.send_message(surb_id, &payload, dest_node, dest_queue).await?;
    println!("after calling send_message");

    println!("⏳ Waiting for message reply...");
    let state_for_reply_wait = Arc::clone(&state);

    let result = timeout(Duration::from_secs(5), async move {
        loop {
            if let Some(reply) = state_for_reply_wait.await_message_reply() {
                if let Some(Value::Bytes(payload2)) = reply.get(&Value::Text("payload".to_string())) {
                    let payload2 = &payload2[..payload.len()];
                    assert_eq!(payload, payload2, "Reply does not match payload!");
                    println!("✅ Received valid reply, stopping client.");
                    return Ok::<(), Box<dyn std::error::Error>>(());
                }
            }
            tokio::task::yield_now().await;
        }
    }).await;

    result.map_err(|e| Box::new(e))??;
    client.stop().await;
    println!("✅ Client stopped successfully.");
    Ok(())
}

§See Also

Modules§

error

Structs§

Config
Our configuration defines some callbacks which the thin client will envoke when it receives the corresponding event from the client daemon.
ConfigFile
EventSinkReceiver
Wrapper for event sink receiver that automatically removes the drain when dropped
Geometry
ReadChannelReply
Reply from ReadChannel operation, matching Go ReadChannelReply
ServiceDescriptor
ServiceDescriptor is used when we are searching the PKI document for a specific service.
ThinClient
This is our ThinClient type which encapsulates our thin client connection management and message processing.
WriteChannelReply
Reply from WriteChannel operation, matching Go WriteChannelReply

Enums§

ReadHalf
This represent the read half of our network socket.
WriteHalf
This represent the write half of our network socket.

Constants§

THIN_CLIENT_ERROR_CHANNEL_NOT_FOUND
ThinClientErrorChannelNotFound indicates that the specified channel does not exist or has been garbage collected.
THIN_CLIENT_ERROR_CONNECTION_LOST
ThinClientErrorConnectionLost indicates that the connection to the daemon was lost during the operation. The client should attempt to reconnect.
THIN_CLIENT_ERROR_COURIER_CACHE_CORRUPTION
ThinClientErrorCourierCacheCorruption indicates that the courier’s cache has detected corruption.
THIN_CLIENT_ERROR_DUPLICATE_CAPABILITY
ThinClientErrorDuplicateCapability indicates that the provided capability (read or write cap) has already been used and is considered a duplicate.
THIN_CLIENT_ERROR_INTERNAL_ERROR
ThinClientErrorInternalError indicates an internal error occurred within the client daemon or thin client that prevented operation completion.
THIN_CLIENT_ERROR_INVALID_CHANNEL
ThinClientErrorInvalidChannel indicates that the specified channel ID is invalid or malformed.
THIN_CLIENT_ERROR_INVALID_PAYLOAD
ThinClientErrorInvalidPayload indicates that the message payload was invalid, too large, or otherwise could not be processed.
THIN_CLIENT_ERROR_INVALID_REQUEST
ThinClientErrorInvalidRequest indicates that the request format was invalid or contained malformed data that could not be processed.
THIN_CLIENT_ERROR_MAX_RETRIES
ThinClientErrorMaxRetries indicates that the maximum number of retry attempts was exceeded for a reliable operation (such as ARQ).
THIN_CLIENT_ERROR_PERMISSION_DENIED
ThinClientErrorPermissionDenied indicates that the operation was denied due to insufficient permissions or capability restrictions.
THIN_CLIENT_ERROR_SERVICE_UNAVAILABLE
ThinClientErrorServiceUnavailable indicates that the requested service or functionality is currently unavailable.
THIN_CLIENT_ERROR_TIMEOUT
ThinClientErrorTimeout indicates that the operation timed out before completion. This may occur during network operations or when waiting for responses from the mixnet.
THIN_CLIENT_PROPAGATION_ERROR
ThinClientPropagationError indicates that the request could not be propagated to replicas.
THIN_CLIENT_SUCCESS
ThinClientSuccess indicates that the operation completed successfully with no errors. This is the default success state.

Functions§

find_services
Find a specific mixnet service if it exists.
pretty_print_pki_doc
Pretty prints a PKI document which you can gather from the client with it’s pki_document method, documented above.
thin_client_error_to_string
Converts a thin client error code to a human-readable string. This function provides consistent error message formatting across the thin client protocol and is used for logging and error reporting.