1use std::{borrow::Cow, fmt::Formatter, marker::PhantomData, ops::Not};
2
3use hopr_crypto_sphinx::{
4 errors::SphinxError,
5 prelude::{PaddedPayload, SURB, SphinxHeaderSpec, SphinxSuite},
6};
7use hopr_types::primitive::prelude::GeneralError;
8
9use crate::{HoprSphinxHeaderSpec, HoprSphinxSuite, PAYLOAD_SIZE_INT};
10
11flagset::flags! {
12 #[repr(u8)]
14 #[derive(PartialOrd, Ord, strum::EnumString, strum::Display)]
15 pub enum PacketSignal: u8 {
16 SurbDistress = 0b0000_0001,
20 OutOfSurbs = 0b0000_0011,
27 }
28}
29
30pub type PacketSignals = flagset::FlagSet<PacketSignal>;
35
36pub struct PacketMessage<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize>(PaddedPayload<P>, PhantomData<(S, H)>);
38
39pub type HoprPacketMessage = PacketMessage<HoprSphinxSuite, HoprSphinxHeaderSpec, PAYLOAD_SIZE_INT>;
41
42pub struct PacketParts<'a, S: SphinxSuite, H: SphinxHeaderSpec> {
45 pub surbs: Vec<SURB<S, H>>,
47 pub payload: Cow<'a, [u8]>,
49 pub signals: PacketSignals,
51}
52
53impl<S: SphinxSuite, H: SphinxHeaderSpec> Clone for PacketParts<'_, S, H>
54where
55 H::KeyId: Clone,
56 H::SurbReceiverData: Clone,
57{
58 fn clone(&self) -> Self {
59 Self {
60 surbs: self.surbs.clone(),
61 payload: self.payload.clone(),
62 signals: self.signals,
63 }
64 }
65}
66
67impl<S: SphinxSuite, H: SphinxHeaderSpec> std::fmt::Debug for PacketParts<'_, S, H>
68where
69 H::KeyId: std::fmt::Debug,
70 H::SurbReceiverData: std::fmt::Debug,
71{
72 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
73 f.debug_struct("PacketParts")
74 .field("surbs", &self.surbs)
75 .field("payload", &self.payload)
76 .field("signals", &self.signals)
77 .finish()
78 }
79}
80
81impl<S: SphinxSuite, H: SphinxHeaderSpec> PartialEq for PacketParts<'_, S, H>
82where
83 H::KeyId: PartialEq,
84 H::SurbReceiverData: PartialEq,
85{
86 fn eq(&self, other: &Self) -> bool {
87 self.surbs == other.surbs && self.payload == other.payload && self.signals == other.signals
88 }
89}
90
91impl<S: SphinxSuite, H: SphinxHeaderSpec> Eq for PacketParts<'_, S, H>
92where
93 H::KeyId: Eq,
94 H::SurbReceiverData: Eq,
95{
96}
97
98pub type HoprPacketParts<'a> = PacketParts<'a, HoprSphinxSuite, HoprSphinxHeaderSpec>;
100
101pub(crate) const S_MASK: u8 = 0b0000_1111;
103
104impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> PacketMessage<S, H, P> {
105 pub const HEADER_LEN: usize = 1;
109 pub const MAX_SURBS_PER_MESSAGE: usize = S_MASK as usize;
114}
115
116impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> TryFrom<PacketParts<'_, S, H>> for PacketMessage<S, H, P> {
117 type Error = SphinxError;
118
119 fn try_from(value: PacketParts<S, H>) -> Result<Self, Self::Error> {
120 if value.surbs.len() > Self::MAX_SURBS_PER_MESSAGE {
121 return Err(GeneralError::ParseError("HoprPacketMessage.num_surbs not valid".into()).into());
122 }
123
124 if value.signals.bits() > S_MASK {
125 return Err(GeneralError::ParseError("HoprPacketMessage.flags not valid".into()).into());
126 }
127
128 if Self::HEADER_LEN + value.surbs.len() * SURB::<S, H>::SIZE + value.payload.len() > P {
130 return Err(GeneralError::ParseError("HoprPacketMessage.size not valid".into()).into());
131 }
132
133 let mut ret = Vec::with_capacity(PaddedPayload::<P>::SIZE);
134 let flags_and_len = (value.signals.bits() << S_MASK.trailing_ones()) | (value.surbs.len() as u8 & S_MASK);
135 ret.push(flags_and_len);
136 for surb in value.surbs.into_iter().map(|s| s.into_boxed()) {
137 ret.extend(surb);
138 }
139 ret.extend_from_slice(value.payload.as_ref());
140
141 Ok(Self(PaddedPayload::new_from_vec(ret)?, PhantomData))
143 }
144}
145
146impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> TryFrom<PacketMessage<S, H, P>> for PacketParts<'_, S, H> {
147 type Error = SphinxError;
148
149 fn try_from(value: PacketMessage<S, H, P>) -> Result<Self, Self::Error> {
150 let data = value.0.into_unpadded()?;
151 if data.is_empty() {
152 return Err(GeneralError::ParseError("HoprPacketMessage.size".into()).into());
153 }
154
155 let num_surbs = (data[0] & S_MASK) as usize;
156 let signals = PacketSignals::new((data[0] & S_MASK.not()) >> S_MASK.trailing_ones())
157 .map_err(|_| GeneralError::ParseError("HoprPacketMessage.signals".into()))?;
158
159 if num_surbs > 0 {
160 let surb_end = num_surbs * SURB::<S, H>::SIZE;
161 if surb_end >= data.len() {
162 return Err(GeneralError::ParseError("HoprPacketMessage.num_surbs not valid".into()).into());
163 }
164
165 let mut data = data.into_vec();
166
167 let surbs = data[1..=surb_end]
168 .chunks_exact(SURB::<S, H>::SIZE)
169 .map(SURB::<S, H>::try_from)
170 .collect::<Result<Vec<_>, _>>()?;
171
172 data.drain(0..=surb_end).for_each(drop);
174
175 Ok(PacketParts {
176 surbs,
177 payload: Cow::Owned(data),
178 signals,
179 })
180 } else {
181 let mut data = data.into_vec();
182 data.remove(0);
183 Ok(PacketParts {
184 surbs: Vec::with_capacity(0),
185 payload: Cow::Owned(data),
186 signals,
187 })
188 }
189 }
190}
191
192impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> From<PaddedPayload<P>> for PacketMessage<S, H, P> {
193 fn from(value: PaddedPayload<P>) -> Self {
194 Self(value, PhantomData)
195 }
196}
197
198impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> From<PacketMessage<S, H, P>> for PaddedPayload<P> {
199 fn from(value: PacketMessage<S, H, P>) -> Self {
200 value.0
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use anyhow::anyhow;
207 use bimap::BiHashMap;
208 use hex_literal::hex;
209 use hopr_crypto_sphinx::prelude::*;
210 use hopr_types::{
211 crypto::prelude::*, crypto_random::Randomizable, internal::routing::HoprSenderId, primitive::prelude::*,
212 };
213
214 use super::*;
215 use crate::{
216 HoprSphinxHeaderSpec, HoprSphinxSuite, HoprSurb,
217 packet::HoprPacket,
218 por::{SurbReceiverInfo, generate_proof_of_relay},
219 };
220
221 lazy_static::lazy_static! {
222 static ref PEERS: [(ChainKeypair, OffchainKeypair); 4] = [
223 (hex!("a7c486ceccf5ab53bd428888ab1543dc2667abd2d5e80aae918da8d4b503a426"), hex!("5eb212d4d6aa5948c4f71574d45dad43afef6d330edb873fca69d0e1b197e906")),
224 (hex!("9a82976f7182c05126313bead5617c623b93d11f9f9691c87b1a26f869d569ed"), hex!("e995db483ada5174666c46bafbf3628005aca449c94ebdc0c9239c3f65d61ae0")),
225 (hex!("ca4bdfd54a8467b5283a0216288fdca7091122479ccf3cfb147dfa59d13f3486"), hex!("9dec751c00f49e50fceff7114823f726a0425a68a8dc6af0e4287badfea8f4a4")),
226 (hex!("e306ebfb0d01d0da0952c9a567d758093a80622c6cb55052bf5f1a6ebd8d7b5c"), hex!("9a82976f7182c05126313bead5617c623b93d11f9f9691c87b1a26f869d569ed"))
227 ].map(|(p1,p2)| (ChainKeypair::from_secret(&p1).expect("lazy static keypair should be valid"), OffchainKeypair::from_secret(&p2).expect("lazy static keypair should be valid")));
228
229 static ref MAPPER: SimpleBiMapper<HoprSphinxSuite, HoprSphinxHeaderSpec> = PEERS
230 .iter()
231 .enumerate()
232 .map(|(i, (_, k))| (KeyIdent::from(i as u32), *k.public()))
233 .collect::<BiHashMap<_, _>>()
234 .into();
235 }
236
237 fn generate_surbs(count: usize) -> anyhow::Result<Vec<SURB<HoprSphinxSuite, HoprSphinxHeaderSpec>>> {
238 let path = PEERS.iter().map(|(_, k)| *k.public()).collect::<Vec<_>>();
239 let path_ids = MAPPER
240 .map_keys_to_ids(&path)
241 .into_iter()
242 .map(|v| v.ok_or(anyhow!("missing id")))
243 .collect::<Result<Vec<_>, _>>()?;
244 let pseudonym = SimplePseudonym::random();
245 let recv_data = HoprSenderId::new(&pseudonym);
246
247 Ok((0..count)
248 .map(|_| {
249 let shared_keys = HoprSphinxSuite::new_shared_keys(&path)?;
250 let (por_strings, por_values) = generate_proof_of_relay(&shared_keys.secrets)
251 .map_err(|e| CryptoError::Other(GeneralError::NonSpecificError(e.to_string())))?;
252
253 create_surb::<HoprSphinxSuite, HoprSphinxHeaderSpec>(
254 shared_keys,
255 &path_ids,
256 &por_strings,
257 recv_data,
258 SurbReceiverInfo::new(por_values, [0u8; 32]),
259 )
260 .map(|(s, _)| s)
261 })
262 .collect::<Result<Vec<_>, _>>()?)
263 }
264
265 #[test]
266 fn hopr_packet_message_message_only() -> anyhow::Result<()> {
267 let parts_1 = HoprPacketParts {
268 surbs: vec![],
269 payload: b"test message".into(),
270 signals: PacketSignal::OutOfSurbs.into(),
271 };
272
273 let parts_2: HoprPacketParts = HoprPacketMessage::try_from(parts_1.clone())?.try_into()?;
274 assert_eq!(parts_1, parts_2);
275
276 Ok(())
277 }
278
279 #[test]
280 fn hopr_packet_message_surbs_only() -> anyhow::Result<()> {
281 let parts_1 = HoprPacketParts {
282 surbs: generate_surbs(2)?,
283 payload: Cow::default(),
284 signals: PacketSignal::OutOfSurbs.into(),
285 };
286
287 let parts_2: HoprPacketParts = HoprPacketMessage::try_from(parts_1.clone())?.try_into()?;
288 assert_eq!(parts_1, parts_2);
289
290 Ok(())
291 }
292
293 #[test]
294 fn hopr_packet_message_surbs_and_msg() -> anyhow::Result<()> {
295 let parts_1 = HoprPacketParts {
296 surbs: generate_surbs(2)?,
297 payload: b"test msg".into(),
298 signals: PacketSignal::OutOfSurbs.into(),
299 };
300
301 let parts_2: HoprPacketParts = HoprPacketMessage::try_from(parts_1.clone())?.try_into()?;
302 assert_eq!(parts_1, parts_2);
303
304 Ok(())
305 }
306
307 #[test]
308 fn hopr_packet_size_msg_size_limit() {
309 let res = HoprPacketMessage::try_from(HoprPacketParts {
310 surbs: vec![],
311 payload: (&[1u8; HoprPacket::PAYLOAD_SIZE + 1]).into(),
312 signals: None.into(),
313 });
314 assert!(res.is_err());
315 }
316
317 #[test]
318 fn hopr_packet_message_surbs_size_limit() -> anyhow::Result<()> {
319 let res = HoprPacketMessage::try_from(PacketParts {
320 surbs: generate_surbs(HoprPacketMessage::MAX_SURBS_PER_MESSAGE + 1)?,
321 payload: Cow::default(),
322 signals: None.into(),
323 });
324 assert!(res.is_err());
325
326 let res = HoprPacketMessage::try_from(HoprPacketParts {
327 surbs: generate_surbs(3)?,
328 payload: Cow::default(),
329 signals: None.into(),
330 });
331 assert!(res.is_err());
332
333 Ok(())
334 }
335
336 #[test]
337 fn hopr_packet_message_surbs_flag_limit() -> anyhow::Result<()> {
338 let res = HoprPacketMessage::try_from(PacketParts {
339 surbs: generate_surbs(3)?,
340 payload: Cow::default(),
341 signals: unsafe { PacketSignals::new_unchecked(16) },
342 });
343 assert!(res.is_err());
344
345 Ok(())
346 }
347
348 #[test]
349 fn hopr_packet_size_msg_and_surb_size_limit() -> anyhow::Result<()> {
350 let res = HoprPacketMessage::try_from(PacketParts {
351 surbs: generate_surbs(2)?,
352 payload: (&[1u8; HoprPacket::PAYLOAD_SIZE - 2 * HoprSurb::SIZE + 1]).into(),
353 signals: None.into(),
354 });
355 assert!(res.is_err());
356
357 Ok(())
358 }
359}