[Add] AuthenticationPolicy on host
This commit is contained in:
parent
c4a52c61b6
commit
56903049b6
4 changed files with 349 additions and 102 deletions
|
|
@ -22,6 +22,7 @@ pub struct ClientConfig {
|
|||
pub url: String,
|
||||
pub tls: ClientTlsConfig,
|
||||
pub client_id: u64,
|
||||
pub description: Option<String>,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub auth_timeout: Duration,
|
||||
}
|
||||
|
|
@ -38,6 +39,7 @@ impl ClientConfig {
|
|||
url: url.into(),
|
||||
tls: ClientTlsConfig::SystemRoots,
|
||||
client_id: 0,
|
||||
description: None,
|
||||
#[cfg(feature = "crypto")]
|
||||
auth_timeout: Duration::from_secs(30),
|
||||
}
|
||||
|
|
@ -57,6 +59,11 @@ impl ClientConfig {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_description(mut self, description: impl Into<String>) -> Self {
|
||||
self.description = Some(description.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn with_auth_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.auth_timeout = timeout;
|
||||
|
|
@ -76,6 +83,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")]
|
||||
|
|
@ -150,12 +158,15 @@ impl MTPClient {
|
|||
mtp_transport::connect(&config.url, config.server_cert(), Policy::default()).await?;
|
||||
|
||||
let version_str = format!("{}", PROTOCOL_VERSION);
|
||||
let ident = CommunicationValue::new(mtp_codec::CommunicationType::Identification)
|
||||
let mut ident = CommunicationValue::new(mtp_codec::CommunicationType::Identification)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version_str))
|
||||
.add_typed_default(
|
||||
DataType::Id,
|
||||
DataValue::UnsignedNumber(config.client_id.into()),
|
||||
);
|
||||
if let Some(desc) = &config.description {
|
||||
ident = ident.add_typed_default(DataType::Description, DataValue::Str(desc.clone()));
|
||||
}
|
||||
|
||||
sender.send(&ident).await?;
|
||||
|
||||
|
|
@ -163,6 +174,7 @@ impl MTPClient {
|
|||
version: PROTOCOL_VERSION,
|
||||
sender,
|
||||
receiver,
|
||||
description: config.description,
|
||||
#[cfg(feature = "crypto")]
|
||||
auth_state: AuthState::Unauthenticated,
|
||||
#[cfg(feature = "crypto")]
|
||||
|
|
@ -379,12 +391,15 @@ impl MTPClient {
|
|||
let version_str = format!("{}", PROTOCOL_VERSION);
|
||||
|
||||
// 1. Send the unsigned Identification hello (version + claimed id).
|
||||
let ident = CommunicationValue::new(mtp_codec::CommunicationType::Identification)
|
||||
let mut ident = CommunicationValue::new(mtp_codec::CommunicationType::Identification)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version_str.clone()))
|
||||
.add_typed_default(
|
||||
DataType::Id,
|
||||
DataValue::UnsignedNumber(config.client_id as u128),
|
||||
);
|
||||
if let Some(desc) = &config.description {
|
||||
ident = ident.add_typed_default(DataType::Description, DataValue::Str(desc.clone()));
|
||||
}
|
||||
if let Err(e) = sender.send(&ident).await {
|
||||
sender.close();
|
||||
return Err(e);
|
||||
|
|
@ -464,6 +479,7 @@ impl MTPClient {
|
|||
version: PROTOCOL_VERSION,
|
||||
sender,
|
||||
receiver,
|
||||
description: config.description,
|
||||
auth_state: AuthState::Authenticated,
|
||||
client_id: config.client_id,
|
||||
})
|
||||
|
|
@ -519,9 +535,12 @@ impl MTPClient {
|
|||
let pk_bytes = pk_bundle.as_bytes();
|
||||
|
||||
// 1. Send the unsigned Register hello (version + public-key bundle).
|
||||
let register = CommunicationValue::new(mtp_codec::CommunicationType::Register)
|
||||
let mut register = CommunicationValue::new(mtp_codec::CommunicationType::Register)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version_str.clone()))
|
||||
.add_typed_default(DataType::PublicKeys, DataValue::Bytes(pk_bytes.clone()));
|
||||
if let Some(desc) = &config.description {
|
||||
register = register.add_typed_default(DataType::Description, DataValue::Str(desc.clone()));
|
||||
}
|
||||
if let Err(e) = sender.send(®ister).await {
|
||||
sender.close();
|
||||
return Err(e);
|
||||
|
|
@ -607,6 +626,7 @@ impl MTPClient {
|
|||
version: PROTOCOL_VERSION,
|
||||
sender,
|
||||
receiver,
|
||||
description: config.description,
|
||||
auth_state: AuthState::Authenticated,
|
||||
client_id: assigned_id,
|
||||
})
|
||||
|
|
|
|||
388
host/src/lib.rs
388
host/src/lib.rs
|
|
@ -29,6 +29,14 @@ type CompleteRegister = Box<
|
|||
+ Sync,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AuthenticationPolicy {
|
||||
ForceAuthentication,
|
||||
AllowAuthentication,
|
||||
Unauthenticated,
|
||||
}
|
||||
|
||||
/* Host configuration. */
|
||||
pub struct HostConfig {
|
||||
pub ip: IpAddr,
|
||||
|
|
@ -37,7 +45,7 @@ pub struct HostConfig {
|
|||
pub tls_key: Vec<u8>,
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub require_authentication: bool,
|
||||
pub authentication_policy: AuthenticationPolicy,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub auth_timeout: Duration,
|
||||
#[cfg(feature = "crypto")]
|
||||
|
|
@ -56,7 +64,7 @@ impl HostConfig {
|
|||
tls_fullchain,
|
||||
tls_key,
|
||||
#[cfg(feature = "crypto")]
|
||||
require_authentication: false,
|
||||
authentication_policy: AuthenticationPolicy::Unauthenticated,
|
||||
#[cfg(feature = "crypto")]
|
||||
auth_timeout: Duration::from_secs(30),
|
||||
#[cfg(feature = "crypto")]
|
||||
|
|
@ -93,13 +101,19 @@ impl HostConfig {
|
|||
+ Sync
|
||||
+ 'static,
|
||||
) -> Self {
|
||||
self.require_authentication = true;
|
||||
self.authentication_policy = AuthenticationPolicy::ForceAuthentication;
|
||||
self.host_keyring = host_keyring;
|
||||
self.get_existing_user = Box::new(get_existing_user);
|
||||
self.complete_register = Box::new(complete_register);
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn with_authentication_policy(mut self, policy: AuthenticationPolicy) -> Self {
|
||||
self.authentication_policy = policy;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn with_auth_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.auth_timeout = timeout;
|
||||
|
|
@ -152,6 +166,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")]
|
||||
|
|
@ -203,49 +218,94 @@ impl MTPHost {
|
|||
};
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
if self.config.require_authentication {
|
||||
let timeout = self.config.auth_timeout;
|
||||
return match tokio::time::timeout(timeout, self.accept_authenticated(sender, receiver))
|
||||
match self.config.authentication_policy {
|
||||
AuthenticationPolicy::ForceAuthentication => {
|
||||
let timeout = self.config.auth_timeout;
|
||||
return match tokio::time::timeout(
|
||||
timeout,
|
||||
self.accept_authenticated(sender, receiver),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(result) => result,
|
||||
Err(_) => Err(AcceptError::AuthenticationTimedOut),
|
||||
};
|
||||
{
|
||||
Ok(result) => result,
|
||||
Err(_) => Err(AcceptError::AuthenticationTimedOut),
|
||||
};
|
||||
}
|
||||
AuthenticationPolicy::AllowAuthentication => {
|
||||
return self.accept_allow_auth(sender, receiver).await;
|
||||
}
|
||||
AuthenticationPolicy::Unauthenticated => {
|
||||
let first_msg = match receiver.receive().await {
|
||||
Ok(m) => m,
|
||||
Err(e) => return Err(AcceptError::Receive(e)),
|
||||
};
|
||||
if first_msg.get_type()
|
||||
== mtp_codec::CommunicationType::Register.to_id(&mtp_codec::TypeMap::latest())
|
||||
{
|
||||
sender.close();
|
||||
return Err(AcceptError::AuthenticationFailed(
|
||||
"authentication not allowed on this host".into(),
|
||||
));
|
||||
}
|
||||
let client_version = match extract_version(&first_msg) {
|
||||
Some(v) => v,
|
||||
None => return Err(AcceptError::MissingVersion),
|
||||
};
|
||||
let negotiated = match self.registry.negotiate(std::slice::from_ref(&client_version))
|
||||
{
|
||||
Some(v) => v,
|
||||
None => return Err(AcceptError::UnsupportedVersion(client_version)),
|
||||
};
|
||||
let codec = VersionedCodec::new(self.registry.clone());
|
||||
let description = match first_msg.get_data(DataType::Description) {
|
||||
DataValue::Str(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
};
|
||||
Ok(Some(MTPConnection {
|
||||
version: negotiated,
|
||||
codec,
|
||||
sender,
|
||||
receiver,
|
||||
description,
|
||||
#[cfg(feature = "crypto")]
|
||||
auth_state: AuthState::Unauthenticated,
|
||||
#[cfg(feature = "crypto")]
|
||||
client_id: rand::random(),
|
||||
#[cfg(feature = "crypto")]
|
||||
client_public_key: None,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// Read the first message (always encoded with reserved types).
|
||||
let first_msg = match receiver.receive().await {
|
||||
Ok(m) => m,
|
||||
Err(e) => return Err(AcceptError::Receive(e)),
|
||||
};
|
||||
|
||||
let client_version = match extract_version(&first_msg) {
|
||||
Some(v) => v,
|
||||
None => return Err(AcceptError::MissingVersion),
|
||||
};
|
||||
|
||||
let negotiated = match self
|
||||
.registry
|
||||
.negotiate(std::slice::from_ref(&client_version))
|
||||
// Non-crypto fallback: no authentication feature, just read and respond.
|
||||
#[cfg(not(feature = "crypto"))]
|
||||
{
|
||||
Some(v) => v,
|
||||
None => return Err(AcceptError::UnsupportedVersion(client_version)),
|
||||
};
|
||||
|
||||
let codec = VersionedCodec::new(self.registry.clone());
|
||||
|
||||
Ok(Some(MTPConnection {
|
||||
version: negotiated,
|
||||
codec,
|
||||
sender,
|
||||
receiver,
|
||||
#[cfg(feature = "crypto")]
|
||||
auth_state: AuthState::Unauthenticated,
|
||||
#[cfg(feature = "crypto")]
|
||||
client_id: 0,
|
||||
#[cfg(feature = "crypto")]
|
||||
client_public_key: None,
|
||||
}))
|
||||
let first_msg = match receiver.receive().await {
|
||||
Ok(m) => m,
|
||||
Err(e) => return Err(AcceptError::Receive(e)),
|
||||
};
|
||||
let client_version = match extract_version(&first_msg) {
|
||||
Some(v) => v,
|
||||
None => return Err(AcceptError::MissingVersion),
|
||||
};
|
||||
let negotiated = match self.registry.negotiate(std::slice::from_ref(&client_version))
|
||||
{
|
||||
Some(v) => v,
|
||||
None => return Err(AcceptError::UnsupportedVersion(client_version)),
|
||||
};
|
||||
let codec = VersionedCodec::new(self.registry.clone());
|
||||
let description = match first_msg.get_data(DataType::Description) {
|
||||
DataValue::Str(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
};
|
||||
return Ok(Some(MTPConnection {
|
||||
version: negotiated,
|
||||
codec,
|
||||
sender,
|
||||
receiver,
|
||||
description,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_addr(&self) -> std::net::SocketAddr {
|
||||
|
|
@ -257,6 +317,18 @@ impl MTPHost {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
enum Flow {
|
||||
Login {
|
||||
id: u64,
|
||||
bundle: mtp_crypto::PublicKeyBundle,
|
||||
},
|
||||
Register {
|
||||
bundle: mtp_crypto::PublicKeyBundle,
|
||||
pk_bytes: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
impl MTPHost {
|
||||
/*
|
||||
|
|
@ -278,51 +350,9 @@ impl MTPHost {
|
|||
sender: Sender,
|
||||
receiver: Receiver,
|
||||
) -> Result<Option<MTPConnection>, AcceptError> {
|
||||
use mtp_crypto::{
|
||||
Ed25519Signer, MlDsaSigner, PublicKeyBundle, SignatureScheme, auth, verify_ed25519,
|
||||
verify_ml_dsa,
|
||||
};
|
||||
|
||||
// Flow-specific state resolved from the client's opening hello.
|
||||
enum Flow {
|
||||
Login {
|
||||
id: u64,
|
||||
bundle: PublicKeyBundle,
|
||||
},
|
||||
Register {
|
||||
bundle: PublicKeyBundle,
|
||||
pk_bytes: Vec<u8>,
|
||||
},
|
||||
}
|
||||
use mtp_crypto::PublicKeyBundle;
|
||||
|
||||
let tm = mtp_codec::TypeMap::latest();
|
||||
let pq_enabled = !self
|
||||
.config
|
||||
.host_keyring
|
||||
.sig_pq_secret_key
|
||||
.as_bytes()
|
||||
.is_empty();
|
||||
|
||||
// Sign `payload` with the host keys (Ed25519 always, ML-DSA when configured).
|
||||
let host_sign = |payload: &[u8]| -> Result<(Vec<u8>, Vec<u8>), AcceptError> {
|
||||
let signer = Ed25519Signer::new(&self.config.host_keyring.sig_cl_secret_key)
|
||||
.map_err(|e| AcceptError::AuthenticationFailed(e.to_string()))?;
|
||||
let sig = signer
|
||||
.sign(payload)
|
||||
.map_err(|e| AcceptError::AuthenticationFailed(e.to_string()))?;
|
||||
let pq_sig = if pq_enabled {
|
||||
let pq = MlDsaSigner::new(
|
||||
&self.config.host_keyring.sig_pq_secret_key,
|
||||
&self.config.host_keyring.sig_pq_public_key,
|
||||
)
|
||||
.map_err(|e| AcceptError::AuthenticationFailed(e.to_string()))?;
|
||||
pq.sign(payload)
|
||||
.map_err(|e| AcceptError::AuthenticationFailed(e.to_string()))?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Ok((sig, pq_sig))
|
||||
};
|
||||
|
||||
// ===== Step 1: receive the client's unsigned hello =====
|
||||
let hello = match receiver.receive().await {
|
||||
|
|
@ -347,6 +377,11 @@ impl MTPHost {
|
|||
}
|
||||
};
|
||||
|
||||
let description = match hello.get_data(DataType::Description) {
|
||||
DataValue::Str(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let (flow, response_type) =
|
||||
if hello.get_type() == mtp_codec::CommunicationType::Identification.to_id(&tm) {
|
||||
let cid = match hello.get_data(DataType::Id) {
|
||||
|
|
@ -400,6 +435,57 @@ impl MTPHost {
|
|||
));
|
||||
};
|
||||
|
||||
self.complete_auth_handshake(sender, receiver, flow, response_type, &version_str, client_version, description).await
|
||||
}
|
||||
|
||||
/*
|
||||
* Steps 2-4 of the authenticated handshake, shared by both ForceAuthentication
|
||||
* and AllowAuthentication. Takes the already-parsed hello (step 1) via `flow`,
|
||||
* `version_str`, and `client_version`.
|
||||
*/
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn complete_auth_handshake(
|
||||
&self,
|
||||
sender: Sender,
|
||||
receiver: Receiver,
|
||||
flow: Flow,
|
||||
response_type: mtp_codec::CommunicationType,
|
||||
version_str: &str,
|
||||
client_version: Version,
|
||||
description: Option<String>,
|
||||
) -> Result<Option<MTPConnection>, AcceptError> {
|
||||
use mtp_crypto::{
|
||||
auth, verify_ed25519, verify_ml_dsa, Ed25519Signer, MlDsaSigner, SignatureScheme,
|
||||
};
|
||||
|
||||
let tm = mtp_codec::TypeMap::latest();
|
||||
let pq_enabled = !self
|
||||
.config
|
||||
.host_keyring
|
||||
.sig_pq_secret_key
|
||||
.as_bytes()
|
||||
.is_empty();
|
||||
|
||||
let host_sign = |payload: &[u8]| -> Result<(Vec<u8>, Vec<u8>), AcceptError> {
|
||||
let signer = Ed25519Signer::new(&self.config.host_keyring.sig_cl_secret_key)
|
||||
.map_err(|e| AcceptError::AuthenticationFailed(e.to_string()))?;
|
||||
let sig = signer
|
||||
.sign(payload)
|
||||
.map_err(|e| AcceptError::AuthenticationFailed(e.to_string()))?;
|
||||
let pq_sig = if pq_enabled {
|
||||
let pq = MlDsaSigner::new(
|
||||
&self.config.host_keyring.sig_pq_secret_key,
|
||||
&self.config.host_keyring.sig_pq_public_key,
|
||||
)
|
||||
.map_err(|e| AcceptError::AuthenticationFailed(e.to_string()))?;
|
||||
pq.sign(payload)
|
||||
.map_err(|e| AcceptError::AuthenticationFailed(e.to_string()))?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Ok((sig, pq_sig))
|
||||
};
|
||||
|
||||
let challenge_id = match &flow {
|
||||
Flow::Login { id, .. } => *id,
|
||||
Flow::Register { .. } => 0,
|
||||
|
|
@ -464,18 +550,13 @@ impl MTPHost {
|
|||
|
||||
let (proof_payload, bundle) = match &flow {
|
||||
Flow::Login { id, bundle } => (
|
||||
auth::login_proof_payload(&version_str, *id, server_challenge, client_nonce),
|
||||
auth::login_proof_payload(version_str, *id, server_challenge, client_nonce),
|
||||
bundle,
|
||||
),
|
||||
Flow::Register {
|
||||
bundle, pk_bytes, ..
|
||||
} => (
|
||||
auth::register_proof_payload(
|
||||
&version_str,
|
||||
pk_bytes,
|
||||
server_challenge,
|
||||
client_nonce,
|
||||
),
|
||||
auth::register_proof_payload(version_str, pk_bytes, server_challenge, client_nonce),
|
||||
bundle,
|
||||
),
|
||||
};
|
||||
|
|
@ -551,11 +632,134 @@ impl MTPHost {
|
|||
codec,
|
||||
sender,
|
||||
receiver,
|
||||
description,
|
||||
auth_state: AuthState::Authenticated,
|
||||
client_id: assigned_id,
|
||||
client_public_key: Some(client_bundle),
|
||||
}))
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow-authentication accept: clients may connect with or without
|
||||
* authentication. Register messages always trigger the auth handshake.
|
||||
* Identification with a known client-id triggers login; otherwise the
|
||||
* client is treated as unauthenticated with a random id.
|
||||
*/
|
||||
async fn accept_allow_auth(
|
||||
&mut self,
|
||||
sender: Sender,
|
||||
receiver: Receiver,
|
||||
) -> Result<Option<MTPConnection>, AcceptError> {
|
||||
use mtp_crypto::PublicKeyBundle;
|
||||
|
||||
let tm = mtp_codec::TypeMap::latest();
|
||||
|
||||
let hello = match receiver.receive().await {
|
||||
Ok(m) => m,
|
||||
Err(e) => return Err(AcceptError::Receive(e)),
|
||||
};
|
||||
|
||||
let version_str = match hello.get_data(DataType::Version) {
|
||||
DataValue::Str(s) => s.clone(),
|
||||
_ => {
|
||||
sender.close();
|
||||
return Err(AcceptError::MissingVersion);
|
||||
}
|
||||
};
|
||||
let client_version = match Version::parse(&version_str) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
sender.close();
|
||||
return Err(AcceptError::MissingVersion);
|
||||
}
|
||||
};
|
||||
|
||||
let description = match hello.get_data(DataType::Description) {
|
||||
DataValue::Str(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Register → authenticated registration.
|
||||
if hello.get_type() == mtp_codec::CommunicationType::Register.to_id(&tm) {
|
||||
let bundle = match hello.get_data(DataType::PublicKeys) {
|
||||
DataValue::Bytes(b) => PublicKeyBundle::from_bytes(b).map_err(|_| {
|
||||
sender.close();
|
||||
AcceptError::AuthenticationFailed("invalid public key bundle".into())
|
||||
})?,
|
||||
_ => {
|
||||
sender.close();
|
||||
return Err(AcceptError::AuthenticationFailed("missing public keys".into()));
|
||||
}
|
||||
};
|
||||
let pk_bytes = bundle.as_bytes();
|
||||
return self
|
||||
.complete_auth_handshake(
|
||||
sender,
|
||||
receiver,
|
||||
Flow::Register {
|
||||
bundle,
|
||||
pk_bytes,
|
||||
},
|
||||
mtp_codec::CommunicationType::RegisterResponse,
|
||||
&version_str,
|
||||
client_version,
|
||||
description,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Identification with a known client-id → login.
|
||||
if hello.get_type() == mtp_codec::CommunicationType::Identification.to_id(&tm) {
|
||||
let cid = match hello.get_data(DataType::Id) {
|
||||
DataValue::UnsignedNumber(n) => *n as u64,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
if cid > 0
|
||||
&& let Some(bundle) = (self.config.get_existing_user)(cid).await
|
||||
{
|
||||
return self
|
||||
.complete_auth_handshake(
|
||||
sender,
|
||||
receiver,
|
||||
Flow::Login {
|
||||
id: cid,
|
||||
bundle,
|
||||
},
|
||||
mtp_codec::CommunicationType::IdentificationResponse,
|
||||
&version_str,
|
||||
client_version,
|
||||
description,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Unknown (or zero) client id → unauthenticated connection.
|
||||
let negotiated = match self
|
||||
.registry
|
||||
.negotiate(std::slice::from_ref(&client_version))
|
||||
{
|
||||
Some(v) => v,
|
||||
None => return Err(AcceptError::UnsupportedVersion(client_version)),
|
||||
};
|
||||
let codec = VersionedCodec::new(self.registry.clone());
|
||||
return Ok(Some(MTPConnection {
|
||||
version: negotiated,
|
||||
codec,
|
||||
sender,
|
||||
receiver,
|
||||
description,
|
||||
auth_state: AuthState::Unauthenticated,
|
||||
client_id: rand::random(),
|
||||
client_public_key: None,
|
||||
}));
|
||||
}
|
||||
|
||||
sender.close();
|
||||
Err(AcceptError::AuthenticationFailed(
|
||||
"unexpected message type".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -302,12 +302,15 @@ impl WasmClient {
|
|||
.await?;
|
||||
|
||||
let version_str = format!("{}", PROTOCOL_VERSION);
|
||||
let ident = CommunicationValue::new(CommunicationType::Identification)
|
||||
let mut ident = CommunicationValue::new(CommunicationType::Identification)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version_str))
|
||||
.add_typed_default(
|
||||
DataType::Id,
|
||||
DataValue::UnsignedNumber(config.client_id as u128),
|
||||
);
|
||||
if let Some(desc) = &config.description {
|
||||
ident = ident.add_typed_default(DataType::Description, DataValue::Str(desc.clone()));
|
||||
}
|
||||
let ident_bytes = ident
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
|
|
@ -351,12 +354,16 @@ impl WasmClient {
|
|||
.await?;
|
||||
|
||||
// 1. Send the unsigned Identification hello.
|
||||
let hello = CommunicationValue::new(CommunicationType::Identification)
|
||||
let mut hello = CommunicationValue::new(CommunicationType::Identification)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version_str.clone()))
|
||||
.add_typed_default(DataType::Id, DataValue::UnsignedNumber(client_id as u128))
|
||||
.add_typed_default(DataType::Id, DataValue::UnsignedNumber(client_id as u128));
|
||||
if let Some(desc) = &config.description {
|
||||
hello = hello.add_typed_default(DataType::Description, DataValue::Str(desc.clone()));
|
||||
}
|
||||
let hello_bytes = hello
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
transport.send_frame(&hello).await?;
|
||||
transport.send_frame(&hello_bytes).await?;
|
||||
|
||||
// 2. Receive and verify the host's challenge.
|
||||
let server_challenge = self
|
||||
|
|
@ -463,12 +470,16 @@ impl WasmClient {
|
|||
.await?;
|
||||
|
||||
// 1. Send the unsigned Register hello (version + public-key bundle).
|
||||
let hello = CommunicationValue::new(CommunicationType::Register)
|
||||
let mut hello = CommunicationValue::new(CommunicationType::Register)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version_str.clone()))
|
||||
.add_typed_default(DataType::PublicKeys, DataValue::Bytes(pk_bytes.clone()))
|
||||
.add_typed_default(DataType::PublicKeys, DataValue::Bytes(pk_bytes.clone()));
|
||||
if let Some(desc) = &config.description {
|
||||
hello = hello.add_typed_default(DataType::Description, DataValue::Str(desc.clone()));
|
||||
}
|
||||
let hello_bytes = hello
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
transport.send_frame(&hello).await?;
|
||||
transport.send_frame(&hello_bytes).await?;
|
||||
|
||||
// 2. Receive and verify the host's challenge (register binds id = 0).
|
||||
let server_challenge = self
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ pub struct ConnectionConfig {
|
|||
pub(crate) server_certificate_hashes: Option<Vec<String>>,
|
||||
pub(crate) client_id: u64,
|
||||
pub(crate) max_message_size: u32,
|
||||
pub(crate) description: Option<String>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
|
@ -17,6 +18,7 @@ impl ConnectionConfig {
|
|||
server_certificate_hashes: None,
|
||||
client_id: 0,
|
||||
max_message_size: 1_000_000_000,
|
||||
description: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,4 +51,14 @@ impl ConnectionConfig {
|
|||
pub fn max_message_size(&self) -> u32 {
|
||||
self.max_message_size
|
||||
}
|
||||
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_description(&mut self, description: String) {
|
||||
self.description = Some(description);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn description(&self) -> Option<String> {
|
||||
self.description.clone()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue