• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tox-rs / tox / 7247588774

18 Dec 2023 11:45AM UTC coverage: 94.85% (-0.03%) from 94.876%
7247588774

Pull #477

github

web-flow
Merge 6a5a935cc into 5ab95a61f
Pull Request #477: Add functions for tox crate version information

0 of 10 new or added lines in 1 file covered. (0.0%)

13 existing lines in 7 files now uncovered.

16816 of 17729 relevant lines covered (94.85%)

1.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.62
/tox_packet/src/onion/mod.rs
1
/*! Onion UDP Packets
2
*/
3

4
mod friend_request;
5
mod inner_onion_request;
6
mod inner_onion_response;
7
mod onion_announce_request;
8
mod onion_announce_response;
9
mod onion_data_request;
10
mod onion_data_response;
11
mod onion_request_0;
12
mod onion_request_1;
13
mod onion_request_2;
14
mod onion_response_1;
15
mod onion_response_2;
16
mod onion_response_3;
17

18
pub use self::friend_request::*;
19
pub use self::inner_onion_request::*;
20
pub use self::inner_onion_response::*;
21
pub use self::onion_announce_request::*;
22
pub use self::onion_announce_response::*;
23
pub use self::onion_data_request::*;
24
pub use self::onion_data_response::*;
25
pub use self::onion_request_0::*;
26
pub use self::onion_request_1::*;
27
pub use self::onion_request_2::*;
28
pub use self::onion_response_1::*;
29
pub use self::onion_response_2::*;
30
pub use self::onion_response_3::*;
31

32
use crate::dht::packed_node::PackedNode;
33
use crate::ip_port::*;
34
use crypto_box::{
35
    aead::{generic_array::typenum::marker_traits::Unsigned, AeadCore},
36
    SalsaBox,
37
};
38
use tox_binary_io::*;
39
use tox_crypto::*;
40
use xsalsa20poly1305::{
41
    aead::{Aead, Error as AeadError},
42
    XSalsa20Poly1305,
43
};
44

45
use rand::{CryptoRng, Rng};
46

47
use nom::error::{make_error, ErrorKind};
48
use nom::number::complete::le_u8;
49

50
use cookie_factory::{do_gen, gen_be_u8, gen_call, gen_cond, gen_le_u64, gen_many_ref, gen_slice};
51

52
use nom::{
53
    combinator::{cond, rest, rest_len},
54
    Err,
55
};
56
use std::io::{Error, ErrorKind as IoErrorKind};
57

58
const ONION_SEND_BASE: usize = crypto_box::KEY_SIZE + SIZE_IPPORT + <SalsaBox as AeadCore>::TagSize::USIZE;
59
const ONION_SEND_1: usize = xsalsa20poly1305::NONCE_SIZE + ONION_SEND_BASE * 3;
60
const MAX_ONION_DATA_SIZE: usize = ONION_MAX_PACKET_SIZE - (ONION_SEND_1 + 1); // 1 is for packet_id
61
const MIN_ONION_DATA_REQUEST_SIZE: usize = 1
62
    + crypto_box::KEY_SIZE
63
    + xsalsa20poly1305::NONCE_SIZE
64
    + crypto_box::KEY_SIZE
65
    + <SalsaBox as AeadCore>::TagSize::USIZE; // 1 is for packet_id
66
/// Maximum size in butes of Onion Data Request packet
67
pub const MAX_DATA_REQUEST_SIZE: usize = MAX_ONION_DATA_SIZE - MIN_ONION_DATA_REQUEST_SIZE;
68
/// Minimum size in bytes of Onion Data Response packet
69
pub const MIN_ONION_DATA_RESPONSE_SIZE: usize = crypto_box::KEY_SIZE + <SalsaBox as AeadCore>::TagSize::USIZE;
70
/// Maximum size in bytes of Onion Data Response inner payload
71
pub const MAX_ONION_CLIENT_DATA_SIZE: usize = MAX_DATA_REQUEST_SIZE - MIN_ONION_DATA_RESPONSE_SIZE;
72

73
/// Size of first `OnionReturn` struct with no inner `OnionReturn`s.
74
pub const ONION_RETURN_1_SIZE: usize =
75
    xsalsa20poly1305::NONCE_SIZE + SIZE_IPPORT + <SalsaBox as AeadCore>::TagSize::USIZE; // 59
