diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 43c294268..f9e9046fe 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -8,12 +8,6 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - [[package]] name = "bincode" version = "1.3.3" @@ -23,22 +17,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitcoin-io" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" - -[[package]] -name = "bitcoin_hashes" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" -dependencies = [ - "bitcoin-io", - "hex-conservative", -] - [[package]] name = "bitflags" version = "2.9.0" @@ -105,15 +83,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6c0438de3ca4d8cac2eec62b228e2f8865cfe9ebefea720406774223fa2d2e" -[[package]] -name = "hex-conservative" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" -dependencies = [ - "arrayvec", -] - [[package]] name = "hex_lit" version = "0.1.1" @@ -232,7 +201,6 @@ version = "0.31.1" dependencies = [ "arbitrary", "bincode", - "bitcoin_hashes", "getrandom", "hex_lit", "rand", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index ffdc4af39..788f9181f 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -8,12 +8,6 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - [[package]] name = "bincode" version = "1.3.3" @@ -23,22 +17,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitcoin-io" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" - -[[package]] -name = "bitcoin_hashes" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" -dependencies = [ - "bitcoin-io", - "hex-conservative", -] - [[package]] name = "bitflags" version = "2.9.0" @@ -99,15 +77,6 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "hex-conservative" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" -dependencies = [ - "arrayvec", -] - [[package]] name = "hex_lit" version = "0.1.1" @@ -223,7 +192,6 @@ version = "0.31.1" dependencies = [ "arbitrary", "bincode", - "bitcoin_hashes", "getrandom", "hex_lit", "rand", diff --git a/Cargo.toml b/Cargo.toml index 948a5769d..950552cb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["std"] -std = ["alloc", "secp256k1-sys/std", "rand?/std", "rand?/std_rng", "rand?/thread_rng", "hashes?/std"] +std = ["alloc", "secp256k1-sys/std", "rand?/std", "rand?/std_rng", "rand?/thread_rng"] # allow use of Secp256k1::new and related API that requires an allocator alloc = ["secp256k1-sys/alloc"] recovery = ["secp256k1-sys/recovery"] @@ -38,7 +38,6 @@ arbitrary = ["dep:arbitrary"] secp256k1-sys = { version = "0.11.0", default-features = false, path = "./secp256k1-sys" } arbitrary = { version = "1.4", optional = true } -hashes = { package = "bitcoin_hashes", version = "0.14", default-features = false, optional = true } rand = { version = "0.9", default-features = false, optional = true } serde = { version = "1.0.103", default-features = false, optional = true } @@ -58,11 +57,11 @@ unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(secp256k1_fu [[example]] name = "sign_verify_recovery" -required-features = ["recovery", "hashes", "std"] +required-features = ["recovery", "std"] [[example]] name = "sign_verify" -required-features = ["hashes", "std"] +required-features = ["std"] [[example]] name = "generate_keys" diff --git a/examples/sign_verify.rs b/examples/sign_verify.rs index 983646672..8e0c33eb5 100644 --- a/examples/sign_verify.rs +++ b/examples/sign_verify.rs @@ -1,17 +1,29 @@ -extern crate hashes; +//! Signing and Verification Example +//! +//! In this example we directly sign the "output of a hash function" +//! as represented by a 32-byte array. In practice, when signing with +//! the ECDSA API, you should not sign an arbitrary hash like this, +//! whether it is typed as a `[u8; 32]` or a `sha2::Sha256` or whatever. +//! +//! Instead, you should have a dedicated signature hash type, which has +//! constructors ensuring that it represents an (ideally) domain-separated +//! hash of the data you intend to sign. This type should implement +//! `Into` so it can be passed to `sign_ecdsa` as a message. +//! +//! An example of such a type is `bitcoin::LegacySighash` from rust-bitcoin. +//! + extern crate secp256k1; -use hashes::{sha256, Hash}; use secp256k1::{ecdsa, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification}; fn verify( secp: &Secp256k1, - msg: &[u8], + msg_digest: [u8; 32], sig: [u8; 64], pubkey: [u8; 33], ) -> Result { - let msg = sha256::Hash::hash(msg); - let msg = Message::from_digest(msg.to_byte_array()); + let msg = Message::from_digest(msg_digest); let sig = ecdsa::Signature::from_compact(&sig)?; let pubkey = PublicKey::from_slice(&pubkey)?; @@ -20,11 +32,10 @@ fn verify( fn sign( secp: &Secp256k1, - msg: &[u8], + msg_digest: [u8; 32], seckey: [u8; 32], ) -> Result { - let msg = sha256::Hash::hash(msg); - let msg = Message::from_digest(msg.to_byte_array()); + let msg = Message::from_digest(msg_digest); let seckey = SecretKey::from_byte_array(seckey)?; Ok(secp.sign_ecdsa(msg, &seckey)) } @@ -40,11 +51,11 @@ fn main() { 2, 29, 21, 35, 7, 198, 183, 43, 14, 208, 65, 139, 14, 112, 205, 128, 231, 245, 41, 91, 141, 134, 245, 114, 45, 63, 82, 19, 251, 210, 57, 79, 54, ]; - let msg = b"This is some message"; + let msg_digest = *b"this must be secure hash output."; - let signature = sign(&secp, msg, seckey).unwrap(); + let signature = sign(&secp, msg_digest, seckey).unwrap(); let serialize_sig = signature.serialize_compact(); - assert!(verify(&secp, msg, serialize_sig, pubkey).unwrap()); + assert!(verify(&secp, msg_digest, serialize_sig, pubkey).unwrap()); } diff --git a/examples/sign_verify_recovery.rs b/examples/sign_verify_recovery.rs index 78cbbd54e..8cb2b43a7 100644 --- a/examples/sign_verify_recovery.rs +++ b/examples/sign_verify_recovery.rs @@ -1,17 +1,14 @@ -extern crate hashes; extern crate secp256k1; -use hashes::{sha256, Hash}; use secp256k1::{ecdsa, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification}; fn recover( secp: &Secp256k1, - msg: &[u8], + msg_digest: [u8; 32], sig: [u8; 64], recovery_id: u8, ) -> Result { - let msg = sha256::Hash::hash(msg); - let msg = Message::from_digest(msg.to_byte_array()); + let msg = Message::from_digest(msg_digest); let id = ecdsa::RecoveryId::try_from(i32::from(recovery_id))?; let sig = ecdsa::RecoverableSignature::from_compact(&sig, id)?; @@ -20,11 +17,10 @@ fn recover( fn sign_recovery( secp: &Secp256k1, - msg: &[u8], + msg_digest: [u8; 32], seckey: [u8; 32], ) -> Result { - let msg = sha256::Hash::hash(msg); - let msg = Message::from_digest(msg.to_byte_array()); + let msg = Message::from_digest(msg_digest); let seckey = SecretKey::from_byte_array(seckey)?; Ok(secp.sign_ecdsa_recoverable(msg, &seckey)) } @@ -41,11 +37,11 @@ fn main() { 134, 245, 114, 45, 63, 82, 19, 251, 210, 57, 79, 54, ]) .unwrap(); - let msg = b"This is some message"; + let msg_digest = *b"this must be secure hash output."; - let signature = sign_recovery(&secp, msg, seckey).unwrap(); + let signature = sign_recovery(&secp, msg_digest, seckey).unwrap(); let (recovery_id, serialize_sig) = signature.serialize_compact(); - assert_eq!(recover(&secp, msg, serialize_sig, recovery_id.to_u8()), Ok(pubkey)); + assert_eq!(recover(&secp, msg_digest, serialize_sig, recovery_id.to_u8()), Ok(pubkey)); } diff --git a/src/ecdh.rs b/src/ecdh.rs index 4fd90b788..816be5723 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -110,10 +110,9 @@ impl AsRef<[u8]> for SharedSecret { /// 64 bytes representing the (x,y) co-ordinates of a point on the curve (32 bytes each). /// /// # Examples -/// ``` -/// # #[cfg(all(feature = "hashes", feature = "rand", feature = "std"))] { -/// # use secp256k1::{ecdh, rand, Secp256k1, PublicKey, SecretKey}; -/// # use secp256k1::hashes::{Hash, sha512}; +/// ```ignore +/// use bitcoin_hashes::{Hash, sha512}; +/// use secp256k1::{ecdh, rand, Secp256k1, PublicKey, SecretKey}; /// /// let s = Secp256k1::new(); /// let (sk1, pk1) = s.generate_keypair(&mut rand::rng()); @@ -124,7 +123,6 @@ impl AsRef<[u8]> for SharedSecret { /// let point2 = ecdh::shared_secret_point(&pk1, &sk2); /// let secret2 = sha512::Hash::hash(&point2); /// assert_eq!(secret1, secret2) -/// # } /// ``` pub fn shared_secret_point(point: &PublicKey, scalar: &SecretKey) -> [u8; 64] { let mut xy = [0u8; 64]; @@ -224,32 +222,6 @@ mod tests { assert_eq!(y, new_y); } - #[test] - #[cfg(not(secp256k1_fuzz))] - #[cfg(all(feature = "hashes", feature = "rand", feature = "std"))] - fn hashes_and_sys_generate_same_secret() { - use hashes::{sha256, Hash, HashEngine}; - - use crate::ecdh::shared_secret_point; - - let s = Secp256k1::signing_only(); - let (sk1, _) = s.generate_keypair(&mut rand::rng()); - let (_, pk2) = s.generate_keypair(&mut rand::rng()); - - let secret_sys = SharedSecret::new(&pk2, &sk1); - - let xy = shared_secret_point(&pk2, &sk1); - - // Mimics logic in `bitcoin-core/secp256k1/src/module/main_impl.h` - let version = (xy[63] & 0x01) | 0x02; - let mut engine = sha256::HashEngine::default(); - engine.input(&[version]); - engine.input(&xy.as_ref()[..32]); - let secret_bh = sha256::Hash::from_engine(engine); - - assert_eq!(secret_bh.as_byte_array(), secret_sys.as_ref()); - } - #[test] #[cfg(all(feature = "serde", feature = "alloc"))] fn serde() { diff --git a/src/key.rs b/src/key.rs index f556b0c62..33480926e 100644 --- a/src/key.rs +++ b/src/key.rs @@ -16,9 +16,6 @@ use crate::ellswift::ElligatorSwift; use crate::ffi::types::c_uint; use crate::ffi::{self, CPtr}; use crate::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey}; -#[cfg(feature = "hashes")] -#[allow(deprecated)] -use crate::ThirtyTwoByteHash; #[cfg(feature = "global-context")] use crate::SECP256K1; use crate::{ @@ -371,15 +368,6 @@ impl SecretKey { } } -#[cfg(feature = "hashes")] -#[allow(deprecated)] -impl From for SecretKey { - /// Converts a 32-byte hash directly to a secret key without error paths. - fn from(t: T) -> SecretKey { - SecretKey::from_byte_array(t.into_32()).expect("failed to create secret key") - } -} - #[cfg(feature = "serde")] impl serde::Serialize for SecretKey { fn serialize(&self, s: S) -> Result { @@ -1889,15 +1877,12 @@ mod test { } #[test] - #[cfg(all(feature = "rand", feature = "alloc", not(feature = "hashes")))] + #[cfg(all(feature = "rand", feature = "alloc"))] fn test_debug_output() { let s = Secp256k1::new(); let (sk, _) = s.generate_keypair(&mut StepRng::new(1, 1)); - assert_eq!( - &format!("{:?}", sk), - "" - ); + assert_eq!(&format!("{:?}", sk), "SecretKey(7ad2d060fb2971d6)"); let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2]; assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index ea372573d..88485f1e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,15 +28,22 @@ //! trigger any assertion failures in the upstream library. //! //! ```rust -//! # #[cfg(all(feature = "rand", feature = "hashes", feature = "std"))] { +//! # #[cfg(all(feature = "rand", feature = "std"))] { //! use secp256k1::rand; //! use secp256k1::{Secp256k1, Message}; -//! use secp256k1::hashes::{sha256, Hash}; +//! +//! // Our message to sign. We explicitly obtain a hash and convert it to a +//! // `Message`. In a real application, we would produce a signature hash +//! // type, e.g. `bitcoin::LegacySigHash`, which is convertible to `Message` +//! // and can be passed directly to `sign_ecdsa`. +//! const HELLO_WORLD_SHA2: [u8; 32] = [ +//! 0x31, 0x5f, 0x5b, 0xdb, 0x76, 0xd0, 0x78, 0xc4, 0x3b, 0x8a, 0xc0, 0x06, 0x4e, 0x4a, 0x01, 0x64, +//! 0x61, 0x2b, 0x1f, 0xce, 0x77, 0xc8, 0x69, 0x34, 0x5b, 0xfc, 0x94, 0xc7, 0x58, 0x94, 0xed, 0xd3, +//! ]; //! //! let secp = Secp256k1::new(); //! let (secret_key, public_key) = secp.generate_keypair(&mut rand::rng()); -//! let digest = sha256::Hash::hash("Hello World!".as_bytes()); -//! let message = Message::from_digest(digest.to_byte_array()); +//! let message = Message::from_digest(HELLO_WORLD_SHA2); //! //! let sig = secp.sign_ecdsa(message, &secret_key); //! assert!(secp.verify_ecdsa(&sig, message, &public_key).is_ok()); @@ -46,13 +53,17 @@ //! If the "global-context" feature is enabled you have access to an alternate API. //! //! ```rust -//! # #[cfg(all(feature = "global-context", feature = "hashes", feature = "rand", feature = "std"))] { +//! # #[cfg(all(feature = "global-context", feature = "rand", feature = "std"))] { //! use secp256k1::{rand, generate_keypair, Message}; -//! use secp256k1::hashes::{sha256, Hash}; +//! +//! // See previous example regarding this constant. +//! const HELLO_WORLD_SHA2: [u8; 32] = [ +//! 0x31, 0x5f, 0x5b, 0xdb, 0x76, 0xd0, 0x78, 0xc4, 0x3b, 0x8a, 0xc0, 0x06, 0x4e, 0x4a, 0x01, 0x64, +//! 0x61, 0x2b, 0x1f, 0xce, 0x77, 0xc8, 0x69, 0x34, 0x5b, 0xfc, 0x94, 0xc7, 0x58, 0x94, 0xed, 0xd3, +//! ]; //! //! let (secret_key, public_key) = generate_keypair(&mut rand::rng()); -//! let digest = sha256::Hash::hash("Hello World!".as_bytes()); -//! let message = Message::from_digest(digest.to_byte_array()); +//! let message = Message::from_digest(HELLO_WORLD_SHA2); //! //! let sig = secret_key.sign_ecdsa(message); //! assert!(sig.verify(message, &public_key).is_ok()); @@ -152,9 +163,6 @@ extern crate core; #[cfg(bench)] extern crate test; -#[cfg(feature = "hashes")] -pub extern crate hashes; - #[macro_use] mod macros; #[macro_use] diff --git a/src/secret.rs b/src/secret.rs index f5fc1060d..35f7b464b 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -11,31 +11,39 @@ use crate::to_hex; macro_rules! impl_display_secret { // Default hasher exists only in standard library and not alloc ($thing:ident) => { - #[cfg(feature = "hashes")] impl ::core::fmt::Debug for $thing { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use hashes::{sha256, Hash, HashEngine}; + use core::fmt; - let tag = "rust-secp256k1DEBUG"; + use secp256k1_sys::secp256k1_ecdh_hash_function_default; - let mut engine = sha256::Hash::engine(); - let tag_hash = sha256::Hash::hash(tag.as_bytes()); - engine.input(&tag_hash.as_ref()); - engine.input(&tag_hash.as_ref()); - engine.input(&self.secret_bytes()); - let hash = sha256::Hash::from_engine(engine); + struct Hex16Writer([u8; 32]); - f.debug_tuple(stringify!($thing)).field(&format_args!("#{:.16}", hash)).finish() - } - } + let mut output = Hex16Writer([0u8; 32]); + unsafe { + // SAFETY: this function pointer is an Option so we need to unwrap it, even + // though we know that the FFI has provided a non-null value for us. + let ecdh_hash = secp256k1_ecdh_hash_function_default.unwrap(); + // SAFETY: output points to >= 32 mutable bytes; both inputs are >= 32 bytes + assert!(self.as_ref().len() >= 32); + ecdh_hash( + output.0.as_mut_ptr(), + self.as_ref().as_ptr(), + "a debug-only byte 'y coordinate'".as_ptr(), + core::ptr::null_mut(), + ); + } - #[cfg(not(feature = "hashes"))] - impl ::core::fmt::Debug for $thing { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!( - f, - "" - ) + impl fmt::Debug for Hex16Writer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for byte in self.0.iter().copied().take(8) { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } + } + + f.debug_tuple(stringify!($thing)).field(&output).finish() } } };