1use std::{
2 fmt::{Debug, Formatter},
3 marker::PhantomData,
4 ops::{Deref, DerefMut},
5};
6
7use hopr_crypto_types::{crypto_traits::StreamCipher, prelude::*};
8use hopr_primitive_types::prelude::*;
9use typenum::Unsigned;
10
11use crate::{
12 derivation::derive_packet_tag,
13 errors::SphinxError,
14 routing::{ForwardedHeader, RoutingInfo, SphinxHeaderSpec, forward_header},
15 shared_keys::{Alpha, GroupElement, SharedKeys, SharedSecret, SphinxSuite},
16 surb::{ReplyOpener, SURB},
17};
18
19#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct PaddedPayload<const P: usize>(Box<[u8]>);
24
25impl<const P: usize> PaddedPayload<P> {
26 pub const PADDING: u8 = 0x00;
28 pub const PADDING_TAG: u8 = 0xaa;
30 pub const SIZE: usize = P + size_of_val(&Self::PADDING_TAG);
32
33 pub fn new(msg: &[u8]) -> Result<Self, SphinxError> {
41 if msg.len() < Self::SIZE {
42 let mut ret = vec![Self::PADDING; Self::SIZE];
44 ret[Self::SIZE - msg.len() - 1] = Self::PADDING_TAG;
45 ret[Self::SIZE - msg.len()..].copy_from_slice(msg);
46
47 Ok(Self(ret.into_boxed_slice()))
48 } else {
49 Err(SphinxError::PaddingError)
50 }
51 }
52
53 pub fn new_from_vec(mut msg: Vec<u8>) -> Result<Self, SphinxError> {
56 let len = msg.len();
57 if len >= Self::SIZE {
58 return Err(SphinxError::PaddingError);
59 }
60
61 msg.resize(Self::SIZE, Self::PADDING); msg.copy_within(0..len, Self::SIZE - len);
63 msg[0..Self::SIZE - len].fill(Self::PADDING);
64 msg[Self::SIZE - len - 1] = Self::PADDING_TAG;
65
66 Ok(Self(msg.into_boxed_slice()))
67 }
68
69 pub fn from_padded(msg: Vec<u8>) -> Result<Self, SphinxError> {
79 if msg.len() == Self::SIZE {
80 Ok(Self(msg.into_boxed_slice()))
81 } else {
82 Err(SphinxError::PaddingError)
83 }
84 }
85
86 pub fn into_unpadded(self) -> Result<Box<[u8]>, SphinxError> {
92 self.0
93 .iter()
94 .position(|x| *x == Self::PADDING_TAG)
95 .map(|tag_pos| {
96 let mut data = self.0.into_vec();
97 data.drain(0..=tag_pos);
98 data.into_boxed_slice()
99 })
100 .ok_or(SphinxError::PaddingError)
101 }
102}
103
104impl<const P: usize> AsRef<[u8]> for PaddedPayload<P> {
105 fn as_ref(&self) -> &[u8] {
106 self.0.as_ref()
107 }
108}
109
110impl<const P: usize> Deref for PaddedPayload<P> {
111 type Target = [u8];
112
113 fn deref(&self) -> &Self::Target {
114 self.0.deref()
115 }
116}
117
118impl<const P: usize> DerefMut for PaddedPayload<P> {
119 fn deref_mut(&mut self) -> &mut Self::Target {
120 self.0.deref_mut()
121 }
122}
123
124pub trait KeyIdMapper<S: SphinxSuite, H: SphinxHeaderSpec> {
129 fn map_key_to_id(&self, key: &<S::P as Keypair>::Public) -> Option<H::KeyId>;
131 fn map_id_to_public(&self, id: &H::KeyId) -> Option<<S::P as Keypair>::Public>;
133 fn map_keys_to_ids(&self, keys: &[<S::P as Keypair>::Public]) -> Vec<Option<H::KeyId>> {
135 keys.iter().map(|key| self.map_key_to_id(key)).collect()
136 }
137 fn map_ids_to_keys(&self, ids: &[H::KeyId]) -> Vec<Option<<S::P as Keypair>::Public>> {
139 ids.iter().map(|id| self.map_id_to_public(id)).collect()
140 }
141}
142
143impl<S, H> KeyIdMapper<S, H> for bimap::BiHashMap<H::KeyId, <S::P as Keypair>::Public>
144where
145 S: SphinxSuite,
146 H: SphinxHeaderSpec,
147 <S::P as Keypair>::Public: Eq + std::hash::Hash,
148 H::KeyId: Eq + std::hash::Hash,
149{
150 fn map_key_to_id(&self, key: &<S::P as Keypair>::Public) -> Option<H::KeyId> {
151 self.get_by_right(key).cloned()
152 }
153
154 fn map_id_to_public(&self, id: &H::KeyId) -> Option<<S::P as Keypair>::Public> {
155 self.get_by_left(id).cloned()
156 }
157}
158
159pub enum MetaPacketRouting<'a, S: SphinxSuite, H: SphinxHeaderSpec> {
161 ForwardPath {
163 shared_keys: SharedKeys<S::E, S::G>,
165 forward_path: &'a [<S::P as Keypair>::Public],
167 additional_data_relayer: &'a [H::RelayerData],
169 receiver_data: &'a H::PacketReceiverData,
171 no_ack: bool,
173 },
174 Surb(SURB<S, H>, &'a H::PacketReceiverData),
176}
177
178#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
186pub struct PartialPacket<S: SphinxSuite, H: SphinxHeaderSpec> {
187 alpha: Alpha<<S::G as GroupElement<S::E>>::AlphaLen>,
188 routing_info: RoutingInfo<H>,
189 prp_inits: Vec<IvKey<S::PRP>>,
190}
191
192impl<S: SphinxSuite, H: SphinxHeaderSpec> PartialPacket<S, H> {
193 pub fn new<M: KeyIdMapper<S, H>>(routing: MetaPacketRouting<S, H>, key_mapper: &M) -> Result<Self, SphinxError> {
196 match routing {
197 MetaPacketRouting::ForwardPath {
198 shared_keys,
199 forward_path,
200 additional_data_relayer,
201 receiver_data,
202 no_ack,
203 } => {
204 let routing_info = RoutingInfo::<H>::new(
205 &forward_path
206 .iter()
207 .map(|key| {
208 key_mapper.map_key_to_id(key).ok_or_else(|| {
209 SphinxError::PacketConstructionError(format!("key id not found for {}", key.to_hex()))
210 })
211 })
212 .collect::<Result<Vec<_>, SphinxError>>()?,
213 &shared_keys.secrets,
214 additional_data_relayer,
215 receiver_data,
216 false,
217 no_ack,
218 )?;
219
220 Ok(Self {
221 alpha: shared_keys.alpha,
222 routing_info,
223 prp_inits: shared_keys
224 .secrets
225 .into_iter()
226 .rev()
227 .map(|key| S::new_prp_init(&key))
228 .collect::<Result<Vec<_>, _>>()?,
229 })
230 }
231 MetaPacketRouting::Surb(surb, receiver_data) => Ok(Self {
232 alpha: surb.alpha,
233 routing_info: surb.header,
234 prp_inits: vec![S::new_reply_prp_init(&surb.sender_key, receiver_data.as_ref())?],
235 }),
236 }
237 }
238
239 pub fn into_meta_packet<const P: usize>(self, mut payload: PaddedPayload<P>) -> MetaPacket<S, H, P> {
241 for iv_key in self.prp_inits {
242 let mut prp = iv_key.into_init::<S::PRP>();
243 prp.apply_keystream(&mut payload);
244 }
245
246 MetaPacket::new_from_parts(self.alpha, self.routing_info, &payload)
247 }
248}
249
250impl<S: SphinxSuite, H: SphinxHeaderSpec> Debug for PartialPacket<S, H> {
251 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
252 f.debug_struct("PartialPacket")
253 .field("alpha", &self.alpha)
254 .field("routing_info", &self.routing_info)
255 .field("prp_inits", &self.prp_inits)
256 .finish()
257 }
258}
259
260impl<S: SphinxSuite, H: SphinxHeaderSpec> Clone for PartialPacket<S, H> {
261 fn clone(&self) -> Self {
262 Self {
263 alpha: self.alpha.clone(),
264 routing_info: self.routing_info.clone(),
265 prp_inits: self.prp_inits.clone(),
266 }
267 }
268}
269
270impl<S: SphinxSuite, H: SphinxHeaderSpec> PartialEq for PartialPacket<S, H> {
271 fn eq(&self, other: &Self) -> bool {
272 self.alpha == other.alpha && self.routing_info == other.routing_info && self.prp_inits == other.prp_inits
273 }
274}
275
276impl<S: SphinxSuite, H: SphinxHeaderSpec> Eq for PartialPacket<S, H> {}
277
278pub struct MetaPacket<S, H, const P: usize> {
289 packet: Box<[u8]>,
290 _d: PhantomData<(S, H)>,
291}
292
293impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> Debug for MetaPacket<S, H, P> {
294 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
295 write!(f, "{}", self.to_hex())
296 }
297}
298
299impl<S, H, const P: usize> Clone for MetaPacket<S, H, P> {
301 fn clone(&self) -> Self {
302 Self {
303 packet: self.packet.clone(),
304 _d: PhantomData,
305 }
306 }
307}
308
309#[allow(dead_code)]
316pub enum ForwardedMetaPacket<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> {
317 Relayed {
319 packet: MetaPacket<S, H, P>,
321 next_node: <S::P as Keypair>::Public,
323 path_pos: u8,
325 additional_info: H::RelayerData,
330 derived_secret: SharedSecret,
332 packet_tag: PacketTag,
334 },
335 Final {
337 plain_text: PaddedPayload<P>,
339 receiver_data: H::PacketReceiverData,
341 derived_secret: SharedSecret,
343 packet_tag: PacketTag,
345 no_ack: bool,
347 },
348}
349
350impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> MetaPacket<S, H, P> {
351 pub const PACKET_LEN: usize = <S::P as Keypair>::Public::SIZE + RoutingInfo::<H>::SIZE + PaddedPayload::<P>::SIZE;
353
354 pub fn new<M: KeyIdMapper<S, H>>(
359 payload: PaddedPayload<P>,
360 routing: MetaPacketRouting<S, H>,
361 key_mapper: &M,
362 ) -> Result<Self, SphinxError> {
363 Ok(PartialPacket::new(routing, key_mapper)?.into_meta_packet(payload))
364 }
365
366 fn new_from_parts(
367 alpha: Alpha<<S::G as GroupElement<S::E>>::AlphaLen>,
368 routing_info: RoutingInfo<H>,
369 payload: &[u8],
370 ) -> Self {
371 let mut packet = Vec::with_capacity(Self::SIZE);
372 packet.extend_from_slice(&alpha);
373 packet.extend_from_slice(routing_info.as_ref());
374 packet.extend_from_slice(&payload[0..PaddedPayload::<P>::SIZE]);
375
376 Self {
377 packet: packet.into_boxed_slice(),
378 _d: PhantomData,
379 }
380 }
381
382 fn alpha(&self) -> &[u8] {
384 let len = <S::G as GroupElement<S::E>>::AlphaLen::USIZE;
385 &self.packet[..len]
386 }
387
388 fn routing_info_mut(&mut self) -> &mut [u8] {
390 let base = <S::G as GroupElement<S::E>>::AlphaLen::USIZE;
391 &mut self.packet[base..base + RoutingInfo::<H>::SIZE]
392 }
393
394 fn payload_mut(&mut self) -> &mut [u8] {
396 let base = <S::G as GroupElement<S::E>>::AlphaLen::USIZE + RoutingInfo::<H>::SIZE;
397 &mut self.packet[base..base + PaddedPayload::<P>::SIZE]
398 }
399
400 pub fn into_forwarded<K, F>(
403 mut self,
404 node_keypair: &S::P,
405 key_mapper: &K,
406 mut reply_openers: F,
407 ) -> Result<ForwardedMetaPacket<S, H, P>, SphinxError>
408 where
409 K: KeyIdMapper<S, H>,
410 F: FnMut(&H::PacketReceiverData) -> Option<ReplyOpener>,
411 {
412 let (alpha, secret) = SharedKeys::<S::E, S::G>::forward_transform(
413 Alpha::<<S::G as GroupElement<S::E>>::AlphaLen>::from_slice(self.alpha()),
414 &(node_keypair.into()),
415 &(node_keypair.public().into()),
416 )?;
417
418 let fwd_header = forward_header::<H>(&secret, self.routing_info_mut())?;
420
421 let decrypted = self.payload_mut();
423 let mut prp = S::new_prp_init(&secret)?.into_init::<S::PRP>();
424 prp.apply_keystream(decrypted);
425
426 Ok(match fwd_header {
427 ForwardedHeader::Relayed {
428 next_header,
429 path_pos,
430 next_node,
431 additional_info,
432 } => ForwardedMetaPacket::Relayed {
433 packet: Self::new_from_parts(alpha, next_header, decrypted),
434 packet_tag: derive_packet_tag(&secret)?,
435 derived_secret: secret,
436 next_node: key_mapper.map_id_to_public(&next_node).ok_or_else(|| {
437 SphinxError::PacketDecodingError(format!("couldn't map id to public key: {}", next_node.to_hex()))
438 })?,
439 path_pos,
440 additional_info,
441 },
442 ForwardedHeader::Final {
443 receiver_data,
444 is_reply,
445 no_ack,
446 } => {
447 if is_reply {
450 let local_surb = reply_openers(&receiver_data).ok_or_else(|| {
451 SphinxError::PacketDecodingError(format!(
452 "couldn't find reply opener for pseudonym: {}",
453 receiver_data.to_hex()
454 ))
455 })?;
456
457 for secret in local_surb.shared_secrets.into_iter().rev() {
460 let mut prp = S::new_prp_init(&secret)?.into_init::<S::PRP>();
461 prp.apply_keystream(decrypted);
462 }
463
464 let mut prp =
466 S::new_reply_prp_init(&local_surb.sender_key, receiver_data.as_ref())?.into_init::<S::PRP>();
467 prp.apply_keystream(decrypted);
468 }
469
470 let mut payload = self.packet.into_vec();
473 payload.drain(..<S::G as GroupElement<S::E>>::AlphaLen::USIZE + RoutingInfo::<H>::SIZE);
474
475 ForwardedMetaPacket::Final {
476 packet_tag: derive_packet_tag(&secret)?,
477 derived_secret: secret,
478 plain_text: PaddedPayload::from_padded(payload)?,
479 receiver_data,
480 no_ack,
481 }
482 }
483 })
484 }
485}
486
487impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> AsRef<[u8]> for MetaPacket<S, H, P> {
488 fn as_ref(&self) -> &[u8] {
489 self.packet.as_ref()
490 }
491}
492
493impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> TryFrom<&[u8]> for MetaPacket<S, H, P> {
494 type Error = GeneralError;
495
496 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
497 if value.len() == Self::SIZE {
498 Ok(Self {
499 packet: value.into(),
500 _d: PhantomData,
501 })
502 } else {
503 Err(GeneralError::ParseError("MetaPacket".into()))
504 }
505 }
506}
507
508impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> BytesRepresentable for MetaPacket<S, H, P> {
509 const SIZE: usize =
510 <S::G as GroupElement<S::E>>::AlphaLen::USIZE + RoutingInfo::<H>::SIZE + PaddedPayload::<P>::SIZE;
511}
512
513#[cfg(test)]
514pub(crate) mod tests {
515 use std::{hash::Hash, num::NonZeroUsize};
516
517 use anyhow::anyhow;
518 use bimap::BiHashMap;
519 use hopr_crypto_random::Randomizable;
520 use hopr_crypto_types::keypairs::{Keypair, OffchainKeypair};
521 use parameterized::parameterized;
522
523 use super::*;
524 use crate::{surb::create_surb, tests::WrappedBytes};
525
526 #[derive(Debug, Clone, Copy)]
527 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
528 struct TestHeader<S: SphinxSuite>(PhantomData<S>);
529
530 impl<S: SphinxSuite> SphinxHeaderSpec for TestHeader<S> {
531 type KeyId = KeyIdent<4>;
532 type PRG = hopr_crypto_types::primitives::ChaCha20;
533 type PacketReceiverData = SimplePseudonym;
534 type Pseudonym = SimplePseudonym;
535 type RelayerData = WrappedBytes<53>;
536 type SurbReceiverData = WrappedBytes<54>;
537 type UH = hopr_crypto_types::primitives::Poly1305;
538
539 const MAX_HOPS: NonZeroUsize = NonZeroUsize::new(4).unwrap();
540 }
541
542 const PAYLOAD_SIZE: usize = 799;
543
544 #[test]
545 fn test_padding() -> anyhow::Result<()> {
546 let data = b"some testing forward message";
547 let padded = PaddedPayload::<PAYLOAD_SIZE>::new(data)?;
548
549 let mut expected = vec![0u8; PAYLOAD_SIZE - data.len()];
550 expected.push(PaddedPayload::<PAYLOAD_SIZE>::PADDING_TAG);
551 expected.extend_from_slice(data);
552 assert_eq!(expected.len(), padded.len());
553 assert_eq!(&expected, padded.as_ref());
554
555 let padded_from_vec = PaddedPayload::<PAYLOAD_SIZE>::new_from_vec(data.to_vec())?;
556 assert_eq!(padded, padded_from_vec);
557
558 let unpadded = padded.into_unpadded()?;
559 assert!(!unpadded.is_empty());
560 assert_eq!(data, unpadded.as_ref());
561
562 Ok(())
563 }
564
565 #[test]
566 fn test_padding_zero_length() -> anyhow::Result<()> {
567 let data = [];
568 let padded = PaddedPayload::<9>::new(&data)?;
569 assert_eq!(padded.len(), 10);
570 assert_eq!(padded.as_ref()[9], PaddedPayload::<9>::PADDING_TAG);
571 assert_eq!(&padded.as_ref()[0..9], &[0u8; 9]);
572
573 Ok(())
574 }
575
576 #[test]
577 fn test_padding_full_length() -> anyhow::Result<()> {
578 let data = [1u8; 9];
579 let padded = PaddedPayload::<9>::new(&data)?;
580 assert_eq!(padded.len(), 10);
581 assert_eq!(padded.as_ref()[0], PaddedPayload::<9>::PADDING_TAG);
582 assert_eq!(padded.as_ref()[1..], data);
583
584 Ok(())
585 }
586
587 #[cfg(feature = "serde")]
588 fn generic_test_partial_packet_serialization<S>(keypairs: Vec<S::P>) -> anyhow::Result<()>
589 where
590 S: SphinxSuite + PartialEq,
591 <S::P as Keypair>::Public: Eq + Hash,
592 {
593 let pubkeys = keypairs.iter().map(|kp| kp.public().clone()).collect::<Vec<_>>();
594 let mapper = keypairs
595 .iter()
596 .enumerate()
597 .map(|(i, k)| (KeyIdent::from(i as u32), k.public().clone()))
598 .collect::<BiHashMap<_, _>>();
599
600 let shared_keys = S::new_shared_keys(&pubkeys)?;
601 let por_strings = vec![WrappedBytes::<53>::default(); shared_keys.secrets.len() - 1];
602 let pseudonym = SimplePseudonym::random();
603
604 let packet_1 = PartialPacket::<S, TestHeader<S>>::new(
605 MetaPacketRouting::ForwardPath {
606 shared_keys,
607 forward_path: &pubkeys,
608 additional_data_relayer: &por_strings,
609 receiver_data: &pseudonym,
610 no_ack: false,
611 },
612 &mapper,
613 )?;
614
615 const BINCODE_CONFIGURATION: bincode::config::Configuration = bincode::config::standard()
616 .with_little_endian()
617 .with_variable_int_encoding();
618
619 let encoded_1 = bincode::serde::encode_to_vec(&packet_1, BINCODE_CONFIGURATION)?;
620 let packet_2: PartialPacket<S, TestHeader<S>> =
621 bincode::serde::decode_from_slice(&encoded_1, BINCODE_CONFIGURATION)?.0;
622
623 assert_eq!(packet_1, packet_2);
624 Ok(())
625 }
626
627 fn generic_test_meta_packet<S>(keypairs: Vec<S::P>) -> anyhow::Result<()>
628 where
629 S: SphinxSuite,
630 <S::P as Keypair>::Public: Eq + Hash,
631 {
632 let pubkeys = keypairs.iter().map(|kp| kp.public().clone()).collect::<Vec<_>>();
633 let mapper = keypairs
634 .iter()
635 .enumerate()
636 .map(|(i, k)| (KeyIdent::from(i as u32), k.public().clone()))
637 .collect::<BiHashMap<_, _>>();
638
639 let shared_keys = S::new_shared_keys(&pubkeys)?;
640 let por_strings = vec![WrappedBytes::<53>::default(); shared_keys.secrets.len() - 1];
641 let pseudonym = SimplePseudonym::random();
642
643 assert_eq!(shared_keys.secrets.len() - 1, por_strings.len());
644
645 let msg = b"some random test message";
646
647 let mut mp = MetaPacket::<S, TestHeader<S>, PAYLOAD_SIZE>::new(
648 PaddedPayload::new(msg)?,
649 MetaPacketRouting::ForwardPath {
650 shared_keys,
651 forward_path: &pubkeys,
652 additional_data_relayer: &por_strings,
653 receiver_data: &pseudonym,
654 no_ack: false,
655 },
656 &mapper,
657 )?;
658
659 assert!(mp.as_ref().len() < 1492, "metapacket too long {}", mp.as_ref().len());
660
661 let mut received_plaintext = Box::default();
662 for (i, pair) in keypairs.iter().enumerate() {
663 let fwd = mp
664 .clone()
665 .into_forwarded(pair, &mapper, |_| None)
666 .unwrap_or_else(|_| panic!("failed to unwrap at {i}"));
667
668 match fwd {
669 ForwardedMetaPacket::Relayed { packet, .. } => {
670 assert!(i < keypairs.len() - 1);
671 mp = packet;
672 }
673 ForwardedMetaPacket::Final { plain_text, .. } => {
674 assert_eq!(keypairs.len() - 1, i);
675 received_plaintext = plain_text.into_unpadded()?;
676 }
677 }
678 }
679
680 assert_eq!(msg, received_plaintext.as_ref());
681
682 Ok(())
683 }
684
685 fn generic_meta_packet_reply_test<S>(keypairs: Vec<S::P>) -> anyhow::Result<()>
686 where
687 S: SphinxSuite,
688 <S::P as Keypair>::Public: Eq + Hash,
689 {
690 let pubkeys = keypairs.iter().map(|kp| kp.public().clone()).collect::<Vec<_>>();
691 let mapper = keypairs
692 .iter()
693 .enumerate()
694 .map(|(i, k)| (KeyIdent::from(i as u32), k.public().clone()))
695 .collect::<BiHashMap<_, _>>();
696
697 let shared_keys = S::new_shared_keys(&pubkeys)?;
698 let por_strings = vec![WrappedBytes::default(); shared_keys.secrets.len() - 1];
699 let por_values = WrappedBytes::default();
700 let pseudonym = SimplePseudonym::random();
701
702 let ids = <BiHashMap<_, _> as KeyIdMapper<S, TestHeader<S>>>::map_keys_to_ids(&mapper, &pubkeys)
703 .into_iter()
704 .map(|v| v.ok_or_else(|| anyhow!("failed to map keys to ids")))
705 .collect::<anyhow::Result<Vec<KeyIdent>>>()?;
706
707 let (surb, opener) = create_surb::<S, TestHeader<S>>(shared_keys, &ids, &por_strings, pseudonym, por_values)?;
708
709 let msg = b"some random reply test message";
710
711 let mut mp = MetaPacket::<S, TestHeader<S>, PAYLOAD_SIZE>::new(
712 PaddedPayload::new(msg)?,
713 MetaPacketRouting::Surb(surb, &pseudonym),
714 &mapper,
715 )?;
716
717 let surb_retriever = |p: &SimplePseudonym| {
718 assert_eq!(pseudonym, *p);
719 Some(opener.clone())
720 };
721
722 let mut received_plaintext = Box::default();
723 for (i, pair) in keypairs.iter().enumerate() {
724 let fwd = mp
725 .clone()
726 .into_forwarded(pair, &mapper, surb_retriever)
727 .unwrap_or_else(|_| panic!("failed to unwrap at {i}"));
728
729 match fwd {
730 ForwardedMetaPacket::Relayed { packet, .. } => {
731 assert!(i < keypairs.len() - 1);
732 mp = packet;
733 }
734 ForwardedMetaPacket::Final { plain_text, .. } => {
735 assert_eq!(keypairs.len() - 1, i);
736 received_plaintext = plain_text.into_unpadded()?;
737 }
738 }
739 }
740
741 assert_eq!(msg, received_plaintext.as_ref());
742
743 Ok(())
744 }
745
746 #[cfg(feature = "x25519")]
747 #[parameterized(amount = { 4, 3, 2, 1 })]
748 fn test_x25519_meta_packet(amount: usize) -> anyhow::Result<()> {
749 generic_test_meta_packet::<crate::ec_groups::X25519Suite>(
750 (0..amount).map(|_| OffchainKeypair::random()).collect(),
751 )
752 }
753
754 #[cfg(feature = "x25519")]
755 #[parameterized(amount = { 4, 3, 2, 1 })]
756 fn test_x25519_reply_meta_packet(amount: usize) -> anyhow::Result<()> {
757 generic_meta_packet_reply_test::<crate::ec_groups::X25519Suite>(
758 (0..amount).map(|_| OffchainKeypair::random()).collect(),
759 )
760 }
761
762 #[cfg(all(feature = "x25519", feature = "serde"))]
763 #[parameterized(amount = { 4, 3, 2, 1 })]
764 fn test_x25519_partial_packet_serialize(amount: usize) -> anyhow::Result<()> {
765 generic_test_partial_packet_serialization::<crate::ec_groups::X25519Suite>(
766 (0..amount).map(|_| OffchainKeypair::random()).collect(),
767 )
768 }
769
770 #[cfg(feature = "ed25519")]
771 #[parameterized(amount = { 4, 3, 2, 1 })]
772 fn test_ed25519_meta_packet(amount: usize) -> anyhow::Result<()> {
773 generic_test_meta_packet::<crate::ec_groups::Ed25519Suite>(
774 (0..amount).map(|_| OffchainKeypair::random()).collect(),
775 )
776 }
777
778 #[cfg(feature = "ed25519")]
779 #[parameterized(amount = { 4, 3, 2, 1 })]
780 fn test_ed25519_reply_meta_packet(amount: usize) -> anyhow::Result<()> {
781 generic_meta_packet_reply_test::<crate::ec_groups::Ed25519Suite>(
782 (0..amount).map(|_| OffchainKeypair::random()).collect(),
783 )
784 }
785
786 #[cfg(all(feature = "ed25519", feature = "serde"))]
787 #[parameterized(amount = { 4, 3, 2, 1 })]
788 fn test_ed25519_partial_packet_serialize(amount: usize) -> anyhow::Result<()> {
789 generic_test_partial_packet_serialization::<crate::ec_groups::Ed25519Suite>(
790 (0..amount).map(|_| OffchainKeypair::random()).collect(),
791 )
792 }
793
794 #[cfg(feature = "secp256k1")]
795 #[parameterized(amount = { 4, 3, 2, 1 })]
796 fn test_secp256k1_meta_packet(amount: usize) -> anyhow::Result<()> {
797 generic_test_meta_packet::<crate::ec_groups::Secp256k1Suite>(
798 (0..amount)
799 .map(|_| hopr_crypto_types::keypairs::ChainKeypair::random())
800 .collect(),
801 )
802 }
803
804 #[cfg(feature = "secp256k1")]
805 #[parameterized(amount = { 4, 3, 2, 1 })]
806 fn test_secp256k1_reply_meta_packet(amount: usize) -> anyhow::Result<()> {
807 generic_meta_packet_reply_test::<crate::ec_groups::Secp256k1Suite>(
808 (0..amount)
809 .map(|_| hopr_crypto_types::keypairs::ChainKeypair::random())
810 .collect(),
811 )
812 }
813
814 #[cfg(all(feature = "secp256k1", feature = "serde"))]
815 #[parameterized(amount = { 4, 3, 2, 1 })]
816 fn test_secp256k1_partial_packet_serialize(amount: usize) -> anyhow::Result<()> {
817 generic_test_partial_packet_serialization::<crate::ec_groups::Secp256k1Suite>(
818 (0..amount)
819 .map(|_| hopr_crypto_types::keypairs::ChainKeypair::random())
820 .collect(),
821 )
822 }
823}