76
/// Size of second `OnionReturn` struct with one inner `OnionReturn`.
77
pub const ONION_RETURN_2_SIZE: usize =
78
    xsalsa20poly1305::NONCE_SIZE + SIZE_IPPORT + <SalsaBox as AeadCore>::TagSize::USIZE + ONION_RETURN_1_SIZE; // 118
79
/// Size of third `OnionReturn` struct with two inner `OnionReturn`s.
80
pub const ONION_RETURN_3_SIZE: usize =
81
    xsalsa20poly1305::NONCE_SIZE + SIZE_IPPORT + <SalsaBox as AeadCore>::TagSize::USIZE + ONION_RETURN_2_SIZE; // 177
82

83
/// The maximum size of onion packet including public key, nonce, packet kind
84
/// byte, onion return.
85
pub const ONION_MAX_PACKET_SIZE: usize = 1400;
86

87
/** Encrypted onion return addresses. Payload contains encrypted with symmetric
88
key `IpPort` and possibly inner `OnionReturn`.
89

90
When DHT node receives OnionRequest packet it appends `OnionReturn` to the end
91
of the next request packet it will send. So when DHT node receives OnionResponse
92
packet it will know where to send the next response packet by decrypting
93
`OnionReturn` from received packet. If node can't decrypt `OnionReturn` that
94
means that onion path is expired and packet should be dropped.
95

96
Serialized form:
97

98
Length                | Content
99
--------              | ------
100
`24`                  | `Nonce`
101
`35` or `94` or `153` | Payload
102

103
where payload is encrypted inner `OnionReturn`
104

105
*/
106
#[derive(Clone, Debug, Eq, PartialEq)]
107
pub struct OnionReturn {
108
    /// Nonce for the current encrypted payload
109
    pub nonce: [u8; xsalsa20poly1305::NONCE_SIZE],
110
    /// Encrypted payload
111
    pub payload: Vec<u8>,
112
}
113

114
impl FromBytes for OnionReturn {
115
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
116
        let (input, nonce) = <[u8; xsalsa20poly1305::NONCE_SIZE]>::from_bytes(input)?;
2✔
117
        let (input, payload) = rest(input)?;
4✔
118
        Ok((
2✔
119
            input,
120
            OnionReturn {
2✔
121
                nonce,
122
                payload: payload.to_vec(),
2✔
123
            },
124
        ))
125
    }
126
}
127

128
impl ToBytes for OnionReturn {
129
    #[rustfmt::skip]
130
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
131
        do_gen!(buf,
4✔
132
            gen_slice!(self.nonce.as_ref()) >>
2✔
133
            gen_slice!(self.payload.as_slice())
2✔
134
        )
135
    }
136
}
137

138
impl OnionReturn {
139
    #[allow(clippy::needless_pass_by_value)]
140
    #[rustfmt::skip]
141
    fn inner_to_bytes<'a>(ip_port: &IpPort, inner: Option<&OnionReturn>, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
142
        do_gen!(buf,
2✔
143
            gen_call!(|buf, ip_port| IpPort::to_bytes(ip_port, buf, IpPortPadding::WithPadding), ip_port) >>
4✔
144
            gen_call!(|buf, inner| match inner {
4✔
145
                Some(inner) => OnionReturn::to_bytes(inner, buf),
2✔
146
                None => Ok(buf)
2✔
147
            }, inner)
2✔
148
        )
149
    }
150

151
    fn inner_from_bytes(input: &[u8]) -> IResult<&[u8], (IpPort, Option<OnionReturn>)> {
2✔
152
        let (input, ip_port) = IpPort::from_bytes(input, IpPortPadding::WithPadding)?;
3✔
153
        let (input, rest_len) = rest_len(input)?;
4✔
154
        let (input, inner) = cond(rest_len > 0, OnionReturn::from_bytes)(input)?;
4✔
155
        Ok((input, (ip_port, inner)))
2✔
156
    }
157

158
    /// Create new `OnionReturn` object using symmetric key for encryption.
159
    pub fn new<R: Rng + CryptoRng>(
2✔
160
        rng: &mut R,
161
        symmetric_key: &XSalsa20Poly1305,
162
        ip_port: &IpPort,
163
        inner: Option<&OnionReturn>,
164
    ) -> OnionReturn {
165
        let nonce = XSalsa20Poly1305::generate_nonce(rng);
2✔
166
        let mut buf = [0; ONION_RETURN_2_SIZE + SIZE_IPPORT];
2✔
167
        let (_, size) = OnionReturn::inner_to_bytes(ip_port, inner, (&mut buf, 0)).unwrap();
2✔
168
        let payload = symmetric_key.encrypt(&nonce, &buf[..size]).unwrap();
2✔
169

170
        OnionReturn {
171
            nonce: nonce.into(),
2✔
172
            payload,
173
        }
174
    }
