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