// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 // all variable size data is always prefixed with u64 length // tags are u8 use crate::error::{self, ErrorKind}; use crate::text::ServerResponseText; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE}; use nym_sphinx::receiver::ReconstructedMessage; use std::mem::size_of; #[repr(u8)] enum ServerResponseTag { /// Value tag representing [`Error`] variant of the [`ServerResponse`] Error = 0x00, /// Value tag representing [`Received`] variant of the [`ServerResponse`] Received = 0x01, /// Value tag representing [`SelfAddress`] variant of the [`ServerResponse`] SelfAddress = 0x02, /// Value tag representing [`LaneQueueLength`] variant of the [`ServerResponse`] LaneQueueLength = 0x03, } impl TryFrom for ServerResponseTag { type Error = error::Error; fn try_from(value: u8) -> Result { match value { _ if value == (Self::Error as u8) => Ok(Self::Error), _ if value == (Self::Received as u8) => Ok(Self::Received), _ if value == (Self::SelfAddress as u8) => Ok(Self::SelfAddress), _ if value == (Self::LaneQueueLength as u8) => Ok(Self::LaneQueueLength), n => Err(error::Error::new( ErrorKind::UnknownResponse, format!("{n} does not correspond to any valid response tag"), )), } } } #[derive(Debug)] pub enum ServerResponse { Received(ReconstructedMessage), SelfAddress(Box), LaneQueueLength { lane: u64, queue_length: usize }, Error(error::Error), } impl ServerResponse { pub fn new_error>(message: S) -> Self { ServerResponse::Error(error::Error { kind: ErrorKind::Other, message: message.into(), }) } // RECEIVED_RESPONSE_TAG || 1 | 0 indicating sender_tag || Option || msg_len || msg fn serialize_received(reconstructed_message: ReconstructedMessage) -> Vec { let message_len_bytes = (reconstructed_message.message.len() as u64).to_be_bytes(); if let Some(sender_tag) = reconstructed_message.sender_tag { std::iter::once(ServerResponseTag::Received as u8) .chain(std::iter::once(true as u8)) .chain(sender_tag.to_bytes()) .chain(message_len_bytes.iter().cloned()) .chain(reconstructed_message.message) .collect() } else { std::iter::once(ServerResponseTag::Received as u8) .chain(std::iter::once(false as u8)) .chain(message_len_bytes.iter().cloned()) .chain(reconstructed_message.message) .collect() } } // RECEIVED_RESPONSE_TAG || 1 | 0 indicating sender_tag || Option || msg_len || msg fn deserialize_received(b: &[u8]) -> Result { // this MUST match because it was called by 'deserialize' // we must be able to read at the very least if it has a reply_surb and length of some field if b.len() < 2 + size_of::() { return Err(error::Error::new( ErrorKind::TooShortResponse, "not enough data provided to recover 'received'".to_string(), )); } debug_assert_eq!(b[0], ServerResponseTag::Received as u8); let has_sender_tag = match b[1] { 0 => false, 1 => true, n => { return Err(error::Error::new( ErrorKind::MalformedResponse, format!("invalid sender tag flag {n}"), )) } }; let mut i = 2; let sender_tag = if has_sender_tag { if b[2..].len() < SENDER_TAG_SIZE { return Err(error::Error::new( ErrorKind::TooShortResponse, "not enough data provided to recover 'received'".to_string(), )); } i += SENDER_TAG_SIZE; Some(AnonymousSenderTag::from_bytes( b[2..2 + SENDER_TAG_SIZE].try_into().unwrap(), )) } else { None }; if b[i..].len() < size_of::() { return Err(error::Error::new( ErrorKind::TooShortResponse, "not enough data provided to recover 'received'".to_string(), )); } let message_len = u64::from_be_bytes(b[i..i + size_of::()].try_into().unwrap()); let message = &b[i + size_of::()..]; if message.len() as u64 != message_len { return Err(error::Error::new( ErrorKind::MalformedResponse, format!( "message len has inconsistent length. specified: {} got: {}", message_len, message.len() ), )); } Ok(ServerResponse::Received(ReconstructedMessage { message: message.to_vec(), sender_tag, })) } // SELF_ADDRESS_RESPONSE_TAG || self_address fn serialize_self_address(address: Recipient) -> Vec { std::iter::once(ServerResponseTag::SelfAddress as u8) .chain(address.to_bytes()) .collect() } // SELF_ADDRESS_RESPONSE_TAG || self_address fn deserialize_self_address(b: &[u8]) -> Result { if b.len() != 1 + Recipient::LEN { return Err(error::Error::new( ErrorKind::TooShortResponse, "not enough data provided to recover 'self_address'".to_string(), )); } // this MUST match because it was called by 'deserialize' debug_assert_eq!(b[0], ServerResponseTag::SelfAddress as u8); let mut recipient_bytes = [0u8; Recipient::LEN]; recipient_bytes.copy_from_slice(&b[1..1 + Recipient::LEN]); let recipient = match Recipient::try_from_bytes(recipient_bytes) { Ok(recipient) => recipient, Err(err) => { return Err(error::Error::new( ErrorKind::MalformedResponse, format!("malformed Recipient: {err}"), )) } }; Ok(ServerResponse::SelfAddress(Box::new(recipient))) } // LANE_QUEUE_LENGTH_RESPONSE_TAG || lane || queue_length fn serialize_lane_queue_length(lane: u64, queue_length: usize) -> Vec { std::iter::once(ServerResponseTag::LaneQueueLength as u8) .chain(lane.to_be_bytes().iter().cloned()) .chain(queue_length.to_be_bytes().iter().cloned()) .collect() } // LANE_QUEUE_LENGTH_RESPONSE_TAG || lane || queue_length fn deserialize_lane_queue_length(b: &[u8]) -> Result { // this MUST match because it was called by 'deserialize' debug_assert_eq!(b[0], ServerResponseTag::LaneQueueLength as u8); let mut lane_bytes = [0u8; size_of::()]; lane_bytes.copy_from_slice(&b[1..=size_of::()]); let lane = u64::from_be_bytes(lane_bytes); let mut queue_length_bytes = [0u8; size_of::()]; queue_length_bytes .copy_from_slice(&b[1 + size_of::()..1 + size_of::() + size_of::()]); let queue_length = usize::from_be_bytes(queue_length_bytes); Ok(ServerResponse::LaneQueueLength { lane, queue_length }) } // ERROR_RESPONSE_TAG || err_code || msg_len || msg fn serialize_error(error: error::Error) -> Vec { let message_len_bytes = (error.message.len() as u64).to_be_bytes(); std::iter::once(ServerResponseTag::Error as u8) .chain(std::iter::once(error.kind as u8)) .chain(message_len_bytes) .chain(error.message.into_bytes()) .collect() } // ERROR_RESPONSE_TAG || err_code || msg_len || msg fn deserialize_error(b: &[u8]) -> Result { // this MUST match because it was called by 'deserialize' debug_assert_eq!(b[0], ServerResponseTag::Error as u8); if b.len() < size_of::() + size_of::() { return Err(error::Error::new( ErrorKind::TooShortResponse, "not enough data provided to recover 'error'".to_string(), )); } let error_kind = ErrorKind::try_from(b[1])?; let message_len = u64::from_be_bytes(b[2..2 + size_of::()].try_into().unwrap()); let message = &b[2 + size_of::()..]; if message.len() as u64 != message_len { return Err(error::Error::new( ErrorKind::MalformedResponse, format!( "message len has inconsistent length. specified: {} got: {}", message_len, message.len() ), )); } let err_message = match String::from_utf8(message.to_vec()) { Ok(msg) => msg, Err(err) => { return Err(error::Error::new( ErrorKind::MalformedResponse, format!("malformed error message: {err}"), )) } }; Ok(ServerResponse::Error(error::Error::new( error_kind, err_message, ))) } pub fn serialize(self) -> Vec { match self { ServerResponse::Received(reconstructed_message) => { Self::serialize_received(reconstructed_message) } ServerResponse::SelfAddress(address) => Self::serialize_self_address(*address), ServerResponse::LaneQueueLength { lane, queue_length } => { Self::serialize_lane_queue_length(lane, queue_length) } ServerResponse::Error(err) => Self::serialize_error(err), } } pub fn deserialize(b: &[u8]) -> Result { if b.is_empty() { // technically I'm not even sure this can ever be returned, because reading empty // request would imply closed socket, but let's include it for completion sake return Err(error::Error::new( ErrorKind::EmptyResponse, "no data provided".to_string(), )); } if b.len() < size_of::() { return Err(error::Error::new( ErrorKind::TooShortResponse, format!( "not enough data provided to recover response tag. Provided only {} bytes", b.len() ), )); } let response_tag = ServerResponseTag::try_from(b[0])?; // determine what kind of response that is and try to deserialize it match response_tag { ServerResponseTag::Received => Self::deserialize_received(b), ServerResponseTag::SelfAddress => Self::deserialize_self_address(b), ServerResponseTag::LaneQueueLength => Self::deserialize_lane_queue_length(b), ServerResponseTag::Error => Self::deserialize_error(b), } } pub fn into_binary(self) -> Vec { self.serialize() } pub fn into_text(self) -> String { // use the intermediate string structure and let serde do bunch of work for us let text_resp = ServerResponseText::from(self); text_resp.into() } } #[cfg(test)] mod tests { use super::*; #[test] fn received_response_serialization_works() { let received_with_sender_tag = ServerResponse::Received(ReconstructedMessage { message: b"foomp".to_vec(), sender_tag: Some([42u8; SENDER_TAG_SIZE].into()), }); let bytes = received_with_sender_tag.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { ServerResponse::Received(reconstructed) => { assert_eq!(reconstructed.message, b"foomp".to_vec()); assert_eq!( reconstructed.sender_tag, Some([42u8; SENDER_TAG_SIZE].into()) ) } _ => unreachable!(), } let received_without_sender_tag = ServerResponse::Received(ReconstructedMessage { message: b"foomp".to_vec(), sender_tag: None, }); let bytes = received_without_sender_tag.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { ServerResponse::Received(reconstructed) => { assert_eq!(reconstructed.message, b"foomp".to_vec()); assert!(reconstructed.sender_tag.is_none()) } _ => unreachable!(), } } #[test] fn self_address_response_serialization_works() { let recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap(); let recipient_string = recipient.to_string(); let self_address_response = ServerResponse::SelfAddress(Box::new(recipient)); let bytes = self_address_response.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { ServerResponse::SelfAddress(recipient) => { assert_eq!(recipient.to_string(), recipient_string) } _ => unreachable!(), } } #[test] fn lane_queue_length_response_serialization_works() { let lane_queue_length_response = ServerResponse::LaneQueueLength { lane: 13, queue_length: 42, }; let bytes = lane_queue_length_response.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { ServerResponse::LaneQueueLength { lane, queue_length } => { assert_eq!(lane, 13); assert_eq!(queue_length, 42) } _ => unreachable!(), } } #[test] fn error_response_serialization_works() { let dummy_error = error::Error::new(ErrorKind::UnknownRequest, "foomp message".to_string()); let error_response = ServerResponse::Error(dummy_error.clone()); let bytes = error_response.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { ServerResponse::Error(error) => assert_eq!(error, dummy_error), _ => unreachable!(), } } }