175

176
    /** Decrypt payload with symmetric key and try to parse it as `IpPort` with possibly inner `OnionReturn`.
177

178
    Returns `Error` in case of failure:
179

180
    - fails to decrypt
181
    - fails to parse as `IpPort` with possibly inner `OnionReturn`
182
    */
183
    pub fn get_payload(&self, symmetric_key: &XSalsa20Poly1305) -> Result<(IpPort, Option<OnionReturn>), Error> {
2✔
184
        let decrypted = symmetric_key
4✔
185
            .decrypt(&self.nonce.into(), self.payload.as_slice())
2✔
186
            .map_err(|AeadError| Error::new(IoErrorKind::Other, "OnionReturn decrypt error."))?;
6✔
187
        match OnionReturn::inner_from_bytes(&decrypted) {
5✔
188
            Err(Err::Incomplete(e)) => Err(Error::new(
×
UNCOV
189
                IoErrorKind::Other,
×
190
                format!("Inner onion return deserialize error: {:?}", e),
×
191
            )),
192
            Err(Err::Error(e)) => Err(Error::new(
2✔
193
                IoErrorKind::Other,
1✔
194
                format!("Inner onion return deserialize error: {:?}", e),
1✔
195
            )),
196
            Err(Err::Failure(e)) => Err(Error::new(
×
UNCOV
197
                IoErrorKind::Other,
×
198
                format!("Inner onion return deserialize error: {:?}", e),
×
199
            )),
200
            Ok((_, inner)) => Ok(inner),
2✔
201
        }
202
    }
203
}
204

205
/** Represents the result of sent `AnnounceRequest`.
206

207
Also known as `is_stored` number.
208

209
*/
210
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
211
pub enum AnnounceStatus {
212
    /// Failed to announce ourselves or find requested node
213
    Failed = 0,
214
    /// Requested node is found by its long term `PublicKey`
215
    Found = 1,
216
    /// We successfully announced ourselves
217
    Announced = 2,
218
}
219

220
impl FromBytes for AnnounceStatus {
221
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
222
        let (input, b) = le_u8(input)?;
3✔
223
        match b {
2✔
224
            0 => Ok((input, AnnounceStatus::Failed)),
2✔
225
            1 => Ok((input, AnnounceStatus::Found)),
2✔
226
            2 => Ok((input, AnnounceStatus::Announced)),
2✔
227
            _ => Err(nom::Err::Error(make_error(input, ErrorKind::Switch))),
1✔
228
        }
229
    }
230
}
231

232
impl ToBytes for AnnounceStatus {
233
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
234
        gen_be_u8!(buf, *self as u8)
2✔
235
    }
236
}
237

