diff --git a/common/nym-lp/src/codec.rs b/common/nym-lp/src/codec.rs index 62c59cc33a..02fca3df5e 100644 --- a/common/nym-lp/src/codec.rs +++ b/common/nym-lp/src/codec.rs @@ -111,9 +111,7 @@ fn parse_message_from_type_and_content( content.to_vec(), ))), MessageType::ClientHello => { - let data: ClientHelloData = lp_bincode_serializer() - .deserialize(content) - .map_err(|e| LpError::DeserializationError(e.to_string()))?; + let data = ClientHelloData::decode(content)?; Ok(LpMessage::ClientHello(data)) } MessageType::KKTRequest => Ok(LpMessage::KKTRequest(KKTRequestData(content.to_vec()))), @@ -791,7 +789,7 @@ mod tests { } #[test] - fn test_parse_client_hello_malformed_bincode() { + fn test_parse_client_hello_malformed_data() { // Create a buffer with ClientHello message type but invalid bincode data let mut buf = BytesMut::new(); buf.extend_from_slice(&[1, 0, 0, 0]); // Version + reserved @@ -799,15 +797,15 @@ mod tests { buf.extend_from_slice(&123u64.to_le_bytes()); // Counter buf.extend_from_slice(&MessageType::ClientHello.to_u32().to_le_bytes()); // ClientHello type - // Add malformed bincode data (random bytes that won't deserialize to ClientHelloData) - buf.extend_from_slice(&[0xFF; 50]); // Invalid bincode data + // Add data that does not equal to ClientHelloData::LEN + buf.extend_from_slice(&[0xFF; 50]); // invalid data buf.extend_from_slice(&[0; TRAILER_LEN]); // Trailer // Attempt to parse let result = parse_lp_packet(&buf, None); assert!(result.is_err()); match result { - Err(LpError::DeserializationError(_)) => {} // Expected error + Err(LpError::DeserializationError(msg)) => {} // Expected error Err(e) => panic!("Expected DeserializationError, got {:?}", e), Ok(_) => panic!("Expected error, but got Ok"), } diff --git a/common/nym-lp/src/message.rs b/common/nym-lp/src/message.rs index 3bc0f1765c..cea662e0b3 100644 --- a/common/nym-lp/src/message.rs +++ b/common/nym-lp/src/message.rs @@ -1,6 +1,7 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::LpError; use crate::serialisation::{BincodeOptions, lp_bincode_serializer}; use bytes::{BufMut, BytesMut}; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -22,6 +23,8 @@ pub struct ClientHelloData { } impl ClientHelloData { + pub const LEN: usize = 100; + /// Generates a new ClientHelloData with fresh salt. /// /// Salt format: 8 bytes timestamp (u64 LE) + 24 bytes random nonce @@ -61,6 +64,33 @@ impl ClientHelloData { timestamp_bytes.copy_from_slice(&self.salt[..8]); u64::from_le_bytes(timestamp_bytes) } + + pub fn encode(&self) -> Vec { + let mut out = Vec::with_capacity(Self::LEN); + out.put_u32_le(self.receiver_index); + out.put_slice(&self.client_lp_public_key); + out.put_slice(&self.client_ed25519_public_key); + out.put_slice(&self.salt); + out + } + pub fn decode(b: &[u8]) -> Result { + if b.len() != Self::LEN { + return Err(LpError::DeserializationError(format!( + "Expected {} bytes to deserialise ClientHelloData. got {}", + Self::LEN, + b.len() + ))); + } + + // SAFETY: we checked for valid byte lengths + #[allow(clippy::unwrap_used)] + Ok(ClientHelloData { + receiver_index: u32::from_le_bytes([b[0], b[1], b[2], b[3]]), + client_lp_public_key: b[4..36].try_into().unwrap(), + client_ed25519_public_key: b[36..68].try_into().unwrap(), + salt: b[68..].try_into().unwrap(), + }) + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)] @@ -238,8 +268,8 @@ impl LpMessage { LpMessage::Busy => 0, LpMessage::Handshake(payload) => payload.0.len(), LpMessage::EncryptedData(payload) => payload.0.len(), - // 4 bytes receiver_index + 32 bytes x25519 key + 32 bytes ed25519 key + 32 bytes salt + bincode overhead - LpMessage::ClientHello(_) => 101, + // 4 bytes receiver_index + 32 bytes x25519 key + 32 bytes ed25519 key + 32 bytes salt + LpMessage::ClientHello(_) => ClientHelloData::LEN, LpMessage::KKTRequest(payload) => payload.0.len(), LpMessage::KKTResponse(payload) => payload.0.len(), LpMessage::ForwardPacket(data) => { @@ -286,11 +316,7 @@ impl LpMessage { dst.put_slice(&payload.0); } LpMessage::ClientHello(data) => { - // Serialize ClientHelloData using bincode - let serialized = lp_bincode_serializer() - .serialize(data) - .expect("Failed to serialize ClientHelloData"); - dst.put_slice(&serialized); + dst.put_slice(&data.encode()); } LpMessage::KKTRequest(payload) => { dst.put_slice(&payload.0);