diff --git a/host/src/config.rs b/host/src/config.rs index 0cef52a..99d62a2 100644 --- a/host/src/config.rs +++ b/host/src/config.rs @@ -251,6 +251,8 @@ pub struct HostConfig { #[cfg(feature = "crypto")] pub authentication_policy: AuthenticationPolicy, #[cfg(feature = "crypto")] + authentication_policy_explicit: bool, + #[cfg(feature = "crypto")] pub auth_timeout: Duration, #[cfg(feature = "crypto")] pub require_pq: bool, @@ -288,6 +290,8 @@ impl HostConfig { #[cfg(feature = "crypto")] authentication_policy: AuthenticationPolicy::Unauthenticated, #[cfg(feature = "crypto")] + authentication_policy_explicit: false, + #[cfg(feature = "crypto")] auth_timeout: Duration::from_secs(30), #[cfg(feature = "crypto")] require_pq: true, @@ -341,7 +345,9 @@ impl HostConfig { get_existing_client: GetExistingClient, complete_register: CompleteRegister, ) -> Self { - self.authentication_policy = AuthenticationPolicy::ForceAuthentication; + if !self.authentication_policy_explicit { + self.authentication_policy = AuthenticationPolicy::ForceAuthentication; + } self.host_keyring = host_keyring; self.get_existing_client = Box::new(get_existing_client); self.complete_register = Box::new(complete_register); @@ -351,6 +357,7 @@ impl HostConfig { #[cfg(feature = "crypto")] pub fn with_authentication_policy(mut self, policy: AuthenticationPolicy) -> Self { self.authentication_policy = policy; + self.authentication_policy_explicit = true; self } @@ -441,4 +448,78 @@ mod tests { .expect("repeated registration decision") ); } + + fn test_keyring() -> mtp_crypto::Keyring { + mtp_crypto::Keyring::new( + mtp_crypto::KemPublicKey::new(Vec::new()), + mtp_crypto::KemPrivateKey::new(Vec::new()), + mtp_crypto::SignaturePqPublicKey::new(Vec::new()), + mtp_crypto::SignaturePqPrivateKey::new(Vec::new()), + mtp_crypto::SignaturePublicKey::new(Vec::new()), + mtp_crypto::SignaturePrivateKey::new(Vec::new()), + ) + } + + fn test_get_existing_client() -> GetExistingClient { + Box::new(|_, _| Box::pin(async { None })) + } + + fn test_complete_register() -> CompleteRegister { + Box::new(|_, _| Box::pin(async { 1 })) + } + + fn test_config() -> HostConfig { + HostConfig::new( + IpAddr::V4(std::net::Ipv4Addr::LOCALHOST), + 4433, + Vec::new(), + Vec::new(), + ) + } + + #[test] + fn with_authentication_defaults_to_force_authentication() { + let config = test_config().with_authentication( + test_keyring(), + test_get_existing_client(), + test_complete_register(), + ); + + assert_eq!( + config.authentication_policy, + AuthenticationPolicy::ForceAuthentication + ); + } + + #[test] + fn explicit_authentication_policy_before_with_authentication_is_preserved() { + let config = test_config() + .with_authentication_policy(AuthenticationPolicy::AllowAuthentication) + .with_authentication( + test_keyring(), + test_get_existing_client(), + test_complete_register(), + ); + + assert_eq!( + config.authentication_policy, + AuthenticationPolicy::AllowAuthentication + ); + } + + #[test] + fn explicit_authentication_policy_after_with_authentication_is_preserved() { + let config = test_config() + .with_authentication( + test_keyring(), + test_get_existing_client(), + test_complete_register(), + ) + .with_authentication_policy(AuthenticationPolicy::AllowAuthentication); + + assert_eq!( + config.authentication_policy, + AuthenticationPolicy::AllowAuthentication + ); + } } diff --git a/host/src/engine.rs b/host/src/engine.rs old mode 100644 new mode 100755 index d1ebd19..08ca87f --- a/host/src/engine.rs +++ b/host/src/engine.rs @@ -309,6 +309,12 @@ impl HandshakeEngine { ) || registration || first_msg.get_data(DataType::PublicKeys).is_some() || claimed_client_id.is_some_and(|client_id| client_id != 0); + tracing::info!( + claimed_client_id = ?claimed_client_id, + registration, + authentication_requested, + "classified MTP opening authentication mode" + ); if authentication_requested { let attempt = crate::config::AuthenticationAttempt { peer_network_identity: _authentication_context.peer_network_identity.clone(), @@ -566,6 +572,7 @@ impl HandshakeEngine { } // Unknown or zero ID: fall back to guest + tracing::info!("allocating MTP guest identity"); let guest_id_lease = match self.assign_guest_id().await { Ok(lease) => lease, Err(error) => { @@ -574,6 +581,7 @@ impl HandshakeEngine { } }; let guest_id = guest_id_lease.id; + tracing::info!(guest_id, "allocated MTP guest identity"); send_accepted_generic(sender, &negotiated, tm, Some(guest_id)) .await .map_err(AcceptError::Send)?; diff --git a/transport/src/generic_connection.rs b/transport/src/generic_connection.rs index 830fb40..a7a32de 100644 --- a/transport/src/generic_connection.rs +++ b/transport/src/generic_connection.rs @@ -9,7 +9,7 @@ use crate::{ connection::{DecodeRejectionCounters, RuntimePolicy, classify_decode_error}, framing::{RetryClassifier, write_frame}, }; -use mtp_codec::{CommunicationValue, DecodeLimits, TypeMap}; +use mtp_codec::{CommunicationValue, DataType, DecodeLimits, TypeMap}; use mtp_common::CommunicationError; use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; @@ -67,6 +67,15 @@ impl GenericSender { if self.connection.close_reason().is_some() { return Err(CommunicationError::StreamClosed); } + if let Some(version) = value.get_str(DataType::Version) { + tracing::debug!( + message_type = ?value.get_type(), + version, + connected = ?value.get_data(DataType::Connected), + client_id = ?value.get_data(DataType::Id), + "sending MTP handshake response frame" + ); + } match self.policy.send_mode { crate::SendMode::SingleStreamPerMessage => { let mut stream = self.open().await?; diff --git a/wasm/src/client/authentication.rs b/wasm/src/client/authentication.rs index df7989e..73bfc9f 100644 --- a/wasm/src/client/authentication.rs +++ b/wasm/src/client/authentication.rs @@ -99,10 +99,19 @@ impl WasmClient { return Err(js_error(message)); } + let missing_version = || { + js_error(format!( + "host omitted a valid negotiated protocol version (response_type={:?}, connected={:?}, frame_len={})", + outcome.get_type(), + outcome.get_data(DataType::Connected), + outcome_bytes.len(), + )) + }; + let negotiated_version = match outcome.get_data(DataType::Version) { Some(DataValue::Str(version)) => mtp_codec::Version::parse(version) - .ok_or_else(|| js_error("host omitted a valid negotiated protocol version"))?, - _ => return Err(js_error("host omitted a valid negotiated protocol version")), + .ok_or_else(|| missing_version())?, + _ => return Err(missing_version()), }; if negotiated_version != PROTOCOL_VERSION { return Err(js_error(