Skip to main content

tox_packet/onion/
onion_announce_response.rs

1/*! OnionAnnounceResponse packet with OnionAnnounceResponsePayload
2*/
3
4use super::*;
5
6use tox_binary_io::*;
7use tox_crypto::*;
8use crate::dht::*;
9
10use nom::{
11    many0,
12    number::complete::le_u64,
13    combinator::{rest, rest_len},
14};
15
16/** It's used to respond to `OnionAnnounceRequest` packet.
17
18sendback_data is the data from `OnionAnnounceRequest` that should be sent in the
19response as is. It's used in onion client to match onion response with sent
20request.
21
22Serialized form:
23
24Length   | Content
25-------- | ------
26`1`      | `0x84`
27`8`      | Data to send back in response
28`24`     | `Nonce`
29variable | Payload
30
31where payload is encrypted [`OnionAnnounceResponsePayload`](./struct.OnionAnnounceResponsePayload.html)
32
33*/
34#[derive(Clone, Debug, Eq, PartialEq)]
35pub struct OnionAnnounceResponse {
36    /// Data to send back in response
37    pub sendback_data: u64,
38    /// Nonce for the current encrypted payload
39    pub nonce: Nonce,
40    /// Encrypted payload
41    pub payload: Vec<u8>
42}
43
44impl FromBytes for OnionAnnounceResponse {
45    named!(from_bytes<OnionAnnounceResponse>, do_parse!(
46        verify!(rest_len, |len| *len <= ONION_MAX_PACKET_SIZE) >>
47        tag!(&[0x84][..]) >>
48        sendback_data: le_u64 >>
49        nonce: call!(Nonce::from_bytes) >>
50        payload: rest >>
51        (OnionAnnounceResponse {
52            sendback_data,
53            nonce,
54            payload: payload.to_vec()
55        })
56    ));
57}
58
59impl ToBytes for OnionAnnounceResponse {
60    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
61        do_gen!(buf,
62            gen_be_u8!(0x84) >>
63            gen_le_u64!(self.sendback_data) >>
64            gen_slice!(self.nonce.as_ref()) >>
65            gen_slice!(self.payload.as_slice()) >>
66            gen_len_limit(ONION_MAX_PACKET_SIZE)
67        )
68    }
69}
70
71impl OnionAnnounceResponse {
72    /// Create new `OnionAnnounceResponse` object.
73    pub fn new(shared_secret: &PrecomputedKey, sendback_data: u64, payload: &OnionAnnounceResponsePayload) -> OnionAnnounceResponse {
74        let nonce = gen_nonce();
75        let mut buf = [0; ONION_MAX_PACKET_SIZE];
76        let (_, size) = payload.to_bytes((&mut buf, 0)).unwrap();
77        let payload = seal_precomputed(&buf[..size], &nonce, shared_secret);
78
79        OnionAnnounceResponse { sendback_data, nonce, payload }
80    }
81
82    /** Decrypt payload and try to parse it as `OnionAnnounceResponsePayload`.
83
84    Returns `Error` in case of failure:
85
86    - fails to decrypt
87    - fails to parse as `OnionAnnounceResponsePayload`
88    */
89    pub fn get_payload(&self, shared_secret: &PrecomputedKey) -> Result<OnionAnnounceResponsePayload, GetPayloadError> {
90        let decrypted = open_precomputed(&self.payload, &self.nonce, shared_secret)
91            .map_err(|()| {
92                GetPayloadError::decrypt()
93            })?;
94        match OnionAnnounceResponsePayload::from_bytes(&decrypted) {
95            Err(error) => {
96                Err(GetPayloadError::deserialize(error, decrypted.clone()))
97            },
98            Ok((_, inner)) => {
99                Ok(inner)
100            }
101        }
102    }
103}
104
105/** Unencrypted payload of `OnionAnnounceResponse` packet.
106
107`announce_status` variable contains the result of sent request. It might have
108values:
109
110* 0: failed to announce ourselves or find requested node
111* 1: requested node is found by its long term `PublicKey`
112* 2: we successfully announced ourselves
113
114In case of announce_status is equal to 1 ping_id will contain `PublicKey` that
115should be used to send data packets to the requested node. In other cases it
116will contain ping id that should be used for announcing ourselves.
117
118Serialized form:
119
120Length   | Content
121-------- | ------
122`1`      | `announce_status` (aka `is_stored`)
123`32`     | Onion ping id or `PublicKey`
124`[0, 204]` | Nodes in packed format
125
126*/
127#[derive(Clone, Debug, Eq, PartialEq)]
128pub struct OnionAnnounceResponsePayload {
129    /// Variable that represents result of sent `OnionAnnounceRequest`. Also known
130    /// as `is_stored` variable
131    pub announce_status: AnnounceStatus,
132    /// Onion ping id or PublicKey that should be used to send data packets
133    pub ping_id_or_pk: sha256::Digest,
134    /// Up to 4 closest to the requested PublicKey DHT nodes
135    pub nodes: Vec<PackedNode>
136}
137
138impl FromBytes for OnionAnnounceResponsePayload {
139    named!(from_bytes<OnionAnnounceResponsePayload>, do_parse!(
140        announce_status: call!(AnnounceStatus::from_bytes) >>
141        ping_id_or_pk: call!(sha256::Digest::from_bytes) >>
142        nodes: many0!(PackedNode::from_bytes) >>
143        _len: verify!(value!(nodes.len()), |len| *len <= 4 as usize) >>
144        eof!() >>
145        (OnionAnnounceResponsePayload {
146            announce_status,
147            ping_id_or_pk,
148            nodes
149        })
150    ));
151}
152
153impl ToBytes for OnionAnnounceResponsePayload {
154    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
155        do_gen!(buf,
156            gen_call!(|buf, announce_status| AnnounceStatus::to_bytes(announce_status, buf), &self.announce_status) >>
157            gen_slice!(self.ping_id_or_pk.as_ref()) >>
158            gen_cond!(
159                self.nodes.len() <= 4,
160                gen_many_ref!(&self.nodes, |buf, node| PackedNode::to_bytes(node, buf))
161            )
162        )
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    use std::net::SocketAddr;
171
172    encode_decode_test!(
173        tox_crypto::crypto_init().unwrap(),
174        onion_announce_response_encode_decode,
175        OnionAnnounceResponse {
176            sendback_data: 12345,
177            nonce: gen_nonce(),
178            payload: vec![42; 123]
179        }
180    );
181
182    encode_decode_test!(
183        tox_crypto::crypto_init().unwrap(),
184        onion_announce_response_payload_encode_decode,
185        OnionAnnounceResponsePayload {
186            announce_status: AnnounceStatus::Found,
187            ping_id_or_pk: sha256::hash(&[1, 2, 3]),
188            nodes: vec![
189                PackedNode::new(SocketAddr::V4("5.6.7.8:12345".parse().unwrap()), &gen_keypair().0)
190            ]
191        }
192    );
193
194    #[test]
195    fn onion_announce_response_payload_encrypt_decrypt() {
196        crypto_init().unwrap();
197        let (_alice_pk, alice_sk) = gen_keypair();
198        let (bob_pk, _bob_sk) = gen_keypair();
199        let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
200        let payload = OnionAnnounceResponsePayload {
201            announce_status: AnnounceStatus::Found,
202            ping_id_or_pk: sha256::hash(&[1, 2, 3]),
203            nodes: vec![
204                PackedNode::new(SocketAddr::V4("5.6.7.8:12345".parse().unwrap()), &gen_keypair().0)
205            ]
206        };
207        // encode payload with shared secret
208        let onion_packet = OnionAnnounceResponse::new(&shared_secret, 12345, &payload);
209        // decode payload with bob's secret key
210        let decoded_payload = onion_packet.get_payload(&shared_secret).unwrap();
211        // payloads should be equal
212        assert_eq!(decoded_payload, payload);
213    }
214
215    #[test]
216    fn onion_announce_response_payload_encrypt_decrypt_invalid_key() {
217        crypto_init().unwrap();
218        let (_alice_pk, alice_sk) = gen_keypair();
219        let (bob_pk, _bob_sk) = gen_keypair();
220        let (_eve_pk, eve_sk) = gen_keypair();
221        let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
222        let payload = OnionAnnounceResponsePayload {
223            announce_status: AnnounceStatus::Found,
224            ping_id_or_pk: sha256::hash(&[1, 2, 3]),
225            nodes: vec![
226                PackedNode::new(SocketAddr::V4("5.6.7.8:12345".parse().unwrap()), &gen_keypair().0)
227            ]
228        };
229        // encode payload with shared secret
230        let onion_packet = OnionAnnounceResponse::new(&shared_secret, 12345, &payload);
231        // try to decode payload with eve's secret key
232        let eve_shared_secret = encrypt_precompute(&bob_pk, &eve_sk);
233        let decoded_payload = onion_packet.get_payload(&eve_shared_secret);
234        assert!(decoded_payload.is_err());
235    }
236
237    #[test]
238    fn onion_announce_response_decrypt_invalid() {
239        crypto_init().unwrap();
240        let (_alice_pk, alice_sk) = gen_keypair();
241        let (bob_pk, _bob_sk) = gen_keypair();
242        let shared_secret = precompute(&bob_pk, &alice_sk);
243        let nonce = gen_nonce();
244        // Try long invalid array
245        let invalid_payload = [42; 123];
246        let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
247        let invalid_onion_announce_response = OnionAnnounceResponse {
248            sendback_data: 12345,
249            nonce,
250            payload: invalid_payload_encoded
251        };
252        assert!(invalid_onion_announce_response.get_payload(&shared_secret).is_err());
253        // Try short incomplete array
254        let invalid_payload = [];
255        let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
256        let invalid_onion_announce_response = OnionAnnounceResponse {
257            sendback_data: 12345,
258            nonce,
259            payload: invalid_payload_encoded
260        };
261        assert!(invalid_onion_announce_response.get_payload(&shared_secret).is_err());
262    }
263}