diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 3f62f86..3399ddb 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -43,8 +43,18 @@ jobs: - name: Run clippy run: | export PATH="$HOME/.cargo/bin:$PATH" - export MTP_TYPE_MAPS="$PWD/example-usage/type-maps.yaml" - cargo clippy --workspace --exclude mtp-wasm --all-targets --all-features -- -D warnings -W unreachable-pub + export MTP_TYPE_MAPS="$PWD/example/type-maps.yaml" + cargo clippy --workspace --exclude mtp-wasm --all-targets --all-features -- -D warnings \ + -W unreachable-pub \ + -W clippy::cognitive_complexity \ + -W clippy::missing_docs_in_private_items \ + -W clippy::missing_errors_doc \ + -W clippy::missing_panics_doc \ + -W clippy::missing_safety_doc \ + -W clippy::undocumented_unsafe_blocks \ + -W clippy::pedantic \ + -W clippy::restriction \ + -A clippy::blanket_clippy_restriction_lints test: name: test @@ -62,7 +72,7 @@ jobs: - name: Run tests run: | export PATH="$HOME/.cargo/bin:$PATH" - export MTP_TYPE_MAPS="$PWD/example-usage/type-maps.yaml" + export MTP_TYPE_MAPS="$PWD/example/type-maps.yaml" cargo test --workspace --exclude mtp-wasm --all-features wasm: @@ -81,13 +91,13 @@ jobs: - name: Build wasm crate run: | export PATH="$HOME/.cargo/bin:$PATH" - export MTP_TYPE_MAPS="$PWD/example-usage/type-maps.yaml" + export MTP_TYPE_MAPS="$PWD/example/type-maps.yaml" cargo build -p mtp-wasm --target wasm32-unknown-unknown env: RUSTFLAGS: --cfg web_sys_unstable_apis - example-usage: - name: example usage + example: + name: example runs-on: docker steps: - uses: https://data.forgejo.org/actions/checkout@v4 @@ -100,7 +110,7 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal - name: Check example usage workspace - working-directory: example-usage + working-directory: example run: | export PATH="$HOME/.cargo/bin:$PATH" export MTP_TYPE_MAPS="$PWD/type-maps.yaml" @@ -194,7 +204,7 @@ jobs: - name: Build wasm package run: | export PATH="$HOME/.cargo/bin:$PATH" - export MTP_TYPE_MAPS="$PWD/example-usage/type-maps.yaml" + export MTP_TYPE_MAPS="$PWD/example/type-maps.yaml" wasm-pack build wasm --target web env: RUSTFLAGS: --cfg web_sys_unstable_apis @@ -204,13 +214,13 @@ jobs: curl -fsSL https://bun.sh/install | bash - name: Install dependencies - working-directory: example-usage/web-client + working-directory: example/web-client run: | export PATH="$HOME/.bun/bin:$PATH" bun install --frozen-lockfile - name: Build web client - working-directory: example-usage/web-client + working-directory: example/web-client run: | export PATH="$HOME/.bun/bin:$PATH" bun run build diff --git a/client/src/lib.rs b/client/src/lib.rs index ea42ddb..1627d63 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -185,6 +185,75 @@ fn check_connected( } } +#[cfg(feature = "crypto")] +fn signed_challenge_response( + keys: &mtp_crypto::Keyring, + proof_payload: &[u8], + client_nonce: u128, +) -> Result { + use mtp_crypto::{Ed25519Signer, MlDsaSigner, SignatureScheme}; + + let signer = Ed25519Signer::new(&keys.sig_cl_secret_key) + .map_err(|e| CommunicationError::Other(e.to_string()))?; + let signature = signer + .sign(proof_payload) + .map_err(|e| CommunicationError::Other(e.to_string()))?; + + let mut proof = CommunicationValue::new(mtp_codec::CommunicationType::ChallengeResponse) + .add_typed_default( + DataType::ClientNonce, + DataValue::UnsignedNumber(client_nonce), + ) + .add_typed_default(DataType::Signature, DataValue::Bytes(signature)); + + if !keys.sig_pq_secret_key.as_bytes().is_empty() { + let pq_signer = MlDsaSigner::new(&keys.sig_pq_secret_key, &keys.sig_pq_public_key) + .map_err(|e| CommunicationError::Other(e.to_string()))?; + let pq_signature = pq_signer + .sign(proof_payload) + .map_err(|e| CommunicationError::Other(e.to_string()))?; + proof = proof.add_typed_default(DataType::PqSignature, DataValue::Bytes(pq_signature)); + } + + Ok(proof) +} + +#[cfg(feature = "crypto")] +async fn receive_verified_challenge( + receiver: &Receiver, + tm: &mtp_codec::TypeMap, + host_public_key_bundle: &mtp_crypto::PublicKeyBundle, + bound_id: u64, + context: &str, +) -> Result { + let challenge = receiver.receive().await?; + let expected = mtp_codec::CommunicationType::Challenge.to_id(tm); + if challenge.get_type() != expected { + return Err(unexpected_response_type_error( + context, expected, &challenge, + )); + } + + let server_challenge = match challenge.get_data(DataType::ServerNonce.to_id(tm)) { + DataValue::UnsignedNumber(n) => *n, + _ => { + return Err(CommunicationError::AuthenticationFailed( + "Missing server challenge".into(), + )); + } + }; + + verify_host_challenge( + &challenge, + tm, + host_public_key_bundle, + bound_id, + server_challenge, + )?; + + Ok(server_challenge) +} + #[cfg(feature = "crypto")] impl MTPClient { pub async fn auth_connect( @@ -192,7 +261,7 @@ impl MTPClient { keys: &mtp_crypto::Keyring, host_public_key_bundle: &mtp_crypto::PublicKeyBundle, ) -> Result { - use mtp_crypto::{Ed25519Signer, MlDsaSigner, SignatureScheme, auth}; + use mtp_crypto::auth; let (sender, receiver) = mtp_transport::connect(&config.url, config.server_cert, Policy::default()).await?; @@ -210,30 +279,14 @@ impl MTPClient { sender.send(&ident).await?; // 2. Receive and verify the host's challenge. - let challenge = receiver.receive().await?; - let expected = mtp_codec::CommunicationType::Challenge.to_id(&tm); - if challenge.get_type() != expected { - return Err(unexpected_response_type_error( - "auth_connect challenge", - expected, - &challenge, - )); - } - let server_challenge = match challenge.get_data(DataType::ServerNonce.to_id(&tm)) { - DataValue::UnsignedNumber(n) => *n, - _ => { - return Err(CommunicationError::AuthenticationFailed( - "Missing server challenge".into(), - )); - } - }; - verify_host_challenge( - &challenge, + let server_challenge = receive_verified_challenge( + &receiver, &tm, host_public_key_bundle, config.client_id, - server_challenge, - )?; + "auth_connect challenge", + ) + .await?; // 3. Sign the host's challenge and send the proof. let client_nonce: u128 = rand::random(); @@ -244,26 +297,7 @@ impl MTPClient { client_nonce, ); - let signer = Ed25519Signer::new(&keys.sig_cl_secret_key) - .map_err(|e| CommunicationError::Other(e.to_string()))?; - let signature = signer - .sign(&proof_payload) - .map_err(|e| CommunicationError::Other(e.to_string()))?; - - let mut proof = CommunicationValue::new(mtp_codec::CommunicationType::ChallengeResponse) - .add_typed_default( - DataType::ClientNonce, - DataValue::UnsignedNumber(client_nonce), - ) - .add_typed_default(DataType::Signature, DataValue::Bytes(signature)); - if !keys.sig_pq_secret_key.as_bytes().is_empty() { - let pq_signer = MlDsaSigner::new(&keys.sig_pq_secret_key, &keys.sig_pq_public_key) - .map_err(|e| CommunicationError::Other(e.to_string()))?; - let pq_signature = pq_signer - .sign(&proof_payload) - .map_err(|e| CommunicationError::Other(e.to_string()))?; - proof = proof.add_typed_default(DataType::PqSignature, DataValue::Bytes(pq_signature)); - } + let proof = signed_challenge_response(keys, &proof_payload, client_nonce)?; sender.send(&proof).await?; // 4. Receive and verify the host's final confirmation. @@ -300,7 +334,7 @@ impl MTPClient { keys: &mtp_crypto::Keyring, host_public_key_bundle: &mtp_crypto::PublicKeyBundle, ) -> Result { - use mtp_crypto::{Ed25519Signer, MlDsaSigner, SignatureScheme, auth}; + use mtp_crypto::auth; let (sender, receiver) = mtp_transport::connect(&config.url, config.server_cert, Policy::default()).await?; @@ -317,50 +351,21 @@ impl MTPClient { sender.send(®ister).await?; // 2. Receive and verify the host's challenge (register binds id = 0). - let challenge = receiver.receive().await?; - let expected = mtp_codec::CommunicationType::Challenge.to_id(&tm); - if challenge.get_type() != expected { - return Err(unexpected_response_type_error( - "auth_register challenge", - expected, - &challenge, - )); - } - let server_challenge = match challenge.get_data(DataType::ServerNonce.to_id(&tm)) { - DataValue::UnsignedNumber(n) => *n, - _ => { - return Err(CommunicationError::AuthenticationFailed( - "Missing server challenge".into(), - )); - } - }; - verify_host_challenge(&challenge, &tm, host_public_key_bundle, 0, server_challenge)?; + let server_challenge = receive_verified_challenge( + &receiver, + &tm, + host_public_key_bundle, + 0, + "auth_register challenge", + ) + .await?; // 3. Sign the host's challenge over the bundle and send the proof. let client_nonce: u128 = rand::random(); let proof_payload = auth::register_proof_payload(&version_str, &pk_bytes, server_challenge, client_nonce); - let signer = Ed25519Signer::new(&keys.sig_cl_secret_key) - .map_err(|e| CommunicationError::Other(e.to_string()))?; - let signature = signer - .sign(&proof_payload) - .map_err(|e| CommunicationError::Other(e.to_string()))?; - - let mut proof = CommunicationValue::new(mtp_codec::CommunicationType::ChallengeResponse) - .add_typed_default( - DataType::ClientNonce, - DataValue::UnsignedNumber(client_nonce), - ) - .add_typed_default(DataType::Signature, DataValue::Bytes(signature)); - if !keys.sig_pq_secret_key.as_bytes().is_empty() { - let pq_signer = MlDsaSigner::new(&keys.sig_pq_secret_key, &keys.sig_pq_public_key) - .map_err(|e| CommunicationError::Other(e.to_string()))?; - let pq_signature = pq_signer - .sign(&proof_payload) - .map_err(|e| CommunicationError::Other(e.to_string()))?; - proof = proof.add_typed_default(DataType::PqSignature, DataValue::Bytes(pq_signature)); - } + let proof = signed_challenge_response(keys, &proof_payload, client_nonce)?; sender.send(&proof).await?; // 4. Receive the host's final confirmation; extract the assigned id and diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..881b50f --- /dev/null +++ b/clippy.toml @@ -0,0 +1,2 @@ +cognitive-complexity-threshold = 25 +avoid-breaking-exported-api = false diff --git a/codec/src/data_value.rs b/codec/src/data_value.rs index 886fe71..5f4aced 100644 --- a/codec/src/data_value.rs +++ b/codec/src/data_value.rs @@ -477,8 +477,7 @@ impl DataValue { let kind = Self::kind_marker(value); buf.push(kind); - if kind == Self::KIND_BOOL_TRUE || kind == Self::KIND_BOOL_FALSE || kind == Self::KIND_NULL - { + if Self::kind_has_no_payload(kind) { buf.write_u16::(key.0) .map_err(|_| CodecError::InvalidEncoding)?; return Ok(()); @@ -513,8 +512,7 @@ impl DataValue { let kind = Self::kind_marker(value); buf.push(kind); - if kind == Self::KIND_BOOL_TRUE || kind == Self::KIND_BOOL_FALSE || kind == Self::KIND_NULL - { + if Self::kind_has_no_payload(kind) { return Ok(()); } @@ -625,18 +623,9 @@ impl DataValue { for _ in 0..count { let kind = cursor.read_u8().ok()?; - if kind == Self::KIND_BOOL_TRUE - || kind == Self::KIND_BOOL_FALSE - || kind == Self::KIND_NULL - { + if Self::kind_has_no_payload(kind) { let key = DataTypeId(cursor.read_u16::().ok()?); - let value = if kind == Self::KIND_BOOL_TRUE { - DataValue::BoolTrue - } else if kind == Self::KIND_BOOL_FALSE { - DataValue::BoolFalse - } else { - DataValue::Null - }; + let value = Self::read_payloadless_value(kind)?; entries.push((key, value)); continue; } @@ -644,20 +633,12 @@ impl DataValue { let len = cursor.read_u32::().ok()? as usize; let key = DataTypeId(cursor.read_u16::().ok()?); - let start = cursor.position() as usize; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - - let payload = &cursor.get_ref()[start..end]; + let payload = Self::read_payload_slice(cursor, len)?; let mut inner = Cursor::new(payload); let value = Self::read_value_by_kind(&mut inner, kind, Some(len))?; if inner.position() as usize != len { return None; } - - cursor.set_position(end as u64); entries.push((key, value)); } @@ -675,36 +656,19 @@ impl DataValue { for _ in 0..count { let kind = cursor.read_u8().ok()?; - if kind == Self::KIND_BOOL_TRUE - || kind == Self::KIND_BOOL_FALSE - || kind == Self::KIND_NULL - { - let value = if kind == Self::KIND_BOOL_TRUE { - DataValue::BoolTrue - } else if kind == Self::KIND_BOOL_FALSE { - DataValue::BoolFalse - } else { - DataValue::Null - }; + if Self::kind_has_no_payload(kind) { + let value = Self::read_payloadless_value(kind)?; out.push(value); continue; } let len = cursor.read_u32::().ok()? as usize; - let start = cursor.position() as usize; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - - let payload = &cursor.get_ref()[start..end]; + let payload = Self::read_payload_slice(cursor, len)?; let mut inner = Cursor::new(payload); let value = Self::read_value_by_kind(&mut inner, kind, Some(len))?; if inner.position() as usize != len { return None; } - - cursor.set_position(end as u64); out.push(value); } @@ -731,95 +695,45 @@ impl DataValue { Some(DataValue::Float(a, b)) } Self::KIND_STR => { - let len = payload_len?; - let start = cursor.position() as usize; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - let s = std::str::from_utf8(&cursor.get_ref()[start..end]) + let s = std::str::from_utf8(Self::read_payload_slice(cursor, payload_len?)?) .ok()? .to_string(); - cursor.set_position(end as u64); Some(DataValue::Str(s)) } - Self::KIND_BYTES => { - let len = payload_len?; - let start = cursor.position() as usize; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - let b = cursor.get_ref()[start..end].to_vec(); - cursor.set_position(end as u64); - Some(DataValue::Bytes(b)) - } + Self::KIND_BYTES => Some(DataValue::Bytes(Self::read_blob_payload( + cursor, + payload_len?, + )?)), Self::KIND_ARRAY => { - let start = cursor.position() as usize; let len = payload_len?; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - let mut inner = Cursor::new(&cursor.get_ref()[start..end]); + let mut inner = Cursor::new(Self::read_payload_slice(cursor, len)?); let arr = Self::read_array(&mut inner)?; if inner.position() as usize != len { return None; } - cursor.set_position(end as u64); Some(arr) } Self::KIND_CONTAINER => { - let start = cursor.position() as usize; let len = payload_len?; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - let mut inner = Cursor::new(&cursor.get_ref()[start..end]); + let mut inner = Cursor::new(Self::read_payload_slice(cursor, len)?); let c = Self::try_read_container(&mut inner)?; if inner.position() as usize != len { return None; } - cursor.set_position(end as u64); Some(c) } #[cfg(feature = "crypto")] - Self::KIND_ENCRYPTED_CONTAINER => { - let len = payload_len?; - let start = cursor.position() as usize; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - let data = cursor.get_ref()[start..end].to_vec(); - cursor.set_position(end as u64); - Some(DataValue::EncryptedContainer(data)) - } + Self::KIND_ENCRYPTED_CONTAINER => Some(DataValue::EncryptedContainer( + Self::read_blob_payload(cursor, payload_len?)?, + )), #[cfg(feature = "crypto")] - Self::KIND_SIGNED_CONTAINER => { - let len = payload_len?; - let start = cursor.position() as usize; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - let data = cursor.get_ref()[start..end].to_vec(); - cursor.set_position(end as u64); - Some(DataValue::SignedContainer(data)) - } + Self::KIND_SIGNED_CONTAINER => Some(DataValue::SignedContainer( + Self::read_blob_payload(cursor, payload_len?)?, + )), #[cfg(feature = "crypto")] - Self::KIND_SIGNED_ENCRYPTED_CONTAINER => { - let len = payload_len?; - let start = cursor.position() as usize; - let end = start.checked_add(len)?; - if end > cursor.get_ref().len() { - return None; - } - let data = cursor.get_ref()[start..end].to_vec(); - cursor.set_position(end as u64); - Some(DataValue::SignedEncryptedContainer(data)) - } + Self::KIND_SIGNED_ENCRYPTED_CONTAINER => Some(DataValue::SignedEncryptedContainer( + Self::read_blob_payload(cursor, payload_len?)?, + )), Self::KIND_NULL => Some(DataValue::Null), #[cfg(not(feature = "crypto"))] 0x0A | 0x0B | 0x0C => None, @@ -854,6 +768,33 @@ impl DataValue { DataValue::Null => Self::KIND_NULL, } } + + fn kind_has_no_payload(kind: u8) -> bool { + kind == Self::KIND_BOOL_TRUE || kind == Self::KIND_BOOL_FALSE || kind == Self::KIND_NULL + } + + fn read_payloadless_value(kind: u8) -> Option { + match kind { + Self::KIND_BOOL_TRUE => Some(DataValue::BoolTrue), + Self::KIND_BOOL_FALSE => Some(DataValue::BoolFalse), + Self::KIND_NULL => Some(DataValue::Null), + _ => None, + } + } + + fn read_payload_slice<'a>(cursor: &mut Cursor<&'a [u8]>, len: usize) -> Option<&'a [u8]> { + let start = cursor.position() as usize; + let end = start.checked_add(len)?; + if end > cursor.get_ref().len() { + return None; + } + cursor.set_position(end as u64); + Some(&cursor.get_ref()[start..end]) + } + + fn read_blob_payload(cursor: &mut Cursor<&[u8]>, len: usize) -> Option> { + Some(Self::read_payload_slice(cursor, len)?.to_vec()) + } } impl fmt::Display for DataValue { @@ -1003,9 +944,18 @@ mod tests { container_roundtrip(vec![ (DataType::Version.to_id(&tm), DataValue::SignedNumber(0)), (DataType::Id.to_id(&tm), DataValue::SignedNumber(42)), - (DataType::ClientNonce.to_id(&tm), DataValue::SignedNumber(-42)), - (DataType::ServerNonce.to_id(&tm), DataValue::SignedNumber(i128::MAX)), - (DataType::PublicKeys.to_id(&tm), DataValue::SignedNumber(i128::MIN)), + ( + DataType::ClientNonce.to_id(&tm), + DataValue::SignedNumber(-42), + ), + ( + DataType::ServerNonce.to_id(&tm), + DataValue::SignedNumber(i128::MAX), + ), + ( + DataType::PublicKeys.to_id(&tm), + DataValue::SignedNumber(i128::MIN), + ), ]); } @@ -1015,7 +965,10 @@ mod tests { container_roundtrip(vec![ (DataType::Version.to_id(&tm), DataValue::UnsignedNumber(0)), (DataType::Id.to_id(&tm), DataValue::UnsignedNumber(42)), - (DataType::ClientNonce.to_id(&tm), DataValue::UnsignedNumber(u128::MAX)), + ( + DataType::ClientNonce.to_id(&tm), + DataValue::UnsignedNumber(u128::MAX), + ), ]); } @@ -1025,7 +978,10 @@ mod tests { container_roundtrip(vec![ (DataType::Version.to_id(&tm), DataValue::Float(0, 0)), (DataType::Id.to_id(&tm), DataValue::Float(2, 12345)), - (DataType::ClientNonce.to_id(&tm), DataValue::Float(255, 4294967295)), + ( + DataType::ClientNonce.to_id(&tm), + DataValue::Float(255, 4294967295), + ), ]); } @@ -1035,7 +991,10 @@ mod tests { container_roundtrip(vec![ (DataType::Version.to_id(&tm), DataValue::Str(String::new())), (DataType::Id.to_id(&tm), DataValue::Str("hello".to_string())), - (DataType::ClientNonce.to_id(&tm), DataValue::Str("a".repeat(1000))), + ( + DataType::ClientNonce.to_id(&tm), + DataValue::Str("a".repeat(1000)), + ), ]); } @@ -1044,8 +1003,14 @@ mod tests { let tm = TypeMap::latest(); container_roundtrip(vec![ (DataType::Version.to_id(&tm), DataValue::Bytes(vec![])), - (DataType::Id.to_id(&tm), DataValue::Bytes(vec![0x00, 0xFF, 0xAB])), - (DataType::ClientNonce.to_id(&tm), DataValue::Bytes(vec![0x42; 100])), + ( + DataType::Id.to_id(&tm), + DataValue::Bytes(vec![0x00, 0xFF, 0xAB]), + ), + ( + DataType::ClientNonce.to_id(&tm), + DataValue::Bytes(vec![0x42; 100]), + ), ]); } @@ -1084,8 +1049,14 @@ mod tests { container_roundtrip(vec![ (DataType::Version.to_id(&tm), DataValue::BoolTrue), (DataType::Id.to_id(&tm), DataValue::SignedNumber(-100)), - (DataType::ClientNonce.to_id(&tm), DataValue::Str("test".to_string())), - (DataType::ServerNonce.to_id(&tm), DataValue::UnsignedNumber(u128::MAX)), + ( + DataType::ClientNonce.to_id(&tm), + DataValue::Str("test".to_string()), + ), + ( + DataType::ServerNonce.to_id(&tm), + DataValue::UnsignedNumber(u128::MAX), + ), (DataType::PublicKeys.to_id(&tm), DataValue::Null), ]); } @@ -1141,27 +1112,40 @@ mod tests { fn test_as_accessors() { let tm = TypeMap::latest(); let dv = DataValue::Container(vec![ - (DataType::Version.to_id(&tm), DataValue::Str("alice".to_string())), + ( + DataType::Version.to_id(&tm), + DataValue::Str("alice".to_string()), + ), (DataType::Id.to_id(&tm), DataValue::SignedNumber(42)), - (DataType::ClientNonce.to_id(&tm), DataValue::Bytes(vec![0x01, 0x02])), - (DataType::ServerNonce.to_id(&tm), DataValue::Array(vec![DataValue::BoolTrue])), + ( + DataType::ClientNonce.to_id(&tm), + DataValue::Bytes(vec![0x01, 0x02]), + ), + ( + DataType::ServerNonce.to_id(&tm), + DataValue::Array(vec![DataValue::BoolTrue]), + ), ]); let map = dv.as_map().expect("should be a container"); assert_eq!( - map.get(&DataType::Version.to_id(&tm)).and_then(|v| v.as_str()), + map.get(&DataType::Version.to_id(&tm)) + .and_then(|v| v.as_str()), Some("alice") ); assert_eq!( - map.get(&DataType::Id.to_id(&tm)).and_then(|v| v.as_signed_number()), + map.get(&DataType::Id.to_id(&tm)) + .and_then(|v| v.as_signed_number()), Some(42) ); assert_eq!( - map.get(&DataType::ClientNonce.to_id(&tm)).and_then(|v| v.as_bytes()), + map.get(&DataType::ClientNonce.to_id(&tm)) + .and_then(|v| v.as_bytes()), Some(vec![0x01, 0x02]) ); assert_eq!( - map.get(&DataType::ServerNonce.to_id(&tm)).and_then(|v| v.as_array()), + map.get(&DataType::ServerNonce.to_id(&tm)) + .and_then(|v| v.as_array()), Some(vec![DataValue::BoolTrue]) ); } @@ -1206,7 +1190,10 @@ mod tests { #[test] fn test_truncated_container_rejected() { let tm = TypeMap::latest(); - let dv = DataValue::Container(vec![(DataType::Version.to_id(&tm), DataValue::Str("hello".to_string()))]); + let dv = DataValue::Container(vec![( + DataType::Version.to_id(&tm), + DataValue::Str("hello".to_string()), + )]); let bytes = dv.to_bytes().expect("encode failed"); // Truncate to fewer than 2 bytes so neither container nor array can be read assert!(DataValue::from_bytes(&bytes[..1]).is_none()); @@ -1264,8 +1251,14 @@ mod tests { fn test_container_display() { let tm = TypeMap::latest(); let dv = DataValue::Container(vec![ - (DataType::ServerNonce.to_id(&tm), DataValue::Str("v2.0".to_string())), - (DataType::PqSignature.to_id(&tm), DataValue::UnsignedNumber(42)), + ( + DataType::ServerNonce.to_id(&tm), + DataValue::Str("v2.0".to_string()), + ), + ( + DataType::PqSignature.to_id(&tm), + DataValue::UnsignedNumber(42), + ), ]); let s = format!("{}", dv); assert!(s.contains("3:")); @@ -1290,7 +1283,10 @@ mod tests { let bundle = keyring.public_key_bundle(); let mut dv = DataValue::Container(vec![ - (DataType::Version.to_id(&tm), DataValue::Str("secret".to_string())), + ( + DataType::Version.to_id(&tm), + DataValue::Str("secret".to_string()), + ), (DataType::Id.to_id(&tm), DataValue::UnsignedNumber(42)), ]); @@ -1315,8 +1311,10 @@ mod tests { let keyring_a = Keyring::generate(); let keyring_b = Keyring::generate(); - let mut dv = - DataValue::Container(vec![(DataType::Version.to_id(&tm), DataValue::Str("secret".to_string()))]); + let mut dv = DataValue::Container(vec![( + DataType::Version.to_id(&tm), + DataValue::Str("secret".to_string()), + )]); assert!( dv.encrypt_container( @@ -1336,8 +1334,10 @@ mod tests { let tm = TypeMap::latest(); let keyring = Keyring::generate(); - let mut dv = - DataValue::Container(vec![(DataType::Version.to_id(&tm), DataValue::Str("secret".to_string()))]); + let mut dv = DataValue::Container(vec![( + DataType::Version.to_id(&tm), + DataValue::Str("secret".to_string()), + )]); assert!( dv.encrypt_container( diff --git a/crypto/src/aead.rs b/crypto/src/aead.rs index 3729ebd..d708d7f 100644 --- a/crypto/src/aead.rs +++ b/crypto/src/aead.rs @@ -18,6 +18,14 @@ pub trait AeadCipher: AeadEncrypt + AeadDecrypt { fn key_size() -> usize; } +#[cfg(any(feature = "chacha20poly1305", feature = "aes-gcm"))] +fn prepend_nonce(nonce: &[u8], ciphertext: &mut Vec) -> Vec { + let mut out = Vec::with_capacity(nonce.len() + ciphertext.len()); + out.extend_from_slice(nonce); + out.append(ciphertext); + out +} + #[cfg(feature = "chacha20poly1305")] pub struct ChaCha20Poly1305 { key: [u8; 32], @@ -53,10 +61,7 @@ impl AeadEncrypt for ChaCha20Poly1305 { .encrypt(nonce_ref, payload) .map_err(|_| CryptoError::EncryptionFailed)?; - let mut out = Vec::with_capacity(nonce.len() + ciphertext.len()); - out.extend_from_slice(&nonce); - out.append(&mut ciphertext); - Ok(out) + Ok(prepend_nonce(&nonce, &mut ciphertext)) } } @@ -126,10 +131,7 @@ impl AeadEncrypt for Aes256Gcm { .encrypt(nonce_ref, payload) .map_err(|_| CryptoError::EncryptionFailed)?; - let mut out = Vec::with_capacity(nonce.len() + ciphertext.len()); - out.extend_from_slice(&nonce); - out.append(&mut ciphertext); - Ok(out) + Ok(prepend_nonce(&nonce, &mut ciphertext)) } } diff --git a/example-usage/web-client/package-lock.json b/example-usage/web-client/package-lock.json deleted file mode 100644 index f83ec9b..0000000 --- a/example-usage/web-client/package-lock.json +++ /dev/null @@ -1,1041 +0,0 @@ -{ - "name": "mtp-web-client", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "mtp-web-client", - "version": "0.1.0", - "devDependencies": { - "typescript": "^5.4.0", - "vite": "^5.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", - "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", - "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", - "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", - "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", - "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", - "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", - "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", - "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", - "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", - "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", - "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", - "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", - "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", - "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", - "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", - "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", - "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", - "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", - "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", - "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", - "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", - "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", - "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", - "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", - "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/rollup": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", - "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.62.2", - "@rollup/rollup-android-arm64": "4.62.2", - "@rollup/rollup-darwin-arm64": "4.62.2", - "@rollup/rollup-darwin-x64": "4.62.2", - "@rollup/rollup-freebsd-arm64": "4.62.2", - "@rollup/rollup-freebsd-x64": "4.62.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", - "@rollup/rollup-linux-arm-musleabihf": "4.62.2", - "@rollup/rollup-linux-arm64-gnu": "4.62.2", - "@rollup/rollup-linux-arm64-musl": "4.62.2", - "@rollup/rollup-linux-loong64-gnu": "4.62.2", - "@rollup/rollup-linux-loong64-musl": "4.62.2", - "@rollup/rollup-linux-ppc64-gnu": "4.62.2", - "@rollup/rollup-linux-ppc64-musl": "4.62.2", - "@rollup/rollup-linux-riscv64-gnu": "4.62.2", - "@rollup/rollup-linux-riscv64-musl": "4.62.2", - "@rollup/rollup-linux-s390x-gnu": "4.62.2", - "@rollup/rollup-linux-x64-gnu": "4.62.2", - "@rollup/rollup-linux-x64-musl": "4.62.2", - "@rollup/rollup-openbsd-x64": "4.62.2", - "@rollup/rollup-openharmony-arm64": "4.62.2", - "@rollup/rollup-win32-arm64-msvc": "4.62.2", - "@rollup/rollup-win32-ia32-msvc": "4.62.2", - "@rollup/rollup-win32-x64-gnu": "4.62.2", - "@rollup/rollup-win32-x64-msvc": "4.62.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - } - } -} diff --git a/example-usage/.cargo/config.toml b/example/.cargo/config.toml similarity index 100% rename from example-usage/.cargo/config.toml rename to example/.cargo/config.toml diff --git a/example-usage/.gitignore b/example/.gitignore similarity index 100% rename from example-usage/.gitignore rename to example/.gitignore diff --git a/example-usage/Cargo.lock b/example/Cargo.lock similarity index 99% rename from example-usage/Cargo.lock rename to example/Cargo.lock index 48f612f..104fc8b 100644 --- a/example-usage/Cargo.lock +++ b/example/Cargo.lock @@ -908,7 +908,6 @@ dependencies = [ "mtp-crypto", "mtp-transport", "rand 0.8.6", - "tokio", ] [[package]] @@ -953,12 +952,12 @@ dependencies = [ name = "mtp-host" version = "0.1.0" dependencies = [ + "log", "mtp-codec", "mtp-common", "mtp-crypto", "mtp-transport", "rand 0.8.6", - "tokio", ] [[package]] @@ -968,10 +967,8 @@ dependencies = [ "log", "mtp-codec", "mtp-common", - "quinn", "rustls", "rustls-native-certs", - "thiserror", "tokio", "wtransport", ] @@ -1553,7 +1550,6 @@ dependencies = [ "hex", "mtp", "rcgen", - "serde_core", "serde_json", "tokio", ] diff --git a/example-usage/Cargo.toml b/example/Cargo.toml similarity index 100% rename from example-usage/Cargo.toml rename to example/Cargo.toml diff --git a/example-usage/client/Cargo.toml b/example/client/Cargo.toml similarity index 100% rename from example-usage/client/Cargo.toml rename to example/client/Cargo.toml diff --git a/example-usage/client/src/auth.rs b/example/client/src/auth.rs similarity index 100% rename from example-usage/client/src/auth.rs rename to example/client/src/auth.rs diff --git a/example-usage/client/src/main.rs b/example/client/src/main.rs similarity index 92% rename from example-usage/client/src/main.rs rename to example/client/src/main.rs index b822805..a9931b0 100644 --- a/example-usage/client/src/main.rs +++ b/example/client/src/main.rs @@ -9,8 +9,8 @@ use mtp::crypto::{KemPublicKey, PublicKeyBundle, SignaturePqPublicKey, Signature fn dev_cert_path() -> String { std::env::var("MTP_DEV_CERT").unwrap_or_else(|_| { - if Path::new("example-usage/dev-cert/cert.pem").exists() { - "example-usage/dev-cert/cert.pem".to_string() + if Path::new("example/dev-cert/cert.pem").exists() { + "example/dev-cert/cert.pem".to_string() } else { "dev-cert/cert.pem".to_string() } diff --git a/example-usage/client/src/messages.rs b/example/client/src/messages.rs similarity index 100% rename from example-usage/client/src/messages.rs rename to example/client/src/messages.rs diff --git a/example-usage/server/Cargo.toml b/example/server/Cargo.toml similarity index 100% rename from example-usage/server/Cargo.toml rename to example/server/Cargo.toml diff --git a/example-usage/server/src/clients.rs b/example/server/src/clients.rs similarity index 100% rename from example-usage/server/src/clients.rs rename to example/server/src/clients.rs diff --git a/example-usage/server/src/handlers.rs b/example/server/src/handlers.rs similarity index 100% rename from example-usage/server/src/handlers.rs rename to example/server/src/handlers.rs diff --git a/example-usage/server/src/keys.rs b/example/server/src/keys.rs similarity index 100% rename from example-usage/server/src/keys.rs rename to example/server/src/keys.rs diff --git a/example-usage/server/src/main.rs b/example/server/src/main.rs similarity index 94% rename from example-usage/server/src/main.rs rename to example/server/src/main.rs index 28d1ff5..01a748e 100644 --- a/example-usage/server/src/main.rs +++ b/example/server/src/main.rs @@ -9,15 +9,15 @@ use std::path::Path; fn dev_cert_paths() -> (String, String) { let cert = std::env::var("MTP_DEV_CERT").unwrap_or_else(|_| { - if Path::new("example-usage/dev-cert/cert.pem").exists() { - "example-usage/dev-cert/cert.pem".to_string() + if Path::new("example/dev-cert/cert.pem").exists() { + "example/dev-cert/cert.pem".to_string() } else { "dev-cert/cert.pem".to_string() } }); let key = std::env::var("MTP_DEV_KEY").unwrap_or_else(|_| { - if Path::new("example-usage/dev-cert/key.pem").exists() { - "example-usage/dev-cert/key.pem".to_string() + if Path::new("example/dev-cert/key.pem").exists() { + "example/dev-cert/key.pem".to_string() } else { "dev-cert/key.pem".to_string() } diff --git a/example-usage/server/src/tls.rs b/example/server/src/tls.rs similarity index 95% rename from example-usage/server/src/tls.rs rename to example/server/src/tls.rs index b312100..a119b5d 100644 --- a/example-usage/server/src/tls.rs +++ b/example/server/src/tls.rs @@ -52,7 +52,7 @@ pub fn export_webtransport_cert_hash(hash: &str) -> Result<(), Box Result<(), Box v, None => { log::warn!("accept: negotiate failed for client version {client_version:?}"); diff --git a/package.json b/package.json index 80cea8e..06100bd 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "wasm/pkg/mtp_wasm_bg.wasm.d.ts" ], "scripts": { - "build": "MTP_TYPE_MAPS=$PWD/example-usage/type-maps.yaml RUSTFLAGS='--cfg web_sys_unstable_apis' wasm-pack build wasm --target web --out-dir pkg --release", + "build": "MTP_TYPE_MAPS=$PWD/example/type-maps.yaml RUSTFLAGS='--cfg web_sys_unstable_apis' wasm-pack build wasm --target web --out-dir pkg --release", "build:all": "nix run .#build-all", "dup": "jscpd --pattern '**/*.rs' --ignore 'target/**' --ignore 'wasm/pkg/**' --ignore '.git/**' --min-lines 8 --min-tokens 80 --threshold 4 --reporters console --no-tips ." }, diff --git a/transport/src/client.rs b/transport/src/client.rs index 811f5d7..115b37e 100644 --- a/transport/src/client.rs +++ b/transport/src/client.rs @@ -52,19 +52,7 @@ fn configure_client_with_cert( .map_err(|_| CommunicationError::CertificateParseFailed)?; } - let mut tls_config = RustlsClientConfig::builder() - .with_root_certificates(root_store) - .with_no_client_auth(); - - tls_config.alpn_protocols = vec![b"h3".to_vec()]; - - Ok(ClientConfig::builder() - .with_bind_default() - .with_custom_tls(tls_config) - .keep_alive_interval(policy.keep_alive_interval) - .max_idle_timeout(policy.max_idle_timeout) - .map_err(|e| CommunicationError::Other(e.to_string()))? - .build()) + client_config_from_roots(root_store, policy) } fn configure_client_system_roots(policy: &Policy) -> Result { @@ -77,6 +65,13 @@ fn configure_client_system_roots(policy: &Policy) -> Result Result { let mut tls_config = RustlsClientConfig::builder() .with_root_certificates(root_store) .with_no_client_auth(); diff --git a/transport/tests/integration.rs b/transport/tests/integration.rs index db96e8f..73fc824 100644 --- a/transport/tests/integration.rs +++ b/transport/tests/integration.rs @@ -1,7 +1,7 @@ use std::net::{IpAddr, Ipv4Addr}; -use mtp_codec::{CommunicationType, DataType, TypeMap}; -use mtp_transport::{Policy, connect, host}; +use mtp_codec::{CommunicationType, CommunicationValue, DataType, DataValue, TypeMap}; +use mtp_transport::{Host, Policy, Receiver, Sender, connect, host}; fn generate_self_signed_cert() -> (Vec, Vec) { let key_pair = rcgen::KeyPair::generate().unwrap(); @@ -13,10 +13,8 @@ fn generate_self_signed_cert() -> (Vec, Vec) { (cert_pem.into_bytes(), key_pem.into_bytes()) } -#[tokio::test] -async fn test_host_start_and_stop() { - let (cert_pem, key_pem) = generate_self_signed_cert(); - let h = host( +async fn start_test_host(cert_pem: Vec, key_pem: Vec) -> Host { + host( IpAddr::V4(Ipv4Addr::LOCALHOST), 0, cert_pem, @@ -24,7 +22,48 @@ async fn test_host_start_and_stop() { Policy::default(), ) .await - .unwrap(); + .unwrap() +} + +async fn connect_to_host(h: &Host, cert_pem: Vec) -> (Sender, Receiver) { + let url = format!("https://127.0.0.1:{}", h.local_addr().port()); + connect(&url, Some(cert_pem), Policy::default()) + .await + .unwrap() +} + +async fn connected_pair() -> (Host, Sender, Receiver, Sender, Receiver) { + let (cert_pem, key_pem) = generate_self_signed_cert(); + let mut h = start_test_host(cert_pem.clone(), key_pem).await; + let (client_tx, client_rx) = connect_to_host(&h, cert_pem).await; + let (host_tx, host_rx) = h.next().await.unwrap(); + (h, client_tx, client_rx, host_tx, host_rx) +} + +fn numbered_message(comm_type: CommunicationType, value: u128, tm: &TypeMap) -> CommunicationValue { + CommunicationValue::new(comm_type).add_data( + DataType::PqSignature.to_id(tm), + DataValue::UnsignedNumber(value), + ) +} + +fn assert_numbered_message( + message: &CommunicationValue, + comm_type: CommunicationType, + value: u128, + tm: &TypeMap, +) { + assert_eq!(message.get_type(), comm_type.to_id(tm)); + assert_eq!( + message.get_data(DataType::PqSignature.to_id(tm)).clone(), + DataValue::UnsignedNumber(value) + ); +} + +#[tokio::test] +async fn test_host_start_and_stop() { + let (cert_pem, key_pem) = generate_self_signed_cert(); + let h = start_test_host(cert_pem, key_pem).await; let addr = h.local_addr(); // Port should be non-zero (OS-assigned) assert!(addr.port() > 0); @@ -32,58 +71,25 @@ async fn test_host_start_and_stop() { #[tokio::test] async fn test_send_receive_roundtrip() { - let (cert_pem, key_pem) = generate_self_signed_cert(); - let mut h = host( - IpAddr::V4(Ipv4Addr::LOCALHOST), - 0, - cert_pem.clone(), - key_pem, - Policy::default(), - ) - .await - .unwrap(); - let addr = h.local_addr(); - - let url = format!("https://127.0.0.1:{}", addr.port()); - let (client_tx, client_rx) = connect(&url, Some(cert_pem), Policy::default()) - .await - .unwrap(); - - // Accept on host side - let (host_tx, host_rx) = h.next().await.unwrap(); + let (_h, client_tx, client_rx, host_tx, host_rx) = connected_pair().await; let tm = TypeMap::latest(); // Client sends a simple message - let msg = mtp_codec::CommunicationValue::new(CommunicationType::Ping).add_data( - DataType::PqSignature.to_id(&tm), - mtp_codec::DataValue::UnsignedNumber(42), - ); + let msg = numbered_message(CommunicationType::Ping, 42, &tm); client_tx.send(&msg).await.unwrap(); // Host receives it let received = host_rx.receive().await.unwrap(); - assert_eq!(received.get_type(), CommunicationType::Ping.to_id(&tm)); - let val = received.get_data(DataType::PqSignature.to_id(&tm)).clone(); - assert_eq!(val, mtp_codec::DataValue::UnsignedNumber(42)); + assert_numbered_message(&received, CommunicationType::Ping, 42, &tm); // Host sends a response - let resp = mtp_codec::CommunicationValue::new(CommunicationType::Pong).add_data( - DataType::PqSignature.to_id(&tm), - mtp_codec::DataValue::UnsignedNumber(99), - ); + let resp = numbered_message(CommunicationType::Pong, 99, &tm); host_tx.send(&resp).await.unwrap(); // Client receives it let client_received = client_rx.receive().await.unwrap(); - assert_eq!( - client_received.get_type(), - CommunicationType::Pong.to_id(&tm) - ); - let client_val = client_received - .get_data(DataType::PqSignature.to_id(&tm)) - .clone(); - assert_eq!(client_val, mtp_codec::DataValue::UnsignedNumber(99)); + assert_numbered_message(&client_received, CommunicationType::Pong, 99, &tm); // Close both sides client_tx.close(); @@ -92,56 +98,31 @@ async fn test_send_receive_roundtrip() { #[tokio::test] async fn test_concurrent_messages() { - let (cert_pem, key_pem) = generate_self_signed_cert(); - let mut h = host( - IpAddr::V4(Ipv4Addr::LOCALHOST), - 0, - cert_pem.clone(), - key_pem, - Policy::default(), - ) - .await - .unwrap(); - let addr = h.local_addr(); - - let url = format!("https://127.0.0.1:{}", addr.port()); - let (client_tx, _client_rx) = connect(&url, Some(cert_pem), Policy::default()) - .await - .unwrap(); - - let (_host_tx, host_rx) = h.next().await.unwrap(); + let (_h, client_tx, _client_rx, _host_tx, host_rx) = connected_pair().await; let tm = TypeMap::latest(); // Send 5 messages in sequence for i in 0..5u128 { - let msg = mtp_codec::CommunicationValue::new(CommunicationType::Ping).add_data( - DataType::PqSignature.to_id(&tm), - mtp_codec::DataValue::UnsignedNumber(i), - ); + let msg = numbered_message(CommunicationType::Ping, i, &tm); client_tx.send(&msg).await.unwrap(); } // Receive all 5 in order for i in 0..5u128 { let received = host_rx.receive().await.unwrap(); - let val = received.get_data(DataType::PqSignature.to_id(&tm)).clone(); - assert_eq!(val, mtp_codec::DataValue::UnsignedNumber(i)); + assert_numbered_message(&received, CommunicationType::Ping, i, &tm); } // Send 3 responses back for i in 0..3u128 { - let msg = mtp_codec::CommunicationValue::new(CommunicationType::Pong).add_data( - DataType::PqSignature.to_id(&tm), - mtp_codec::DataValue::UnsignedNumber(i * 10), - ); + let msg = numbered_message(CommunicationType::Pong, i * 10, &tm); client_tx.send(&msg).await.unwrap(); } for i in 0..3u128 { let received = host_rx.receive().await.unwrap(); - let val = received.get_data(DataType::PqSignature.to_id(&tm)).clone(); - assert_eq!(val, mtp_codec::DataValue::UnsignedNumber(i * 10)); + assert_numbered_message(&received, CommunicationType::Pong, i * 10, &tm); } client_tx.close(); @@ -149,27 +130,10 @@ async fn test_concurrent_messages() { #[tokio::test] async fn test_close_detection() { - let (cert_pem, key_pem) = generate_self_signed_cert(); - let mut h = host( - IpAddr::V4(Ipv4Addr::LOCALHOST), - 0, - cert_pem.clone(), - key_pem, - Policy::default(), - ) - .await - .unwrap(); - let addr = h.local_addr(); - - let url = format!("https://127.0.0.1:{}", addr.port()); - let (client_tx, _client_rx) = connect(&url, Some(cert_pem), Policy::default()) - .await - .unwrap(); - - let (_host_tx, host_rx) = h.next().await.unwrap(); + let (_h, client_tx, _client_rx, _host_tx, host_rx) = connected_pair().await; // Send a message then close - let msg = mtp_codec::CommunicationValue::new(CommunicationType::Ping); + let msg = CommunicationValue::new(CommunicationType::Ping); client_tx.send(&msg).await.unwrap(); client_tx.close(); @@ -186,17 +150,8 @@ async fn test_close_detection() { #[tokio::test] async fn test_host_shutdown_stops_accepting() { let (cert_pem, key_pem) = generate_self_signed_cert(); - let mut h = host( - IpAddr::V4(Ipv4Addr::LOCALHOST), - 0, - cert_pem.clone(), - key_pem, - Policy::default(), - ) - .await - .unwrap(); - let addr = h.local_addr(); - let url = format!("https://127.0.0.1:{}", addr.port()); + let mut h = start_test_host(cert_pem.clone(), key_pem).await; + let url = format!("https://127.0.0.1:{}", h.local_addr().port()); // A connection succeeds while the host is accepting. let (_c_tx, _c_rx) = connect(&url, Some(cert_pem.clone()), Policy::default()) @@ -222,27 +177,10 @@ async fn test_host_shutdown_stops_accepting() { #[tokio::test] async fn test_drop_receiver_keeps_sender_alive() { - let (cert_pem, key_pem) = generate_self_signed_cert(); - let mut h = host( - IpAddr::V4(Ipv4Addr::LOCALHOST), - 0, - cert_pem.clone(), - key_pem, - Policy::default(), - ) - .await - .unwrap(); - let addr = h.local_addr(); - - let url = format!("https://127.0.0.1:{}", addr.port()); - let (client_tx, client_rx) = connect(&url, Some(cert_pem), Policy::default()) - .await - .unwrap(); - - let (host_tx, host_rx) = h.next().await.unwrap(); + let (_h, client_tx, client_rx, host_tx, host_rx) = connected_pair().await; // Client sends a message the host receives. - let msg = mtp_codec::CommunicationValue::new(CommunicationType::Ping); + let msg = CommunicationValue::new(CommunicationType::Ping); client_tx.send(&msg).await.unwrap(); let _ = host_rx.receive().await.unwrap(); @@ -252,18 +190,11 @@ async fn test_drop_receiver_keeps_sender_alive() { let tm = TypeMap::latest(); - let resp = mtp_codec::CommunicationValue::new(CommunicationType::Pong).add_data( - DataType::PqSignature.to_id(&tm), - mtp_codec::DataValue::UnsignedNumber(7), - ); + let resp = numbered_message(CommunicationType::Pong, 7, &tm); host_tx.send(&resp).await.unwrap(); let got = client_rx.receive().await.unwrap(); - assert_eq!(got.get_type(), CommunicationType::Pong.to_id(&tm)); - assert_eq!( - got.get_data(DataType::PqSignature.to_id(&tm)).clone(), - mtp_codec::DataValue::UnsignedNumber(7) - ); + assert_numbered_message(&got, CommunicationType::Pong, 7, &tm); client_tx.close(); host_tx.close(); diff --git a/type-map/build.rs b/type-map/build.rs index 5d3d321..9c4c318 100755 --- a/type-map/build.rs +++ b/type-map/build.rs @@ -123,10 +123,7 @@ const RESERVED_DATA_TYPES: &[ReservedEntry] = &[ name: "Version", id: 0, }, - ReservedEntry { - name: "Id", - id: 1, - }, + ReservedEntry { name: "Id", id: 1 }, ReservedEntry { name: "ClientNonce", id: 2, @@ -275,18 +272,7 @@ fn generate(config: &Config, multi_version: bool) -> String { } fn generate_protocol_version(out: &mut String, config: &Config) { - let (major, minor) = if config.protocol_version.is_empty() { - (0, 0) - } else { - let parts: Vec<&str> = config.protocol_version.split('.').collect(); - if parts.len() == 2 { - let maj = parts[0].parse::().unwrap_or(0); - let min = parts[1].parse::().unwrap_or(0); - (maj, min) - } else { - (0, 0) - } - }; + let (major, minor) = parse_protocol_version(&config.protocol_version); writeln!( out, "pub const PROTOCOL_VERSION: Version = Version({}, {});", @@ -297,18 +283,7 @@ fn generate_protocol_version(out: &mut String, config: &Config) { } fn generate_latest_method(out: &mut String, config: &Config) { - let (major, minor) = if config.protocol_version.is_empty() { - (0, 0) - } else { - let parts: Vec<&str> = config.protocol_version.split('.').collect(); - if parts.len() == 2 { - let maj = parts[0].parse::().unwrap_or(0); - let min = parts[1].parse::().unwrap_or(0); - (maj, min) - } else { - (0, 0) - } - }; + let (major, minor) = parse_protocol_version(&config.protocol_version); writeln!(out, "impl TypeMap {{").unwrap(); writeln!(out, " pub fn latest() -> Self {{").unwrap(); writeln!( @@ -322,6 +297,17 @@ fn generate_latest_method(out: &mut String, config: &Config) { writeln!(out).unwrap(); } +fn parse_protocol_version(version: &str) -> (u16, u16) { + let Some((major, minor)) = version.split_once('.') else { + return (0, 0); + }; + + ( + major.parse::().unwrap_or(0), + minor.parse::().unwrap_or(0), + ) +} + fn generate_comm_type_enum(out: &mut String, user_names: &BTreeSet<&str>) { writeln!( out, diff --git a/wasm/pkg/mtp_wasm.d.ts b/wasm/pkg/mtp_wasm.d.ts index 4e3f9d0..c99ca38 100644 --- a/wasm/pkg/mtp_wasm.d.ts +++ b/wasm/pkg/mtp_wasm.d.ts @@ -228,7 +228,6 @@ export interface InitOutput { readonly format_frame: (a: number, b: number) => [number, number, number, number]; readonly parse_auth_response: (a: number, b: number) => [number, number, number]; readonly parse_response_frame: (a: number, b: number) => [number, number, number, number]; - readonly main: () => void; readonly __wbg_connectionconfig_free: (a: number, b: number) => void; readonly __wbg_wasmclient_free: (a: number, b: number) => void; readonly connectionconfig_client_id: (a: number) => bigint; @@ -244,9 +243,10 @@ export interface InitOutput { readonly wasmclient_new: (a: any, b: any, c: any) => number; readonly wasmclient_send: (a: number, b: number, c: number) => any; readonly wasmclient_state: (a: number) => number; + readonly main: () => void; readonly wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584: (a: number, b: number, c: any) => [number, number]; - readonly wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369: (a: number, b: number, c: any) => [number, number]; - readonly wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369_2: (a: number, b: number, c: any) => [number, number]; + readonly wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f: (a: number, b: number, c: any) => [number, number]; + readonly wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2: (a: number, b: number, c: any) => [number, number]; readonly wasm_bindgen__convert__closures_____invoke__h4bf2427f775cf424: (a: number, b: number, c: any, d: any) => void; readonly __wbindgen_malloc: (a: number, b: number) => number; readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; diff --git a/wasm/pkg/mtp_wasm.js b/wasm/pkg/mtp_wasm.js index bbe86a2..946089e 100644 --- a/wasm/pkg/mtp_wasm.js +++ b/wasm/pkg/mtp_wasm.js @@ -977,18 +977,18 @@ function __wbg_get_imports() { return ret; }, __wbindgen_cast_0000000000000001: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 137, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 135, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584); return ret; }, __wbindgen_cast_0000000000000002: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("WebTransportSendStream")], shim_idx: 63, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369); + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("WebTransportSendStream")], shim_idx: 64, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f); return ret; }, __wbindgen_cast_0000000000000003: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("undefined")], shim_idx: 63, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369_2); + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("undefined")], shim_idx: 64, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2); return ret; }, __wbindgen_cast_0000000000000004: function(arg0) { @@ -1034,15 +1034,15 @@ function wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584(arg0, arg } } -function wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369(arg0, arg1, arg2) { - const ret = wasm.wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369(arg0, arg1, arg2); +function wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f(arg0, arg1, arg2) { + const ret = wasm.wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f(arg0, arg1, arg2); if (ret[1]) { throw takeFromExternrefTable0(ret[0]); } } -function wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369_2(arg0, arg1, arg2) { - const ret = wasm.wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369_2(arg0, arg1, arg2); +function wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2(arg0, arg1, arg2) { + const ret = wasm.wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2(arg0, arg1, arg2); if (ret[1]) { throw takeFromExternrefTable0(ret[0]); } diff --git a/wasm/pkg/mtp_wasm_bg.wasm b/wasm/pkg/mtp_wasm_bg.wasm index 379e5b2..8feff5e 100644 Binary files a/wasm/pkg/mtp_wasm_bg.wasm and b/wasm/pkg/mtp_wasm_bg.wasm differ diff --git a/wasm/pkg/mtp_wasm_bg.wasm.d.ts b/wasm/pkg/mtp_wasm_bg.wasm.d.ts index 82b833e..f7dd026 100644 --- a/wasm/pkg/mtp_wasm_bg.wasm.d.ts +++ b/wasm/pkg/mtp_wasm_bg.wasm.d.ts @@ -32,7 +32,6 @@ export const build_request_frame: (a: number, b: number, c: number, d: number, e export const format_frame: (a: number, b: number) => [number, number, number, number]; export const parse_auth_response: (a: number, b: number) => [number, number, number]; export const parse_response_frame: (a: number, b: number) => [number, number, number, number]; -export const main: () => void; export const __wbg_connectionconfig_free: (a: number, b: number) => void; export const __wbg_wasmclient_free: (a: number, b: number) => void; export const connectionconfig_client_id: (a: number) => bigint; @@ -48,9 +47,10 @@ export const wasmclient_is_supported: () => number; export const wasmclient_new: (a: any, b: any, c: any) => number; export const wasmclient_send: (a: number, b: number, c: number) => any; export const wasmclient_state: (a: number) => number; +export const main: () => void; export const wasm_bindgen__convert__closures_____invoke__h6588d25cdde23584: (a: number, b: number, c: any) => [number, number]; -export const wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369: (a: number, b: number, c: any) => [number, number]; -export const wasm_bindgen__convert__closures_____invoke__h5c81efa430e34369_2: (a: number, b: number, c: any) => [number, number]; +export const wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f: (a: number, b: number, c: any) => [number, number]; +export const wasm_bindgen__convert__closures_____invoke__h2faccefeed15993f_2: (a: number, b: number, c: any) => [number, number]; export const wasm_bindgen__convert__closures_____invoke__h4bf2427f775cf424: (a: number, b: number, c: any, d: any) => void; export const __wbindgen_malloc: (a: number, b: number) => number; export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; diff --git a/wasm/src/client.rs b/wasm/src/client.rs index 36604d6..0df8f24 100644 --- a/wasm/src/client.rs +++ b/wasm/src/client.rs @@ -103,6 +103,33 @@ fn verify_host_final( Ok(()) } +fn random_nonce() -> Result { + let mut nonce_bytes = [0u8; 16]; + getrandom::fill(&mut nonce_bytes).map_err(|_| js_error("rng failed"))?; + Ok(u128::from_be_bytes(nonce_bytes)) +} + +fn signed_challenge_response_bytes( + keyring: &mtp_crypto::Keyring, + proof_payload: &[u8], + client_nonce: u128, +) -> Result, JsValue> { + 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)))?; + + 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))) +} + #[wasm_bindgen] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConnectionState { @@ -206,17 +233,7 @@ impl WasmClient { .map_err(|e| js_error(&format!("encode failed: {}", e)))?; transport.send_frame(&ident_bytes).await?; - let loop_transport = transport.clone(); - self.transport = Some(transport); - self.set_state(ConnectionState::Connected); - - let state = self.state.clone(); - let on_msg = self.on_message.clone(); - let on_err = self.on_error.clone(); - wasm_bindgen_futures::spawn_local(async move { - loop_transport.receive_loop(on_msg, on_err).await; - state.set(ConnectionState::Disconnected); - }); + self.start_receive_loop(transport); Ok(()) } @@ -258,38 +275,18 @@ impl WasmClient { 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( + let server_challenge = self + .read_verified_challenge( + &transport, + &tm, + &host_pk, + client_id, "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); - } + ) + .await?; // 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); + let client_nonce = random_nonce()?; let proof_payload = mtp_crypto::auth::login_proof_payload( &version_str, @@ -297,20 +294,7 @@ impl WasmClient { 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)))?; + let proof = signed_challenge_response_bytes(&keyring, &proof_payload, client_nonce)?; transport.send_frame(&proof).await?; // 4. Receive and verify the host's final confirmation. @@ -357,17 +341,7 @@ impl WasmClient { } }; - let loop_transport = transport.clone(); - self.transport = Some(transport); - self.set_state(ConnectionState::Connected); - - let state = self.state.clone(); - let on_msg = self.on_message.clone(); - let on_err = self.on_error.clone(); - wasm_bindgen_futures::spawn_local(async move { - loop_transport.receive_loop(on_msg, on_err).await; - state.set(ConnectionState::Disconnected); - }); + self.start_receive_loop(transport); Ok(assigned_id) } @@ -409,36 +383,12 @@ impl WasmClient { 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); - } + let server_challenge = self + .read_verified_challenge(&transport, &tm, &host_pk, 0, "auth_register challenge") + .await?; // 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 client_nonce = random_nonce()?; let proof_payload = mtp_crypto::auth::register_proof_payload( &version_str, @@ -446,20 +396,7 @@ impl WasmClient { 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)))?; + let proof = signed_challenge_response_bytes(&keyring, &proof_payload, client_nonce)?; transport.send_frame(&proof).await?; // 4. Receive the host's final confirmation; extract + verify assigned id. @@ -505,17 +442,7 @@ impl WasmClient { return Err(e); } - let loop_transport = transport.clone(); - self.transport = Some(transport); - self.set_state(ConnectionState::Connected); - - let state = self.state.clone(); - let on_msg = self.on_message.clone(); - let on_err = self.on_error.clone(); - wasm_bindgen_futures::spawn_local(async move { - loop_transport.receive_loop(on_msg, on_err).await; - state.set(ConnectionState::Disconnected); - }); + self.start_receive_loop(transport); Ok(assigned_id) } @@ -543,4 +470,57 @@ impl WasmClient { .on_state_change .call1(&JsValue::NULL, &JsValue::from(new_state as u8)); } + + fn start_receive_loop(&mut self, transport: WasmTransport) { + let loop_transport = transport.clone(); + self.transport = Some(transport); + self.set_state(ConnectionState::Connected); + + let state = self.state.clone(); + let on_msg = self.on_message.clone(); + let on_err = self.on_error.clone(); + wasm_bindgen_futures::spawn_local(async move { + loop_transport.receive_loop(on_msg, on_err).await; + state.set(ConnectionState::Disconnected); + }); + } + + async fn read_verified_challenge( + &self, + transport: &WasmTransport, + tm: &mtp_codec::TypeMap, + host_pk: &mtp_crypto::PublicKeyBundle, + bound_id: u64, + context: &str, + ) -> Result { + 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( + context, + 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, bound_id, server_challenge) { + self.set_state(ConnectionState::Disconnected); + return Err(e); + } + + Ok(server_challenge) + } } diff --git a/wasm/src/message.rs b/wasm/src/message.rs index bad844f..2d94873 100644 --- a/wasm/src/message.rs +++ b/wasm/src/message.rs @@ -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, TypeMap}; +use mtp_type_map::{TypeMap, communication_type_name}; use crate::error::js_error; @@ -54,8 +54,14 @@ pub fn build_demo_message( // Encrypted container let inner_enc = DataValue::Container(vec![ - (DataType::Version.to_id(&TypeMap::latest()), DataValue::Str("secret inner data".into())), - (DataType::Id.to_id(&TypeMap::latest()), 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 +70,14 @@ pub fn build_demo_message( // Signed container let inner_sig = DataValue::Container(vec![ - (DataType::Version.to_id(&TypeMap::latest()), DataValue::Str("signed by client".into())), - (DataType::Id.to_id(&TypeMap::latest()), 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 @@ -78,7 +90,10 @@ pub fn build_demo_message( DataType::Version.to_id(&TypeMap::latest()), DataValue::Str("signed+encrypted payload".into()), ), - (DataType::Id.to_id(&TypeMap::latest()), DataValue::UnsignedNumber(7)), + ( + DataType::Id.to_id(&TypeMap::latest()), + DataValue::UnsignedNumber(7), + ), ]); let mut dv_sec = inner_sec; dv_sec @@ -115,7 +130,10 @@ pub fn parse_auth_response(response: &[u8]) -> Result { let comm = CommunicationValue::from_bytes(response) .map_err(|e| js_error(&format!("parse failed: {}", e)))?; - let connected = matches!(comm.get_data(DataType::Connected.to_id(&TypeMap::latest())), DataValue::BoolTrue); + let connected = matches!( + comm.get_data(DataType::Connected.to_id(&TypeMap::latest())), + DataValue::BoolTrue + ); let client_nonce = match comm.get_data(DataType::ClientNonce.to_id(&TypeMap::latest())) { DataValue::UnsignedNumber(n) => Some(*n), @@ -279,7 +297,10 @@ mod tests { cv.get_data(DataType::Description.to_id(&tm)), &DataValue::Str("with-data".into()) ); - assert_eq!(cv.get_data(DataType::Timestamp.to_id(&tm)), &DataValue::UnsignedNumber(555)); + assert_eq!( + cv.get_data(DataType::Timestamp.to_id(&tm)), + &DataValue::UnsignedNumber(555) + ); assert_eq!( cv.get_data(DataType::Id.to_id(&tm)), &DataValue::Bytes(payload.to_vec()) diff --git a/wasm/src/transport.rs b/wasm/src/transport.rs index 85b2010..795a6f4 100644 --- a/wasm/src/transport.rs +++ b/wasm/src/transport.rs @@ -51,10 +51,18 @@ enum FrameOutcome { * [11..] id/sender/receiver/signature/data, gated by `flags` */ fn log_frame_bytes(wire: &[u8]) { - let hex: String = wire.iter().map(|b| format!("{b:02x}")).collect::>().join(" "); + let hex: String = wire + .iter() + .map(|b| format!("{b:02x}")) + .collect::>() + .join(" "); - let outer_len = wire.get(0..4).map(|b| u32::from_be_bytes([b[0], b[1], b[2], b[3]])); - let inner_len = wire.get(4..8).map(|b| u32::from_be_bytes([b[0], b[1], b[2], b[3]])); + let outer_len = wire + .get(0..4) + .map(|b| u32::from_be_bytes([b[0], b[1], b[2], b[3]])); + let inner_len = wire + .get(4..8) + .map(|b| u32::from_be_bytes([b[0], b[1], b[2], b[3]])); let comm_type = wire.get(8..10).map(|b| u16::from_be_bytes([b[0], b[1]])); let flags = wire.get(10).copied();