Skip to content

Commit 4501086

Browse files
committed
refactor
- align `gix-hash` integration tests with 'new' style. - reorganize `hasher` module to avoid duplicate paths to the same types/functions. - use shorter paths to `gix_hash::hasher|io` everywhere.
1 parent f879654 commit 4501086

File tree

36 files changed

+288
-292
lines changed

36 files changed

+288
-292
lines changed

gix-features/src/hash.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
33
/// Compute a CRC32 hash from the given `bytes`, returning the CRC32 hash.
44
///
5-
/// When calling this function for the first time, `previous_value` should be `0`. Otherwise it
6-
/// should be the previous return value of this function to provide a hash of multiple sequential
7-
/// chunks of `bytes`.
5+
/// When calling this function for the first time, `previous_value` should be `0`.
6+
/// Otherwise, it should be the previous return value of this function to provide a hash
7+
/// of multiple sequential chunks of `bytes`.
88
#[cfg(feature = "crc32")]
99
pub fn crc32_update(previous_value: u32, bytes: &[u8]) -> u32 {
1010
let mut h = crc32fast::Hasher::new_with_initial(previous_value);

gix-hash/src/hasher.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/// The error returned by [`Hasher::try_finalize()`](crate::Hasher::try_finalize()).
2+
#[derive(Debug, thiserror::Error)]
3+
#[allow(missing_docs)]
4+
pub enum Error {
5+
#[error("Detected SHA-1 collision attack with digest {digest}")]
6+
CollisionAttack { digest: crate::ObjectId },
7+
}
8+
9+
pub(super) mod _impl {
10+
use crate::hasher::Error;
11+
use sha1_checked::{CollisionResult, Digest};
12+
13+
/// An implementation of the Sha1 hash, which can be used once.
14+
///
15+
/// We use [`sha1_checked`] to implement the same collision detection
16+
/// algorithm as Git.
17+
#[derive(Clone)]
18+
pub struct Hasher(sha1_checked::Sha1);
19+
20+
impl Hasher {
21+
/// Let's not provide a public default implementation to force people to go through [`hasher()`].
22+
fn default() -> Self {
23+
// This matches the configuration used by Git, which only uses
24+
// the collision detection to bail out, rather than computing
25+
// alternate “safe hashes” for inputs where a collision attack
26+
// was detected.
27+
Self(sha1_checked::Builder::default().safe_hash(false).build())
28+
}
29+
}
30+
31+
impl Hasher {
32+
/// Digest the given `bytes`.
33+
pub fn update(&mut self, bytes: &[u8]) {
34+
self.0.update(bytes);
35+
}
36+
37+
/// Finalize the hash and produce an object ID.
38+
///
39+
/// Returns [`Error`] if a collision attack is detected.
40+
#[inline]
41+
pub fn try_finalize(self) -> Result<crate::ObjectId, Error> {
42+
match self.0.try_finalize() {
43+
CollisionResult::Ok(digest) => Ok(crate::ObjectId::Sha1(digest.into())),
44+
CollisionResult::Mitigated(_) => {
45+
// SAFETY: `CollisionResult::Mitigated` is only
46+
// returned when `safe_hash()` is on. `Hasher`’s field
47+
// is private, and we only construct it in the
48+
// `Default` instance, which turns `safe_hash()` off.
49+
//
50+
// As of Rust 1.84.1, the compiler can’t figure out
51+
// this function cannot panic without this.
52+
#[allow(unsafe_code)]
53+
unsafe {
54+
std::hint::unreachable_unchecked()
55+
}
56+
}
57+
CollisionResult::Collision(digest) => Err(Error::CollisionAttack {
58+
digest: crate::ObjectId::Sha1(digest.into()),
59+
}),
60+
}
61+
}
62+
}
63+
64+
/// Produce a hasher suitable for the given `kind` of hash.
65+
#[inline]
66+
pub fn hasher(kind: crate::Kind) -> Hasher {
67+
match kind {
68+
crate::Kind::Sha1 => Hasher::default(),
69+
}
70+
}
71+
}

gix-hash/src/hasher/io.rs

-120
This file was deleted.

gix-hash/src/hasher/mod.rs

-71
This file was deleted.

0 commit comments

Comments
 (0)