1#![warn(missing_docs)]
2mod config_types;
5mod crc32c;
6mod create;
7mod detect;
8mod error;
9mod file_priority;
10mod file_selection;
11mod file_tree;
12mod hash;
13mod hash_picker;
14mod hash_request;
15mod info_hashes;
16mod lengths;
17mod live_guard;
18mod magnet;
19mod merkle;
20mod merkle_state;
21mod metainfo;
22mod metainfo_v2;
23mod net_util;
24mod peer_id;
25mod preallocate_mode;
26mod resume_data;
27mod storage_mode;
28mod torrent_version;
29mod web_seed_stats;
30
31pub use config_types::{
32 AlertCategory, ChokingAlgorithm, MixedModeAlgorithm, ProxyConfig, ProxyType,
33 SeedChokingAlgorithm,
34};
35pub use crc32c::crc32c;
36pub use create::{CreateTorrent, CreateTorrentResult, auto_piece_size};
37pub use detect::{TorrentMeta, torrent_from_bytes_any};
38pub use error::{Error, Result};
39pub use file_priority::FilePriority;
40pub use file_selection::FileSelection;
41pub use file_tree::{FileTreeNode, V2FileAttr, V2FileInfo};
42pub use hash::{Id20, Id32};
43pub use hash_picker::{AddHashesResult, FileHashInfo, HashPicker};
44pub use hash_request::{HashRequest, validate_hash_request};
45pub use info_hashes::InfoHashes;
46pub use lengths::{DEFAULT_CHUNK_SIZE, Lengths};
47pub use live_guard::LiveConnectionGuard;
48pub use magnet::Magnet;
49pub use merkle::MerkleTree;
50pub use merkle_state::{MerkleTreeState, SetBlockResult};
51pub use metainfo::{FileEntry, FileInfo, InfoDict, TorrentMetaV1, torrent_from_bytes};
52pub use metainfo_v2::{InfoDictV2, TorrentMetaV2, torrent_v2_from_bytes};
53pub use net_util::is_local_network;
54pub use peer_id::PeerId;
55pub use preallocate_mode::PreallocateMode;
56pub use resume_data::{FastResumeData, UnfinishedPiece, validate_resume_bitfield};
57pub use storage_mode::StorageMode;
58pub use torrent_version::TorrentVersion;
59pub use web_seed_stats::{WebSeedState, WebSeedStats};
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65pub enum AddressFamily {
66 V4,
68 V6,
70}
71
72#[must_use]
85pub fn xorshift64_step(mut state: u64) -> u64 {
86 state ^= state << 13;
87 state ^= state >> 7;
88 state ^= state << 17;
89 state
90}
91
92#[cfg(all(
96 feature = "crypto-ring",
97 not(feature = "crypto-openssl"),
98 not(feature = "crypto-aws-lc")
99))]
100pub fn sha1(data: &[u8]) -> Id20 {
101 let hash = ring::digest::digest(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY, data);
102 let mut id = [0u8; 20];
103 id.copy_from_slice(hash.as_ref());
104 Id20(id)
105}
106
107#[cfg(all(
111 feature = "crypto-ring",
112 not(feature = "crypto-openssl"),
113 not(feature = "crypto-aws-lc")
114))]
115pub fn sha1_chunks<'a>(chunks: impl IntoIterator<Item = &'a [u8]>) -> Id20 {
116 let mut ctx = ring::digest::Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY);
117 for chunk in chunks {
118 ctx.update(chunk);
119 }
120 let hash = ctx.finish();
121 let mut id = [0u8; 20];
122 id.copy_from_slice(hash.as_ref());
123 Id20(id)
124}
125
126#[cfg(all(
128 feature = "crypto-ring",
129 not(feature = "crypto-openssl"),
130 not(feature = "crypto-aws-lc")
131))]
132pub fn sha256(data: &[u8]) -> Id32 {
133 let hash = ring::digest::digest(&ring::digest::SHA256, data);
134 let mut id = [0u8; 32];
135 id.copy_from_slice(hash.as_ref());
136 Id32(id)
137}
138
139#[cfg(all(
141 feature = "crypto-ring",
142 not(feature = "crypto-openssl"),
143 not(feature = "crypto-aws-lc")
144))]
145pub fn sha256_chunks<'a>(chunks: impl IntoIterator<Item = &'a [u8]>) -> Id32 {
146 let mut ctx = ring::digest::Context::new(&ring::digest::SHA256);
147 for chunk in chunks {
148 ctx.update(chunk);
149 }
150 let hash = ctx.finish();
151 let mut id = [0u8; 32];
152 id.copy_from_slice(hash.as_ref());
153 Id32(id)
154}
155
156#[cfg(feature = "crypto-openssl")]
160pub fn sha1(data: &[u8]) -> Id20 {
161 let hash = openssl::hash::hash(openssl::hash::MessageDigest::sha1(), data).unwrap();
162 let mut id = [0u8; 20];
163 id.copy_from_slice(&hash);
164 Id20(id)
165}
166
167#[cfg(feature = "crypto-openssl")]
171pub fn sha1_chunks<'a>(chunks: impl IntoIterator<Item = &'a [u8]>) -> Id20 {
172 let mut hasher = openssl::hash::Hasher::new(openssl::hash::MessageDigest::sha1()).unwrap();
173 for chunk in chunks {
174 hasher.update(chunk).unwrap();
175 }
176 let hash = hasher.finish().unwrap();
177 let mut id = [0u8; 20];
178 id.copy_from_slice(&hash);
179 Id20(id)
180}
181
182#[cfg(feature = "crypto-openssl")]
184pub fn sha256(data: &[u8]) -> Id32 {
185 let hash = openssl::hash::hash(openssl::hash::MessageDigest::sha256(), data).unwrap();
186 let mut id = [0u8; 32];
187 id.copy_from_slice(&hash);
188 Id32(id)
189}
190
191#[cfg(feature = "crypto-openssl")]
193pub fn sha256_chunks<'a>(chunks: impl IntoIterator<Item = &'a [u8]>) -> Id32 {
194 let mut hasher = openssl::hash::Hasher::new(openssl::hash::MessageDigest::sha256()).unwrap();
195 for chunk in chunks {
196 hasher.update(chunk).unwrap();
197 }
198 let hash = hasher.finish().unwrap();
199 let mut id = [0u8; 32];
200 id.copy_from_slice(&hash);
201 Id32(id)
202}
203
204#[cfg(all(feature = "crypto-aws-lc", not(feature = "crypto-openssl")))]
208#[must_use]
209pub fn sha1(data: &[u8]) -> Id20 {
210 let hash = aws_lc_rs::digest::digest(&aws_lc_rs::digest::SHA1_FOR_LEGACY_USE_ONLY, data);
211 let mut id = [0u8; 20];
212 id.copy_from_slice(hash.as_ref());
213 Id20(id)
214}
215
216#[cfg(all(feature = "crypto-aws-lc", not(feature = "crypto-openssl")))]
220pub fn sha1_chunks<'a>(chunks: impl IntoIterator<Item = &'a [u8]>) -> Id20 {
221 let mut ctx = aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA1_FOR_LEGACY_USE_ONLY);
222 for chunk in chunks {
223 ctx.update(chunk);
224 }
225 let hash = ctx.finish();
226 let mut id = [0u8; 20];
227 id.copy_from_slice(hash.as_ref());
228 Id20(id)
229}
230
231#[cfg(all(feature = "crypto-aws-lc", not(feature = "crypto-openssl")))]
233#[must_use]
234pub fn sha256(data: &[u8]) -> Id32 {
235 let hash = aws_lc_rs::digest::digest(&aws_lc_rs::digest::SHA256, data);
236 let mut id = [0u8; 32];
237 id.copy_from_slice(hash.as_ref());
238 Id32(id)
239}
240
241#[cfg(all(feature = "crypto-aws-lc", not(feature = "crypto-openssl")))]
243pub fn sha256_chunks<'a>(chunks: impl IntoIterator<Item = &'a [u8]>) -> Id32 {
244 let mut ctx = aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA256);
245 for chunk in chunks {
246 ctx.update(chunk);
247 }
248 let hash = ctx.finish();
249 let mut id = [0u8; 32];
250 id.copy_from_slice(hash.as_ref());
251 Id32(id)
252}
253
254pub struct Sha1Hasher {
262 #[cfg(all(
263 feature = "crypto-ring",
264 not(feature = "crypto-openssl"),
265 not(feature = "crypto-aws-lc")
266 ))]
267 ctx: ring::digest::Context,
268 #[cfg(feature = "crypto-openssl")]
269 ctx: openssl::hash::Hasher,
270 #[cfg(all(feature = "crypto-aws-lc", not(feature = "crypto-openssl")))]
271 ctx: aws_lc_rs::digest::Context,
272}
273
274impl Sha1Hasher {
275 #[must_use]
277 pub fn new() -> Self {
278 Self {
279 #[cfg(all(
280 feature = "crypto-ring",
281 not(feature = "crypto-openssl"),
282 not(feature = "crypto-aws-lc")
283 ))]
284 ctx: ring::digest::Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY),
285 #[cfg(feature = "crypto-openssl")]
286 ctx: openssl::hash::Hasher::new(openssl::hash::MessageDigest::sha1()).unwrap(),
287 #[cfg(all(feature = "crypto-aws-lc", not(feature = "crypto-openssl")))]
288 ctx: aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA1_FOR_LEGACY_USE_ONLY),
289 }
290 }
291
292 pub fn update(&mut self, data: &[u8]) {
294 #[cfg(all(
295 feature = "crypto-ring",
296 not(feature = "crypto-openssl"),
297 not(feature = "crypto-aws-lc")
298 ))]
299 self.ctx.update(data);
300
301 #[cfg(feature = "crypto-openssl")]
302 self.ctx.update(data).unwrap();
303
304 #[cfg(all(feature = "crypto-aws-lc", not(feature = "crypto-openssl")))]
305 self.ctx.update(data);
306 }
307
308 #[cfg(all(
310 feature = "crypto-ring",
311 not(feature = "crypto-openssl"),
312 not(feature = "crypto-aws-lc")
313 ))]
314 pub fn finish(self) -> Id20 {
315 let hash = self.ctx.finish();
316 let mut id = [0u8; 20];
317 id.copy_from_slice(hash.as_ref());
318 Id20(id)
319 }
320
321 #[cfg(feature = "crypto-openssl")]
323 pub fn finish(mut self) -> Id20 {
324 let hash = self.ctx.finish().unwrap();
325 let mut id = [0u8; 20];
326 id.copy_from_slice(&hash);
327 Id20(id)
328 }
329
330 #[cfg(all(feature = "crypto-aws-lc", not(feature = "crypto-openssl")))]
332 #[must_use]
333 pub fn finish(self) -> Id20 {
334 let hash = self.ctx.finish();
335 let mut id = [0u8; 20];
336 id.copy_from_slice(hash.as_ref());
337 Id20(id)
338 }
339}
340
341impl Default for Sha1Hasher {
342 fn default() -> Self {
343 Self::new()
344 }
345}
346
347pub fn random_bytes(buf: &mut [u8]) {
349 for b in buf.iter_mut() {
350 *b = peer_id::random_byte();
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357
358 #[test]
359 fn sha256_empty_string() {
360 let hash = sha256(b"");
361 assert_eq!(
362 hash.to_hex(),
363 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
364 );
365 }
366
367 #[test]
368 fn sha256_hello() {
369 let hash = sha256(b"hello");
370 assert_eq!(
371 hash.to_hex(),
372 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
373 );
374 }
375
376 #[test]
377 fn random_bytes_fills_buffer() {
378 let mut buf = [0u8; 32];
379 random_bytes(&mut buf);
380 assert!(buf.iter().any(|&b| b != 0));
382 }
383
384 #[test]
385 fn sha1_hasher_matches_oneshot() {
386 let data = b"hello world, this is a streaming hash test";
387 let expected = sha1(data);
388
389 let mut hasher = Sha1Hasher::new();
390 hasher.update(&data[..12]);
391 hasher.update(&data[12..]);
392 assert_eq!(hasher.finish(), expected);
393 }
394
395 #[test]
396 fn sha1_hasher_empty() {
397 let expected = sha1(b"");
398 let hasher = Sha1Hasher::new();
399 assert_eq!(hasher.finish(), expected);
400 }
401}