238
#[cfg(test)]
239
mod tests {
240
    use super::*;
241

242
    use rand::thread_rng;
243
    use xsalsa20poly1305::KeyInit;
244

245
    const ONION_RETURN_1_PAYLOAD_SIZE: usize = ONION_RETURN_1_SIZE - xsalsa20poly1305::NONCE_SIZE;
246

247
    encode_decode_test!(
248
        onion_return_encode_decode,
249
        OnionReturn {
1✔
250
            nonce: [42; xsalsa20poly1305::NONCE_SIZE],
1✔
251
            payload: vec![42; ONION_RETURN_1_PAYLOAD_SIZE]
1✔
252
        }
253
    );
254

255
    encode_decode_test!(announce_status_failed, AnnounceStatus::Failed);
1✔
256

257
    encode_decode_test!(announce_status_found, AnnounceStatus::Found);
1✔
258

259
    encode_decode_test!(announce_status_accounced, AnnounceStatus::Announced);
1✔
260

261
    #[test]
262
    fn onion_return_encrypt_decrypt() {
3✔
263
        let mut rng = thread_rng();
1✔
264
        let alice_symmetric_key = XSalsa20Poly1305::new(&XSalsa20Poly1305::generate_key(&mut rng));
2✔
265
        let bob_symmetric_key = XSalsa20Poly1305::new(&XSalsa20Poly1305::generate_key(&mut rng));
2✔
266
        // alice encrypt
267
        let ip_port_1 = IpPort {
268
            protocol: ProtocolType::Udp,
269
            ip_addr: "5.6.7.8".parse().unwrap(),
2✔
270
            port: 12345,
271
        };
272
        let onion_return_1 = OnionReturn::new(&mut rng, &alice_symmetric_key, &ip_port_1, None);
1✔
273
        // bob encrypt
274
        let ip_port_2 = IpPort {
275
            protocol: ProtocolType::Udp,
276
            ip_addr: "7.8.5.6".parse().unwrap(),
2✔
277
            port: 54321,
278
        };
279
        let onion_return_2 = OnionReturn::new(&mut rng, &bob_symmetric_key, &ip_port_2, Some(&onion_return_1));
1✔
280
        // bob can decrypt it's return address
281
        let (decrypted_ip_port_2, decrypted_onion_return_1) = onion_return_2.get_payload(&bob_symmetric_key).unwrap();
2✔
282
        assert_eq!(decrypted_ip_port_2, ip_port_2);
2✔
283
        assert_eq!(decrypted_onion_return_1.unwrap(), onion_return_1);
1✔
284
        // alice can decrypt it's return address
285
        let (decrypted_ip_port_1, none) = onion_return_1.get_payload(&alice_symmetric_key).unwrap();
1✔
286
        assert_eq!(decrypted_ip_port_1, ip_port_1);
2✔
287
        assert!(none.is_none());
1✔
288
    }
289

290
    #[test]
291
    fn onion_return_encrypt_decrypt_invalid_key() {
3✔
292
        let mut rng = thread_rng();
1✔
293
        let alice_symmetric_key = XSalsa20Poly1305::new(&XSalsa20Poly1305::generate_key(&mut rng));
2✔
294
        let bob_symmetric_key = XSalsa20Poly1305::new(&XSalsa20Poly1305::generate_key(&mut rng));
2✔
295
        let eve_symmetric_key = XSalsa20Poly1305::new(&XSalsa20Poly1305::generate_key(&mut rng));
2✔
296
        // alice encrypt
297
        let ip_port_1 = IpPort {
298
            protocol: ProtocolType::Udp,
299
            ip_addr: "5.6.7.8".parse().unwrap(),
2✔
300
            port: 12345,
301
        };
302
        let onion_return_1 = OnionReturn::new(&mut rng, &alice_symmetric_key, &ip_port_1, None);
1✔
303
        // bob encrypt
304
        let ip_port_2 = IpPort {
305
            protocol: ProtocolType::Udp,
306
            ip_addr: "7.8.5.6".parse().unwrap(),
2✔
307
            port: 54321,
308
        };
309
        let onion_return_2 = OnionReturn::new(&mut rng, &bob_symmetric_key, &ip_port_2, Some(&onion_return_1));
1✔
310
        // eve can't decrypt return addresses
311
        assert!(onion_return_1.get_payload(&eve_symmetric_key).is_err());
2✔
312
        assert!(onion_return_2.get_payload(&eve_symmetric_key).is_err());
1✔
313
    }
314

315
    #[test]
316
    fn onion_return_decrypt_invalid() {
3✔
317
        let mut rng = thread_rng();
1✔
318
        let symmetric_key = XSalsa20Poly1305::new(&XSalsa20Poly1305::generate_key(&mut rng));
2✔
319
        let nonce = XSalsa20Poly1305::generate_nonce(&mut rng);
1✔
320
        // Try long invalid array
321
        let invalid_payload = [42; 123];
1✔
322
        let invalid_payload_encoded = symmetric_key.encrypt(&nonce, &invalid_payload[..]).unwrap();
1✔
323
        let invalid_onion_return = OnionReturn {
324
            nonce: nonce.into(),
1✔
325
            payload: invalid_payload_encoded,
326
        };
327
        assert!(invalid_onion_return.get_payload(&symmetric_key).is_err());
2✔
328
        // Try short incomplete array
329
        let invalid_payload = [];
330
        let invalid_payload_encoded = symmetric_key.encrypt(&nonce, &invalid_payload[..]).unwrap();
1✔
331
        let invalid_onion_return = OnionReturn {
332
            nonce: nonce.into(),
1✔
333
            payload: invalid_payload_encoded,
334
        };
335
        assert!(invalid_onion_return.get_payload(&symmetric_key).is_err());
2✔
336
    }
337
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc