Host & Client force randomness on each other.
Updated Reserved entry order. Made DataType ID changes easier in future (this MAY NOT happen again once in use).
This commit is contained in:
parent
687e6f9642
commit
f4118f28ba
25 changed files with 1032 additions and 667 deletions
|
|
@ -36,23 +36,54 @@ fn unexpected_response_type_error(
|
|||
))
|
||||
}
|
||||
|
||||
/// Verify the host's authentication signature over the handshake response,
|
||||
/// mirroring the native client (`client/src/lib.rs`). The signed payload is
|
||||
/// `0x01 || id.to_be_bytes() || client_nonce.to_be_bytes() || host_nonce.to_be_bytes()`,
|
||||
/// where `id` is the client id for login and the host-assigned id for register.
|
||||
/// The Ed25519 signature is mandatory; the ML-DSA signature is verified only
|
||||
/// when the host included one.
|
||||
fn verify_host_signature(
|
||||
/*
|
||||
* Verify the host's signature over the challenge it issued (step 2), mirroring
|
||||
* the native client (`client/src/lib.rs`). `id` is the client id for a login or
|
||||
* `0` for a registration. The Ed25519 signature is mandatory; the ML-DSA
|
||||
* signature is verified only when the host included one.
|
||||
*/
|
||||
fn verify_host_challenge(
|
||||
challenge: &CommunicationValue,
|
||||
tm: &mtp_codec::TypeMap,
|
||||
host_pk: &mtp_crypto::PublicKeyBundle,
|
||||
id: u64,
|
||||
server_challenge: u128,
|
||||
) -> Result<(), JsValue> {
|
||||
let sig = match challenge.get_data(DataType::Signature.to_id(tm)) {
|
||||
DataValue::Bytes(b) => b.clone(),
|
||||
_ => return Err(js_error("missing host challenge signature")),
|
||||
};
|
||||
let pq_sig = match challenge.get_data(DataType::PqSignature.to_id(tm)) {
|
||||
DataValue::Bytes(b) => b.clone(),
|
||||
_ => vec![],
|
||||
};
|
||||
|
||||
let payload = mtp_crypto::auth::challenge_payload(id, server_challenge);
|
||||
mtp_crypto::verify_ed25519(&host_pk.sig_cl_public_key, &payload, &sig)
|
||||
.map_err(|_| js_error("host challenge signature invalid"))?;
|
||||
if !pq_sig.is_empty() {
|
||||
mtp_crypto::verify_ml_dsa(&host_pk.sig_pq_public_key, &payload, &pq_sig)
|
||||
.map_err(|_| js_error("host challenge PQ signature invalid"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the host's final confirmation (step 4): the echoed `client_nonce` and
|
||||
* the host signature over the handshake transcript. `id` is the client id for a
|
||||
* login and the host-assigned id for a register.
|
||||
*/
|
||||
fn verify_host_final(
|
||||
resp: &CommunicationValue,
|
||||
tm: &mtp_codec::TypeMap,
|
||||
host_pk: &mtp_crypto::PublicKeyBundle,
|
||||
id: u64,
|
||||
client_nonce: u128,
|
||||
server_challenge: u128,
|
||||
) -> Result<(), JsValue> {
|
||||
let host_new_nonce = match resp.get_data(DataType::Timestamp.to_id(tm)) {
|
||||
DataValue::UnsignedNumber(n) => *n,
|
||||
_ => return Err(js_error("missing host nonce")),
|
||||
};
|
||||
if *resp.get_data(DataType::ClientNonce.to_id(tm)) != DataValue::UnsignedNumber(client_nonce) {
|
||||
return Err(js_error("nonce mismatch"));
|
||||
}
|
||||
let host_sig = match resp.get_data(DataType::Signature.to_id(tm)) {
|
||||
DataValue::Bytes(b) => b.clone(),
|
||||
_ => return Err(js_error("missing host signature")),
|
||||
|
|
@ -62,12 +93,7 @@ fn verify_host_signature(
|
|||
_ => vec![],
|
||||
};
|
||||
|
||||
let mut payload = Vec::new();
|
||||
payload.push(0x01);
|
||||
payload.extend_from_slice(&id.to_be_bytes());
|
||||
payload.extend_from_slice(&client_nonce.to_be_bytes());
|
||||
payload.extend_from_slice(&host_new_nonce.to_be_bytes());
|
||||
|
||||
let payload = mtp_crypto::auth::host_final_payload(id, client_nonce, server_challenge);
|
||||
mtp_crypto::verify_ed25519(&host_pk.sig_cl_public_key, &payload, &host_sig)
|
||||
.map_err(|_| js_error("host signature invalid"))?;
|
||||
if !host_pq_sig.is_empty() {
|
||||
|
|
@ -219,26 +245,68 @@ impl WasmClient {
|
|||
let keyring = mtp_crypto::Keyring::from_bytes(keyring_bytes)
|
||||
.map_err(|e| js_error(&format!("invalid keyring: {}", e)))?;
|
||||
|
||||
let tm = mtp_codec::TypeMap::latest();
|
||||
let version_str = format!("{}", PROTOCOL_VERSION);
|
||||
|
||||
let transport =
|
||||
WasmTransport::connect(&config.url, config.server_certificate_hashes.clone()).await?;
|
||||
let inner = transport.inner().clone();
|
||||
|
||||
// 1. Send the unsigned Identification hello.
|
||||
let 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))
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
transport.send_frame(&hello).await?;
|
||||
|
||||
// 2. Receive and verify the host's challenge.
|
||||
let challenge_bytes = transport.read_one_frame().await?;
|
||||
let challenge = CommunicationValue::from_bytes(&challenge_bytes)
|
||||
.map_err(|e| js_error(&format!("parse challenge: {}", e)))?;
|
||||
let expected = CommunicationType::Challenge.to_id(&tm);
|
||||
if challenge.get_type() != expected {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(unexpected_response_type_error(
|
||||
"auth_connect challenge",
|
||||
expected,
|
||||
challenge.get_type(),
|
||||
&challenge_bytes,
|
||||
&challenge,
|
||||
));
|
||||
}
|
||||
let server_challenge = match challenge.get_data(DataType::ServerNonce.to_id(&tm)) {
|
||||
DataValue::UnsignedNumber(n) => *n,
|
||||
_ => {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(js_error("missing server challenge"));
|
||||
}
|
||||
};
|
||||
if let Err(e) =
|
||||
verify_host_challenge(&challenge, &tm, &host_pk, client_id, server_challenge)
|
||||
{
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// 3. Sign the host's challenge and send the proof.
|
||||
let mut nonce_bytes = [0u8; 16];
|
||||
getrandom::fill(&mut nonce_bytes).map_err(|_| js_error("rng failed"))?;
|
||||
let client_nonce = u128::from_be_bytes(nonce_bytes);
|
||||
|
||||
// Build signature payload: version || client_id || client_nonce
|
||||
let mut sig_payload = Vec::new();
|
||||
sig_payload.extend_from_slice(version_str.as_bytes());
|
||||
sig_payload.extend_from_slice(&client_id.to_be_bytes());
|
||||
sig_payload.extend_from_slice(&client_nonce.to_be_bytes());
|
||||
|
||||
let proof_payload = mtp_crypto::auth::login_proof_payload(
|
||||
&version_str,
|
||||
client_id,
|
||||
server_challenge,
|
||||
client_nonce,
|
||||
);
|
||||
let signer = mtp_crypto::Ed25519Signer::new(&keyring.sig_cl_secret_key)
|
||||
.map_err(|e| js_error(&format!("signer creation failed: {}", e)))?;
|
||||
let signature = signer
|
||||
.sign(&sig_payload)
|
||||
.sign(&proof_payload)
|
||||
.map_err(|e| js_error(&format!("signature failed: {}", e)))?;
|
||||
|
||||
let frame = CommunicationValue::new(CommunicationType::Identification)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version_str))
|
||||
.add_typed_default(DataType::Id, DataValue::UnsignedNumber(client_id as u128))
|
||||
let proof = CommunicationValue::new(CommunicationType::ChallengeResponse)
|
||||
.add_typed_default(
|
||||
DataType::ClientNonce,
|
||||
DataValue::UnsignedNumber(client_nonce),
|
||||
|
|
@ -246,18 +314,12 @@ impl WasmClient {
|
|||
.add_typed_default(DataType::Signature, DataValue::Bytes(signature))
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
transport.send_frame(&proof).await?;
|
||||
|
||||
let transport =
|
||||
WasmTransport::connect(&config.url, config.server_certificate_hashes.clone()).await?;
|
||||
let inner = transport.inner().clone();
|
||||
transport.send_frame(&frame).await?;
|
||||
|
||||
// Read and verify the host's IdentificationResponse
|
||||
// 4. Receive and verify the host's final confirmation.
|
||||
let response = transport.read_one_frame().await?;
|
||||
let resp_comm = CommunicationValue::from_bytes(&response)
|
||||
.map_err(|e| js_error(&format!("parse response: {}", e)))?;
|
||||
|
||||
let tm = mtp_codec::TypeMap::latest();
|
||||
let resp_type = resp_comm.get_type();
|
||||
let expected_type = CommunicationType::IdentificationResponse.to_id(&tm);
|
||||
if resp_type != expected_type {
|
||||
|
|
@ -276,15 +338,15 @@ impl WasmClient {
|
|||
return Err(js_error("host rejected authentication"));
|
||||
}
|
||||
|
||||
// Verify echoed nonce
|
||||
let echo_nonce = resp_comm.get_data(DataType::ClientNonce.to_id(&tm));
|
||||
if *echo_nonce != DataValue::UnsignedNumber(client_nonce) {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(js_error("nonce mismatch"));
|
||||
}
|
||||
|
||||
// Verify the host's signature over the handshake (login: id is client_id).
|
||||
if let Err(e) = verify_host_signature(&resp_comm, &tm, &host_pk, client_id, client_nonce) {
|
||||
// Verify echoed nonce + host signature (login: id is client_id).
|
||||
if let Err(e) = verify_host_final(
|
||||
&resp_comm,
|
||||
&tm,
|
||||
&host_pk,
|
||||
client_id,
|
||||
client_nonce,
|
||||
server_challenge,
|
||||
) {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(e);
|
||||
}
|
||||
|
|
@ -335,46 +397,80 @@ impl WasmClient {
|
|||
let keyring = mtp_crypto::Keyring::from_bytes(keyring_bytes)
|
||||
.map_err(|e| js_error(&format!("invalid keyring: {}", e)))?;
|
||||
|
||||
let tm = mtp_codec::TypeMap::latest();
|
||||
let version_str = format!("{}", PROTOCOL_VERSION);
|
||||
let mut nonce_bytes = [0u8; 16];
|
||||
getrandom::fill(&mut nonce_bytes).map_err(|_| js_error("rng failed"))?;
|
||||
let client_nonce = u128::from_be_bytes(nonce_bytes);
|
||||
|
||||
let pk_bytes = keyring.public_key_bundle().as_bytes();
|
||||
|
||||
// Build signature payload: version || client_nonce || pk_bytes
|
||||
let mut sig_payload = Vec::new();
|
||||
sig_payload.extend_from_slice(version_str.as_bytes());
|
||||
sig_payload.extend_from_slice(&client_nonce.to_be_bytes());
|
||||
sig_payload.extend_from_slice(&pk_bytes);
|
||||
|
||||
let signer = mtp_crypto::Ed25519Signer::new(&keyring.sig_cl_secret_key)
|
||||
.map_err(|e| js_error(&format!("signer creation failed: {}", e)))?;
|
||||
let signature = signer
|
||||
.sign(&sig_payload)
|
||||
.map_err(|e| js_error(&format!("signature failed: {}", e)))?;
|
||||
|
||||
let frame = CommunicationValue::new(CommunicationType::Register)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version_str))
|
||||
.add_typed_default(
|
||||
DataType::ClientNonce,
|
||||
DataValue::UnsignedNumber(client_nonce),
|
||||
)
|
||||
.add_typed_default(DataType::PublicKeys, DataValue::Bytes(pk_bytes))
|
||||
.add_typed_default(DataType::Signature, DataValue::Bytes(signature))
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
|
||||
let transport =
|
||||
WasmTransport::connect(&config.url, config.server_certificate_hashes.clone()).await?;
|
||||
let inner = transport.inner().clone();
|
||||
transport.send_frame(&frame).await?;
|
||||
|
||||
// 1. Send the unsigned Register hello (version + public-key bundle).
|
||||
let 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()))
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
transport.send_frame(&hello).await?;
|
||||
|
||||
// 2. Receive and verify the host's challenge (register binds id = 0).
|
||||
let challenge_bytes = transport.read_one_frame().await?;
|
||||
let challenge = CommunicationValue::from_bytes(&challenge_bytes)
|
||||
.map_err(|e| js_error(&format!("parse challenge: {}", e)))?;
|
||||
let expected = CommunicationType::Challenge.to_id(&tm);
|
||||
if challenge.get_type() != expected {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(unexpected_response_type_error(
|
||||
"auth_register challenge",
|
||||
expected,
|
||||
challenge.get_type(),
|
||||
&challenge_bytes,
|
||||
&challenge,
|
||||
));
|
||||
}
|
||||
let server_challenge = match challenge.get_data(DataType::ServerNonce.to_id(&tm)) {
|
||||
DataValue::UnsignedNumber(n) => *n,
|
||||
_ => {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(js_error("missing server challenge"));
|
||||
}
|
||||
};
|
||||
if let Err(e) = verify_host_challenge(&challenge, &tm, &host_pk, 0, server_challenge) {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// 3. Sign the host's challenge over the bundle and send the proof.
|
||||
let mut nonce_bytes = [0u8; 16];
|
||||
getrandom::fill(&mut nonce_bytes).map_err(|_| js_error("rng failed"))?;
|
||||
let client_nonce = u128::from_be_bytes(nonce_bytes);
|
||||
|
||||
let proof_payload = mtp_crypto::auth::register_proof_payload(
|
||||
&version_str,
|
||||
&pk_bytes,
|
||||
server_challenge,
|
||||
client_nonce,
|
||||
);
|
||||
let signer = mtp_crypto::Ed25519Signer::new(&keyring.sig_cl_secret_key)
|
||||
.map_err(|e| js_error(&format!("signer creation failed: {}", e)))?;
|
||||
let signature = signer
|
||||
.sign(&proof_payload)
|
||||
.map_err(|e| js_error(&format!("signature failed: {}", e)))?;
|
||||
|
||||
let proof = CommunicationValue::new(CommunicationType::ChallengeResponse)
|
||||
.add_typed_default(
|
||||
DataType::ClientNonce,
|
||||
DataValue::UnsignedNumber(client_nonce),
|
||||
)
|
||||
.add_typed_default(DataType::Signature, DataValue::Bytes(signature))
|
||||
.to_bytes()
|
||||
.map_err(|e| js_error(&format!("encode failed: {}", e)))?;
|
||||
transport.send_frame(&proof).await?;
|
||||
|
||||
// 4. Receive the host's final confirmation; extract + verify assigned id.
|
||||
let response = transport.read_one_frame().await?;
|
||||
let resp_comm = CommunicationValue::from_bytes(&response)
|
||||
.map_err(|e| js_error(&format!("parse response: {}", e)))?;
|
||||
|
||||
let tm = mtp_codec::TypeMap::latest();
|
||||
let resp_type = resp_comm.get_type();
|
||||
let expected_type = CommunicationType::RegisterResponse.to_id(&tm);
|
||||
if resp_type != expected_type {
|
||||
|
|
@ -393,12 +489,6 @@ impl WasmClient {
|
|||
return Err(js_error("host rejected registration"));
|
||||
}
|
||||
|
||||
let echo = resp_comm.get_data(DataType::ClientNonce.to_id(&tm));
|
||||
if *echo != DataValue::UnsignedNumber(client_nonce) {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(js_error("nonce mismatch"));
|
||||
}
|
||||
|
||||
let assigned_id = match resp_comm.get_data(DataType::Id.to_id(&tm)) {
|
||||
DataValue::UnsignedNumber(n) => *n as u64,
|
||||
_ => {
|
||||
|
|
@ -407,10 +497,15 @@ impl WasmClient {
|
|||
}
|
||||
};
|
||||
|
||||
// Verify the host's signature over the handshake (register: id is the
|
||||
// host-assigned id).
|
||||
if let Err(e) = verify_host_signature(&resp_comm, &tm, &host_pk, assigned_id, client_nonce)
|
||||
{
|
||||
// Verify echoed nonce + host signature (register: id is host-assigned).
|
||||
if let Err(e) = verify_host_final(
|
||||
&resp_comm,
|
||||
&tm,
|
||||
&host_pk,
|
||||
assigned_id,
|
||||
client_nonce,
|
||||
server_challenge,
|
||||
) {
|
||||
self.set_state(ConnectionState::Disconnected);
|
||||
return Err(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ pub mod error;
|
|||
pub mod message;
|
||||
pub mod transport;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use wasm_bindgen::prelude::*;
|
|||
|
||||
use mtp_codec::{CommunicationType, CommunicationValue, DataType, DataTypeId, DataValue};
|
||||
use mtp_crypto::{Ed25519Signer, EncryptionType, Keyring, PublicKeyBundle, SigAlgorithm};
|
||||
use mtp_type_map::communication_type_name;
|
||||
use mtp_type_map::{communication_type_name, TypeMap};
|
||||
|
||||
use crate::error::js_error;
|
||||
|
||||
|
|
@ -52,10 +52,10 @@ pub fn build_demo_message(
|
|||
let signer = Ed25519Signer::new(&keyring.sig_cl_secret_key)
|
||||
.map_err(|e| js_error(&format!("signer creation failed: {}", e)))?;
|
||||
|
||||
// Encrypted container (DataTypeId 1 = arbitrary custom)
|
||||
// Encrypted container
|
||||
let inner_enc = DataValue::Container(vec![
|
||||
(DataTypeId(1), DataValue::Str("secret inner data".into())),
|
||||
(DataTypeId(2), DataValue::UnsignedNumber(42)),
|
||||
(DataType::Version.to_id(&TypeMap::latest()), DataValue::Str("secret inner data".into())),
|
||||
(DataType::Id.to_id(&TypeMap::latest()), DataValue::UnsignedNumber(42)),
|
||||
]);
|
||||
let mut dv_enc = inner_enc;
|
||||
dv_enc
|
||||
|
|
@ -64,8 +64,8 @@ pub fn build_demo_message(
|
|||
|
||||
// Signed container
|
||||
let inner_sig = DataValue::Container(vec![
|
||||
(DataTypeId(1), DataValue::Str("signed by client".into())),
|
||||
(DataTypeId(2), DataValue::UnsignedNumber(99)),
|
||||
(DataType::Version.to_id(&TypeMap::latest()), DataValue::Str("signed by client".into())),
|
||||
(DataType::Id.to_id(&TypeMap::latest()), DataValue::UnsignedNumber(99)),
|
||||
]);
|
||||
let mut dv_sig = inner_sig;
|
||||
dv_sig
|
||||
|
|
@ -75,10 +75,10 @@ pub fn build_demo_message(
|
|||
// Signed + encrypted container
|
||||
let inner_sec = DataValue::Container(vec![
|
||||
(
|
||||
DataTypeId(1),
|
||||
DataType::Version.to_id(&TypeMap::latest()),
|
||||
DataValue::Str("signed+encrypted payload".into()),
|
||||
),
|
||||
(DataTypeId(2), DataValue::UnsignedNumber(7)),
|
||||
(DataType::Id.to_id(&TypeMap::latest()), DataValue::UnsignedNumber(7)),
|
||||
]);
|
||||
let mut dv_sec = inner_sec;
|
||||
dv_sec
|
||||
|
|
@ -115,24 +115,24 @@ pub fn parse_auth_response(response: &[u8]) -> Result<JsValue, JsValue> {
|
|||
let comm = CommunicationValue::from_bytes(response)
|
||||
.map_err(|e| js_error(&format!("parse failed: {}", e)))?;
|
||||
|
||||
let connected = matches!(comm.get_data(DataTypeId(11)), DataValue::BoolTrue);
|
||||
let connected = matches!(comm.get_data(DataType::Connected.to_id(&TypeMap::latest())), DataValue::BoolTrue);
|
||||
|
||||
let client_nonce = match comm.get_data(DataTypeId(7)) {
|
||||
let client_nonce = match comm.get_data(DataType::ClientNonce.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let assigned_id = match comm.get_data(DataTypeId(6)) {
|
||||
let assigned_id = match comm.get_data(DataType::Id.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n as u64),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let timestamp = match comm.get_data(DataTypeId(5)) {
|
||||
let timestamp = match comm.get_data(DataType::Timestamp.to_id(&TypeMap::latest())) {
|
||||
DataValue::UnsignedNumber(n) => Some(*n),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let signature = match comm.get_data(DataTypeId(10)) {
|
||||
let signature = match comm.get_data(DataType::Signature.to_id(&TypeMap::latest())) {
|
||||
DataValue::Bytes(b) => Some(b.clone()),
|
||||
_ => None,
|
||||
};
|
||||
|
|
@ -252,15 +252,16 @@ mod tests {
|
|||
fn build_ping_frame_roundtrip() {
|
||||
let bytes = build_ping_frame(42, "test-ping", 1234567890, &[]).expect("encode failed");
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
let tm = TypeMap::latest();
|
||||
|
||||
assert_eq!(cv.get_type(), CommunicationTypeId(19)); // Ping
|
||||
assert_eq!(cv.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(cv.get_sender(), 42);
|
||||
assert_eq!(
|
||||
cv.get_data(DataTypeId(4)),
|
||||
cv.get_data(DataType::Description.to_id(&tm)),
|
||||
&DataValue::Str("test-ping".into())
|
||||
);
|
||||
assert_eq!(
|
||||
cv.get_data(DataTypeId(5)),
|
||||
cv.get_data(DataType::Timestamp.to_id(&tm)),
|
||||
&DataValue::UnsignedNumber(1234567890)
|
||||
);
|
||||
}
|
||||
|
|
@ -270,16 +271,17 @@ mod tests {
|
|||
let payload = b"attachment-data";
|
||||
let bytes = build_ping_frame(99, "with-data", 555, payload).expect("encode failed");
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
let tm = TypeMap::latest();
|
||||
|
||||
assert_eq!(cv.get_type(), CommunicationTypeId(19));
|
||||
assert_eq!(cv.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(cv.get_sender(), 99);
|
||||
assert_eq!(
|
||||
cv.get_data(DataTypeId(4)),
|
||||
cv.get_data(DataType::Description.to_id(&tm)),
|
||||
&DataValue::Str("with-data".into())
|
||||
);
|
||||
assert_eq!(cv.get_data(DataTypeId(5)), &DataValue::UnsignedNumber(555));
|
||||
assert_eq!(cv.get_data(DataType::Timestamp.to_id(&tm)), &DataValue::UnsignedNumber(555));
|
||||
assert_eq!(
|
||||
cv.get_data(DataTypeId(6)),
|
||||
cv.get_data(DataType::Id.to_id(&tm)),
|
||||
&DataValue::Bytes(payload.to_vec())
|
||||
);
|
||||
}
|
||||
|
|
@ -304,11 +306,12 @@ mod tests {
|
|||
|
||||
let bytes = result.unwrap();
|
||||
let cv = CommunicationValue::from_bytes(&bytes).expect("decode failed");
|
||||
let tm = TypeMap::latest();
|
||||
|
||||
assert_eq!(cv.get_type(), CommunicationTypeId(19)); // Ping
|
||||
assert_eq!(cv.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(cv.get_sender(), 7);
|
||||
assert_eq!(
|
||||
cv.get_data(DataTypeId(4)),
|
||||
cv.get_data(DataType::Description.to_id(&tm)),
|
||||
&DataValue::Str("MTP WASM Demo".into())
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,7 +223,10 @@ impl WasmTransport {
|
|||
let incoming = self.inner.incoming_unidirectional_streams();
|
||||
|
||||
let reader_fn = match js_sys::Reflect::get(&incoming, &JsValue::from_str("getReader")) {
|
||||
Ok(f) => f.dyn_into::<js_sys::Function>().unwrap(),
|
||||
Ok(f) => match f.dyn_into::<js_sys::Function>() {
|
||||
Ok(f) => f,
|
||||
Err(_) => return,
|
||||
},
|
||||
Err(_) => return,
|
||||
};
|
||||
let reader_val = match reader_fn.call0(&incoming) {
|
||||
|
|
@ -233,7 +236,10 @@ impl WasmTransport {
|
|||
|
||||
loop {
|
||||
let read_fn = match js_sys::Reflect::get(&reader_val, &JsValue::from_str("read")) {
|
||||
Ok(f) => f.dyn_into::<js_sys::Function>().unwrap(),
|
||||
Ok(f) => match f.dyn_into::<js_sys::Function>() {
|
||||
Ok(f) => f,
|
||||
Err(_) => break,
|
||||
},
|
||||
Err(_) => break,
|
||||
};
|
||||
let result = match read_fn.call0(&reader_val) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue