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