Skip to content

Commit 4663198

Browse files
committed
Deprecate ThirtyTwoByteHash
The implementations of `ThirtyTwoByteHash` for types from the `hashes` crate are problematic during upgrades because both `bitcoin` and `secp256k1` depend on `hashes` and when the versions of `hashes` get out of sync usage of the trait breaks. Deprecate the `ThirtyTwoByteHash` trait and remove the impls for types from `bitcoin_hashes`. Add an explanation in the changelog because its too long to go in the deprecation message.
1 parent d279c13 commit 4663198

File tree

3 files changed

+21
-92
lines changed

3 files changed

+21
-92
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Unreleased
22

3+
* Deprecate `ThirtyTwoByteHash`
4+
5+
This trait turned out to be problematic during upgrade because we support a ranged dependency for
6+
`bitcoin_hashes`. Consider implementing `From<T> for Message` for your type iff your type is a 32
7+
byte hash (ie, output from a hash algorithm that produces a 32 byte digest like sha256). When
8+
using the impl, consider using `Message::from` instead of `hash.into()` because we will be
9+
introducing generics in a future version and the compiler will not be able to work out the target
10+
type.
11+
312
* Bump MSRV to Rust `v1.56.1`
413
* Upgrade `hashes` using range dependency `version = ">= 0.12, <= 0.14"`.
514

src/key.rs

+3-25
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use crate::{
2020
constants, ecdsa, from_hex, schnorr, Message, Scalar, Secp256k1, Signing, Verification,
2121
};
2222
#[cfg(feature = "hashes")]
23-
use crate::{hashes, ThirtyTwoByteHash};
23+
#[allow(deprecated)]
24+
use crate::ThirtyTwoByteHash;
2425

2526
/// Secret key - a 256-bit key used to create ECDSA and Taproot signatures.
2627
///
@@ -257,30 +258,6 @@ impl SecretKey {
257258
SecretKey(sk)
258259
}
259260

260-
/// Constructs a [`SecretKey`] by hashing `data` with hash algorithm `H`.
261-
///
262-
/// Requires the feature `hashes` to be enabled.
263-
///
264-
/// # Examples
265-
///
266-
/// ```
267-
/// # #[cfg(feature="hashes")] {
268-
/// use secp256k1::hashes::{sha256, Hash};
269-
/// use secp256k1::SecretKey;
270-
///
271-
/// let sk1 = SecretKey::from_hashed_data::<sha256::Hash>("Hello world!".as_bytes());
272-
/// // is equivalent to
273-
/// let sk2 = SecretKey::from(sha256::Hash::hash("Hello world!".as_bytes()));
274-
///
275-
/// assert_eq!(sk1, sk2);
276-
/// # }
277-
/// ```
278-
#[cfg(feature = "hashes")]
279-
#[inline]
280-
pub fn from_hashed_data<H: ThirtyTwoByteHash + hashes::Hash>(data: &[u8]) -> Self {
281-
<H as hashes::Hash>::hash(data).into()
282-
}
283-
284261
/// Returns the secret key as a byte value.
285262
#[inline]
286263
pub fn secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] { self.0 }
@@ -373,6 +350,7 @@ impl SecretKey {
373350
}
374351

