sphinx_packet/packet/
mod.rs

1use crate::header::shared_secret::ExpandedSharedSecret;
2use crate::version::Version;
3use crate::{
4    header::{self, delays::Delay, HEADER_SIZE},
5    payload::{Payload, PAYLOAD_OVERHEAD_SIZE},
6    route::{Destination, DestinationAddressBytes, Node, NodeAddressBytes, SURBIdentifier},
7    Error, ErrorKind, Result,
8};
9use builder::SphinxPacketBuilder;
10use header::SphinxHeader;
11use x25519_dalek::{PublicKey, StaticSecret};
12
13pub mod builder;
14
15pub struct ProcessedPacket {
16    pub version: Version,
17    pub data: ProcessedPacketData,
18}
19
20pub enum ProcessedPacketData {
21    ForwardHop {
22        next_hop_packet: SphinxPacket,
23        next_hop_address: NodeAddressBytes,
24        delay: Delay,
25    },
26    FinalHop {
27        destination: DestinationAddressBytes,
28        identifier: SURBIdentifier,
29        payload: Payload,
30    },
31}
32
33impl ProcessedPacket {
34    pub fn shared_secret(&self) -> Option<PublicKey> {
35        match &self.data {
36            ProcessedPacketData::ForwardHop {
37                next_hop_packet, ..
38            } => Some(next_hop_packet.shared_secret()),
39            ProcessedPacketData::FinalHop { .. } => None,
40        }
41    }
42}
43
44pub struct SphinxPacket {
45    pub header: SphinxHeader,
46    pub payload: Payload,
47}
48
49#[allow(clippy::len_without_is_empty)]
50impl SphinxPacket {
51    // `new` works as before and does not care about changes made; it uses default values everywhere
52    pub fn new(
53        message: Vec<u8>,
54        route: &[Node],
55        destination: &Destination,
56        delays: &[Delay],
57    ) -> Result<SphinxPacket> {
58        SphinxPacketBuilder::default().build_packet(message, route, destination, delays)
59    }
60
61    pub fn shared_secret(&self) -> PublicKey {
62        self.header.shared_secret
63    }
64
65    pub fn len(&self) -> usize {
66        // header always has constant size
67        HEADER_SIZE + self.payload.len()
68    }
69
70    /// Processes the packet with the provided expanded secret.
71    /// It could be useful in the situation where caller has already derived the value,
72    /// because, for example, he had to obtain the reply tag.
73    pub fn process_with_expanded_secret(
74        self,
75        expanded_shared_secret: &ExpandedSharedSecret,
76    ) -> Result<ProcessedPacket> {
77        let unwrapped_header = self
78            .header
79            .process_with_expanded_secret(expanded_shared_secret)?;
80        let unwrapped_payload = self.payload.unwrap(unwrapped_header.payload_key())?;
81
82        Ok(unwrapped_header.attach_payload(unwrapped_payload))
83    }
84
85    // TODO: we should have some list of 'seen shared_keys' for replay detection, but this should be handled by a mix node
86    pub fn process(self, node_secret_key: &StaticSecret) -> Result<ProcessedPacket> {
87        let unwrapped_header = self.header.process(node_secret_key)?;
88        let unwrapped_payload = self.payload.unwrap(unwrapped_header.payload_key())?;
89
90        Ok(unwrapped_header.attach_payload(unwrapped_payload))
91    }
92
93    pub fn to_bytes(&self) -> Vec<u8> {
94        self.header
95            .to_bytes()
96            .iter()
97            .copied()
98            .chain(self.payload.as_bytes().iter().copied())
99            .collect()
100    }
101
102    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
103        // with payloads being dynamic in size, the only thing we can do
104        // is to check if it at least is longer than the minimum length
105        if bytes.len() < HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE {
106            return Err(Error::new(
107                ErrorKind::InvalidPacket,
108                format!(
109                    "tried to recover sphinx packet using {} bytes, expected at least {}",
110                    bytes.len(),
111                    HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE
112                ),
113            ));
114        }
115
116        let header_bytes = &bytes[..HEADER_SIZE];
117        let payload_bytes = &bytes[HEADER_SIZE..];
118        let header = SphinxHeader::from_bytes(header_bytes)?;
119        let payload = Payload::from_bytes(payload_bytes)?;
120
121        Ok(SphinxPacket { header, payload })
122    }
123}
124
125#[cfg(test)]
126mod test_building_packet_from_bytes {
127    use super::*;
128
129    #[test]
130    fn from_bytes_returns_error_if_bytes_are_too_short() {
131        let bytes = [0u8; 1];
132        let expected = ErrorKind::InvalidPacket;
133        match SphinxPacket::from_bytes(&bytes) {
134            Err(err) => assert_eq!(expected, err.kind()),
135            _ => panic!("Should have returned an error when packet bytes too short"),
136        };
137    }
138}