From 186442a75a1dc616057b8998ad68e7c78570c4cc Mon Sep 17 00:00:00 2001 From: James Date: Tue, 11 Aug 2026 13:07:47 -0400 Subject: [PATCH] chore(deps): migrate off deprecated generic-array to hybrid-array majors digest/sha2/sha3/ripemd/hmac/pbkdf2/k256 all moved from generic-array 0.14 to hybrid-array 0.4 in their latest majors, so bumping generic-array alone (as in c1b9680) can't actually clear the deprecation - it just adds a second, unrelated major version to the tree. Bump the whole RustCrypto chain together instead: - digest 0.10->0.11, sha2 0.10->0.11, sha3 0.10->0.12, ripemd 0.1->0.2, hmac 0.12->0.13, pbkdf2 0.12->0.13, k256 0.13->0.14 - coins-core's GenericArray re-export now points at hybrid_array::Array - k256 0.14 pulls ecdsa 0.17/signature 3.0, which redesigned DigestSigner/DigestVerifier to take a closure over the hasher instead of a finalized digest value; updated inherit_signer!/inherit_verifier! and all call sites accordingly - elliptic_curve::sec1::{FromEncodedPoint,ToEncodedPoint} are now deprecated stub traits; switched to FromSec1Point/ToSec1Point - MSRV 1.80->1.85 (k256 0.14 requires it) - pinned getrandom 0.4 w/ wasm_js for wasm32, matching the existing gr03/gr02 shims, since crypto-bigint/crypto-common now pull it in Verified locally: cargo test (default + --no-default-features), wasm check, clippy -D warnings, fmt --check, and nightly doc -D warnings all pass. Co-Authored-By: Claude Sonnet 5 --- Cargo.toml | 18 ++--- crates/bip32/Cargo.toml | 1 + crates/bip32/src/derived.rs | 139 +++++++++++++++++++++++++++--------- crates/bip32/src/lib.rs | 12 +++- crates/bip32/src/macros.rs | 24 ++++--- crates/bip32/src/prelude.rs | 2 +- crates/bip32/src/xkeys.rs | 30 ++++++-- crates/bip39/Cargo.toml | 1 + crates/bip39/src/lib.rs | 1 + crates/core/Cargo.toml | 2 +- crates/core/src/hashes.rs | 9 +-- 11 files changed, 170 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca292e4d..9aac74b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ resolver = "2" [workspace.package] version = "0.13.1" edition = "2021" -rust-version = "1.80" +rust-version = "1.85" authors = [ "Summa ", "James Prestwich ", @@ -26,22 +26,22 @@ coins-ledger = { path = "crates/ledger", version = "0.13" } base64 = "0.21" bech32 = "0.9" bs58 = "0.5.1" -digest = "0.10.7" -generic-array = "0.14.7" +digest = "0.11" +hybrid-array = "0.4" hex = { package = "const-hex", version = "1.12.0" } -ripemd = "0.1.3" +ripemd = "0.2" serde = "1.0.203" -sha3 = "0.10.8" +sha3 = "0.12" # bip32 -hmac = "0.12.1" -k256 = { version = "0.13", features = ["std", "arithmetic"] } -sha2 = "0.10.8" +hmac = "0.13" +k256 = { version = "0.14", features = ["std", "arithmetic"] } +sha2 = "0.11" thiserror = "1.0.61" # bip39 bitvec = "1.0.1" -pbkdf2 = "0.12.2" +pbkdf2 = "0.13" rand = "0.9.0" once_cell = "1.19.0" diff --git a/crates/bip32/Cargo.toml b/crates/bip32/Cargo.toml index b400af4b..81f11b8f 100644 --- a/crates/bip32/Cargo.toml +++ b/crates/bip32/Cargo.toml @@ -24,6 +24,7 @@ sha2.workspace = true thiserror.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] +gr04 = { package = "getrandom", version = "0.4", features = ["wasm_js"] } gr03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] } gr02 = { package = "getrandom", version = "0.2", features = ["js"] } diff --git a/crates/bip32/src/derived.rs b/crates/bip32/src/derived.rs index 87fbcff9..952bc5c6 100644 --- a/crates/bip32/src/derived.rs +++ b/crates/bip32/src/derived.rs @@ -429,19 +429,48 @@ mod test { let key_pub = key.verify_key(); // sign_digest + verify_digest - let sig: Signature = key.sign_digest(digest.clone()); - key_pub.verify_digest(digest.clone(), &sig).unwrap(); + let sig: Signature = key.sign_digest(|d: &mut Hash256| *d = digest.clone()); + key_pub + .verify_digest( + |d: &mut Hash256| { + *d = digest.clone(); + Ok(()) + }, + &sig, + ) + .unwrap(); - let err_bad_sig = key_pub.verify_digest(wrong_digest.clone(), &sig); + let err_bad_sig = key_pub.verify_digest( + |d: &mut Hash256| { + *d = wrong_digest.clone(); + Ok(()) + }, + &sig, + ); match err_bad_sig { Err(_) => {} _ => panic!("expected signature validation error"), } - let (sig, _): (Signature, RecoveryId) = key.sign_digest(digest.clone()); - key_pub.verify_digest(digest, &sig).unwrap(); + let (sig, _): (Signature, RecoveryId) = + key.sign_digest(|d: &mut Hash256| *d = digest.clone()); + key_pub + .verify_digest( + |d: &mut Hash256| { + *d = digest.clone(); + Ok(()) + }, + &sig, + ) + .unwrap(); - let err_bad_sig = key_pub.verify_digest(wrong_digest.clone(), &sig); + let err_bad_sig = key_pub.verify_digest( + |d: &mut Hash256| { + *d = wrong_digest.clone(); + Ok(()) + }, + &sig, + ); match err_bad_sig { Err(_) => {} _ => panic!("expected signature validation error"), @@ -468,69 +497,115 @@ mod test { assert_eq!(key.derivation(), &fake_deriv); // sign_digest + verify_digest - let sig: Signature = key.derive_path(&path).unwrap().sign_digest(digest.clone()); + let sig: Signature = key + .derive_path(&path) + .unwrap() + .sign_digest(|d: &mut Hash256| *d = digest.clone()); key_pub .derive_path(&path) .unwrap() - .verify_digest(digest.clone(), &sig) + .verify_digest( + |d: &mut Hash256| { + *d = digest.clone(); + Ok(()) + }, + &sig, + ) .unwrap(); - let err_bad_sig = key_pub - .derive_path(&path) - .unwrap() - .verify_digest(wrong_digest.clone(), &sig); + let err_bad_sig = key_pub.derive_path(&path).unwrap().verify_digest( + |d: &mut Hash256| { + *d = wrong_digest.clone(); + Ok(()) + }, + &sig, + ); match err_bad_sig { Err(_) => {} _ => panic!("expected signature validation error"), } - let (sig, _): (Signature, RecoveryId) = - key.derive_path(&path).unwrap().sign_digest(digest.clone()); + let (sig, _): (Signature, RecoveryId) = key + .derive_path(&path) + .unwrap() + .sign_digest(|d: &mut Hash256| *d = digest.clone()); key_pub .derive_path(&path) .unwrap() - .verify_digest(digest.clone(), &sig) + .verify_digest( + |d: &mut Hash256| { + *d = digest.clone(); + Ok(()) + }, + &sig, + ) .unwrap(); - let err_bad_sig = key_pub - .derive_path(&path) - .unwrap() - .verify_digest(wrong_digest.clone(), &sig); + let err_bad_sig = key_pub.derive_path(&path).unwrap().verify_digest( + |d: &mut Hash256| { + *d = wrong_digest.clone(); + Ok(()) + }, + &sig, + ); match err_bad_sig { Err(_) => {} _ => panic!("expected signature validation error"), } // sign + verify - let sig: Signature = key.derive_path(&path).unwrap().sign_digest(digest.clone()); + let sig: Signature = key + .derive_path(&path) + .unwrap() + .sign_digest(|d: &mut Hash256| *d = digest.clone()); key_pub .derive_path(&path) .unwrap() - .verify_digest(digest.clone(), &sig) + .verify_digest( + |d: &mut Hash256| { + *d = digest.clone(); + Ok(()) + }, + &sig, + ) .unwrap(); - let err_bad_sig = key_pub - .derive_path(&path) - .unwrap() - .verify_digest(wrong_digest.clone(), &sig); + let err_bad_sig = key_pub.derive_path(&path).unwrap().verify_digest( + |d: &mut Hash256| { + *d = wrong_digest.clone(); + Ok(()) + }, + &sig, + ); match err_bad_sig { Err(_) => {} _ => panic!("expected signature validation error"), } // sign_recoverable + verify_recoverable - let (sig, recovery_id): (Signature, RecoveryId) = - key.derive_path(&path).unwrap().sign_digest(digest.clone()); + let (sig, recovery_id): (Signature, RecoveryId) = key + .derive_path(&path) + .unwrap() + .sign_digest(|d: &mut Hash256| *d = digest.clone()); key_pub .derive_path(&path) .unwrap() - .verify_digest(digest, &sig) + .verify_digest( + |d: &mut Hash256| { + *d = digest.clone(); + Ok(()) + }, + &sig, + ) .unwrap(); - let err_bad_sig = key_pub - .derive_path(&path) - .unwrap() - .verify_digest(wrong_digest.clone(), &sig); + let err_bad_sig = key_pub.derive_path(&path).unwrap().verify_digest( + |d: &mut Hash256| { + *d = wrong_digest.clone(); + Ok(()) + }, + &sig, + ); match err_bad_sig { Err(_) => {} _ => panic!("expected signature validation error"), diff --git a/crates/bip32/src/lib.rs b/crates/bip32/src/lib.rs index 6239a134..90fe94e8 100644 --- a/crates/bip32/src/lib.rs +++ b/crates/bip32/src/lib.rs @@ -39,11 +39,18 @@ //! let xpriv: XPriv = xpriv_str.parse().unwrap(); //! //! let child_xpriv = xpriv.derive_child(33)?; -//! let (sig, _recovery_id): (Signature, RecoveryId) = child_xpriv.sign_digest(digest.clone()); +//! let (sig, _recovery_id): (Signature, RecoveryId) = +//! child_xpriv.sign_digest(|d: &mut coins_core::hashes::Hash256| *d = digest.clone()); //! //! // Signing key types are associated with verifying key types. You can always derive a pubkey //! let child_xpub = child_xpriv.verify_key(); -//! child_xpub.verify_digest(digest.clone(), &sig)?; +//! child_xpub.verify_digest( +//! |d: &mut coins_core::hashes::Hash256| { +//! *d = digest.clone(); +//! Ok(()) +//! }, +//! &sig, +//! )?; //! //! MainnetEncoder::xpub_to_base58(&child_xpub)?; //! # Ok(()) @@ -178,4 +185,5 @@ impl From for Bip32Error { mod _silence_warnings { use gr02 as _; use gr03 as _; + use gr04 as _; } diff --git a/crates/bip32/src/macros.rs b/crates/bip32/src/macros.rs index 583360a3..42ef90f0 100644 --- a/crates/bip32/src/macros.rs +++ b/crates/bip32/src/macros.rs @@ -9,11 +9,11 @@ macro_rules! inherit_signer { + digest::Update + digest::HashMarker, { - fn try_sign_digest( + fn try_sign_digest Result<(), k256::ecdsa::Error>>( &self, - digest: D, + f: F, ) -> Result { - self.$attr.try_sign_digest(digest) + self.$attr.try_sign_digest(f) } } @@ -30,11 +30,11 @@ macro_rules! inherit_signer { + digest::Update + digest::HashMarker, { - fn try_sign_digest( + fn try_sign_digest Result<(), k256::ecdsa::Error>>( &self, - digest: D, + f: F, ) -> Result<(k256::ecdsa::Signature, k256::ecdsa::RecoveryId), k256::ecdsa::Error> { - self.$attr.sign_digest_recoverable(digest) + self.$attr.try_sign_digest(f) } } @@ -53,7 +53,11 @@ macro_rules! inherit_signer { + digest::Update + digest::HashMarker, { - self.$attr.sign_digest_recoverable(digest) + use k256::ecdsa::signature::DigestSigner as _; + self.try_sign_digest(move |d: &mut D| { + *d = digest.clone(); + Ok(()) + }) } } }; @@ -75,12 +79,12 @@ macro_rules! inherit_verifier { where D: digest::Digest + digest::FixedOutput, { - fn verify_digest( + fn verify_digest Result<(), k256::ecdsa::Error>>( &self, - digest: D, + f: F, signature: &k256::ecdsa::Signature, ) -> Result<(), k256::ecdsa::Error> { - self.$attr.verify_digest(digest, signature) + self.$attr.verify_digest(f, signature) } } }; diff --git a/crates/bip32/src/prelude.rs b/crates/bip32/src/prelude.rs index fee4a5d9..c911ca09 100644 --- a/crates/bip32/src/prelude.rs +++ b/crates/bip32/src/prelude.rs @@ -15,7 +15,7 @@ pub use k256::{ signature::{DigestSigner as _, DigestVerifier as _}, RecoveryId, Signature, SigningKey, VerifyingKey, }, - elliptic_curve::sec1::ToEncodedPoint as _, + elliptic_curve::sec1::ToSec1Point as _, }; /// shortcut for easy usage diff --git a/crates/bip32/src/xkeys.rs b/crates/bip32/src/xkeys.rs index 5cb0bbed..df4753ff 100644 --- a/crates/bip32/src/xkeys.rs +++ b/crates/bip32/src/xkeys.rs @@ -1,6 +1,6 @@ use coins_core::hashes::{Hash160, Hash160Digest, MarkedDigest, MarkedDigestOutput}; -use hmac::{Hmac, Mac}; -use k256::{ecdsa, elliptic_curve::sec1::FromEncodedPoint}; +use hmac::{Hmac, KeyInit, Mac}; +use k256::{ecdsa, elliptic_curve::sec1::FromSec1Point}; use sha2::Sha512; use std::{ convert::{TryFrom, TryInto}, @@ -343,7 +343,7 @@ impl Parent for XPub { } let parent_key = - k256::ProjectivePoint::from_encoded_point(&self.key.to_encoded_point(true)).unwrap(); + k256::ProjectivePoint::from_sec1_point(&self.key.to_sec1_point(true)).unwrap(); let mut tweak_point = k256::ProjectivePoint::GENERATOR.mul(*tweak); tweak_point.add_assign(parent_key); @@ -554,10 +554,18 @@ mod test { let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap(); let child = xpriv.derive_child(33).unwrap(); - let sig: ecdsa::Signature = child.sign_digest(digest.clone()); + let sig: ecdsa::Signature = child.sign_digest(|d: &mut Hash256| *d = digest.clone()); let child_xpub = child.verify_key(); - child_xpub.verify_digest(digest, &sig).unwrap(); + child_xpub + .verify_digest( + |d: &mut Hash256| { + *d = digest.clone(); + Ok(()) + }, + &sig, + ) + .unwrap(); } #[test] @@ -570,10 +578,18 @@ mod test { let child = xpriv.derive_child(33).unwrap(); let (sig, recovery_id): (ecdsa::Signature, ecdsa::RecoveryId) = - child.sign_digest(digest.clone()); + child.sign_digest(|d: &mut Hash256| *d = digest.clone()); let child_xpub = child.verify_key(); - child_xpub.verify_digest(digest.clone(), &sig).unwrap(); + child_xpub + .verify_digest( + |d: &mut Hash256| { + *d = digest.clone(); + Ok(()) + }, + &sig, + ) + .unwrap(); let recovered = ecdsa::VerifyingKey::recover_from_digest(digest, &sig, recovery_id).unwrap(); diff --git a/crates/bip39/Cargo.toml b/crates/bip39/Cargo.toml index 075da4dc..7a294acf 100644 --- a/crates/bip39/Cargo.toml +++ b/crates/bip39/Cargo.toml @@ -27,6 +27,7 @@ sha2.workspace = true thiserror.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] +gr04 = { package = "getrandom", version = "0.4", features = ["wasm_js"] } gr03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] } gr02 = { package = "getrandom", version = "0.2", features = ["js"] } diff --git a/crates/bip39/src/lib.rs b/crates/bip39/src/lib.rs index 8f88db18..d06b3c85 100644 --- a/crates/bip39/src/lib.rs +++ b/crates/bip39/src/lib.rs @@ -26,4 +26,5 @@ pub use self::wordlist::*; mod _silence_warnings { use gr02 as _; use gr03 as _; + use gr04 as _; } diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 2647b6ff..8f5e78dc 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -17,7 +17,7 @@ base64.workspace = true bech32.workspace = true bs58 = { workspace = true, features = ["check"] } digest.workspace = true -generic-array.workspace = true +hybrid-array.workspace = true hex.workspace = true ripemd.workspace = true serde = { workspace = true, features = ["derive"] } diff --git a/crates/core/src/hashes.rs b/crates/core/src/hashes.rs index 2b810e29..13d56ff6 100644 --- a/crates/core/src/hashes.rs +++ b/crates/core/src/hashes.rs @@ -6,17 +6,12 @@ //! length. use crate::ser::{ByteFormat, SerError, SerResult}; -use digest::{ - core_api::{BlockSizeUser, OutputSizeUser}, - HashMarker, Output, -}; +use digest::{block_api::BlockSizeUser, HashMarker, Output, OutputSizeUser}; use std::io::Write; // Useful re-exports pub use digest::Digest; -#[allow(deprecated)] -// re-exported for API compatibility; generic-array 1.x migration is a breaking change -pub use generic_array::GenericArray; +pub use hybrid_array::Array as GenericArray; pub use ripemd::Ripemd160; pub use sha2::Sha256; pub use sha3::Sha3_256;