375352
#[cfg(feature = "hashes")]
353+
#[allow(deprecated)]
376354
impl<T: ThirtyTwoByteHash> From<T> for SecretKey {
377355
/// Converts a 32-byte hash directly to a secret key without error paths.
378356
fn from(t: T) -> SecretKey {

src/lib.rs

+9-67
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@
3131
//! # #[cfg(all(feature = "rand-std", feature = "hashes-std"))] {
3232
//! use secp256k1::rand::rngs::OsRng;
3333
//! use secp256k1::{Secp256k1, Message};
34-
//! use secp256k1::hashes::sha256;
34+
//! use secp256k1::hashes::{sha256, Hash};
3535
//!
3636
//! let secp = Secp256k1::new();
3737
//! let (secret_key, public_key) = secp.generate_keypair(&mut OsRng);
38-
//! let message = Message::from_hashed_data::<sha256::Hash>("Hello World!".as_bytes());
38+
//! let digest = sha256::hash("Hello World!".as_bytes());
39+
//! let message = Message::from_digest(digest);
3940
//!
4041
//! let sig = secp.sign_ecdsa(&message, &secret_key);
4142
//! assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok());
@@ -47,10 +48,11 @@
4748
//! ```rust
4849
//! # #[cfg(all(feature = "global-context", feature = "hashes-std", feature = "rand-std"))] {
4950
//! use secp256k1::{generate_keypair, Message};
50-
//! use secp256k1::hashes::sha256;
51+
//! use secp256k1::hashes::{sha256, Hash};
5152
//!
5253
//! let (secret_key, public_key) = generate_keypair(&mut rand::thread_rng());
53-
//! let message = Message::from_hashed_data::<sha256::Hash>("Hello World!".as_bytes());
54+
//! let digest = sha256::hash("Hello World!".as_bytes());
55+
//! let message = Message::from_digest(digest);
5456
//!
5557
//! let sig = secret_key.sign_ecdsa(message);
5658
//! assert!(sig.verify(&message, &public_key).is_ok());
@@ -176,8 +178,6 @@ use core::{fmt, mem, str};
176178

177179
#[cfg(all(feature = "global-context", feature = "std"))]
178180
pub use context::global::{self, SECP256K1};
179-
#[cfg(feature = "hashes")]
180-
use hashes::Hash;
181181
#[cfg(feature = "rand")]
182182
pub use rand;
183183
pub use secp256k1_sys as ffi;
@@ -198,34 +198,20 @@ pub use crate::scalar::Scalar;
198198
/// Trait describing something that promises to be a 32-byte random number; in particular,
199199
/// it has negligible probability of being zero or overflowing the group order. Such objects
200200
/// may be converted to `Message`s without any error paths.
201+
#[deprecated(since = "0.29.0", note = "Please see v0.29.0 rust-secp256k1/CHANGELOG.md for suggestion")]
201202
pub trait ThirtyTwoByteHash {
202203
/// Converts the object into a 32-byte array
203204
fn into_32(self) -> [u8; 32];
204205
}
205206

206-
#[cfg(feature = "hashes")]
207-
impl ThirtyTwoByteHash for hashes::sha256::Hash {
208-
fn into_32(self) -> [u8; 32] { self.to_byte_array() }
209-
}
210-
211-
#[cfg(feature = "hashes")]
212-
impl ThirtyTwoByteHash for hashes::sha256d::Hash {
213-
fn into_32(self) -> [u8; 32] { self.to_byte_array() }
214-
}
215-
216-
#[cfg(feature = "hashes")]
217-
impl<T: hashes::sha256t::Tag> ThirtyTwoByteHash for hashes::sha256t::Hash<T> {
218-
fn into_32(self) -> [u8; 32] { self.to_byte_array() }
219-
}
220-
221207
/// A (hashed) message input to an ECDSA signature.
222208
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
223209
pub struct Message([u8; constants::MESSAGE_SIZE]);
224210
impl_array_newtype!(Message, u8, constants::MESSAGE_SIZE);
225211
impl_pretty_debug!(Message);
226212

227213
impl Message {
228-
/// **If you just want to sign an arbitrary message use `Message::from_hashed_data` instead.**
214+
/// Creates a [`Message`] from a 32 byte slice `digest`.
229215
///
230216
/// Converts a `MESSAGE_SIZE`-byte slice to a message object. **WARNING:** the slice has to be a
231217
/// cryptographically secure hash of the actual message that's going to be signed. Otherwise
@@ -239,8 +225,6 @@ impl Message {
239225

240226
/// Creates a [`Message`] from a `digest`.
241227
///
242-
/// **If you just want to sign an arbitrary message use `Message::from_hashed_data` instead.**
243-
///
244228
/// The `digest` array has to be a cryptographically secure hash of the actual message that's
245229
/// going to be signed. Otherwise the result of signing isn't a [secure signature].
246230
///
@@ -250,8 +234,6 @@ impl Message {
250234

251235
/// Creates a [`Message`] from a 32 byte slice `digest`.
252236
///
253-
/// **If you just want to sign an arbitrary message use `Message::from_hashed_data` instead.**
254-
///
255237
/// The slice has to be 32 bytes long and be a cryptographically secure hash of the actual
256238
/// message that's going to be signed. Otherwise the result of signing isn't a [secure
257239
/// signature].
@@ -272,31 +254,9 @@ impl Message {
272254
_ => Err(Error::InvalidMessage),
273255
}
274256
}
275-
276-
/// Constructs a [`Message`] by hashing `data` with hash algorithm `H`.
277-
///
278-
/// Requires the feature `hashes` to be enabled.
279-
///
280-
/// # Examples
281-
///
282-
/// ```
283-
/// # #[cfg(feature = "hashes")] {
284-
/// use secp256k1::hashes::{sha256, Hash};
285-
/// use secp256k1::Message;
286-
///
287-
/// let m1 = Message::from_hashed_data::<sha256::Hash>("Hello world!".as_bytes());
288-
/// // is equivalent to
289-
/// let m2 = Message::from(sha256::Hash::hash("Hello world!".as_bytes()));
290-
///
291-
/// assert_eq!(m1, m2);
292-
/// # }
293-
/// ```
294-
#[cfg(feature = "hashes")]
295-
pub fn from_hashed_data<H: ThirtyTwoByteHash + hashes::Hash>(data: &[u8]) -> Self {
296-
<H as hashes::Hash>::hash(data).into()
297-
}
298257
}
299258

259+
#[allow(deprecated)]
300260
impl<T: ThirtyTwoByteHash> From<T> for Message {
301261
/// Converts a 32-byte hash directly to a message without error paths.
302262
fn from(t: T) -> Message { Message(t.into_32()) }
@@ -1043,24 +1003,6 @@ mod tests {
10431003
let sig = SECP256K1.sign_ecdsa(&msg, &sk);
10441004
assert!(SECP256K1.verify_ecdsa(&msg, &sig, &pk).is_ok());
10451005
}
1046-
1047-
#[cfg(feature = "hashes")]
1048-
#[test]
1049-
fn test_from_hash() {
1050-
use hashes::{sha256, sha256d, Hash};
1051-
1052-
let test_bytes = "Hello world!".as_bytes();
1053-
1054-
let hash = sha256::Hash::hash(test_bytes);
1055-
let msg = Message::from(hash);
1056-
assert_eq!(msg.0, hash.to_byte_array());
1057-
assert_eq!(msg, Message::from_hashed_data::<hashes::sha256::Hash>(test_bytes));
1058-
1059-
let hash = sha256d::Hash::hash(test_bytes);
1060-
let msg = Message::from(hash);
1061-
assert_eq!(msg.0, hash.to_byte_array());
1062-
assert_eq!(msg, Message::from_hashed_data::<hashes::sha256d::Hash>(test_bytes));
1063-
}
10641006
}
10651007

10661008
#[cfg(bench)]

0 commit comments

Comments
 (0)