This commit is contained in:
parent
56903049b6
commit
5bfcccc056
5 changed files with 36 additions and 5 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<String>` | 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<String>,
|
||||
#[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
|
||||
|
|
|
|||
|
|
@ -48,11 +48,30 @@ let config = HostConfig::new(
|
|||
| `port` | `u16` | Listen port |
|
||||
| `tls_fullchain` | `Vec<u8>` | PEM-encoded TLS certificate chain |
|
||||
| `tls_key` | `Vec<u8>` | 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<Box<dyn Future<Output = Option<PublicKeyBundle>> + Send>> + Send + Sync` (crypto) | Async lookup callback for login |
|
||||
| `complete_register` | `Fn(PublicKeyBundle) -> Pin<Box<dyn Future<Output = u64> + 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<String>,
|
||||
#[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
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
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) =
|
||||
|
|
|
|||
|
|
@ -98,8 +98,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue