Skip to content

Commit 4150203

Browse files
authored
Merge pull request GitoxideLabs#2332 from cruessler/make-sha-1-optional
Make sha1 optional in `gix-hash`
2 parents 9e1ba5e + aed5b66 commit 4150203

File tree

9 files changed

+81
-12
lines changed

9 files changed

+81
-12
lines changed

gix-hash/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ test = false
1818
[features]
1919
default = ["sha1"]
2020
## Support for SHA1 hashes and digests.
21-
sha1 = []
21+
sha1 = ["dep:sha1-checked"]
2222
## Support for SHA256 hashes and digests.
2323
sha256 = ["dep:sha2"]
2424
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
@@ -30,7 +30,7 @@ gix-features = { version = "^0.45.1", path = "../gix-features", features = ["pro
3030
thiserror = "2.0.17"
3131
faster-hex = { version = "0.10.0", default-features = false, features = ["std"] }
3232
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }
33-
sha1-checked = { version = "0.10.0", default-features = false }
33+
sha1-checked = { version = "0.10.0", optional = true, default-features = false }
3434
sha2 = { version = "0.10.0", optional = true, default-features = false }
3535

3636
document-features = { version = "0.2.0", optional = true }

gix-hash/src/hasher.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ pub enum Error {
77
}
88

99
pub(super) mod _impl {
10+
#[cfg(feature = "sha1")]
1011
use sha1_checked::{CollisionResult, Digest};
1112

13+
#[cfg(all(not(feature = "sha1"), feature = "sha256"))]
14+
use sha2::Digest;
15+
1216
use crate::hasher::Error;
1317

1418
/// Hash implementations that can be used once.
@@ -17,6 +21,7 @@ pub(super) mod _impl {
1721
/// An implementation of the SHA1 hash.
1822
///
1923
/// We use [`sha1_checked`] to implement the same collision detection algorithm as Git.
24+
#[cfg(feature = "sha1")]
2025
Sha1(sha1_checked::Sha1),
2126
/// An implementation of the SHA256 hash.
2227
#[cfg(feature = "sha256")]
@@ -25,6 +30,7 @@ pub(super) mod _impl {
2530

2631
impl Hasher {
2732
/// Let's not make this public to force people to go through [`hasher()`].
33+
#[cfg(feature = "sha1")]
2834
fn new_sha1() -> Self {
2935
// This matches the configuration used by Git, which only uses
3036
// the collision detection to bail out, rather than computing
@@ -44,6 +50,7 @@ pub(super) mod _impl {
4450
/// Digest the given `bytes`.
4551
pub fn update(&mut self, bytes: &[u8]) {
4652
match self {
53+
#[cfg(feature = "sha1")]
4754
Hasher::Sha1(sha1) => sha1.update(bytes),
4855
#[cfg(feature = "sha256")]
4956
Hasher::Sha256(sha256) => sha256.update(bytes),
@@ -59,6 +66,7 @@ pub(super) mod _impl {
5966
#[inline]
6067
pub fn try_finalize(self) -> Result<crate::ObjectId, Error> {
6168
match self {
69+
#[cfg(feature = "sha1")]
6270
Hasher::Sha1(sha1) => match sha1.try_finalize() {
6371
CollisionResult::Ok(digest) => Ok(crate::ObjectId::Sha1(digest.into())),
6472
CollisionResult::Mitigated(_) => {
@@ -89,6 +97,7 @@ pub(super) mod _impl {
8997
#[inline]
9098
pub fn hasher(kind: crate::Kind) -> Hasher {
9199
match kind {
100+
#[cfg(feature = "sha1")]
92101
crate::Kind::Sha1 => Hasher::new_sha1(),
93102
#[cfg(feature = "sha256")]
94103
crate::Kind::Sha256 => Hasher::new_sha256(),

gix-hash/src/io.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub(super) mod _impl {
114114
/// Create a new hash writer which hashes all bytes written to `inner` with a hash of `kind`.
115115
pub fn new(inner: T, object_hash: crate::Kind) -> Self {
116116
match object_hash {
117+
#[cfg(feature = "sha1")]
117118
crate::Kind::Sha1 => Write {
118119
inner,
119120
hash: crate::hasher(object_hash),

gix-hash/src/kind.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::str::FromStr;
22

3-
use crate::{oid, Kind, ObjectId, SIZE_OF_SHA1_DIGEST, SIZE_OF_SHA1_HEX_DIGEST};
3+
use crate::{oid, Kind, ObjectId};
4+
5+
#[cfg(feature = "sha1")]
6+
use crate::{SIZE_OF_SHA1_DIGEST, SIZE_OF_SHA1_HEX_DIGEST};
47

58
#[cfg(feature = "sha256")]
69
use crate::{SIZE_OF_SHA256_DIGEST, SIZE_OF_SHA256_HEX_DIGEST};
@@ -10,6 +13,7 @@ impl TryFrom<u8> for Kind {
1013

1114
fn try_from(value: u8) -> Result<Self, Self::Error> {
1215
Ok(match value {
16+
#[cfg(feature = "sha1")]
1317
1 => Kind::Sha1,
1418
#[cfg(feature = "sha256")]
1519
2 => Kind::Sha256,
@@ -23,6 +27,7 @@ impl FromStr for Kind {
2327

2428
fn from_str(s: &str) -> Result<Self, Self::Err> {
2529
Ok(match s {
30+
#[cfg(feature = "sha1")]
2631
"sha1" | "SHA1" | "SHA-1" => Kind::Sha1,
2732
#[cfg(feature = "sha256")]
2833
"sha256" | "SHA256" | "SHA-256" => Kind::Sha256,
@@ -34,6 +39,7 @@ impl FromStr for Kind {
3439
impl std::fmt::Display for Kind {
3540
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3641
match self {
42+
#[cfg(feature = "sha1")]
3743
Kind::Sha1 => f.write_str("sha1"),
3844
#[cfg(feature = "sha256")]
3945
Kind::Sha256 => f.write_str("sha256"),
@@ -84,6 +90,7 @@ impl Kind {
8490
#[inline]
8591
pub const fn len_in_hex(&self) -> usize {
8692
match self {
93+
#[cfg(feature = "sha1")]
8794
Kind::Sha1 => SIZE_OF_SHA1_HEX_DIGEST,
8895
#[cfg(feature = "sha256")]
8996
Kind::Sha256 => SIZE_OF_SHA256_HEX_DIGEST,
@@ -94,6 +101,7 @@ impl Kind {
94101
#[inline]
95102
pub const fn len_in_bytes(&self) -> usize {
96103
match self {
104+
#[cfg(feature = "sha1")]
97105
Kind::Sha1 => SIZE_OF_SHA1_DIGEST,
98106
#[cfg(feature = "sha256")]
99107
Kind::Sha256 => SIZE_OF_SHA256_DIGEST,
@@ -106,6 +114,7 @@ impl Kind {
106114
#[inline]
107115
pub const fn from_hex_len(hex_len: usize) -> Option<Self> {
108116
Some(match hex_len {
117+
#[cfg(feature = "sha1")]
109118
0..=SIZE_OF_SHA1_HEX_DIGEST => Kind::Sha1,
110119
#[cfg(feature = "sha256")]
111120
0..=SIZE_OF_SHA256_HEX_DIGEST => Kind::Sha256,
@@ -124,6 +133,7 @@ impl Kind {
124133
#[inline]
125134
pub(crate) fn from_len_in_bytes(bytes: usize) -> Self {
126135
match bytes {
136+
#[cfg(feature = "sha1")]
127137
SIZE_OF_SHA1_DIGEST => Kind::Sha1,
128138
#[cfg(feature = "sha256")]
129139
SIZE_OF_SHA256_DIGEST => Kind::Sha256,
@@ -135,6 +145,7 @@ impl Kind {
135145
#[inline]
136146
pub fn null_ref(&self) -> &'static oid {
137147
match self {
148+
#[cfg(feature = "sha1")]
138149
Kind::Sha1 => oid::null_sha1(),
139150
#[cfg(feature = "sha256")]
140151
Kind::Sha256 => oid::null_sha256(),
@@ -145,6 +156,7 @@ impl Kind {
145156
#[inline]
146157
pub const fn null(&self) -> ObjectId {
147158
match self {
159+
#[cfg(feature = "sha1")]
148160
Kind::Sha1 => ObjectId::null_sha1(),
149161
#[cfg(feature = "sha256")]
150162
Kind::Sha256 => ObjectId::null_sha256(),

gix-hash/src/lib.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99
#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg))]
1010
#![deny(missing_docs, rust_2018_idioms, unsafe_code)]
1111

12-
// Remove this once other hashes (e.g., SHA256, and potentially others)
13-
// are supported, and this crate can build without [`ObjectId::Sha1`].
14-
#[cfg(not(feature = "sha1"))]
15-
compile_error!("Please set the `sha1` feature flag");
12+
#[cfg(all(not(feature = "sha1"), not(feature = "sha256")))]
13+
compile_error!("Please set either the `sha1` or the `sha256` feature flag");
1614

1715
#[path = "oid.rs"]
1816
mod borrowed;
@@ -47,8 +45,10 @@ pub struct Prefix {
4745
}
4846

4947
/// The size of a SHA1 hash digest in bytes.
48+
#[cfg(feature = "sha1")]
5049
const SIZE_OF_SHA1_DIGEST: usize = 20;
5150
/// The size of a SHA1 hash digest in hex.
51+
#[cfg(feature = "sha1")]
5252
const SIZE_OF_SHA1_HEX_DIGEST: usize = 2 * SIZE_OF_SHA1_DIGEST;
5353

5454
/// The size of a SHA256 hash digest in bytes.
@@ -58,8 +58,10 @@ const SIZE_OF_SHA256_DIGEST: usize = 32;
5858
#[cfg(feature = "sha256")]
5959
const SIZE_OF_SHA256_HEX_DIGEST: usize = 2 * SIZE_OF_SHA256_DIGEST;
6060

61+
#[cfg(feature = "sha1")]
6162
const EMPTY_BLOB_SHA1: &[u8; SIZE_OF_SHA1_DIGEST] =
6263
b"\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91";
64+
#[cfg(feature = "sha1")]
6365
const EMPTY_TREE_SHA1: &[u8; SIZE_OF_SHA1_DIGEST] =
6466
b"\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04";
6567

@@ -74,9 +76,11 @@ const EMPTY_TREE_SHA256: &[u8; SIZE_OF_SHA256_DIGEST] = b"\x6e\xf1\x9b\x41\x22\x
7476
#[non_exhaustive]
7577
pub enum Kind {
7678
/// The SHA1 hash with 160 bits.
77-
#[default]
79+
#[cfg_attr(feature = "sha1", default)]
80+
#[cfg(feature = "sha1")]
7881
Sha1 = 1,
7982
/// The SHA256 hash with 256 bits.
83+
#[cfg_attr(all(not(feature = "sha1"), feature = "sha256"), default)]
8084
#[cfg(feature = "sha256")]
8185
Sha256 = 2,
8286
}

gix-hash/src/object_id.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use std::{
44
ops::Deref,
55
};
66

7-
use crate::{borrowed::oid, Kind, EMPTY_BLOB_SHA1, EMPTY_TREE_SHA1, SIZE_OF_SHA1_DIGEST};
7+
use crate::{borrowed::oid, Kind};
8+
9+
#[cfg(feature = "sha1")]
10+
use crate::{EMPTY_BLOB_SHA1, EMPTY_TREE_SHA1, SIZE_OF_SHA1_DIGEST};
811

912
#[cfg(feature = "sha256")]
1013
use crate::{EMPTY_BLOB_SHA256, EMPTY_TREE_SHA256, SIZE_OF_SHA256_DIGEST};
@@ -15,6 +18,7 @@ use crate::{EMPTY_BLOB_SHA256, EMPTY_TREE_SHA256, SIZE_OF_SHA256_DIGEST};
1518
#[non_exhaustive]
1619
pub enum ObjectId {
1720
/// A SHA1 hash digest
21+
#[cfg(feature = "sha1")]
1822
Sha1([u8; SIZE_OF_SHA1_DIGEST]),
1923
/// A SHA256 hash digest
2024
#[cfg(feature = "sha256")]
@@ -38,7 +42,10 @@ impl Hash for ObjectId {
3842
pub mod decode {
3943
use std::str::FromStr;
4044

41-
use crate::{object_id::ObjectId, SIZE_OF_SHA1_DIGEST, SIZE_OF_SHA1_HEX_DIGEST};
45+
use crate::object_id::ObjectId;
46+
47+
#[cfg(feature = "sha1")]
48+
use crate::{SIZE_OF_SHA1_DIGEST, SIZE_OF_SHA1_HEX_DIGEST};
4249

4350
#[cfg(feature = "sha256")]
4451
use crate::{SIZE_OF_SHA256_DIGEST, SIZE_OF_SHA256_HEX_DIGEST};
@@ -62,6 +69,7 @@ pub mod decode {
6269
/// Such a buffer can be obtained using [`oid::write_hex_to(buffer)`][super::oid::write_hex_to()]
6370
pub fn from_hex(buffer: &[u8]) -> Result<ObjectId, Error> {
6471
match buffer.len() {
72+
#[cfg(feature = "sha1")]
6573
SIZE_OF_SHA1_HEX_DIGEST => Ok({
6674
ObjectId::Sha1({
6775
let mut buf = [0; SIZE_OF_SHA1_DIGEST];
@@ -107,6 +115,7 @@ impl ObjectId {
107115
#[inline]
108116
pub fn kind(&self) -> Kind {
109117
match self {
118+
#[cfg(feature = "sha1")]
110119
ObjectId::Sha1(_) => Kind::Sha1,
111120
#[cfg(feature = "sha256")]
112121
ObjectId::Sha256(_) => Kind::Sha256,
@@ -116,6 +125,7 @@ impl ObjectId {
116125
#[inline]
117126
pub fn as_slice(&self) -> &[u8] {
118127
match self {
128+
#[cfg(feature = "sha1")]
119129
Self::Sha1(b) => b.as_ref(),
120130
#[cfg(feature = "sha256")]
121131
Self::Sha256(b) => b.as_ref(),
@@ -125,6 +135,7 @@ impl ObjectId {
125135
#[inline]
126136
pub fn as_mut_slice(&mut self) -> &mut [u8] {
127137
match self {
138+
#[cfg(feature = "sha1")]
128139
Self::Sha1(b) => b.as_mut(),
129140
#[cfg(feature = "sha256")]
130141
Self::Sha256(b) => b.as_mut(),
@@ -135,6 +146,7 @@ impl ObjectId {
135146
#[inline]
136147
pub const fn empty_blob(hash: Kind) -> ObjectId {
137148
match hash {
149+
#[cfg(feature = "sha1")]
138150
Kind::Sha1 => ObjectId::Sha1(*EMPTY_BLOB_SHA1),
139151
#[cfg(feature = "sha256")]
140152
Kind::Sha256 => ObjectId::Sha256(*EMPTY_BLOB_SHA256),
@@ -145,6 +157,7 @@ impl ObjectId {
145157
#[inline]
146158
pub const fn empty_tree(hash: Kind) -> ObjectId {
147159
match hash {
160+
#[cfg(feature = "sha1")]
148161
Kind::Sha1 => ObjectId::Sha1(*EMPTY_TREE_SHA1),
149162
#[cfg(feature = "sha256")]
150163
Kind::Sha256 => ObjectId::Sha256(*EMPTY_TREE_SHA256),
@@ -156,6 +169,7 @@ impl ObjectId {
156169
#[doc(alias = "zero", alias = "git2")]
157170
pub const fn null(kind: Kind) -> ObjectId {
158171
match kind {
172+
#[cfg(feature = "sha1")]
159173
Kind::Sha1 => Self::null_sha1(),
160174
#[cfg(feature = "sha256")]
161175
Kind::Sha256 => Self::null_sha256(),
@@ -167,6 +181,7 @@ impl ObjectId {
167181
#[doc(alias = "is_zero", alias = "git2")]
168182
pub fn is_null(&self) -> bool {
169183
match self {
184+
#[cfg(feature = "sha1")]
170185
ObjectId::Sha1(digest) => &digest[..] == oid::null_sha1().as_bytes(),
171186
#[cfg(feature = "sha256")]
172187
ObjectId::Sha256(digest) => &digest[..] == oid::null_sha256().as_bytes(),
@@ -193,6 +208,7 @@ impl ObjectId {
193208
/// Use `Self::try_from(bytes)` for a fallible version.
194209
pub fn from_bytes_or_panic(bytes: &[u8]) -> Self {
195210
match bytes.len() {
211+
#[cfg(feature = "sha1")]
196212
SIZE_OF_SHA1_DIGEST => Self::Sha1(bytes.try_into().expect("prior length validation")),
197213
#[cfg(feature = "sha256")]
198214
SIZE_OF_SHA256_DIGEST => Self::Sha256(bytes.try_into().expect("prior length validation")),
@@ -205,6 +221,7 @@ impl ObjectId {
205221
impl ObjectId {
206222
/// Instantiate an `ObjectId` from a 20 bytes SHA1 digest.
207223
#[inline]
224+
#[cfg(feature = "sha1")]
208225
fn new_sha1(id: [u8; SIZE_OF_SHA1_DIGEST]) -> Self {
209226
ObjectId::Sha1(id)
210227
}
@@ -220,6 +237,7 @@ impl ObjectId {
220237
///
221238
/// Panics if the slice doesn't have a length of 20.
222239
#[inline]
240+
#[cfg(feature = "sha1")]
223241
pub(crate) fn from_20_bytes(b: &[u8]) -> ObjectId {
224242
let mut id = [0; SIZE_OF_SHA1_DIGEST];
225243
id.copy_from_slice(b);
@@ -239,6 +257,7 @@ impl ObjectId {
239257

240258
/// Returns an `ObjectId` representing a SHA1 whose memory is zeroed.
241259
#[inline]
260+
#[cfg(feature = "sha1")]
242261
pub(crate) const fn null_sha1() -> ObjectId {
243262
ObjectId::Sha1([0u8; SIZE_OF_SHA1_DIGEST])
244263
}
@@ -254,6 +273,7 @@ impl ObjectId {
254273
impl std::fmt::Debug for ObjectId {
255274
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256275
match self {
276+
#[cfg(feature = "sha1")]
257277
ObjectId::Sha1(_hash) => f.write_str("Sha1(")?,
258278
#[cfg(feature = "sha256")]
259279
ObjectId::Sha256(_) => f.write_str("Sha256(")?,
@@ -265,6 +285,7 @@ impl std::fmt::Debug for ObjectId {
265285
}
266286
}
267287

288+
#[cfg(feature = "sha1")]
268289
impl From<[u8; SIZE_OF_SHA1_DIGEST]> for ObjectId {
269290
fn from(v: [u8; SIZE_OF_SHA1_DIGEST]) -> Self {
270291
Self::new_sha1(v)
@@ -281,6 +302,7 @@ impl From<[u8; SIZE_OF_SHA256_DIGEST]> for ObjectId {
281302
impl From<&oid> for ObjectId {
282303
fn from(v: &oid) -> Self {
283304
match v.kind() {
305+
#[cfg(feature = "sha1")]
284306
Kind::Sha1 => ObjectId::from_20_bytes(v.as_bytes()),
285307
#[cfg(feature = "sha256")]
286308
Kind::Sha256 => ObjectId::from_32_bytes(v.as_bytes()),

0 commit comments

Comments
 (0)