From 5bfcccc056a2a491a72e4315aa95effb2243ad3c Mon Sep 17 00:00:00 2001 From: Alex Emmet <111742636+Alex-Emmet@users.noreply.github.com> Date: Thu, 2 Jul 2026 20:21:12 +0200 Subject: [PATCH] Desctiption-Docs --- docs/CONNECTOR.md | 3 ++- docs/NATIVE-CLIENT.md | 3 +++ docs/NATIVE-HOST.md | 25 +++++++++++++++++++++++-- example/client/src/main.rs | 4 +++- example/server/src/main.rs | 6 +++++- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/docs/CONNECTOR.md b/docs/CONNECTOR.md index 0ef94c1..551b8c8 100644 --- a/docs/CONNECTOR.md +++ b/docs/CONNECTOR.md @@ -71,7 +71,8 @@ The host's `accept()` method: ### Login/Register Handshake -When `require_authentication` is set, the parties run a mutually-authenticated +When `authentication_policy` is `ForceAuthentication` or `AllowAuthentication`, +the parties run a mutually-authenticated **challenge-response**. The client speaks first with an *unsigned* hello: - **Login** (`CommunicationType::Identification`, reserved ID 0): version, client ID diff --git a/docs/NATIVE-CLIENT.md b/docs/NATIVE-CLIENT.md index 5f7d1ac..027aa29 100644 --- a/docs/NATIVE-CLIENT.md +++ b/docs/NATIVE-CLIENT.md @@ -29,6 +29,7 @@ let config = ClientConfig::new("https://host.example.com:4433") | `url` | `String` | `https://host:port` address of the MTP host | | `tls` | `ClientTlsConfig` | `SystemRoots` or `PinnedPem(pem_bytes)` | | `client_id` | `u64` | Client identifier (ignored during `auth_register`) | +| `description` | `Option` | Optional label sent during handshake (e.g. `"phone"`) | | `auth_timeout` | `Duration` (crypto) | Authentication handshake timeout (default 30s) | ### TLS Certificate Handling @@ -60,6 +61,7 @@ pub struct MTPConnection { pub version: Version, pub sender: Sender, pub receiver: Receiver, + pub description: Option, #[cfg(feature = "crypto")] pub auth_state: AuthState, #[cfg(feature = "crypto")] @@ -69,6 +71,7 @@ pub struct MTPConnection { - `version` -- the negotiated protocol version - `sender` / `receiver` -- for message I/O +- `description` -- the label sent during handshake (set via `ClientConfig::with_description`) - `client_id` -- the confirmed/assigned client identifier (crypto only) ### Unauthenticated Connect diff --git a/docs/NATIVE-HOST.md b/docs/NATIVE-HOST.md index caf92f9..631c1ea 100644 --- a/docs/NATIVE-HOST.md +++ b/docs/NATIVE-HOST.md @@ -48,11 +48,30 @@ let config = HostConfig::new( | `port` | `u16` | Listen port | | `tls_fullchain` | `Vec` | PEM-encoded TLS certificate chain | | `tls_key` | `Vec` | PEM-encoded TLS private key | -| `require_authentication` | `bool` (crypto) | Enable login/register handshake | +| `authentication_policy` | `AuthenticationPolicy` (crypto) | `ForceAuthentication`, `AllowAuthentication`, or `Unauthenticated` | | `host_keyring` | `Keyring` (crypto) | Host's signing and KEM keys | | `get_existing_user` | `Fn(u64) -> Pin> + Send>> + Send + Sync` (crypto) | Async lookup callback for login | | `complete_register` | `Fn(PublicKeyBundle) -> Pin + Send>> + Send + Sync` (crypto) | Async registration callback, returns new client ID | +### AuthenticationPolicy + +`ForceAuthentication` requires every client to complete the login/register handshake. `AllowAuthentication` accepts both authenticated and unauthenticated connections — unauthenticated clients get a random ID and `AuthState::Unauthenticated`. `Unauthenticated` rejects any client that tries to authenticate and is the default. + +```rust +use mtp::host::AuthenticationPolicy; + +// Force authentication (default was `require_authentication: true`): +let config = HostConfig::new(ip, port, cert, key) + .with_authentication(host_keyring, get_user, register); + +// Allow both authenticated and unauthenticated: +let config = HostConfig::new(ip, port, cert, key) + .with_allow_authentication(host_keyring, get_user, register); + +// Unauthenticated only (default): +let config = HostConfig::new(ip, port, cert, key); +``` + ### TLS The host requires a TLS certificate. For development, generate a self-signed @@ -82,6 +101,7 @@ pub struct MTPConnection { pub codec: VersionedCodec, pub sender: Sender, pub receiver: Receiver, + pub description: Option, #[cfg(feature = "crypto")] pub auth_state: AuthState, #[cfg(feature = "crypto")] @@ -95,6 +115,7 @@ pub struct MTPConnection { - `codec` -- a `VersionedCodec` scoped to the negotiated version (use for version-aware encode/decode) - `sender` / `receiver` -- for message I/O +- `description` -- optional client-provided label (e.g. `"phone"`, `"desktop"`) - `client_id` -- the authenticated client's ID - `client_public_key` -- the client's public key bundle (for signature verification of subsequent messages) @@ -130,7 +151,7 @@ let negotiated = registry.negotiate(&[Version(1, 0), Version(2, 0)]); ## Authentication Flow -When `require_authentication` is `true`, `accept()` runs a mutually-authenticated +When `authentication_policy` is `ForceAuthentication`, `accept()` runs a mutually-authenticated **challenge-response** handshake before returning the connection. The host issues a fresh, random `server_challenge` that the client must sign, which is what makes the client's proof unreplayable: a captured proof is bound to a one-time challenge diff --git a/example/client/src/main.rs b/example/client/src/main.rs index e940e0c..b44e05d 100644 --- a/example/client/src/main.rs +++ b/example/client/src/main.rs @@ -41,7 +41,9 @@ async fn main() -> Result<(), Box> { println!("Connecting to 127.0.0.1:8080 ..."); - let config = ClientConfig::new("https://127.0.0.1:8080").with_pinned_pem(cert_pem); + let config = ClientConfig::new("https://127.0.0.1:8080") + .with_pinned_pem(cert_pem) + .with_description("MTP example client"); let server_bundle = host_public_key.clone(); let (conn, keyring) = diff --git a/example/server/src/main.rs b/example/server/src/main.rs index a9854a2..3de4a97 100644 --- a/example/server/src/main.rs +++ b/example/server/src/main.rs @@ -98,8 +98,12 @@ async fn main() -> Result<(), Box> { println!("Server listening on {}", host.local_addr()); while let Some(conn) = host.accept().await? { + let desc = conn + .description + .as_deref() + .unwrap_or("(no description)"); println!( - "\n--- New authenticated connection (version {}) ---", + "\n--- New connection (version {}, description: {desc}) ---", conn.version ); println!("Client ID: {}", conn.client_id);