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