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