diff --git a/CHANGELOG.md b/CHANGELOG.md index fd501d27d..48878601c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,44 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). serialization traits as Platform. Existing bincode encodings and C interfaces are unchanged, but Rust consumers must use the fork for compatible `Encode`/`Decode` traits. The dependency switch does not automatically opt types into `DecodeUntrusted`. +- **Breaking:** `dashcore_hashes` re-exports the hash primitives (`sha1`, + `sha256`, `sha256d`, `sha512`, `sha512_256`, `ripemd160`, `hash160`, `hmac`, + `siphash24`, `cmp`, `hex`) from `bitcoin_hashes` 0.14. The raw types no longer + implement bincode or accept byte and sequence forms in serde, `Error` is + `FromSliceError`, and `forward_hex`, `backward_hex` and the `schemars` feature + are removed +- **Breaking:** hashes honour the format precision, so `{:.8}` prints 8 digits +- **Breaking:** `hash_x11::Hash` loses `forward_hex`, `backward_hex`, + `from_bytes_ref` and `from_bytes_mut`, `hash_x11::Midstate` is replaced by + `[u8; 32]`, and `n_bytes_hashed()` returns the bytes written +- **Breaking:** `EcdsaSighashType` comes from `bitcoin-crypto` and gains a + `NonStandard` variant, which `from_consensus` returns for non-standard flags + and which displays, parses and serializes as `0xNN`. The `from_standard` error + is `NonStandardSighashTypeError`, `TapSighashType` converts through `TryFrom`, + and `sighash::Error` no longer derives `Copy`, `PartialOrd`, `Ord` or `Hash` +- **Breaking:** the taproot types come from `bitcoin-crypto`. `Signature` + fields are renamed to `signature` and `sighash_type`, and `taproot::Error` is + `SigFromSliceError`, without a `Secp256k1` variant +- **Breaking:** `dashcore::eddsa` uses the `dash-pkc` types, so `EddsaPkBytes` + and `EddsaSkBytes` no longer implement `BaseCodec` and `EddsaError` changes + shape. Use `EddsaPublicKey::try_from`, `EddsaPkHash::from` and + `EddsaSecretKey::public_key` instead of `EddsaPkBytes::validate`, + `EddsaPkBytes::hash` and `EddsaSkBytes::public_key` +- **Breaking:** `QualifiedMasternodeListEntry::entry_hash` and + `MessageVerificationError::ThresholdSignatureNotValid` hold `Sha256dHash` +- **Breaking:** `dashcore::base58` is `base58ck` 0.5.0, so `Base58CkString` + replaces the `encode_*` functions and the `Base58` error variants carry + `base58::DecodeCheckError` +- Relicense `dash-network`, `dash-network-seeds`, `dash-spv`, `dash-spv-ffi`, + `dash-spv-bench`, `git-state` and `masternode-seeds-fetcher` to CC0-1.0 +- All workspace crates declare a MSRV of 1.89 + +### Fixed + +- Treat `SIGHASH_SINGLE` flags with `ANYONECANPAY` set, such as `0x83`, as + single when checking for the legacy `SIGHASH_SINGLE` bug +- `transaction_sighash` in `key-wallet-ffi` hashes non-standard flags as given + and rejects flags above `0xff` ## 0.44.0 - 2026-07-01 diff --git a/Cargo.toml b/Cargo.toml index b19b39ad4..657504a39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,12 +3,32 @@ members = ["dash", "crypto", "dash-network", "hashes", "internals", "fuzz", "rpc resolver = "2" [workspace.dependencies] +# Packages shared across crates bincode = { package = "grovedb-bincode", version = "=2.1.0" } bincode_derive = { package = "grovedb-bincode-derive", version = "=2.1.0" } +futures = "0.3" +hex = "0.4" +hex_lit = "0.1.1" +rayon = "1.11" +secp256k1 = "0.33.1" # must match with version used in `bitcoin-crypto` and `dash-pkc` +serde = { version = "1.0.219", default-features = false } +zeroize = { version = "1.8", features = ["derive"] } + +# Packages from `dashpay/base-sdk`, MUST share same version ACROSS crates dash-pkc = { git = "https://github.com/dashpay/base-sdk", rev = "e6402ced257c370a586ade9840ebbf545a4b7926", default-features = false } dash-types = { git = "https://github.com/dashpay/base-sdk", rev = "e6402ced257c370a586ade9840ebbf545a4b7926", default-features = false } +# Packages from `rust-bitcoin/rust-bitcoin`, `bitcoin-crypto` uses revision due to `secp256k1` +base58ck = { version = "0.5.0" } +bitcoin_hashes = { version = "0.14.101", default-features = false } +bitcoin-crypto = { git = "https://github.com/rust-bitcoin/rust-bitcoin", rev = "7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516", default-features = false } + [workspace.package] +authors = ["The rust-dashcore Developers"] +edition = "2024" +license = "CC0-1.0" +repository = "https://github.com/dashpay/rust-dashcore" +rust-version = "1.89.0" version = "0.45.0" [patch.crates-io] diff --git a/README.md b/README.md index bc4374d06..5c0726456 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

Crate Info - MIT or Apache-2.0 Licensed + License CI Status Coverage (main) Coverage (dev) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 896d58bcc..39eed93d5 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -1,34 +1,36 @@ [package] name = "dashcore-crypto" -version = { workspace = true } -authors = ["The Dash Core Developers"] -license = "CC0-1.0" -repository = "https://github.com/dashpay/rust-dashcore/" description = "Cryptographic primitives shared by rust-dashcore crates." +edition = "2021" categories = ["cryptography::cryptocurrencies"] keywords = ["crypto", "dash", "bls", "secp256k1"] -edition = "2021" +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [features] default = [] bincode = ["dep:bincode", "dep:bincode_derive", "dashcore_hashes/bincode", "dash-network/bincode"] -bls = ["dep:dash-pkc", "dash-pkc/bls", "dep:tracing"] -eddsa = ["dep:dash-pkc", "dash-pkc/eddsa"] -serde = ["dep:serde", "dash-types/serde", "dashcore_hashes/serde", "dash-network/serde", "secp256k1/serde"] +bls = ["dash-pkc/bls", "dep:tracing"] +eddsa = ["dash-pkc/eddsa"] +serde = ["dep:serde", "bitcoin-crypto/serde", "dash-types/serde", "dashcore_hashes/serde", "dash-network/serde", "dash-pkc/serde", "secp256k1/serde"] [dependencies] -base58ck = { version = "0.1.0" } +base58ck = { workspace = true } +bitcoin-crypto = { workspace = true, features = ["std", "hex"] } bincode = { workspace = true, optional = true } bincode_derive = { workspace = true, optional = true } dash-network = { path = "../dash-network" } -dash-pkc = { workspace = true, features = ["std"], optional = true } +dash-pkc = { workspace = true, features = ["std"] } dash-types = { workspace = true, features = ["codec"] } dashcore_hashes = { path = "../hashes" } -hex = { version = "0.4" } +hex = { workspace = true } internals = { path = "../internals", package = "dashcore-private" } -secp256k1 = { version = "0.33.1" } -serde = { version = "1.0.219", default-features = false, features = [ "derive", "alloc" ], optional = true } +secp256k1 = { workspace = true } +serde = { workspace = true, features = ["derive", "alloc"], optional = true } thiserror = "2" tracing = { version = "0.1", optional = true } @@ -36,5 +38,5 @@ tracing = { version = "0.1", optional = true } unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(fuzzing)', 'cfg(kani)'] } [dev-dependencies] -hex_lit = { version = "0.1.1", features = ["rust_v_1_46"] } +hex_lit = { workspace = true, features = ["rust_v_1_46"] } serde_json = { version = "1.0" } diff --git a/crypto/src/ecdsa.rs b/crypto/src/ecdsa.rs index f3e44e070..dcb9deb82 100644 --- a/crypto/src/ecdsa.rs +++ b/crypto/src/ecdsa.rs @@ -19,7 +19,7 @@ use secp256k1; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::sighash::{EcdsaSighashType, NonStandardSighashType}; +use crate::sighash::EcdsaSighashType; const MAX_SIG_LEN: usize = 73; @@ -61,7 +61,7 @@ impl Signature { let mut buf = [0u8; MAX_SIG_LEN]; let signature = self.sig.serialize_der(); buf[..signature.len()].copy_from_slice(&signature); - buf[signature.len()] = self.hash_ty as u8; + buf[signature.len()] = self.hash_ty.to_consensus_u8(); SerializedSignature { data: buf, len: signature.len() + 1, @@ -74,14 +74,19 @@ impl Signature { /// [`serialize`](Self::serialize) method instead. pub fn to_vec(self) -> Vec { // TODO: add support to serialize to a writer to SerializedSig - self.sig.serialize_der().iter().copied().chain(iter::once(self.hash_ty as u8)).collect() + self.sig + .serialize_der() + .iter() + .copied() + .chain(iter::once(self.hash_ty.to_consensus_u8())) + .collect() } } impl fmt::Display for Signature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::LowerHex::fmt(&self.sig.serialize_der().as_hex(), f)?; - fmt::LowerHex::fmt(&[self.hash_ty as u8].as_hex(), f) + fmt::LowerHex::fmt(&[self.hash_ty.to_consensus_u8()].as_hex(), f) } } @@ -93,7 +98,8 @@ impl FromStr for Signature { let (sighash_byte, signature) = bytes.split_last().ok_or(Error::EmptySignature)?; Ok(Signature { sig: secp256k1::ecdsa::Signature::from_der(signature)?, - hash_ty: EcdsaSighashType::from_standard(*sighash_byte as u32)?, + hash_ty: EcdsaSighashType::from_standard(*sighash_byte as u32) + .map_err(|_| Error::NonStandardSighashType(*sighash_byte as u32))?, }) } } @@ -217,12 +223,12 @@ impl<'a> IntoIterator for &'a SerializedSignature { } /// A key-related error. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] #[non_exhaustive] pub enum Error { /// Hex encoding error - HexEncoding(hex::Error), - /// Base58 encoding error + HexEncoding(hex::HexToBytesError), + /// Non-standard sighash type NonStandardSighashType(u32), /// Empty Signature EmptySignature, @@ -261,14 +267,8 @@ impl From for Error { } } -impl From for Error { - fn from(err: NonStandardSighashType) -> Self { - Error::NonStandardSighashType(err.0) - } -} - -impl From for Error { - fn from(err: hex::Error) -> Self { +impl From for Error { + fn from(err: hex::HexToBytesError) -> Self { Error::HexEncoding(err) } } diff --git a/crypto/src/eddsa.rs b/crypto/src/eddsa.rs index 6a2ae6582..4e71c6f70 100644 --- a/crypto/src/eddsa.rs +++ b/crypto/src/eddsa.rs @@ -7,33 +7,13 @@ //! Ed25519 keys for Platform node identity. #[cfg(feature = "eddsa")] -use dash_pkc::eddsa::{ - EddsaPkBytes as PkcPkBytes, EddsaPublicKey as PkcPublicKey, EddsaSecretKey as PkcSecretKey, -}; -#[cfg(feature = "eddsa")] -use dash_types::Hashable; -use dash_types::{make_bytes, make_sbytes}; -#[cfg(feature = "eddsa")] -use thiserror::Error as ThisError; - -/// Raw Ed25519 public key length. -pub const EDDSA_PK_LEN: usize = 32; +pub use dash_pkc::eddsa::{EddsaError, EddsaPublicKey, EddsaSecretKey}; +pub use dash_pkc::eddsa::{EddsaPkBytes, EddsaSkBytes, EDDSA_PK_LEN, EDDSA_SK_LEN}; +use dash_types::{make_bytes, Hashable}; /// Ed25519 public key hash length. pub const EDDSA_PK_HASH_LEN: usize = 20; -/// Raw Ed25519 secret key (seed) length. -pub const EDDSA_SK_LEN: usize = 32; - -/// Errors produced by Ed25519 operations. -#[cfg(feature = "eddsa")] -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ThisError)] -pub enum EddsaError { - /// Public key bytes are not a usable curve point. - #[error("Invalid Ed25519 public key: {0}")] - InvalidPublicKey(String), -} - make_bytes! { /// Ed25519 public key hash (20 bytes). EddsaPkHash, EDDSA_PK_HASH_LEN, rev @@ -82,42 +62,10 @@ impl<'de, C> bincode::BorrowDecode<'de, C> for EddsaPkHash { } } -make_bytes! { - /// Ed25519 public key (32 bytes, unvalidated). - EddsaPkBytes, EDDSA_PK_LEN -} - -#[cfg(feature = "eddsa")] -impl EddsaPkBytes { - /// Checks these bytes are a usable curve point. - /// - /// # Errors - /// - /// Returns `InvalidPublicKey` when the bytes are not on the curve. - pub fn validate(&self) -> Result<(), EddsaError> { - PkcPublicKey::from_bytes(self.as_bytes()) - .map(|_| ()) - .map_err(|e| EddsaError::InvalidPublicKey(e.to_string())) - } - - /// The CometBFT hash of the public key. - pub fn hash(&self) -> EddsaPkHash { - EddsaPkHash::from_bytes( - *Hashable::hash(&PkcPkBytes::from_bytes(*self.as_bytes())).as_bytes(), - ) - } -} - -make_sbytes! { - /// Ed25519 secret key seed (32 bytes). - EddsaSkBytes, EDDSA_SK_LEN -} - -#[cfg(feature = "eddsa")] -impl EddsaSkBytes { - /// Derives the corresponding public key. - pub fn public_key(&self) -> EddsaPkBytes { - EddsaPkBytes::from_bytes(PkcSecretKey::from_bytes(self.as_bytes()).public_key().to_bytes()) +/// The CometBFT hash of the public key. +impl From for EddsaPkHash { + fn from(public_key: EddsaPkBytes) -> Self { + Self::from_bytes(*Hashable::hash(&public_key).as_bytes()) } } @@ -171,7 +119,6 @@ mod tests { assert_eq!(back, hash); } - #[cfg(feature = "eddsa")] #[test] fn hash_is_truncated_sha256() { use dashcore_hashes::{sha256, Hash}; @@ -180,7 +127,7 @@ mod tests { let digest = sha256::Hash::hash(&public_key); // `EddsaPkHash` holds the wire order, which is the byte-reversal of // the canonical form the digest is read in. - let mut canonical = *EddsaPkBytes::from_bytes(public_key).hash().as_bytes(); + let mut canonical = *EddsaPkHash::from(EddsaPkBytes::from_bytes(public_key)).as_bytes(); canonical.reverse(); assert_eq!(canonical[..], digest.to_byte_array()[..20]); diff --git a/crypto/src/key.rs b/crypto/src/key.rs index f2dfe0dd9..a01e8c3a3 100644 --- a/crypto/src/key.rs +++ b/crypto/src/key.rs @@ -30,7 +30,7 @@ use crate::base58; #[non_exhaustive] pub enum Error { /// Base58 encoding error - Base58(base58::Error), + Base58(base58::DecodeCheckError), /// secp256k1-related error Secp256k1(secp256k1::Error), /// Invalid key prefix error @@ -40,7 +40,7 @@ pub enum Error { /// The base58 decoded correctly but the payload was the wrong length. InvalidBase58PayloadLength(usize), /// Hex decoding error - Hex(hex::Error), + Hex(hex::HexToArrayError), /// `PublicKey` hex should be 66 or 130 digits long. InvalidHexLength(usize), /// Something is not supported based on active features @@ -88,8 +88,8 @@ impl std::error::Error for Error { } #[doc(hidden)] -impl From for Error { - fn from(e: base58::Error) -> Error { +impl From for Error { + fn from(e: base58::DecodeCheckError) -> Error { Error::Base58(e) } } @@ -102,8 +102,8 @@ impl From for Error { } #[doc(hidden)] -impl From for Error { - fn from(e: hex::Error) -> Self { +impl From for Error { + fn from(e: hex::HexToArrayError) -> Self { Error::Hex(e) } } @@ -400,11 +400,11 @@ impl PrivateKey { ret[1..33].copy_from_slice(&self.inner[..]); let privkey = if self.compressed { ret[33] = 1; - base58::encode_check(&ret[..]) + base58::Base58CkString::encode_unbounded(&ret[..]) } else { - base58::encode_check(&ret[..33]) + base58::Base58CkString::encode_unbounded(&ret[..33]) }; - fmt.write_str(&privkey) + fmt.write_str(privkey.as_str()) } /// Get WIF encoding of this private key. diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index bcedd9555..b1f873394 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -12,35 +12,23 @@ extern crate alloc; pub extern crate base58ck as base58; -#[cfg(feature = "bls")] pub extern crate dash_pkc; pub extern crate dashcore_hashes as hashes; pub extern crate secp256k1; #[cfg(feature = "serde")] pub extern crate serde; -#[cfg(feature = "serde")] -#[macro_use] -pub(crate) mod serde_utils; - pub mod bls; pub mod ecdsa; pub mod eddsa; pub mod key; pub mod sighash; -pub mod taproot; -/// Implements `std::error::Error` for a type whose `Display` carries the message. -macro_rules! impl_std_error { - ($type:ty) => { - impl std::error::Error for $type {} - }; - ($type:ty, $field:ident) => { - impl std::error::Error for $type { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(&self.$field) - } - } +/// Taproot signatures. +pub mod taproot { + pub use bitcoin_crypto::taproot::{ + ParseSignatureError, SerializedSignature, SigFromSliceError, Signature, }; + + pub type Error = SigFromSliceError; } -pub(crate) use impl_std_error; diff --git a/crypto/src/serde_utils.rs b/crypto/src/serde_utils.rs deleted file mode 100644 index 41e5b8fe9..000000000 --- a/crypto/src/serde_utils.rs +++ /dev/null @@ -1,60 +0,0 @@ -// -// This file is a part of rust-dashcore. Contains portions from rust-bitcoin. -// SPDX-License-Identifier: CC0-1.0 -// See the accompanying file LICENSE or https://creativecommons.org/publicdomain/zero/1.0 -// - -//! Serde helpers. - -macro_rules! serde_string_serialize_impl { - ($name:ty, $expecting:literal) => { - impl $crate::serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where - S: $crate::serde::Serializer, - { - serializer.collect_str(&self) - } - } - }; -} - -macro_rules! serde_string_deserialize_impl { - ($name:ty, $expecting:literal) => { - impl<'de> $crate::serde::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result<$name, D::Error> - where - D: $crate::serde::de::Deserializer<'de>, - { - use core::fmt::{self, Formatter}; - - struct Visitor; - impl<'de> $crate::serde::de::Visitor<'de> for Visitor { - type Value = $name; - - fn expecting(&self, f: &mut Formatter) -> fmt::Result { - f.write_str($expecting) - } - - fn visit_str(self, v: &str) -> Result - where - E: $crate::serde::de::Error, - { - v.parse::<$name>().map_err(E::custom) - } - } - - deserializer.deserialize_str(Visitor) - } - } - }; -} - -macro_rules! serde_string_impl { - ($name:ty, $expecting:literal) => { - $crate::serde_utils::serde_string_deserialize_impl!($name, $expecting); - $crate::serde_utils::serde_string_serialize_impl!($name, $expecting); - }; -} - -pub(crate) use {serde_string_deserialize_impl, serde_string_impl, serde_string_serialize_impl}; diff --git a/crypto/src/sighash.rs b/crypto/src/sighash.rs index 0bbb45ef1..e35f53540 100644 --- a/crypto/src/sighash.rs +++ b/crypto/src/sighash.rs @@ -6,12 +6,7 @@ //! Signature hash types. -use core::{fmt, str}; -use std::string::String; - -use hashes::{hash_newtype, sha256d, sha256t_hash_newtype, Hash as _}; - -use crate::impl_std_error; +use hashes::{hash_newtype, sha256d, sha256t_hash_newtype}; hash_newtype! { /// Hash of a transaction according to the legacy signature algorithm. @@ -33,135 +28,12 @@ sha256t_hash_newtype! { pub struct TapSighash(_); } -/// Hashtype of an input's signature, encoded in the last byte of the signature. -/// Fixed values so they can be cast as integer types for encoding. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum TapSighashType { - /// 0x0: Used when not explicitly specified, defaults to [`TapSighashType::All`] - Default = 0x00, - /// 0x1: Sign all outputs. - All = 0x01, - /// 0x2: Sign no outputs --- anyone can choose the destination. - None = 0x02, - /// 0x3: Sign the output whose index matches this input's index. If none exists, - /// sign the hash `0000000000000000000000000000000000000000000000000000000000000001`. - /// (This rule is probably an unintentional C++ism, but it's consensus so we have - /// to follow it.) - Single = 0x03, - /// 0x81: Sign all outputs but only this input. - AllPlusAnyoneCanPay = 0x81, - /// 0x82: Sign no outputs and only this input. - NonePlusAnyoneCanPay = 0x82, - /// 0x83: Sign one output and only this input (see `Single` for what "one output" means). - SinglePlusAnyoneCanPay = 0x83, -} -#[cfg(feature = "serde")] -crate::serde_utils::serde_string_impl!(TapSighashType, "a TapSighashType data"); - -impl fmt::Display for TapSighashType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use TapSighashType::*; - - let s = match self { - Default => "SIGHASH_DEFAULT", - All => "SIGHASH_ALL", - None => "SIGHASH_NONE", - Single => "SIGHASH_SINGLE", - AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY", - NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY", - SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY", - }; - f.write_str(s) - } -} - -impl str::FromStr for TapSighashType { - type Err = SighashTypeParseError; - - fn from_str(s: &str) -> Result { - use TapSighashType::*; - - match s { - "SIGHASH_DEFAULT" => Ok(Default), - "SIGHASH_ALL" => Ok(All), - "SIGHASH_NONE" => Ok(None), - "SIGHASH_SINGLE" => Ok(Single), - "SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(AllPlusAnyoneCanPay), - "SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(NonePlusAnyoneCanPay), - "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SinglePlusAnyoneCanPay), - _ => Err(SighashTypeParseError { - unrecognized: s.to_owned(), - }), - } - } -} - -/// Hashtype of an input's signature, encoded in the last byte of the signature. -/// -/// Fixed values so they can be cast as integer types for encoding (see also -/// [`TapSighashType`]). -#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)] -pub enum EcdsaSighashType { - /// 0x1: Sign all outputs. - All = 0x01, - /// 0x2: Sign no outputs --- anyone can choose the destination. - None = 0x02, - /// 0x3: Sign the output whose index matches this input's index. If none exists, - /// sign the hash `0000000000000000000000000000000000000000000000000000000000000001`. - /// (This rule is probably an unintentional C++ism, but it's consensus so we have - /// to follow it.) - Single = 0x03, - /// 0x81: Sign all outputs but only this input. - AllPlusAnyoneCanPay = 0x81, - /// 0x82: Sign no outputs and only this input. - NonePlusAnyoneCanPay = 0x82, - /// 0x83: Sign one output and only this input (see `Single` for what "one output" means). - SinglePlusAnyoneCanPay = 0x83, -} -#[cfg(feature = "serde")] -crate::serde_utils::serde_string_impl!(EcdsaSighashType, "a EcdsaSighashType data"); - -impl fmt::Display for EcdsaSighashType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use EcdsaSighashType::*; - - let s = match self { - All => "SIGHASH_ALL", - None => "SIGHASH_NONE", - Single => "SIGHASH_SINGLE", - AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY", - NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY", - SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY", - }; - f.write_str(s) - } -} - -impl str::FromStr for EcdsaSighashType { - type Err = SighashTypeParseError; - - fn from_str(s: &str) -> Result { - use EcdsaSighashType::*; - - match s { - "SIGHASH_ALL" => Ok(All), - "SIGHASH_NONE" => Ok(None), - "SIGHASH_SINGLE" => Ok(Single), - "SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(AllPlusAnyoneCanPay), - "SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(NonePlusAnyoneCanPay), - "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SinglePlusAnyoneCanPay), - _ => Err(SighashTypeParseError { - unrecognized: s.to_owned(), - }), - } - } -} +pub use bitcoin_crypto::sighash::{ + EcdsaSighashType, InvalidSighashTypeError, NonStandardSighashType, NonStandardSighashTypeError, + SighashTypeParseError, TapSighashType, +}; /// Splits a sighash flag into the "real" flag and the `SIGHASH_ANYONECANPAY` bit. -/// -/// A trait rather than an inherent method so that `dashcore`, which computes -/// the signature hashes, can reach it without the split becoming part of the -/// sighash types' own surface. pub trait SplitAnyoneCanPay { /// Splits the flag into the "real" sighash flag and the ANYONECANPAY boolean. fn split_anyonecanpay_flag(self) -> (Self, bool) @@ -172,7 +44,6 @@ pub trait SplitAnyoneCanPay { impl SplitAnyoneCanPay for EcdsaSighashType { fn split_anyonecanpay_flag(self) -> (EcdsaSighashType, bool) { use EcdsaSighashType::*; - match self { All => (All, false), None => (None, false), @@ -180,81 +51,16 @@ impl SplitAnyoneCanPay for EcdsaSighashType { AllPlusAnyoneCanPay => (All, true), NonePlusAnyoneCanPay => (None, true), SinglePlusAnyoneCanPay => (Single, true), - } - } -} - -impl EcdsaSighashType { - /// Creates a [`EcdsaSighashType`] from a raw `u32`. - /// - /// **Note**: this replicates consensus behaviour, for current standardness rules correctness - /// you probably want [`Self::from_standard`]. - /// - /// This might cause unexpected behavior because it does not roundtrip. That is, - /// `EcdsaSighashType::from_consensus(n) as u32 != n` for non-standard values of `n`. While - /// verifying signatures, the user should retain the `n` and use it compute the signature hash - /// message. - pub fn from_consensus(n: u32) -> EcdsaSighashType { - use EcdsaSighashType::*; - - // In Bitcoin Core, the SignatureHash function will mask the (int32) value with - // 0x1f to (apparently) deactivate ACP when checking for SINGLE and NONE bits. - // We however want to be matching also against on ACP-masked ALL, SINGLE, and NONE. - // So here we re-activate ACP. - let mask = 0x1f | 0x80; - match n & mask { - // "real" sighashes - 0x01 => All, - 0x02 => None, - 0x03 => Single, - 0x81 => AllPlusAnyoneCanPay, - 0x82 => NonePlusAnyoneCanPay, - 0x83 => SinglePlusAnyoneCanPay, - // catchalls - x if x & 0x80 == 0x80 => AllPlusAnyoneCanPay, - _ => All, - } - } - - /// Creates a [`EcdsaSighashType`] from a raw `u32`. - /// - /// # Errors - /// - /// If `n` is a non-standard sighash value. - pub fn from_standard(n: u32) -> Result { - use EcdsaSighashType::*; - - match n { - // Standard sighashes, see https://github.com/bitcoin/bitcoin/blob/b805dbb0b9c90dadef0424e5b3bf86ac308e103e/src/script/interpreter.cpp#L189-L198 - 0x01 => Ok(All), - 0x02 => Ok(None), - 0x03 => Ok(Single), - 0x81 => Ok(AllPlusAnyoneCanPay), - 0x82 => Ok(NonePlusAnyoneCanPay), - 0x83 => Ok(SinglePlusAnyoneCanPay), - non_standard => Err(NonStandardSighashType(non_standard)), - } - } - - /// Converts [`EcdsaSighashType`] to a `u32` sighash flag. - /// - /// The returned value is guaranteed to be a valid according to standardness rules. - pub fn to_u32(self) -> u32 { - self as u32 - } -} - -impl From for TapSighashType { - fn from(s: EcdsaSighashType) -> Self { - use TapSighashType::*; - - match s { - EcdsaSighashType::All => All, - EcdsaSighashType::None => None, - EcdsaSighashType::Single => Single, - EcdsaSighashType::AllPlusAnyoneCanPay => AllPlusAnyoneCanPay, - EcdsaSighashType::NonePlusAnyoneCanPay => NonePlusAnyoneCanPay, - EcdsaSighashType::SinglePlusAnyoneCanPay => SinglePlusAnyoneCanPay, + // Core reads SINGLE/NONE from the low five bits and treats anything + // else as ALL. ANYONECANPAY is the 0x80 bit. + NonStandard(n) => { + let acp = n.to_u32() & 0x80 == 0x80; + match n.to_u32() & 0x1f { + 0x02 => (None, acp), + 0x03 => (Single, acp), + _ => (All, acp), + } + } } } } @@ -262,7 +68,6 @@ impl From for TapSighashType { impl SplitAnyoneCanPay for TapSighashType { fn split_anyonecanpay_flag(self) -> (TapSighashType, bool) { use TapSighashType::*; - match self { Default => (Default, false), All => (All, false), @@ -275,66 +80,20 @@ impl SplitAnyoneCanPay for TapSighashType { } } -impl TapSighashType { - /// Constructs a [`TapSighashType`] from a raw `u8`. - /// - /// # Errors - /// - /// Returns [`InvalidSighashTypeError`] when `hash_ty` names no sighash type. - pub fn from_consensus_u8(hash_ty: u8) -> Result { - use TapSighashType::*; - - Ok(match hash_ty { - 0x00 => Default, - 0x01 => All, - 0x02 => None, - 0x03 => Single, - 0x81 => AllPlusAnyoneCanPay, - 0x82 => NonePlusAnyoneCanPay, - 0x83 => SinglePlusAnyoneCanPay, - x => return Err(InvalidSighashTypeError(x as u32)), - }) - } -} - -/// Integer is not a consensus valid sighash type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InvalidSighashTypeError(pub u32); - -impl fmt::Display for InvalidSighashTypeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "invalid sighash type {}", self.0) - } -} - -impl_std_error!(InvalidSighashTypeError); - -/// This type is consensus valid but an input including it would prevent the transaction from -/// being relayed on today's Bitcoin network. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct NonStandardSighashType(pub u32); - -impl fmt::Display for NonStandardSighashType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Non standard sighash type {}", self.0) - } -} - -impl_std_error!(NonStandardSighashType); - -/// Error returned for failure during parsing one of the sighash types. -/// -/// This is currently returned for unrecognized sighash strings. -#[derive(Debug, Clone)] -pub struct SighashTypeParseError { - /// The unrecognized string we attempted to parse. - pub unrecognized: String, -} - -impl fmt::Display for SighashTypeParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Unrecognized SIGHASH string '{}'", self.unrecognized) +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn split_follows_core() { + for n in 0..=0x1ffu32 { + let ty = match n & 0x1f { + 0x02 => EcdsaSighashType::None, + 0x03 => EcdsaSighashType::Single, + _ => EcdsaSighashType::All, + }; + let got = EcdsaSighashType::from_consensus(n).split_anyonecanpay_flag(); + assert_eq!(got, (ty, n & 0x80 != 0), "sighash flag {n:#04x}"); + } } } - -impl_std_error!(SighashTypeParseError); diff --git a/crypto/src/taproot.rs b/crypto/src/taproot.rs deleted file mode 100644 index 74362831f..000000000 --- a/crypto/src/taproot.rs +++ /dev/null @@ -1,108 +0,0 @@ -// -// This file is a part of rust-dashcore. -// Portions written by Andrew Poelstra for rust-bitcoin. -// SPDX-License-Identifier: CC0-1.0 -// See the accompanying file LICENSE or https://creativecommons.org/publicdomain/zero/1.0 -// - -//! Bitcoin taproot keys. -//! -//! This module provides taproot keys used in Bitcoin (including reexporting secp256k1 keys). -//! - -use core::fmt; - -use internals::write_err; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -pub use secp256k1::{self}; - -use crate::sighash::TapSighashType; - -/// A BIP340-341 serialized taproot signature with the corresponding hash type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Signature { - /// The underlying schnorr signature - pub sig: secp256k1::schnorr::Signature, - /// The corresponding hash type - pub hash_ty: TapSighashType, -} - -impl Signature { - /// Deserialize from slice - pub fn from_slice(sl: &[u8]) -> Result { - let Some((sig, hash_ty)) = - sl.split_first_chunk::<{ secp256k1::constants::SCHNORR_SIGNATURE_SIZE }>() - else { - return Err(Error::InvalidSignatureSize(sl.len())); - }; - let hash_ty = match hash_ty { - // Default sighash type is implied by the absence of a trailing byte. - [] => TapSighashType::Default, - [hash_ty] => TapSighashType::from_consensus_u8(*hash_ty) - .map_err(|_| Error::InvalidSighashType(*hash_ty))?, - _ => return Err(Error::InvalidSignatureSize(sl.len())), - }; - Ok(Signature { - sig: secp256k1::schnorr::Signature::from_byte_array(*sig), - hash_ty, - }) - } - - /// Serialize Signature - pub fn to_vec(self) -> Vec { - // TODO: add support to serialize to a writer to SerializedSig - let mut ser_sig = self.sig.as_ref().to_vec(); - if self.hash_ty == TapSighashType::Default { - // default sighash type, don't add extra sighash byte - } else { - ser_sig.push(self.hash_ty as u8); - } - ser_sig - } -} - -/// A taproot sig related error. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[non_exhaustive] -pub enum Error { - /// Base58 encoding error - InvalidSighashType(u8), - /// Signature has valid size but does not parse correctly - Secp256k1(secp256k1::Error), - /// Invalid taproot signature size - InvalidSignatureSize(usize), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::InvalidSighashType(hash_ty) => { - write!(f, "invalid signature hash type {}", hash_ty) - } - Error::Secp256k1(ref e) => { - write_err!(f, "taproot signature has correct len but is malformed"; e) - } - Error::InvalidSignatureSize(sz) => write!(f, "invalid taproot signature size: {}", sz), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use self::Error::*; - - match self { - Secp256k1(e) => Some(e), - InvalidSighashType(_) | InvalidSignatureSize(_) => None, - } - } -} - -impl From for Error { - fn from(e: secp256k1::Error) -> Error { - Error::Secp256k1(e) - } -} diff --git a/dash-network-seeds/Cargo.toml b/dash-network-seeds/Cargo.toml index a5ce9bff8..0eed93018 100644 --- a/dash-network-seeds/Cargo.toml +++ b/dash-network-seeds/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "dash-network-seeds" -version = { workspace = true } -edition = "2024" -authors = ["Dash Core Team"] description = "Hardcoded masternode IP seed lists for Dash mainnet and testnet, differentiated by regular/evo type. Refreshed weekly via CI from the live Dash P2P network." -license = "MIT" -repository = "https://github.com/dashpay/rust-dashcore" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [dependencies] dash-network = { path = "../dash-network" } diff --git a/dash-network/Cargo.toml b/dash-network/Cargo.toml index 9aeaea805..44ecb63b3 100644 --- a/dash-network/Cargo.toml +++ b/dash-network/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "dash-network" -version = { workspace = true } -edition = "2024" -authors = ["Dash Core Team"] description = "The `Network` enum and its pure helpers (magic bytes, activation heights, default P2P ports) extracted from `dashcore` into a minimal-dependency crate so that lightweight consumers can depend on it without pulling in the full protocol library." -license = "MIT" -repository = "https://github.com/dashpay/rust-dashcore" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [lib] name = "dash_network" @@ -21,7 +22,7 @@ bincode = ["dep:bincode", "dep:bincode_derive"] ffi = [] [dependencies] -serde = { version = "1.0.219", default-features = false, features = ["derive"], optional = true } +serde = { workspace = true, features = ["derive"], optional = true } bincode = { workspace = true, optional = true } bincode_derive = { workspace = true, optional = true } diff --git a/dash-spv-bench/Cargo.toml b/dash-spv-bench/Cargo.toml index 58aa104b0..e83c004d9 100644 --- a/dash-spv-bench/Cargo.toml +++ b/dash-spv-bench/Cargo.toml @@ -1,9 +1,12 @@ [package] name = "dash-spv-bench" -version = { workspace = true } edition = "2021" -license = "MIT" publish = false +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [dependencies] dash-spv = { path = "../dash-spv" } diff --git a/dash-spv-ffi/Cargo.toml b/dash-spv-ffi/Cargo.toml index 19ada2d0e..023995f15 100644 --- a/dash-spv-ffi/Cargo.toml +++ b/dash-spv-ffi/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "dash-spv-ffi" -version = { workspace = true } -edition = "2021" -authors = ["Dash Core Developers"] -license = "MIT" description = "FFI bindings for the Dash SPV client" -repository = "https://github.com/dashpay/rust-dashcore" +edition = "2021" +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [lib] name = "dash_spv_ffi" @@ -16,7 +17,7 @@ dash-spv = { path = "../dash-spv" } dashcore = { path = "../dash" } dash-network = { path = "../dash-network", features = ["ffi"] } tokio = { version = "1", features = ["full"] } -hex = "0.4" +hex = { workspace = true } tracing = "0.1" # Use key-wallet-ffi for all wallet-related FFI types key-wallet-ffi = { path = "../key-wallet-ffi" } diff --git a/dash-spv-ffi/README.md b/dash-spv-ffi/README.md index 747c40696..bd782c34d 100644 --- a/dash-spv-ffi/README.md +++ b/dash-spv-ffi/README.md @@ -109,4 +109,4 @@ The FFI bindings are thread-safe. The client uses internal synchronization to en ## License -MIT +CC0-1.0 diff --git a/dash-spv/Cargo.toml b/dash-spv/Cargo.toml index ad0e10cb7..cfcc08aba 100644 --- a/dash-spv/Cargo.toml +++ b/dash-spv/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "dash-spv" -version = { workspace = true } -edition = "2021" -authors = ["Dash Core Team"] description = "Dash SPV (Simplified Payment Verification) client library" -license = "MIT" -repository = "https://github.com/dashpay/rust-dashcore" -rust-version = "1.89" +edition = "2021" +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [dependencies] # Core Dash libraries @@ -25,13 +25,13 @@ tokio = { version = "1.0", features = ["full"] } tokio-util = "0.7" tokio-stream = { version = "0.1", features = ["sync"] } async-trait = "0.1" -futures = "0.3" +futures = { workspace = true } # Error handling thiserror = "1.0" # Serialization -serde = { version = "1.0", features = ["derive"] } +serde = { workspace = true, features = ["std", "derive"] } serde_json = "1.0" # Logging @@ -42,10 +42,10 @@ chrono = "0.4.20" # Utilities rand = "0.9" -hex = "0.4" +hex = { workspace = true } # Parallelization -rayon = "1.11" +rayon = { workspace = true } # Test utilities (optional) tempfile = { version = "3.0", optional = true } @@ -60,7 +60,7 @@ key-wallet-manager = { path = "../key-wallet-manager", features = ["test-utils"] criterion = { version = "0.8.1", features = ["async_tokio"] } tempfile = "3.0" tokio-test = "0.4" -hex = "0.4" +hex = { workspace = true } test-case = "3.3" [[bench]] diff --git a/dash-spv/README.md b/dash-spv/README.md index 247f93c75..1663fe68e 100644 --- a/dash-spv/README.md +++ b/dash-spv/README.md @@ -133,3 +133,7 @@ See [docs/PEER_REPUTATION_SYSTEM.md](docs/PEER_REPUTATION_SYSTEM.md) for detaile 6. **Performance**: Async design for better resource utilization This refactoring transforms an example script into a production-ready library suitable for integration into wallets, explorers, and other Dash applications requiring SPV functionality. + +## License + +CC0-1.0 diff --git a/dash/Cargo.toml b/dash/Cargo.toml index 3d1e40e9b..a2c576ecf 100644 --- a/dash/Cargo.toml +++ b/dash/Cargo.toml @@ -1,21 +1,20 @@ [package] name = "dashcore" -version = { workspace = true } authors = [ "Samuel Westrich ", "Anton Suprunchuk ", "Dmitrii Golubev " ] -license = "CC0-1.0" -homepage = "https://github.com/dashevo/rust-dashcore/" -repository = "https://github.com/dashevo/rust-dashcore/" -documentation = "https://dashcore.readme.io/docs" description = "General purpose library for using and interoperating with Dash Core payment chain." categories = ["cryptography::cryptocurrencies"] keywords = [ "crypto", "dash" ] readme = "../README.md" exclude = ["tests", "contrib"] -edition = "2024" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true # Please don't forget to add relevant features to docs.rs below [features] @@ -47,14 +46,14 @@ dashcore_hashes = { path = "../hashes" } dashcore-crypto = { path = "../crypto" } dash-network = { path = "../dash-network" } dash-types = { workspace = true, features = ["codec"] } -secp256k1 = { version = "0.33.1" } -serde = { version = "1.0.219", default-features = false, features = [ "derive", "alloc", "rc" ], optional = true } +secp256k1 = { workspace = true } +serde = { workspace = true, features = ["derive", "alloc", "rc"], optional = true } base64 = { version = "0.22", optional = true } bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true } -hex_lit = "0.1.1" +hex_lit = { workspace = true } anyhow = { version= "1.0" } -hex = { version= "0.4" } +hex = { workspace = true } bincode = { workspace = true, optional = true } bincode_derive = { workspace = true, optional = true } blake3 = "1.8.1" @@ -66,7 +65,7 @@ tracing = "0.1" serde_json = "1.0.140" serde_test = "1.0.177" serde_derive = "1.0.219" -secp256k1 = { features = [ "recovery", "rand" ], version = "0.33.1" } +secp256k1 = { workspace = true, features = ["recovery", "rand"] } bincode = { workspace = true, features = ["serde"] } assert_matches = "1.5.0" dashcore = { path = ".", features = ["core-block-hash-use-x11", "message_verification", "quorum_validation", "signer"] } diff --git a/dash/src/address.rs b/dash/src/address.rs index 08f2b6b7f..bff742223 100644 --- a/dash/src/address.rs +++ b/dash/src/address.rs @@ -74,7 +74,7 @@ use crate::taproot::{TapNodeHash, TapTweak}; #[non_exhaustive] pub enum Error { /// Base58 encoding error. - Base58(base58::Error), + Base58(base58::DecodeCheckError), /// The address version byte was not one we recognise. InvalidAddressVersion(u8), /// The base58 decoded correctly but the payload was the wrong length. @@ -178,8 +178,8 @@ impl std::error::Error for Error { } #[doc(hidden)] -impl From for Error { - fn from(e: base58::Error) -> Error { +impl From for Error { + fn from(e: base58::DecodeCheckError) -> Error { Error::Base58(e) } } @@ -649,13 +649,13 @@ impl<'a> fmt::Display for AddressEncoding<'a> { let mut prefixed = [0; 21]; prefixed[0] = self.p2pkh_prefix; prefixed[1..].copy_from_slice(&hash[..]); - base58::encode_check_to_fmt(fmt, &prefixed[..]) + fmt.write_str(base58::Base58CkString::encode_unbounded(&prefixed[..]).as_str()) } Payload::ScriptHash(hash) => { let mut prefixed = [0; 21]; prefixed[0] = self.p2sh_prefix; prefixed[1..].copy_from_slice(&hash[..]); - base58::encode_check_to_fmt(fmt, &prefixed[..]) + fmt.write_str(base58::Base58CkString::encode_unbounded(&prefixed[..]).as_str()) } Payload::WitnessProgram(witness_prog) => { let (version, prog) = (witness_prog.version(), witness_prog.program()); diff --git a/dash/src/blockdata/script/owned.rs b/dash/src/blockdata/script/owned.rs index 4df712ecf..77544a4e5 100644 --- a/dash/src/blockdata/script/owned.rs +++ b/dash/src/blockdata/script/owned.rs @@ -168,7 +168,7 @@ impl ScriptBuf { } /// Creates a [`ScriptBuf`] from a hex string. - pub fn from_hex(s: &str) -> Result { + pub fn from_hex(s: &str) -> Result { use hashes::hex::FromHex; let v = Vec::from_hex(s)?; diff --git a/dash/src/blockdata/transaction/outpoint.rs b/dash/src/blockdata/transaction/outpoint.rs index 46095882f..ec042542f 100644 --- a/dash/src/blockdata/transaction/outpoint.rs +++ b/dash/src/blockdata/transaction/outpoint.rs @@ -58,7 +58,7 @@ impl From for [u8; 36] { fn from(value: OutPoint) -> Self { let mut bytes = [0u8; 36]; // Serialize the txid - let txid_bytes: [u8; 32] = value.txid.to_raw_hash().into(); // Assuming to_raw_hash() returns the hash as sha256d::Hash, which can be converted into [u8; 32] + let txid_bytes: [u8; 32] = hashes::Hash::to_byte_array(value.txid.to_raw_hash()); // Assuming to_raw_hash() returns the hash as sha256d::Hash, which can be converted into [u8; 32] bytes[..32].copy_from_slice(&txid_bytes); // Serialize the vout let vout_bytes = value.vout.to_le_bytes(); @@ -152,7 +152,7 @@ impl TryInto> for OutPoint { #[non_exhaustive] pub enum ParseOutPointError { /// Error in TXID part. - Txid(hashes::hex::Error), + Txid(hashes::hex::HexToArrayError), /// Error in vout part. Vout(crate::error::ParseIntError), /// Error in general format. diff --git a/dash/src/blockdata/witness.rs b/dash/src/blockdata/witness.rs index 9201318a1..6681b4379 100644 --- a/dash/src/blockdata/witness.rs +++ b/dash/src/blockdata/witness.rs @@ -285,7 +285,7 @@ impl Witness { // Note that a maximal length ECDSA signature is 72 bytes, plus the sighash type makes 73 let mut sig = [0; 73]; sig[..signature.len()].copy_from_slice(signature); - sig[signature.len()] = hash_type as u8; + sig[signature.len()] = hash_type.to_consensus_u8(); self.push(&sig[..signature.len() + 1]); } @@ -428,8 +428,8 @@ impl<'de> serde::Deserialize<'de> for Witness { self, mut a: A, ) -> Result { - use hashes::hex::Error::*; use hashes::hex::FromHex; + use hashes::hex::HexToBytesError::*; use serde::de::{self, Unexpected}; let mut ret = match a.size_hint() { @@ -439,22 +439,21 @@ impl<'de> serde::Deserialize<'de> for Witness { while let Some(elem) = a.next_element::()? { let vec = Vec::::from_hex(&elem).map_err(|e| match e { - InvalidChar(b) => match core::char::from_u32(b.into()) { - Some(c) => de::Error::invalid_value( - Unexpected::Char(c), - &"a valid hex character", - ), - None => de::Error::invalid_value( - Unexpected::Unsigned(b.into()), - &"a valid hex character", - ), - }, - OddLengthString(len) => { - de::Error::invalid_length(len, &"an even length string") + InvalidChar(e) => { + let b = e.invalid_char(); + match core::char::from_u32(b.into()) { + Some(c) => de::Error::invalid_value( + Unexpected::Char(c), + &"a valid hex character", + ), + None => de::Error::invalid_value( + Unexpected::Unsigned(b.into()), + &"a valid hex character", + ), + } } - InvalidLength(expected, got) => { - let exp = format!("expected length: {}", expected); - de::Error::invalid_length(got, &exp.as_str()) + OddLengthString(e) => { + de::Error::invalid_length(e.length(), &"an even length string") } })?; ret.push(vec); diff --git a/dash/src/consensus/encode.rs b/dash/src/consensus/encode.rs index a95aaa326..ec2b9bcde 100644 --- a/dash/src/consensus/encode.rs +++ b/dash/src/consensus/encode.rs @@ -107,7 +107,7 @@ pub enum Error { /// The script type was non standard NonStandardScriptPayout(ScriptBuf), /// Hex error - Hex(hashes::hex::Error), + Hex(hashes::hex::HexToArrayError), /// Address error Address(address::Error), /// Invalid enum value diff --git a/dash/src/consensus/serde.rs b/dash/src/consensus/serde.rs index d1a1bc694..0cc35965c 100644 --- a/dash/src/consensus/serde.rs +++ b/dash/src/consensus/serde.rs @@ -103,19 +103,21 @@ pub mod hex { /// Error returned when a hex string decoder can't be created. #[derive(Debug)] - pub struct DecodeInitError(hashes::hex::Error); + pub struct DecodeInitError(hashes::hex::OddLengthStringError); /// Error returned when a hex string contains invalid characters. #[derive(Debug)] - pub struct DecodeError(hashes::hex::Error); + pub struct DecodeError(hashes::hex::InvalidCharError); /// Hex decoder state. - pub struct Decoder<'a>(hashes::hex::HexIterator<'a>); + pub struct Decoder<'a>( + Box> + 'a>, + ); impl<'a> Decoder<'a> { fn new(s: &'a str) -> Result { - match hashes::hex::HexIterator::new(s) { - Ok(iter) => Ok(Decoder(iter)), + match hashes::hex::HexToBytesIter::new(s) { + Ok(iter) => Ok(Decoder(Box::new(iter))), Err(error) => Err(DecodeInitError(error)), } } @@ -141,32 +143,21 @@ pub mod hex { impl super::IntoDeError for DecodeInitError { fn into_de_error(self) -> E { - use hashes::hex::Error; - - match self.0 { - Error::OddLengthString(len) => { - E::invalid_length(len, &"an even number of ASCII-encoded hex digits") - } - error => panic!("unexpected error: {:?}", error), - } + E::invalid_length(self.0.length(), &"an even number of ASCII-encoded hex digits") } } impl super::IntoDeError for DecodeError { fn into_de_error(self) -> E { - use hashes::hex::Error; use serde::de::Unexpected; const EXPECTED_CHAR: &str = "an ASCII-encoded hex digit"; - match self.0 { - Error::InvalidChar(c) if c.is_ascii() => { - E::invalid_value(Unexpected::Char(c as _), &EXPECTED_CHAR) - } - Error::InvalidChar(c) => { - E::invalid_value(Unexpected::Unsigned(c.into()), &EXPECTED_CHAR) - } - error => panic!("unexpected error: {:?}", error), + let c = self.0.invalid_char(); + if c.is_ascii() { + E::invalid_value(Unexpected::Char(c as _), &EXPECTED_CHAR) + } else { + E::invalid_value(Unexpected::Unsigned(c.into()), &EXPECTED_CHAR) } } } diff --git a/dash/src/crypto/sighash.rs b/dash/src/crypto/sighash.rs index 9c7886ad3..2440d1cf6 100644 --- a/dash/src/crypto/sighash.rs +++ b/dash/src/crypto/sighash.rs @@ -19,7 +19,8 @@ use hashes::{Hash, sha256, sha256d}; pub use dashcore_crypto::sighash::SplitAnyoneCanPay; pub use dashcore_crypto::sighash::{ EcdsaSighashType, InvalidSighashTypeError, LegacySighash, NonStandardSighashType, - SegwitV0Sighash, SighashTypeParseError, TapSighash, TapSighashTag, TapSighashType, + NonStandardSighashTypeError, SegwitV0Sighash, SighashTypeParseError, TapSighash, TapSighashTag, + TapSighashType, }; use crate::blockdata::transaction::txin::TxIn; @@ -113,7 +114,7 @@ pub struct ScriptPath<'s> { } /// Possible errors in computing the signature message. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] #[non_exhaustive] pub enum Error { /// Could happen only by using `*_encode_signing_*` methods with custom writers, engines writers @@ -153,7 +154,7 @@ pub enum Error { WrongAnnex, /// Invalid Sighash type. - InvalidSighashType(u32), + InvalidSighashType(InvalidSighashTypeError), } impl fmt::Display for Error { @@ -193,8 +194,8 @@ impl fmt::Display for Error { WrongAnnex => { write!(f, "Annex must be at least one byte long and the first bytes must be `0x50`") } - InvalidSighashType(hash_ty) => { - write!(f, "Invalid taproot signature hash type : {} ", hash_ty) + InvalidSighashType(e) => { + write!(f, "Invalid taproot signature hash type : {} ", e) } } } @@ -291,7 +292,7 @@ impl<'s> From> for TapLeafHash { impl From for Error { fn from(e: InvalidSighashTypeError) -> Self { - Error::InvalidSighashType(e.0) + Error::InvalidSighashType(e) } } @@ -881,8 +882,9 @@ impl<'a> Encodable for Annex<'a> { } fn is_invalid_use_of_sighash_single(sighash: u32, input_index: usize, output_len: usize) -> bool { - let ty = EcdsaSighashType::from_consensus(sighash); - ty == EcdsaSighashType::Single && input_index >= output_len + // Core checks `(nHashType & 0x1f) == SIGHASH_SINGLE`, so ANYONECANPAY + // counts as well. + EcdsaSighashType::from_consensus(sighash).is_single() && input_index >= output_len } #[cfg(test)] @@ -902,8 +904,6 @@ mod tests { #[test] fn sighash_single_bug() { - const SIGHASH_SINGLE: u32 = 3; - // We need a tx with more inputs than outputs. let tx = Transaction { version: 1, @@ -915,10 +915,15 @@ mod tests { let script = ScriptBuf::new(); let cache = SighashCache::new(&tx); - let got = cache.legacy_signature_hash(1, &script, SIGHASH_SINGLE).expect("sighash"); - let want = LegacySighash::from_slice(&UINT256_ONE).unwrap(); + let one = LegacySighash::from_slice(&UINT256_ONE).unwrap(); - assert_eq!(got, want) + // Low five bits say SINGLE, with or without ANYONECANPAY. + for flag in [0x03, 0x23, 0x83, 0xa3] { + let got = cache.legacy_signature_hash(1, &script, flag).expect("sighash"); + assert_eq!(got, one, "{flag:#x}"); + } + let got = cache.legacy_signature_hash(1, &script, 0x01).expect("sighash"); + assert_ne!(got, one); } #[ignore] @@ -1461,7 +1466,7 @@ mod tests { for s in sht_mistakes { assert_eq!( TapSighashType::from_str(s).unwrap_err().to_string(), - format!("Unrecognized SIGHASH string '{}'", s) + format!("failed to parse '{}' as SIGHASH string", s) ); } } diff --git a/dash/src/hash_types.rs b/dash/src/hash_types.rs index 7dc1dbc54..7f359c0ee 100644 --- a/dash/src/hash_types.rs +++ b/dash/src/hash_types.rs @@ -75,7 +75,7 @@ mod newtypes { #[cfg(feature = "core-block-hash-use-x11")] use hashes::hash_x11; - use hashes::hex::Error; + use hashes::hex::HexToArrayError as Error; use hashes::{Hash, hash_newtype, hash_newtype_no_ord, hash160, sha256, sha256d}; use crate::alloc::string::ToString; @@ -400,6 +400,7 @@ mod tests { use super::*; use crate::consensus::{deserialize, serialize}; + use dashcore_crypto::eddsa::EddsaPkBytes; use serde_derive::{Deserialize, Serialize}; @@ -431,11 +432,10 @@ mod tests { /// A node id decoded from the wire must compare equal to one derived from /// the matching Ed25519 public key — the mismatch reported in issue #887. - #[cfg(feature = "eddsa")] #[test] fn decoded_wire_id_matches_derived_id() { let public_key = [7u8; 32]; - let derived = dashcore_crypto::eddsa::EddsaPkBytes::from_bytes(public_key).hash(); + let derived = EddsaPkHash::from(EddsaPkBytes::from_bytes(public_key)); let decoded: EddsaPkHash = deserialize(&derived.to_bytes()).expect("decode node id"); assert_eq!(decoded, derived); diff --git a/dash/src/internal_macros.rs b/dash/src/internal_macros.rs index 55fb282a4..2c893869d 100644 --- a/dash/src/internal_macros.rs +++ b/dash/src/internal_macros.rs @@ -104,26 +104,15 @@ macro_rules! impl_bytes_newtype { } impl $crate::hashes::hex::FromHex for $t { - fn from_byte_iter(iter: I) -> Result - where - I: core::iter::Iterator> - + core::iter::ExactSizeIterator - + core::iter::DoubleEndedIterator, - { - if iter.len() == $len { - let mut ret = [0; $len]; - for (n, byte) in iter.enumerate() { - ret[n] = byte?; - } - Ok($t(ret)) - } else { - Err($crate::hashes::hex::Error::InvalidLength(2 * $len, 2 * iter.len())) - } + type Error = $crate::hashes::hex::HexToArrayError; + + fn from_hex(s: &str) -> Result { + Ok($t(<[u8; $len] as $crate::hashes::hex::FromHex>::from_hex(s)?)) } } impl core::str::FromStr for $t { - type Err = $crate::hashes::hex::Error; + type Err = $crate::hashes::hex::HexToArrayError; fn from_str(s: &str) -> Result { $crate::hashes::hex::FromHex::from_hex(s) } diff --git a/dash/src/sml/masternode_list/merkle_roots.rs b/dash/src/sml/masternode_list/merkle_roots.rs index ba12c7680..1d4e4bcbf 100644 --- a/dash/src/sml/masternode_list/merkle_roots.rs +++ b/dash/src/sml/masternode_list/merkle_roots.rs @@ -161,7 +161,7 @@ impl MasternodeList { pro_tx_hashes.sort_by_key(|&s| s.reverse()); pro_tx_hashes .into_iter() - .map(|hash| self.masternodes[hash].entry_hash) + .map(|hash| self.masternodes[hash].entry_hash.to_raw_hash()) .collect::>() }) } diff --git a/dash/src/sml/masternode_list_entry/qualified_masternode_list_entry.rs b/dash/src/sml/masternode_list_entry/qualified_masternode_list_entry.rs index 67a9707e0..10bb71776 100644 --- a/dash/src/sml/masternode_list_entry/qualified_masternode_list_entry.rs +++ b/dash/src/sml/masternode_list_entry/qualified_masternode_list_entry.rs @@ -2,9 +2,9 @@ use std::cmp::Ordering; #[cfg(feature = "bincode")] use bincode::{Decode, Encode}; -use hashes::{Hash, sha256d}; +use hashes::Hash; -use crate::hash_types::ConfirmedHashHashedWithProRegTx; +use crate::hash_types::{ConfirmedHashHashedWithProRegTx, Sha256dHash}; use crate::sml::masternode_list_entry::MasternodeListEntry; /// A structured representation of a masternode list entry with a cached entry hash and a confirmed @@ -17,7 +17,7 @@ pub struct QualifiedMasternodeListEntry { /// The underlying masternode list entry pub masternode_list_entry: MasternodeListEntry, /// The computed entry hash - pub entry_hash: sha256d::Hash, + pub entry_hash: Sha256dHash, /// The confirmed hash hashed with the pro_reg_tx if the confirmed hash is set pub confirmed_hash_hashed_with_pro_reg_tx: Option, } @@ -36,7 +36,7 @@ impl PartialOrd for QualifiedMasternodeListEntry { impl From for QualifiedMasternodeListEntry { fn from(masternode_list_entry: MasternodeListEntry) -> Self { - let entry_hash = masternode_list_entry.calculate_entry_hash(); + let entry_hash = Sha256dHash::from_raw_hash(masternode_list_entry.calculate_entry_hash()); let confirmed_hash_hashed_with_pro_reg_tx = masternode_list_entry.confirmed_hash.map(|confirmed_hash| { ConfirmedHashHashedWithProRegTx::hash( diff --git a/dash/src/sml/message_verification_error.rs b/dash/src/sml/message_verification_error.rs index 6a64f73c9..05f82cb63 100644 --- a/dash/src/sml/message_verification_error.rs +++ b/dash/src/sml/message_verification_error.rs @@ -1,6 +1,6 @@ +use crate::hash_types::Sha256dHash; #[cfg(feature = "bincode")] use bincode::{Decode, Encode}; -use hashes::sha256d; use thiserror::Error; use crate::QuorumHash; @@ -34,7 +34,7 @@ pub enum MessageVerificationError { )] ThresholdSignatureNotValid( Box, - Box, + Box, Box, QuorumHash, LLMQType, diff --git a/dash/src/sml/quorum_entry/verify_message.rs b/dash/src/sml/quorum_entry/verify_message.rs index c95095f8c..324ee78ef 100644 --- a/dash/src/sml/quorum_entry/verify_message.rs +++ b/dash/src/sml/quorum_entry/verify_message.rs @@ -1,4 +1,4 @@ -use hashes::{Hash, sha256d}; +use hashes::Hash; use crate::bls_sig_utils::{BLSSignature, BlsScheme}; use crate::sml::message_verification_error::MessageVerificationError; @@ -48,7 +48,7 @@ impl QualifiedQuorumEntry { .map_err(|e| { MessageVerificationError::ThresholdSignatureNotValid( Box::new(signature), - Box::new(sha256d::Hash::from_byte_array(message_digest)), + Box::new(crate::hash_types::Sha256dHash::from_byte_array(message_digest)), Box::new(self.quorum_entry.quorum_public_key), self.quorum_entry.quorum_hash, self.quorum_entry.llmq_type, diff --git a/dash/src/taproot.rs b/dash/src/taproot.rs index b9a2b9a71..937a82484 100644 --- a/dash/src/taproot.rs +++ b/dash/src/taproot.rs @@ -1778,6 +1778,7 @@ mod test { #[cfg(feature = "serde")] use { crate::internal_macros::hex, + serde::{Deserialize, Serialize}, serde_test::Configure, serde_test::{Token, assert_tokens}, }; @@ -2137,6 +2138,44 @@ mod test { assert_eq!(leaf_version, leaf_version2); } + /// A tagged hash must accept every shape `ContentDeserializer` can replay, + /// which is why `sha256t_hash_newtype!` routes through our `hash_newtype!` + /// rather than upstream's. + #[test] + #[cfg(feature = "serde")] + fn serde_round_trip_through_internally_tagged_enum() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct WithNodeHash { + node: TapNodeHash, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag = "type")] + enum Tagged { + A(WithNodeHash), + } + + let bytes = hex!("03ba2a4dcd914fed29a1c630c7e811271b081a0e2f2f52cf1c197583dfd46c1b"); + let original = Tagged::A(WithNodeHash { + node: TapNodeHash::from_slice(&bytes).unwrap(), + }); + + let value = serde_json::to_value(&original).unwrap(); + let restored: Tagged = serde_json::from_value(value).unwrap(); + assert_eq!(original, restored); + + let mut object = serde_json::Map::new(); + object.insert("type".into(), serde_json::Value::String("A".into())); + object.insert( + "node".into(), + serde_json::Value::Array( + bytes.iter().map(|b| serde_json::Value::Number((*b).into())).collect(), + ), + ); + let from_seq: Tagged = serde_json::from_value(serde_json::Value::Object(object)).unwrap(); + assert_eq!(from_seq, original); + } + #[test] #[cfg(feature = "serde")] fn test_merkle_branch_serde() { diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 77f84f70d..dc544a36f 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "dash-fuzz" -edition = "2024" -version = { workspace = true } authors = ["Generated by fuzz/generate-files.sh"] -license = "CC0-1.0" publish = false +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(fuzzing)"] } @@ -16,7 +18,7 @@ cargo-fuzz = true honggfuzz = { version = "0.5", default-features = false } dashcore = { path = "../dash", features = [ "serde" ] } -serde = { version = "1.0.219", features = [ "derive" ] } +serde = { workspace = true, features = ["std", "derive"] } serde_json = "1.0" serde_cbor = "0.11" diff --git a/fuzz/README.md b/fuzz/README.md index 47d4d2581..03a089229 100644 --- a/fuzz/README.md +++ b/fuzz/README.md @@ -85,8 +85,8 @@ The final line is a hex-encoded version of the input that caused the crash. You can test this directly by editing the `duplicate_crash` test to copy/paste the hex output into the call to `extend_vec_from_hex`. Then run the test with - RUSTFLAGS=--cfg=fuzzing cargo test + RUSTFLAGS="--cfg=fuzzing --cfg=hashes_fuzz" cargo test -It is important to add the `cfg=fuzzing` flag, which tells rustc to compile the -library as though it were running a fuzztest. In particular, this will disable -or weaken all the cryptography. +It is important to add the `cfg=fuzzing` and `cfg=hashes_fuzz` flags, which +tell rustc to compile the library as though it were running a fuzztest. In +particular, `hashes_fuzz` makes `bitcoin_hashes` weaken its hash functions. diff --git a/fuzz/fuzz.sh b/fuzz/fuzz.sh index 13f55fff3..f54a6e0d2 100755 --- a/fuzz/fuzz.sh +++ b/fuzz/fuzz.sh @@ -31,7 +31,8 @@ cargo --version rustc --version # Ensure we don't trigger ThinLTO/internalization issues with honggfuzz's link flags -export RUSTFLAGS="${RUSTFLAGS:-} -C lto=no" +# `bitcoin_hashes` weakens its hashes under `hashes_fuzz`, not `fuzzing` +export RUSTFLAGS="${RUSTFLAGS:-} -C lto=no --cfg=hashes_fuzz" # Testing cargo install --force honggfuzz --no-default-features diff --git a/fuzz/generate-files.sh b/fuzz/generate-files.sh index b6c239f6c..cb1723afe 100755 --- a/fuzz/generate-files.sh +++ b/fuzz/generate-files.sh @@ -11,11 +11,13 @@ source "$REPO_DIR/fuzz/fuzz-util.sh" cat > "$REPO_DIR/fuzz/Cargo.toml" <"] -license = "CC0-1.0" -repository = "https://github.com/rust-dashcore/dash_hashes/" -documentation = "https://docs.rs/dash_hashes/" description = "Hash functions used by the rust-dash eccosystem" +authors = ["Samuel Westrich "] +edition = "2021" categories = ["algorithms"] keywords = [ "crypto", "dash", "hash", "digest" ] readme = "README.md" -edition = "2021" exclude = ["tests", "contrib"] +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [features] default = [] -schemars = ["actual-schemars"] -serde-std = ["serde/std"] +serde = ["dep:serde", "bitcoin_hashes/serde"] +serde-std = ["serde", "serde/std"] x11 = ["rs-x11-hash"] [package.metadata.docs.rs] @@ -23,15 +23,10 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -internals = { path = "../internals", package = "dashcore-private" } - -# Only enable this if you explicitly do not want to use "std", otherwise enable "serde-std". -serde = { version = "1.0.219", default-features = false, optional = true } -# Do NOT use this as a feature! Use the `schemars` feature instead. Can only be used with "std" enabled. -actual-schemars = { package = "schemars", version = "1.0", optional = true } - -rs-x11-hash = { version = "0.1.8", optional = true } bincode = { workspace = true, optional = true } +bitcoin_hashes = { workspace = true, features = ["std"] } +rs-x11-hash = { version = "0.1.8", optional = true } +serde = { workspace = true, optional = true } [dev-dependencies] serde_test = "1.0" diff --git a/hashes/README.md b/hashes/README.md index 9e72067c1..415257ed2 100644 --- a/hashes/README.md +++ b/hashes/README.md @@ -1,17 +1,14 @@ -[![Status](https://travis-ci.org/rust-bitcoin/dashcore_hashes.png?branch=master)](https://travis-ci.org/rust-bitcoin/dashcore_hashes) +# Dash Hashes Library -# Bitcoin Hashes Library - -This is a simple, no-dependency library which implements the hash functions -needed by Bitcoin. These are SHA1, SHA256, SHA256d, SHA512, and RIPEMD160. As an -ancillary thing, it exposes hexadecimal serialization and deserialization, -since these are needed to display hashes anyway. +This library re-exports the hash functions from +[`bitcoin_hashes`](https://docs.rs/bitcoin_hashes/) and adds the Dash-specific +X11 hash, along with `bincode` and `serde` support for hash newtypes. [Documentation](https://docs.rs/dashcore_hashes/) ## Minimum Supported Rust Version (MSRV) -This library should always compile with any combination of features on **Rust 1.48.0**. +This library should always compile with any combination of features on **Rust 1.89.0**. ## Contributions diff --git a/hashes/src/bincode_macros.rs b/hashes/src/bincode_macros.rs index cdf78036e..62aa68ef5 100644 --- a/hashes/src/bincode_macros.rs +++ b/hashes/src/bincode_macros.rs @@ -28,7 +28,7 @@ macro_rules! bincode_impl { impl<$($gen: $gent),*> bincode::Encode for $t<$($gen),*> { fn encode(&self, encoder: &mut E) -> Result<(), bincode::error::EncodeError> { // Use the as_byte_array method so that we encode the inner byte array - self.as_byte_array().encode(encoder) + $crate::Hash::as_byte_array(self).encode(encoder) } } @@ -36,7 +36,7 @@ macro_rules! bincode_impl { fn decode>(decoder: &mut D) -> Result { // Decode a fixed-length byte array and then reconstruct via from_byte_array let bytes: [u8; $len] = <[u8; $len]>::decode(decoder)?; - Ok(Self::from_byte_array(bytes)) + Ok(::from_byte_array(bytes)) } } @@ -53,7 +53,7 @@ macro_rules! bincode_impl { .map_err(|_| bincode::error::DecodeError::Other("Incorrect byte length".into()))?; // Construct the hash from the reference (cloned since the type is Copy) - Ok(Self::from_byte_array(bytes)) + Ok(::from_byte_array(bytes)) } } }; diff --git a/hashes/src/cmp.rs b/hashes/src/cmp.rs deleted file mode 100644 index d99c57f89..000000000 --- a/hashes/src/cmp.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Useful comparison functions. - -/// Compare two slices for equality in fixed time. Panics if the slices are of non-equal length. -/// -/// This works by XOR'ing each byte of the two inputs together and keeping an OR counter of the -/// results. -/// -/// Instead of doing fancy bit twiddling to try to outsmart the compiler and prevent early exits, -/// which is not guaranteed to remain stable as compilers get ever smarter, we take the hit of -/// writing each intermediate value to memory with a volatile write and then re-reading it with a -/// volatile read. This should remain stable across compiler upgrades, but is much slower. -/// -/// As of rust 1.31.0 disassembly looks completely within reason for this, see -/// . -pub fn fixed_time_eq(a: &[u8], b: &[u8]) -> bool { - assert!(a.len() == b.len()); - let count = a.len(); - let lhs = &a[..count]; - let rhs = &b[..count]; - - let mut r: u8 = 0; - for i in 0..count { - let mut rs = unsafe { core::ptr::read_volatile(&r) }; - rs |= lhs[i] ^ rhs[i]; - unsafe { - core::ptr::write_volatile(&mut r, rs); - } - } - { - let mut t = unsafe { core::ptr::read_volatile(&r) }; - t |= t >> 4; - unsafe { - core::ptr::write_volatile(&mut r, t); - } - } - { - let mut t = unsafe { core::ptr::read_volatile(&r) }; - t |= t >> 2; - unsafe { - core::ptr::write_volatile(&mut r, t); - } - } - { - let mut t = unsafe { core::ptr::read_volatile(&r) }; - t |= t >> 1; - unsafe { - core::ptr::write_volatile(&mut r, t); - } - } - unsafe { (::core::ptr::read_volatile(&r) & 1) == 0 } -} - -#[test] -fn eq_test() { - assert!(fixed_time_eq(&[0b00000000], &[0b00000000])); - assert!(fixed_time_eq(&[0b00000001], &[0b00000001])); - assert!(fixed_time_eq(&[0b00000010], &[0b00000010])); - assert!(fixed_time_eq(&[0b00000100], &[0b00000100])); - assert!(fixed_time_eq(&[0b00001000], &[0b00001000])); - assert!(fixed_time_eq(&[0b00010000], &[0b00010000])); - assert!(fixed_time_eq(&[0b00100000], &[0b00100000])); - assert!(fixed_time_eq(&[0b01000000], &[0b01000000])); - assert!(fixed_time_eq(&[0b10000000], &[0b10000000])); - assert!(fixed_time_eq(&[0b11111111], &[0b11111111])); - - assert!(!fixed_time_eq(&[0b00000001], &[0b00000000])); - assert!(!fixed_time_eq(&[0b00000001], &[0b11111111])); - assert!(!fixed_time_eq(&[0b00000010], &[0b00000000])); - assert!(!fixed_time_eq(&[0b00000010], &[0b11111111])); - assert!(!fixed_time_eq(&[0b00000100], &[0b00000000])); - assert!(!fixed_time_eq(&[0b00000100], &[0b11111111])); - assert!(!fixed_time_eq(&[0b00001000], &[0b00000000])); - assert!(!fixed_time_eq(&[0b00001000], &[0b11111111])); - assert!(!fixed_time_eq(&[0b00010000], &[0b00000000])); - assert!(!fixed_time_eq(&[0b00010000], &[0b11111111])); - assert!(!fixed_time_eq(&[0b00100000], &[0b00000000])); - assert!(!fixed_time_eq(&[0b00100000], &[0b11111111])); - assert!(!fixed_time_eq(&[0b01000000], &[0b00000000])); - assert!(!fixed_time_eq(&[0b01000000], &[0b11111111])); - assert!(!fixed_time_eq(&[0b10000000], &[0b00000000])); - assert!(!fixed_time_eq(&[0b10000000], &[0b11111111])); - - assert!(fixed_time_eq(&[0b00000000, 0b00000000], &[0b00000000, 0b00000000])); - assert!(!fixed_time_eq(&[0b00000001, 0b00000000], &[0b00000000, 0b00000000])); - assert!(!fixed_time_eq(&[0b00000000, 0b00000001], &[0b00000000, 0b00000000])); - assert!(!fixed_time_eq(&[0b00000000, 0b00000000], &[0b00000001, 0b00000000])); - assert!(!fixed_time_eq(&[0b00000000, 0b00000000], &[0b00000001, 0b00000001])); -} diff --git a/hashes/src/error.rs b/hashes/src/error.rs deleted file mode 100644 index 2614d26b6..000000000 --- a/hashes/src/error.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -//! Crate error type. -//! - -use core::fmt; - -/// Crate error type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Error { - /// Tried to create a fixed-length hash from a slice with the wrong size (expected, got). - InvalidLength(usize, usize), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - - match self { - InvalidLength(ell, ell2) => { - write!(f, "invalid slice length {} (expected {})", ell2, ell) - } - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use self::Error::*; - - match self { - InvalidLength(_, _) => None, - } - } -} diff --git a/hashes/src/hash160.rs b/hashes/src/hash160.rs deleted file mode 100644 index 3262026d2..000000000 --- a/hashes/src/hash160.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -// This module is largely copied from the rust-crypto ripemd.rs file; -// while rust-crypto is licensed under Apache, that file specifically -// was written entirely by Andrew Poelstra, who is re-licensing its -// contents here as CC0. - -//! HASH160 (SHA256 then RIPEMD160) implementation. -//! - -use core::ops::Index; -use core::slice::SliceIndex; -use core::str; - -use crate::{ripemd160, sha256, Error}; - -crate::internal_macros::hash_type! { - 160, - false, - "Output of the Bitcoin HASH160 hash function. (RIPEMD160(SHA256))", - "crate::util::json_hex_string::len_20" -} - -type HashEngine = sha256::HashEngine; - -fn from_engine(e: HashEngine) -> Hash { - use crate::Hash as _; - - let sha2 = sha256::Hash::from_engine(e); - let rmd = ripemd160::Hash::hash(&sha2[..]); - - let mut ret = [0; 20]; - ret.copy_from_slice(&rmd[..]); - Hash(ret) -} - -#[cfg(test)] -mod tests { - #[test] - fn test() { - use crate::{hash160, Hash, HashEngine}; - - #[derive(Clone)] - struct Test { - input: Vec, - output: Vec, - output_str: &'static str, - } - - #[rustfmt::skip] - let tests = vec![ - // Uncompressed pubkey obtained from Bitcoin key; data from validateaddress - Test { - input: vec![ - 0x04, 0xa1, 0x49, 0xd7, 0x6c, 0x5d, 0xe2, 0x7a, 0x2d, - 0xdb, 0xfa, 0xa1, 0x24, 0x6c, 0x4a, 0xdc, 0xd2, 0xb6, - 0xf7, 0xaa, 0x29, 0x54, 0xc2, 0xe2, 0x53, 0x03, 0xf5, - 0x51, 0x54, 0xca, 0xad, 0x91, 0x52, 0xe4, 0xf7, 0xe4, - 0xb8, 0x5d, 0xf1, 0x69, 0xc1, 0x8a, 0x3c, 0x69, 0x7f, - 0xbb, 0x2d, 0xc4, 0xec, 0xef, 0x94, 0xac, 0x55, 0xfe, - 0x81, 0x64, 0xcc, 0xf9, 0x82, 0xa1, 0x38, 0x69, 0x1a, - 0x55, 0x19, - ], - output: vec![ - 0xda, 0x0b, 0x34, 0x52, 0xb0, 0x6f, 0xe3, 0x41, - 0x62, 0x6a, 0xd0, 0x94, 0x9c, 0x18, 0x3f, 0xbd, - 0xa5, 0x67, 0x68, 0x26, - ], - output_str: "da0b3452b06fe341626ad0949c183fbda5676826", - }, - ]; - - for test in tests { - // Hash through high-level API, check hex encoding/decoding - let hash = hash160::Hash::hash(&test.input[..]); - assert_eq!(hash, test.output_str.parse::().expect("parse hex")); - assert_eq!(&hash[..], &test.output[..]); - assert_eq!(&hash.to_string(), &test.output_str); - - // Hash through engine, checking that we can input byte by byte - let mut engine = hash160::Hash::engine(); - for ch in test.input { - engine.input(&[ch]); - } - let manual_hash = Hash::from_engine(engine); - assert_eq!(hash, manual_hash); - assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice()); - } - } - - #[cfg(feature = "serde")] - #[test] - fn ripemd_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - use crate::{hash160, Hash}; - - #[rustfmt::skip] - static HASH_BYTES: [u8; 20] = [ - 0x13, 0x20, 0x72, 0xdf, - 0x69, 0x09, 0x33, 0x83, - 0x5e, 0xb8, 0xb6, 0xad, - 0x0b, 0x77, 0xe7, 0xb6, - 0xf1, 0x4a, 0xca, 0xd7, - ]; - - let hash = hash160::Hash::from_slice(&HASH_BYTES).expect("right number of bytes"); - assert_tokens(&hash.compact(), &[Token::BorrowedBytes(&HASH_BYTES[..])]); - assert_tokens(&hash.readable(), &[Token::Str("132072df690933835eb8b6ad0b77e7b6f14acad7")]); - } -} diff --git a/hashes/src/hash_x11.rs b/hashes/src/hash_x11.rs index bedeb9670..533d56e42 100644 --- a/hashes/src/hash_x11.rs +++ b/hashes/src/hash_x11.rs @@ -15,7 +15,7 @@ // If not, see . // -//! An implementation of a hash engine to support the X1 hash, +//! An implementation of a hash engine to support the X11 hash, //! which is a wrapper around the rs-x11-hash library. use core::ops::Index; @@ -24,54 +24,36 @@ use core::str; use std::io; use std::vec::Vec; -use crate::{hex, Error, HashEngine as _}; +use crate::hex::FromHex as _; +use crate::{hex, FromSliceError, HashEngine as _}; -crate::internal_macros::hash_type! { - 256, - true, - "Output of the X11 hash function.", - "crate::util::json_hex_string::len_32" -} - -/// Output of the X11 hash function -fn from_engine(e: HashEngine) -> Hash { - Hash(e.midstate().to_byte_array()) -} +/// Output of the X11 hash function. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct Hash([u8; 32]); -/// A hashing engine of X11 algorithm, which bytes can be serialized into -#[derive(Clone, Default)] -pub struct HashEngine { - buf: Vec, - length: usize, -} - -impl crate::HashEngine for HashEngine { - type MidState = Midstate; - - const BLOCK_SIZE: usize = 32; +crate::hex_fmt_impl!(::DISPLAY_BACKWARD, 32, Hash); +crate::serde_impl!(Hash, 32); +crate::bincode_impl!(Hash, 32); +crate::borrow_slice_impl!(Hash); - fn midstate(&self) -> Self::MidState { - Midstate(rs_x11_hash::get_x11_hash(self.buf.as_slice())) - } +impl str::FromStr for Hash { + type Err = hex::HexToArrayError; - fn input(&mut self, data: &[u8]) { - self.buf.extend_from_slice(data); + fn from_str(s: &str) -> Result { + let mut bytes = <[u8; 32]>::from_hex(s)?; + bytes.reverse(); + Ok(Hash(bytes)) } +} - fn n_bytes_hashed(&self) -> usize { - self.length +impl AsRef<[u8; 32]> for Hash { + fn as_ref(&self) -> &[u8; 32] { + &self.0 } } -/// Output of the X11 hash function -#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] -pub struct Midstate(pub [u8; 32]); - -crate::internal_macros::arr_newtype_fmt_impl!(Midstate, 32); -serde_impl!(Midstate, 32); -borrow_slice_impl!(Midstate); - -impl> Index for Midstate { +impl> Index for Hash { type Output = I::Output; #[inline] @@ -80,56 +62,67 @@ impl> Index for Midstate { } } -impl str::FromStr for Midstate { - type Err = hex::Error; - fn from_str(s: &str) -> Result { - hex::FromHex::from_hex(s) +impl From for [u8; 32] { + fn from(hash: Hash) -> [u8; 32] { + hash.0 } } -impl Midstate { - /// Length of the midstate, in bytes. - const LEN: usize = 32; +impl crate::Hash for Hash { + type Engine = HashEngine; + type Bytes = [u8; 32]; - /// Flag indicating whether user-visible serializations of this hash - /// should be backward. For some reason Satoshi decided this should be - /// true for `Sha256dHash`, so here we are. + const LEN: usize = 32; const DISPLAY_BACKWARD: bool = true; - /// Construct a new [`Midstate`] from the inner value. - pub const fn from_byte_array(inner: [u8; 32]) -> Self { - Midstate(inner) + fn from_engine(e: HashEngine) -> Self { + Hash(e.midstate()) } - /// Copies a byte slice into the [`Midstate`] object. - pub fn from_slice(sl: &[u8]) -> Result { - if sl.len() != Self::LEN { - Err(Error::InvalidLength(Self::LEN, sl.len())) - } else { - let mut ret = [0; 32]; - ret.copy_from_slice(sl); - Ok(Midstate(ret)) - } + fn from_slice(sl: &[u8]) -> Result { + // `FromSliceError` is only constructible upstream, so a hash of the + // same size checks the length. + crate::sha256d::Hash::from_slice(sl).map(|h| Hash(h.to_byte_array())) } - /// Unwraps the [`Midstate`] and returns the underlying byte array. - pub fn to_byte_array(self) -> [u8; 32] { + fn to_byte_array(self) -> [u8; 32] { self.0 } - /// Unwraps the [`Midstate`] and returns the underlying byte array. - pub fn into_inner(self) -> [u8; 32] { - self.0 + fn as_byte_array(&self) -> &[u8; 32] { + &self.0 + } + + fn from_byte_array(bytes: [u8; 32]) -> Self { + Hash(bytes) + } + + fn all_zeros() -> Self { + Hash([0; 32]) } } -impl hex::FromHex for Midstate { - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, - { - // DISPLAY_BACKWARD is true - Ok(Midstate::from_byte_array(hex::FromHex::from_byte_iter(iter.rev())?)) +/// An X11 hashing engine. X11 is not incremental, so the input is buffered. +#[derive(Clone, Default)] +pub struct HashEngine { + buf: Vec, +} + +impl crate::HashEngine for HashEngine { + type MidState = [u8; 32]; + + const BLOCK_SIZE: usize = 32; + + fn midstate(&self) -> [u8; 32] { + rs_x11_hash::get_x11_hash(self.buf.as_slice()) + } + + fn input(&mut self, data: &[u8]) { + self.buf.extend_from_slice(data); + } + + fn n_bytes_hashed(&self) -> usize { + self.buf.len() } } @@ -138,6 +131,7 @@ impl io::Write for HashEngine { self.input(buf); Ok(buf.len()) } + fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/hashes/src/hex.rs b/hashes/src/hex.rs deleted file mode 100644 index 62fa9e08c..000000000 --- a/hashes/src/hex.rs +++ /dev/null @@ -1,233 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -//! Hex encoding and decoding. -//! - -use core::{fmt, str}; -use std::io; - -/// Hex decoding error. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Error { - /// Non-hexadecimal character. - InvalidChar(u8), - /// Purported hex string had odd length. - OddLengthString(usize), - /// Tried to parse fixed-length hash from a string with the wrong type (expected, got). - InvalidLength(usize, usize), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::InvalidChar(ch) => write!(f, "invalid hex character {}", ch), - Error::OddLengthString(ell) => write!(f, "odd hex string length {}", ell), - Error::InvalidLength(ell, ell2) => { - write!(f, "bad hex string length {} (expected {})", ell2, ell) - } - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use self::Error::*; - - match self { - InvalidChar(_) | OddLengthString(_) | InvalidLength(_, _) => None, - } - } -} - -/// Trait for objects that can be deserialized from hex strings. -pub trait FromHex: Sized { - /// Produces an object from a byte iterator. - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator; - - /// Produces an object from a hex string. - fn from_hex(s: &str) -> Result { - Self::from_byte_iter(HexIterator::new(s)?) - } -} - -/// Iterator over a hex-encoded string slice which decodes hex and yields bytes. -pub struct HexIterator<'a> { - /// The `Bytes` iterator whose next two bytes will be decoded to yield - /// the next byte. - iter: str::Bytes<'a>, -} - -impl<'a> HexIterator<'a> { - /// Constructs a new `HexIterator` from a string slice. - /// - /// # Errors - /// - /// If the input string is of odd length. - pub fn new(s: &'a str) -> Result, Error> { - if !s.len().is_multiple_of(2) { - Err(Error::OddLengthString(s.len())) - } else { - Ok(HexIterator { - iter: s.bytes(), - }) - } - } -} - -fn chars_to_hex(hi: u8, lo: u8) -> Result { - let hih = (hi as char).to_digit(16).ok_or(Error::InvalidChar(hi))?; - let loh = (lo as char).to_digit(16).ok_or(Error::InvalidChar(lo))?; - - let ret = (hih << 4) + loh; - Ok(ret as u8) -} - -impl<'a> Iterator for HexIterator<'a> { - type Item = Result; - - fn next(&mut self) -> Option> { - let hi = self.iter.next()?; - let lo = self.iter.next().unwrap(); - Some(chars_to_hex(hi, lo)) - } - - fn size_hint(&self) -> (usize, Option) { - let (min, max) = self.iter.size_hint(); - (min / 2, max.map(|x| x / 2)) - } -} - -impl<'a> io::Read for HexIterator<'a> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let mut bytes_read = 0usize; - for dst in buf { - match self.next() { - Some(Ok(src)) => { - *dst = src; - bytes_read += 1; - } - _ => break, - } - } - Ok(bytes_read) - } -} - -impl<'a> DoubleEndedIterator for HexIterator<'a> { - fn next_back(&mut self) -> Option> { - let lo = self.iter.next_back()?; - let hi = self.iter.next_back().unwrap(); - Some(chars_to_hex(hi, lo)) - } -} - -impl<'a> ExactSizeIterator for HexIterator<'a> {} - -impl FromHex for Vec { - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, - { - iter.collect() - } -} - -macro_rules! impl_fromhex_array { - ($len:expr) => { - impl FromHex for [u8; $len] { - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, - { - if iter.len() == $len { - let mut ret = [0; $len]; - for (n, byte) in iter.enumerate() { - ret[n] = byte?; - } - Ok(ret) - } else { - Err(Error::InvalidLength(2 * $len, 2 * iter.len())) - } - } - } - }; -} - -impl_fromhex_array!(2); -impl_fromhex_array!(4); -impl_fromhex_array!(6); -impl_fromhex_array!(8); -impl_fromhex_array!(10); -impl_fromhex_array!(12); -impl_fromhex_array!(14); -impl_fromhex_array!(16); -impl_fromhex_array!(20); -impl_fromhex_array!(24); -impl_fromhex_array!(28); -impl_fromhex_array!(32); -impl_fromhex_array!(33); -impl_fromhex_array!(48); -impl_fromhex_array!(64); -impl_fromhex_array!(65); -impl_fromhex_array!(96); -impl_fromhex_array!(128); -impl_fromhex_array!(256); -impl_fromhex_array!(384); -impl_fromhex_array!(512); - -#[cfg(test)] -mod tests { - use internals::hex::exts::DisplayHex; - - use super::*; - - #[test] - fn hex_roundtrip() { - let expected = "0123456789abcdef"; - let expected_up = "0123456789ABCDEF"; - - let parse: Vec = FromHex::from_hex(expected).expect("parse lowercase string"); - assert_eq!(parse, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_lower_hex_string(); - assert_eq!(ser, expected); - - let parse: Vec = FromHex::from_hex(expected_up).expect("parse uppercase string"); - assert_eq!(parse, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_lower_hex_string(); - assert_eq!(ser, expected); - - let parse: [u8; 8] = FromHex::from_hex(expected_up).expect("parse uppercase string"); - assert_eq!(parse, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_lower_hex_string(); - assert_eq!(ser, expected); - } - - #[test] - fn hex_error() { - let oddlen = "0123456789abcdef0"; - let badchar1 = "Z123456789abcdef"; - let badchar2 = "012Y456789abcdeb"; - let badchar3 = "«23456789abcdef"; - - assert_eq!(Vec::::from_hex(oddlen), Err(Error::OddLengthString(17))); - assert_eq!(<[u8; 4]>::from_hex(oddlen), Err(Error::OddLengthString(17))); - assert_eq!(<[u8; 8]>::from_hex(oddlen), Err(Error::OddLengthString(17))); - assert_eq!(Vec::::from_hex(badchar1), Err(Error::InvalidChar(b'Z'))); - assert_eq!(Vec::::from_hex(badchar2), Err(Error::InvalidChar(b'Y'))); - assert_eq!(Vec::::from_hex(badchar3), Err(Error::InvalidChar(194))); - } -} diff --git a/hashes/src/hmac.rs b/hashes/src/hmac.rs deleted file mode 100644 index a01e77f95..000000000 --- a/hashes/src/hmac.rs +++ /dev/null @@ -1,400 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -// This module is largely copied from the rust-crypto ripemd.rs file; -// while rust-crypto is licensed under Apache, that file specifically -// was written entirely by Andrew Poelstra, who is re-licensing its -// contents here as CC0. - -//! Hash-based Message Authentication Code (HMAC). -//! - -use core::{borrow, fmt, ops, str}; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -use crate::{Error, Hash, HashEngine}; - -/// A hash computed from a RFC 2104 HMAC. Parameterized by the underlying hash function. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(feature = "schemars", schemars(transparent))] -#[repr(transparent)] -pub struct Hmac(T); - -impl str::FromStr for Hmac { - type Err = ::Err; - fn from_str(s: &str) -> Result { - Ok(Hmac(str::FromStr::from_str(s)?)) - } -} - -/// Pair of underlying hash midstates which represent the current state of an `HmacEngine`. -pub struct HmacMidState { - /// Midstate of the inner hash engine - pub inner: ::MidState, - /// Midstate of the outer hash engine - pub outer: ::MidState, -} - -/// Pair of underlying hash engines, used for the inner and outer hash of HMAC. -#[derive(Clone)] -pub struct HmacEngine { - iengine: T::Engine, - oengine: T::Engine, -} - -impl Default for HmacEngine { - fn default() -> Self { - HmacEngine::new(&[]) - } -} - -impl HmacEngine { - /// Constructs a new keyed HMAC from `key`. - /// - /// We only support underlying hashes whose block sizes are ≤ 128 bytes. - /// - /// # Panics - /// - /// Larger hashes will result in a panic. - pub fn new(key: &[u8]) -> HmacEngine { - debug_assert!(T::Engine::BLOCK_SIZE <= 128); - - let mut ipad = [0x36u8; 128]; - let mut opad = [0x5cu8; 128]; - let mut ret = HmacEngine { - iengine: ::engine(), - oengine: ::engine(), - }; - - if key.len() > T::Engine::BLOCK_SIZE { - let hash = ::hash(key); - for (b_i, b_h) in ipad.iter_mut().zip(&hash[..]) { - *b_i ^= *b_h; - } - for (b_o, b_h) in opad.iter_mut().zip(&hash[..]) { - *b_o ^= *b_h; - } - } else { - for (b_i, b_h) in ipad.iter_mut().zip(key) { - *b_i ^= *b_h; - } - for (b_o, b_h) in opad.iter_mut().zip(key) { - *b_o ^= *b_h; - } - }; - - HashEngine::input(&mut ret.iengine, &ipad[..T::Engine::BLOCK_SIZE]); - HashEngine::input(&mut ret.oengine, &opad[..T::Engine::BLOCK_SIZE]); - ret - } - - /// A special constructor giving direct access to the underlying "inner" and "outer" engines. - pub fn from_inner_engines(iengine: T::Engine, oengine: T::Engine) -> HmacEngine { - HmacEngine { - iengine, - oengine, - } - } -} - -impl HashEngine for HmacEngine { - type MidState = HmacMidState; - - fn midstate(&self) -> Self::MidState { - HmacMidState { - inner: self.iengine.midstate(), - outer: self.oengine.midstate(), - } - } - - const BLOCK_SIZE: usize = T::Engine::BLOCK_SIZE; - - fn n_bytes_hashed(&self) -> usize { - self.iengine.n_bytes_hashed() - } - - fn input(&mut self, buf: &[u8]) { - self.iengine.input(buf) - } -} - -impl fmt::Debug for Hmac { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -impl fmt::Display for Hmac { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -impl fmt::LowerHex for Hmac { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::LowerHex::fmt(&self.0, f) - } -} - -impl ops::Index for Hmac { - type Output = u8; - fn index(&self, index: usize) -> &u8 { - &self.0[index] - } -} - -impl ops::Index> for Hmac { - type Output = [u8]; - fn index(&self, index: ops::Range) -> &[u8] { - &self.0[index] - } -} - -impl ops::Index> for Hmac { - type Output = [u8]; - fn index(&self, index: ops::RangeFrom) -> &[u8] { - &self.0[index] - } -} - -impl ops::Index> for Hmac { - type Output = [u8]; - fn index(&self, index: ops::RangeTo) -> &[u8] { - &self.0[index] - } -} - -impl ops::Index for Hmac { - type Output = [u8]; - fn index(&self, index: ops::RangeFull) -> &[u8] { - &self.0[index] - } -} - -impl borrow::Borrow<[u8]> for Hmac { - fn borrow(&self) -> &[u8] { - &self[..] - } -} - -impl Hash for Hmac { - type Engine = HmacEngine; - type Bytes = T::Bytes; - - fn from_engine(mut e: HmacEngine) -> Hmac { - let ihash = T::from_engine(e.iengine); - e.oengine.input(&ihash[..]); - let ohash = T::from_engine(e.oengine); - Hmac(ohash) - } - - const LEN: usize = T::LEN; - - fn from_slice(sl: &[u8]) -> Result, Error> { - T::from_slice(sl).map(Hmac) - } - - fn to_byte_array(self) -> Self::Bytes { - self.0.to_byte_array() - } - - fn as_byte_array(&self) -> &Self::Bytes { - self.0.as_byte_array() - } - - fn from_byte_array(bytes: T::Bytes) -> Self { - Hmac(T::from_byte_array(bytes)) - } - - fn all_zeros() -> Self { - let zeros = T::all_zeros(); - Hmac(zeros) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Hmac { - fn serialize(&self, s: S) -> Result { - Serialize::serialize(&self.0, s) - } -} - -#[cfg(feature = "serde")] -impl<'de, T: Hash + Deserialize<'de>> Deserialize<'de> for Hmac { - fn deserialize>(d: D) -> Result, D::Error> { - let bytes = Deserialize::deserialize(d)?; - Ok(Hmac(bytes)) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn test() { - use crate::{sha256, Hash, HashEngine, Hmac, HmacEngine}; - - #[derive(Clone)] - struct Test { - key: Vec, - input: Vec, - output: Vec, - } - - #[rustfmt::skip] - let tests = vec![ - // Test vectors copied from libsecp256k1 - // Sadly the RFC2104 test vectors all use MD5 as their underlying hash function, - // which of course this library does not support. - Test { - key: vec![ 0x0b; 20], - input: vec![0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65], - output: vec![ - 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, - 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, - 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, - 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7, - ], - }, - Test { - key: vec![ 0x4a, 0x65, 0x66, 0x65 ], - input: vec![ - 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, - 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, - 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, - 0x69, 0x6e, 0x67, 0x3f, - ], - output: vec![ - 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, - 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, - 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, - 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43, - ], - }, - Test { - key: vec![ 0xaa; 20 ], - input: vec![ 0xdd; 50 ], - output: vec![ - 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, - 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, - 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, - 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe, - ], - }, - Test { - key: vec![ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x19 - ], - input: vec![ 0xcd; 50 ], - output: vec![ - 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, - 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, - 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, - 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b, - ], - }, - Test { - key: vec! [ 0xaa; 131 ], - input: vec![ - 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, - 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, - 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, - 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x2d, 0x20, - 0x48, 0x61, 0x73, 0x68, 0x20, 0x4b, 0x65, 0x79, - 0x20, 0x46, 0x69, 0x72, 0x73, 0x74, - ], - output: vec![ - 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, - 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, - 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, - 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54, - ], - }, - Test { - key: vec! [ 0xaa; 131 ], - input: vec![ - 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, - 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, - 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x6c, - 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, - 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6b, 0x65, - 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, - 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, - 0x68, 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x6b, 0x65, 0x79, 0x20, 0x6e, 0x65, 0x65, - 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, - 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x20, - 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x62, - 0x65, 0x69, 0x6e, 0x67, 0x20, 0x75, 0x73, 0x65, - 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x61, 0x6c, - 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e, - ], - output: vec![ - 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, - 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, - 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, - 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2, - ], - }, - ]; - - for test in tests { - let mut engine = HmacEngine::::new(&test.key); - engine.input(&test.input); - let hash = Hmac::::from_engine(engine); - assert_eq!(&hash[..], &test.output[..]); - assert_eq!(hash.as_byte_array(), test.output.as_slice()); - } - } - - #[cfg(feature = "serde")] - #[test] - fn hmac_sha512_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - use crate::{sha512, Hash, Hmac}; - - #[rustfmt::skip] - static HASH_BYTES: [u8; 64] = [ - 0x8b, 0x41, 0xe1, 0xb7, 0x8a, 0xd1, 0x15, 0x21, - 0x11, 0x3c, 0x52, 0xff, 0x18, 0x2a, 0x1b, 0x8e, - 0x0a, 0x19, 0x57, 0x54, 0xaa, 0x52, 0x7f, 0xcd, - 0x00, 0xa4, 0x11, 0x62, 0x0b, 0x46, 0xf2, 0x0f, - 0xff, 0xfb, 0x80, 0x88, 0xcc, 0xf8, 0x54, 0x97, - 0x12, 0x1a, 0xd4, 0x49, 0x9e, 0x08, 0x45, 0xb8, - 0x76, 0xf6, 0xdd, 0x66, 0x40, 0x08, 0x8a, 0x2f, - 0x0b, 0x2d, 0x8a, 0x60, 0x0b, 0xdf, 0x4c, 0x0c, - ]; - - let hash = Hmac::::from_slice(&HASH_BYTES).expect("right number of bytes"); - assert_tokens(&hash.compact(), &[Token::BorrowedBytes(&HASH_BYTES[..])]); - assert_tokens( - &hash.readable(), - &[Token::Str( - "8b41e1b78ad11521113c52ff182a1b8e0a195754aa527fcd00a411620b46f20f\ - fffb8088ccf85497121ad4499e0845b876f6dd6640088a2f0b2d8a600bdf4c0c", - )], - ); - } -} diff --git a/hashes/src/impls.rs b/hashes/src/impls.rs deleted file mode 100644 index 15e61e5b8..000000000 --- a/hashes/src/impls.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2019 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -use std::io; - -use crate::{hmac, ripemd160, sha1, sha256, sha512, siphash24, HashEngine}; - -impl io::Write for sha1::HashEngine { - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, buf: &[u8]) -> io::Result { - self.input(buf); - Ok(buf.len()) - } -} - -impl io::Write for sha256::HashEngine { - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, buf: &[u8]) -> io::Result { - self.input(buf); - Ok(buf.len()) - } -} - -impl io::Write for sha512::HashEngine { - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, buf: &[u8]) -> io::Result { - self.input(buf); - Ok(buf.len()) - } -} - -impl io::Write for ripemd160::HashEngine { - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, buf: &[u8]) -> io::Result { - self.input(buf); - Ok(buf.len()) - } -} - -impl io::Write for siphash24::HashEngine { - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, buf: &[u8]) -> io::Result { - self.input(buf); - Ok(buf.len()) - } -} - -impl io::Write for hmac::HmacEngine { - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, buf: &[u8]) -> io::Result { - self.input(buf); - Ok(buf.len()) - } -} - -#[cfg(test)] -mod tests { - use super::io::Write; - use crate::{hash160, hmac, ripemd160, sha1, sha256, sha256d, sha512, siphash24, Hash}; - - macro_rules! write_test { - ($mod:ident, $exp_empty:expr, $exp_256:expr, $exp_64k:expr,) => { - #[test] - fn $mod() { - let mut engine = $mod::Hash::engine(); - engine.write_all(&[]).unwrap(); - assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_empty); - - let mut engine = $mod::Hash::engine(); - engine.write_all(&[1; 256]).unwrap(); - assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_256); - - let mut engine = $mod::Hash::engine(); - engine.write_all(&[99; 64000]).unwrap(); - assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_64k); - } - }; - } - - write_test!( - sha1, - "da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ac458b067c6b021c7e9358229b636e9d1e4cb154", - "e4b66838f9f7b6f91e5be32a02ae78094df402e7", - ); - - write_test!( - sha256, - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "2661920f2409dd6c8adeb0c44972959f232b6429afa913845d0fd95e7e768234", - "5c5e904f5d4fd587c7a906bf846e08a927286f388c54c39213a4884695271bbc", - ); - - write_test!( - sha256d, - "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d", - "374000d830c75d10d9417e493a7652920f30efbd300e3fb092f24c28c20baf64", - "0050d4148ad7a0437ca0643fad5bf4614cd95d9ba21fde52370b37dcc3f03307", - ); - - write_test!( - sha512, - "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce\ - 47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", - "57ecf739d3a7ca647639adae80a05f4f361304bfcbfa1ceba93296b096e74287\ - 45fc10c142cecdd3bb587a3dba598c072f6f78b31cc0a06a3da0105ee51f75d6", - "dd28f78c53f3bc9bd0c2dca9642a1ad402a70412f985c1f6e54fadb98ce9c458\ - 4761df8d04ed04bb734ba48dd2106bb9ea54524f1394cdd18e6da3166e71c3ee", - ); - - write_test!( - ripemd160, - "9c1185a5c5e9fc54612808977ee8f548b2258d31", - "e571a1ca5b780aa52bafdb9ec852544ffca418ba", - "ddd2ecce739e823629c7d46ab18918e9c4a51c75", - ); - - write_test!( - hash160, - "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", - "671356a1a874695ad3bc20cae440f4360835bd5a", - "a9608c952c8dbcc20c53803d2ca5ad31d64d9313", - ); - - write_test!(siphash24, "d70077739d4b921e", "3a3ccefde9b5b1e3", "ce456e4e4ecbc5bf",); - - #[test] - fn hmac() { - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); - engine.write_all(&[]).unwrap(); - assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), - "bf5515149cf797955c4d3194cca42472883281951697c8375d9d9b107f384225" - ); - - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); - engine.write_all(&[1; 256]).unwrap(); - assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), - "59c9aca10c81c73cb4c196d94db741b6bf2050e0153d5a45f2526bff34675ac5" - ); - - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); - engine.write_all(&[99; 64000]).unwrap(); - assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), - "30df499717415a395379a1eaabe50038036e4abb5afc94aa55c952f4aa57be08" - ); - } -} diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs deleted file mode 100644 index 9ba9dd2e6..000000000 --- a/hashes/src/internal_macros.rs +++ /dev/null @@ -1,257 +0,0 @@ -//! Non-public macros - -macro_rules! arr_newtype_fmt_impl { - ($ty:ident, $bytes:expr $(, $gen:ident: $gent:ident)*) => { - impl<$($gen: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - #[allow(unused)] - use crate::Hash as _; - let case = internals::hex::Case::Lower; - if <$ty<$($gen),*>>::DISPLAY_BACKWARD { - internals::hex::display::fmt_hex_exact!(f, $bytes, self.0.iter().rev(), case) - } else { - internals::hex::display::fmt_hex_exact!(f, $bytes, self.0.iter(), case) - } - } - } - - impl<$($gen: $gent),*> $crate::_export::_core::fmt::UpperHex for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - #[allow(unused)] - use crate::Hash as _; - let case = internals::hex::Case::Upper; - if <$ty<$($gen),*>>::DISPLAY_BACKWARD { - internals::hex::display::fmt_hex_exact!(f, $bytes, self.0.iter().rev(), case) - } else { - internals::hex::display::fmt_hex_exact!(f, $bytes, self.0.iter(), case) - } - } - } - - impl<$($gen: $gent),*> $crate::_export::_core::fmt::Display for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - $crate::_export::_core::fmt::LowerHex::fmt(self, f) - } - } - - impl<$($gen: $gent),*> $crate::_export::_core::fmt::Debug for $ty<$($gen),*> { - #[inline] - fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { - write!(f, "{:#}", self) - } - } - } -} -pub(crate) use arr_newtype_fmt_impl; - -/// Adds trait impls to the type called `Hash` in the current scope. -/// -/// Implpements various conversion traits as well as the [`crate::Hash`] trait. -/// Arguments: -/// -/// * `$bits` - number of bits this hash type has -/// * `$reverse` - `bool` - `true` if the hash type should be displayed backwards, `false` -/// otherwise. -/// * `$gen: $gent` - generic type(s) and trait bound(s) -/// -/// Restrictions on usage: -/// -/// * There must be a free-standing `fn from_engine(HashEngine) -> Hash` in the scope -/// * `fn internal_new([u8; $bits / 8]) -> Self` must exist on `Hash` -/// * `fn internal_engine() -> HashEngine` must exist on `Hash` -/// -/// `from_engine` obviously implements the finalization algorithm. -/// `internal_new` is required so that types with more than one field are constructible. -/// `internal_engine` is required to initialize the engine for given hash type. -macro_rules! hash_trait_impls { - ($bits:expr, $reverse:expr $(, $gen:ident: $gent:ident)*) => { - impl<$($gen: $gent),*> Hash<$($gen),*> { - /// Displays hex forwards, regardless of how this type would display it naturally. - /// - /// This is mainly intended as an internal method and you shouldn't need it unless - /// you're doing something special. - pub fn forward_hex(&self) -> impl '_ + core::fmt::LowerHex + core::fmt::UpperHex { - internals::hex::display::DisplayHex::as_hex(&self.0) - } - - /// Displays hex backwards, regardless of how this type would display it naturally. - /// - /// This is mainly intended as an internal method and you shouldn't need it unless - /// you're doing something special. - pub fn backward_hex(&self) -> impl '_ + core::fmt::LowerHex + core::fmt::UpperHex { - internals::hex::display::DisplayArray::<_, [u8; $bits / 8 * 2]>::new(self.0.iter().rev()) - } - - /// Zero cost conversion between a fixed length byte array shared reference and - /// a shared reference to this Hash type. - pub fn from_bytes_ref(bytes: &[u8; $bits / 8]) -> &Self { - // Safety: Sound because Self is #[repr(transparent)] containing [u8; $bits / 8] - unsafe { &*(bytes as *const _ as *const Self) } - } - - /// Zero cost conversion between a fixed length byte array exclusive reference and - /// an exclusive reference to this Hash type. - pub fn from_bytes_mut(bytes: &mut [u8; $bits / 8]) -> &mut Self { - // Safety: Sound because Self is #[repr(transparent)] containing [u8; $bits / 8] - unsafe { &mut *(bytes as *mut _ as *mut Self) } - } - } - - impl<$($gen: $gent),*> str::FromStr for Hash<$($gen),*> { - type Err = $crate::hex::Error; - fn from_str(s: &str) -> Result { - use $crate::hex::{FromHex, HexIterator}; - use $crate::Hash; - - let inner: [u8; $bits / 8] = if $reverse { - FromHex::from_byte_iter(HexIterator::new(s)?.rev())? - } else { - FromHex::from_byte_iter(HexIterator::new(s)?)? - }; - Ok(Self::from_byte_array(inner)) - } - } - - $crate::internal_macros::arr_newtype_fmt_impl!(Hash, $bits / 8 $(, $gen: $gent)*); - serde_impl!(Hash, $bits / 8 $(, $gen: $gent)*); - borrow_slice_impl!(Hash $(, $gen: $gent)*); - - impl<$($gen: $gent),*> $crate::_export::_core::convert::AsRef<[u8; $bits / 8]> for Hash<$($gen),*> { - fn as_ref(&self) -> &[u8; $bits / 8] { - &self.0 - } - } - - impl $(, $gen: $gent)*> Index for Hash<$($gen),*> { - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - &self.0[index] - } - } - - impl<$($gen: $gent),*> From> for [u8; $bits / 8] { - fn from(value: Hash<$($gen),*>) -> Self { value.0 } - } - - #[cfg(feature = "bincode")] - impl<$($gen: $gent),*> bincode::Encode for Hash<$($gen),*> { - fn encode(&self, encoder: &mut E) -> Result<(), bincode::error::EncodeError> { - use crate::Hash; - // Use as_byte_array so that we don’t have to consume self - self.as_byte_array().encode(encoder) - } - } - - #[cfg(feature = "bincode")] - impl bincode::Decode for Hash<$($gen),*> { - fn decode>(decoder: &mut D) -> Result { - use crate::Hash; - // Decode a fixed-length byte array and then create the Hash - let bytes: [u8; $bits / 8] = <[u8; $bits / 8]>::decode(decoder)?; - Ok(Self::from_byte_array(bytes)) - } - } - - #[cfg(feature = "bincode")] - impl<'de, C $(, $gen: $gent)*> bincode::BorrowDecode<'de, C> for Hash<$($gen),*> { - fn borrow_decode>(decoder: &mut D) -> Result { - use std::convert::TryInto; - // Decode a borrowed reference to a byte slice - let bytes: &[u8] = bincode::BorrowDecode::borrow_decode(decoder)?; - - // Convert the slice into a fixed-size array - let bytes: &[u8; $bits / 8] = bytes.try_into().map_err(|_| bincode::error::DecodeError::Other("Incorrect byte length".into()))?; - - // Construct the hash from the reference - Ok(Self::from_bytes_ref(bytes).clone()) - } - } - - impl<$($gen: $gent),*> crate::Hash for Hash<$($gen),*> { - type Engine = HashEngine; - type Bytes = [u8; $bits / 8]; - - const LEN: usize = $bits / 8; - const DISPLAY_BACKWARD: bool = $reverse; - - fn engine() -> Self::Engine { - Self::internal_engine() - } - - fn from_engine(e: HashEngine) -> Hash<$($gen),*> { - from_engine(e) - } - - fn from_slice(sl: &[u8]) -> Result, Error> { - if sl.len() != $bits / 8 { - Err(Error::InvalidLength(Self::LEN, sl.len())) - } else { - let mut ret = [0; $bits / 8]; - ret.copy_from_slice(sl); - Ok(Self::internal_new(ret)) - } - } - - fn to_byte_array(self) -> Self::Bytes { - self.0 - } - - fn as_byte_array(&self) -> &Self::Bytes { - &self.0 - } - - fn from_byte_array(bytes: Self::Bytes) -> Self { - Self::internal_new(bytes) - } - - fn all_zeros() -> Self { - Hash::internal_new([0x00; $bits / 8]) - } - } - } -} -pub(crate) use hash_trait_impls; - -/// Creates a type called `Hash` and implements standard interface for it. -/// -/// The created type will have all standard derives, `Hash` impl and implementation of -/// `internal_engine` returning default. The created type has a single field. -/// -/// Arguments: -/// -/// * `$bits` - the number of bits of the hash type -/// * `$reverse` - `true` if the hash should be displayed backwards, `false` otherwise -/// * `$doc` - doc string to put on the type -/// * `$schemars` - a literal that goes into `schema_with`. -/// -/// The `from_engine` free-standing function is still required with this macro. See the doc of -/// [`hash_trait_impls`]. -macro_rules! hash_type { - ($bits:expr, $reverse:expr, $doc:literal, $schemars:literal) => { - #[doc = $doc] - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - #[cfg_attr(feature = "schemars", derive(crate::schemars::JsonSchema))] - #[repr(transparent)] - pub struct Hash( - #[cfg_attr(feature = "schemars", schemars(schema_with = $schemars))] [u8; $bits / 8], - ); - - impl Hash { - fn internal_new(arr: [u8; $bits / 8]) -> Self { - Hash(arr) - } - - fn internal_engine() -> HashEngine { - Default::default() - } - } - - crate::internal_macros::hash_trait_impls!($bits, $reverse); - }; -} -pub(crate) use hash_type; diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index be5153cdc..68dfe07ee 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -14,10 +14,10 @@ //! Rust hashes library. //! -//! This is a simple, no-dependency library which implements the hash functions -//! needed by Bitcoin. These are SHA256, SHA256d, and RIPEMD160. As an ancillary -//! thing, it exposes hexadecimal serialization and deserialization, since these -//! are needed to display hashes anyway. +//! The hash functions themselves are re-exported from `bitcoin_hashes`. What +//! this crate adds is the Dash newtype machinery over them: `hash_newtype!` +//! and its `bincode`, `serde` and hex surface, plus the batch SipHash used by +//! compact filter matching. //! //! ## Commonly used operations //! @@ -93,10 +93,8 @@ #[cfg(feature = "serde")] pub extern crate serde; -#[cfg(all(test, feature = "serde"))] -extern crate serde_test; -// #[cfg(bench)] -// extern crate test; + +pub extern crate bitcoin_hashes; #[doc(hidden)] pub mod _export { @@ -106,146 +104,24 @@ pub mod _export { } } -#[cfg(feature = "schemars")] -extern crate actual_schemars as schemars; +// Primitives come straight from upstream. +// The tag helpers are upstream's verbatim. Only `sha256t_hash_newtype!` stays +// ours, so that tagged hashes route through our `hash_newtype!`. +pub use bitcoin_hashes::{ + cmp, hash160, hex, hmac, ripemd160, sha1, sha256, sha256d, sha512, sha512_256, FromSliceError, + Hash, HashEngine, Hmac, HmacEngine, +}; +pub use bitcoin_hashes::{sha256t_hash_newtype_tag, sha256t_hash_newtype_tag_constructor}; + +/// Kept under the old Dash name. +pub type Error = FromSliceError; -mod internal_macros; #[macro_use] mod util; #[macro_use] pub mod serde_macros; mod bincode_macros; -pub mod cmp; -pub mod error; -pub mod hash160; #[cfg(feature = "x11")] pub mod hash_x11; -pub mod hex; -pub mod hmac; -mod impls; -pub mod ripemd160; -pub mod sha1; -pub mod sha256; -pub mod sha256d; pub mod sha256t; -pub mod sha512; -pub mod sha512_256; pub mod siphash24; - -use core::{borrow, fmt, hash, ops}; - -pub use error::Error; -pub use hmac::{Hmac, HmacEngine}; - -/// A hashing engine which bytes can be serialized into. -pub trait HashEngine: Clone + Default { - /// Byte array representing the internal state of the hash engine. - type MidState; - - /// Outputs the midstate of the hash engine. This function should not be - /// used directly unless you really know what you're doing. - fn midstate(&self) -> Self::MidState; - - /// Length of the hash's internal block size, in bytes. - const BLOCK_SIZE: usize; - - /// Add data to the hash engine. - fn input(&mut self, data: &[u8]); - - /// Return the number of bytes already n_bytes_hashed(inputted). - fn n_bytes_hashed(&self) -> usize; -} - -/// Trait which applies to hashes of all types. -pub trait Hash: - Copy - + Clone - + PartialEq - + Eq - + PartialOrd - + Ord - + hash::Hash - + fmt::Debug - + fmt::Display - + fmt::LowerHex - + ops::Index - + ops::Index, Output = [u8]> - + ops::Index, Output = [u8]> - + ops::Index, Output = [u8]> - + ops::Index - + borrow::Borrow<[u8]> -{ - /// A hashing engine which bytes can be serialized into. It is expected - /// to implement the `io::Write` trait, and to never return errors under - /// any conditions. - type Engine: HashEngine; - - /// The byte array that represents the hash internally. - type Bytes: hex::FromHex + Copy; - - /// Constructs a new engine. - fn engine() -> Self::Engine { - Self::Engine::default() - } - - /// Produces a hash from the current state of a given engine. - fn from_engine(e: Self::Engine) -> Self; - - /// Length of the hash, in bytes. - const LEN: usize; - - /// Copies a byte slice into a hash object. - fn from_slice(sl: &[u8]) -> Result; - - /// Hashes some bytes. - fn hash(data: &[u8]) -> Self { - let mut engine = Self::engine(); - engine.input(data); - Self::from_engine(engine) - } - - /// Flag indicating whether user-visible serializations of this hash - /// should be backward. For some reason Satoshi decided this should be - /// true for `Sha256dHash`, so here we are. - const DISPLAY_BACKWARD: bool = false; - - /// Returns the underlying byte array. - fn to_byte_array(self) -> Self::Bytes; - - /// Returns a reference to the underlying byte array. - fn as_byte_array(&self) -> &Self::Bytes; - - /// Constructs a hash from the underlying byte array. - fn from_byte_array(bytes: Self::Bytes) -> Self; - - /// Returns an all zero hash. - /// - /// An all zeros hash is a made up construct because there is not a known input that can create - /// it, however it is used in various places in Bitcoin e.g., the Bitcoin genesis block's - /// previous blockhash and the coinbase transaction's outpoint txid. - fn all_zeros() -> Self; -} - -#[cfg(test)] -mod tests { - use crate::{sha256d, Hash}; - - hash_newtype! { - /// A test newtype - struct TestNewtype(sha256d::Hash); - - /// A test newtype - struct TestNewtype2(sha256d::Hash); - } - - #[test] - fn convert_newtypes() { - let h1 = TestNewtype::hash(&[]); - let h2: TestNewtype2 = h1.to_raw_hash().into(); - assert_eq!(&h1[..], &h2[..]); - - let h = sha256d::Hash::hash(&[]); - let h2: TestNewtype = h.to_string().parse().unwrap(); - assert_eq!(h2.to_raw_hash(), h); - } -} diff --git a/hashes/src/ripemd160.rs b/hashes/src/ripemd160.rs deleted file mode 100644 index 4e532cb1f..000000000 --- a/hashes/src/ripemd160.rs +++ /dev/null @@ -1,520 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -// This module is largely copied from the rust-crypto ripemd.rs file; -// while rust-crypto is licensed under Apache, that file specifically -// was written entirely by Andrew Poelstra, who is re-licensing its -// contents here as CC0. - -//! RIPEMD160 implementation. -//! - -use core::ops::Index; -use core::slice::SliceIndex; -use core::{cmp, str}; - -use crate::{Error, HashEngine as _}; - -crate::internal_macros::hash_type! { - 160, - false, - "Output of the RIPEMD160 hash function.", - "crate::util::json_hex_string::len_20" -} - -#[cfg(not(fuzzing))] -fn from_engine(mut e: HashEngine) -> Hash { - // pad buffer with a single 1-bit then all 0s, until there are exactly 8 bytes remaining - let data_len = e.length as u64; - - let zeroes = [0; BLOCK_SIZE - 8]; - e.input(&[0x80]); - if e.length % BLOCK_SIZE > zeroes.len() { - e.input(&zeroes); - } - let pad_length = zeroes.len() - (e.length % BLOCK_SIZE); - e.input(&zeroes[..pad_length]); - debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len()); - - e.input(&(8 * data_len).to_le_bytes()); - debug_assert_eq!(e.length % BLOCK_SIZE, 0); - - Hash(e.midstate()) -} - -#[cfg(fuzzing)] -fn from_engine(e: HashEngine) -> Hash { - let mut res = e.midstate(); - res[0] ^= (e.length & 0xff) as u8; - Hash(res) -} - -const BLOCK_SIZE: usize = 64; - -/// Engine to compute RIPEMD160 hash function. -#[derive(Clone)] -pub struct HashEngine { - buffer: [u8; BLOCK_SIZE], - h: [u32; 5], - length: usize, -} - -impl Default for HashEngine { - fn default() -> Self { - HashEngine { - h: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0], - length: 0, - buffer: [0; BLOCK_SIZE], - } - } -} - -impl crate::HashEngine for HashEngine { - type MidState = [u8; 20]; - - #[cfg(not(fuzzing))] - fn midstate(&self) -> [u8; 20] { - let mut ret = [0; 20]; - for (val, ret_bytes) in self.h.iter().zip(ret.as_chunks_mut::<4>().0) { - *ret_bytes = val.to_le_bytes(); - } - ret - } - - #[cfg(fuzzing)] - fn midstate(&self) -> [u8; 20] { - let mut ret = [0; 20]; - ret.copy_from_slice(&self.buffer[..20]); - ret - } - - const BLOCK_SIZE: usize = 64; - - fn n_bytes_hashed(&self) -> usize { - self.length - } - - engine_input_impl!(); -} - -macro_rules! round( - ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, - $x:expr, $bits:expr, $add:expr, $round:expr) => ({ - $a = $a.wrapping_add($round).wrapping_add($x).wrapping_add($add); - $a = $a.rotate_left($bits).wrapping_add($e); - $c = $c.rotate_left(10); - }); -); - -macro_rules! process_block( - ($h:expr, $data:expr, - $( round1: h_ordering $f0:expr, $f1:expr, $f2:expr, $f3:expr, $f4:expr; - data_index $data_index1:expr; roll_shift $bits1:expr; )* - $( round2: h_ordering $g0:expr, $g1:expr, $g2:expr, $g3:expr, $g4:expr; - data_index $data_index2:expr; roll_shift $bits2:expr; )* - $( round3: h_ordering $h0:expr, $h1:expr, $h2:expr, $h3:expr, $h4:expr; - data_index $data_index3:expr; roll_shift $bits3:expr; )* - $( round4: h_ordering $i0:expr, $i1:expr, $i2:expr, $i3:expr, $i4:expr; - data_index $data_index4:expr; roll_shift $bits4:expr; )* - $( round5: h_ordering $j0:expr, $j1:expr, $j2:expr, $j3:expr, $j4:expr; - data_index $data_index5:expr; roll_shift $bits5:expr; )* - $( par_round1: h_ordering $pj0:expr, $pj1:expr, $pj2:expr, $pj3:expr, $pj4:expr; - data_index $pdata_index1:expr; roll_shift $pbits1:expr; )* - $( par_round2: h_ordering $pi0:expr, $pi1:expr, $pi2:expr, $pi3:expr, $pi4:expr; - data_index $pdata_index2:expr; roll_shift $pbits2:expr; )* - $( par_round3: h_ordering $ph0:expr, $ph1:expr, $ph2:expr, $ph3:expr, $ph4:expr; - data_index $pdata_index3:expr; roll_shift $pbits3:expr; )* - $( par_round4: h_ordering $pg0:expr, $pg1:expr, $pg2:expr, $pg3:expr, $pg4:expr; - data_index $pdata_index4:expr; roll_shift $pbits4:expr; )* - $( par_round5: h_ordering $pf0:expr, $pf1:expr, $pf2:expr, $pf3:expr, $pf4:expr; - data_index $pdata_index5:expr; roll_shift $pbits5:expr; )* - ) => ({ - let mut bb = $h; - let mut bbb = $h; - - // Round 1 - $( round!(bb[$f0], bb[$f1], bb[$f2], bb[$f3], bb[$f4], - $data[$data_index1], $bits1, 0x00000000, - bb[$f1] ^ bb[$f2] ^ bb[$f3]); )* - - // Round 2 - $( round!(bb[$g0], bb[$g1], bb[$g2], bb[$g3], bb[$g4], - $data[$data_index2], $bits2, 0x5a827999, - (bb[$g1] & bb[$g2]) | (!bb[$g1] & bb[$g3])); )* - - // Round 3 - $( round!(bb[$h0], bb[$h1], bb[$h2], bb[$h3], bb[$h4], - $data[$data_index3], $bits3, 0x6ed9eba1, - (bb[$h1] | !bb[$h2]) ^ bb[$h3]); )* - - // Round 4 - $( round!(bb[$i0], bb[$i1], bb[$i2], bb[$i3], bb[$i4], - $data[$data_index4], $bits4, 0x8f1bbcdc, - (bb[$i1] & bb[$i3]) | (bb[$i2] & !bb[$i3])); )* - - // Round 5 - $( round!(bb[$j0], bb[$j1], bb[$j2], bb[$j3], bb[$j4], - $data[$data_index5], $bits5, 0xa953fd4e, - bb[$j1] ^ (bb[$j2] | !bb[$j3])); )* - - // Parallel rounds: these are the same as the previous five - // rounds except that the constants have changed, we work - // with the other buffer, and they are applied in reverse - // order. - - // Parallel Round 1 - $( round!(bbb[$pj0], bbb[$pj1], bbb[$pj2], bbb[$pj3], bbb[$pj4], - $data[$pdata_index1], $pbits1, 0x50a28be6, - bbb[$pj1] ^ (bbb[$pj2] | !bbb[$pj3])); )* - - // Porallel Round 2 - $( round!(bbb[$pi0], bbb[$pi1], bbb[$pi2], bbb[$pi3], bbb[$pi4], - $data[$pdata_index2], $pbits2, 0x5c4dd124, - (bbb[$pi1] & bbb[$pi3]) | (bbb[$pi2] & !bbb[$pi3])); )* - - // Parallel Round 3 - $( round!(bbb[$ph0], bbb[$ph1], bbb[$ph2], bbb[$ph3], bbb[$ph4], - $data[$pdata_index3], $pbits3, 0x6d703ef3, - (bbb[$ph1] | !bbb[$ph2]) ^ bbb[$ph3]); )* - - // Parallel Round 4 - $( round!(bbb[$pg0], bbb[$pg1], bbb[$pg2], bbb[$pg3], bbb[$pg4], - $data[$pdata_index4], $pbits4, 0x7a6d76e9, - (bbb[$pg1] & bbb[$pg2]) | (!bbb[$pg1] & bbb[$pg3])); )* - - // Parallel Round 5 - $( round!(bbb[$pf0], bbb[$pf1], bbb[$pf2], bbb[$pf3], bbb[$pf4], - $data[$pdata_index5], $pbits5, 0x00000000, - bbb[$pf1] ^ bbb[$pf2] ^ bbb[$pf3]); )* - - // Combine results - bbb[3] = bbb[3].wrapping_add($h[1]).wrapping_add(bb[2]); - $h[1] = $h[2].wrapping_add(bb[3]).wrapping_add(bbb[4]); - $h[2] = $h[3].wrapping_add(bb[4]).wrapping_add(bbb[0]); - $h[3] = $h[4].wrapping_add(bb[0]).wrapping_add(bbb[1]); - $h[4] = $h[0].wrapping_add(bb[1]).wrapping_add(bbb[2]); - $h[0] = bbb[3]; - }); -); - -impl HashEngine { - fn process_block(&mut self) { - debug_assert_eq!(self.buffer.len(), BLOCK_SIZE); - - let mut w = [0u32; 16]; - for (w_val, buff_bytes) in w.iter_mut().zip(self.buffer.as_chunks::<4>().0) { - *w_val = u32::from_le_bytes(*buff_bytes) - } - - process_block!(self.h, w, - // Round 1 - round1: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 11; - round1: h_ordering 4, 0, 1, 2, 3; data_index 1; roll_shift 14; - round1: h_ordering 3, 4, 0, 1, 2; data_index 2; roll_shift 15; - round1: h_ordering 2, 3, 4, 0, 1; data_index 3; roll_shift 12; - round1: h_ordering 1, 2, 3, 4, 0; data_index 4; roll_shift 5; - round1: h_ordering 0, 1, 2, 3, 4; data_index 5; roll_shift 8; - round1: h_ordering 4, 0, 1, 2, 3; data_index 6; roll_shift 7; - round1: h_ordering 3, 4, 0, 1, 2; data_index 7; roll_shift 9; - round1: h_ordering 2, 3, 4, 0, 1; data_index 8; roll_shift 11; - round1: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 13; - round1: h_ordering 0, 1, 2, 3, 4; data_index 10; roll_shift 14; - round1: h_ordering 4, 0, 1, 2, 3; data_index 11; roll_shift 15; - round1: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 6; - round1: h_ordering 2, 3, 4, 0, 1; data_index 13; roll_shift 7; - round1: h_ordering 1, 2, 3, 4, 0; data_index 14; roll_shift 9; - round1: h_ordering 0, 1, 2, 3, 4; data_index 15; roll_shift 8; - - // Round 2 - round2: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 7; - round2: h_ordering 3, 4, 0, 1, 2; data_index 4; roll_shift 6; - round2: h_ordering 2, 3, 4, 0, 1; data_index 13; roll_shift 8; - round2: h_ordering 1, 2, 3, 4, 0; data_index 1; roll_shift 13; - round2: h_ordering 0, 1, 2, 3, 4; data_index 10; roll_shift 11; - round2: h_ordering 4, 0, 1, 2, 3; data_index 6; roll_shift 9; - round2: h_ordering 3, 4, 0, 1, 2; data_index 15; roll_shift 7; - round2: h_ordering 2, 3, 4, 0, 1; data_index 3; roll_shift 15; - round2: h_ordering 1, 2, 3, 4, 0; data_index 12; roll_shift 7; - round2: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 12; - round2: h_ordering 4, 0, 1, 2, 3; data_index 9; roll_shift 15; - round2: h_ordering 3, 4, 0, 1, 2; data_index 5; roll_shift 9; - round2: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 11; - round2: h_ordering 1, 2, 3, 4, 0; data_index 14; roll_shift 7; - round2: h_ordering 0, 1, 2, 3, 4; data_index 11; roll_shift 13; - round2: h_ordering 4, 0, 1, 2, 3; data_index 8; roll_shift 12; - - // Round 3 - round3: h_ordering 3, 4, 0, 1, 2; data_index 3; roll_shift 11; - round3: h_ordering 2, 3, 4, 0, 1; data_index 10; roll_shift 13; - round3: h_ordering 1, 2, 3, 4, 0; data_index 14; roll_shift 6; - round3: h_ordering 0, 1, 2, 3, 4; data_index 4; roll_shift 7; - round3: h_ordering 4, 0, 1, 2, 3; data_index 9; roll_shift 14; - round3: h_ordering 3, 4, 0, 1, 2; data_index 15; roll_shift 9; - round3: h_ordering 2, 3, 4, 0, 1; data_index 8; roll_shift 13; - round3: h_ordering 1, 2, 3, 4, 0; data_index 1; roll_shift 15; - round3: h_ordering 0, 1, 2, 3, 4; data_index 2; roll_shift 14; - round3: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 8; - round3: h_ordering 3, 4, 0, 1, 2; data_index 0; roll_shift 13; - round3: h_ordering 2, 3, 4, 0, 1; data_index 6; roll_shift 6; - round3: h_ordering 1, 2, 3, 4, 0; data_index 13; roll_shift 5; - round3: h_ordering 0, 1, 2, 3, 4; data_index 11; roll_shift 12; - round3: h_ordering 4, 0, 1, 2, 3; data_index 5; roll_shift 7; - round3: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 5; - - // Round 4 - round4: h_ordering 2, 3, 4, 0, 1; data_index 1; roll_shift 11; - round4: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 12; - round4: h_ordering 0, 1, 2, 3, 4; data_index 11; roll_shift 14; - round4: h_ordering 4, 0, 1, 2, 3; data_index 10; roll_shift 15; - round4: h_ordering 3, 4, 0, 1, 2; data_index 0; roll_shift 14; - round4: h_ordering 2, 3, 4, 0, 1; data_index 8; roll_shift 15; - round4: h_ordering 1, 2, 3, 4, 0; data_index 12; roll_shift 9; - round4: h_ordering 0, 1, 2, 3, 4; data_index 4; roll_shift 8; - round4: h_ordering 4, 0, 1, 2, 3; data_index 13; roll_shift 9; - round4: h_ordering 3, 4, 0, 1, 2; data_index 3; roll_shift 14; - round4: h_ordering 2, 3, 4, 0, 1; data_index 7; roll_shift 5; - round4: h_ordering 1, 2, 3, 4, 0; data_index 15; roll_shift 6; - round4: h_ordering 0, 1, 2, 3, 4; data_index 14; roll_shift 8; - round4: h_ordering 4, 0, 1, 2, 3; data_index 5; roll_shift 6; - round4: h_ordering 3, 4, 0, 1, 2; data_index 6; roll_shift 5; - round4: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 12; - - // Round 5 - round5: h_ordering 1, 2, 3, 4, 0; data_index 4; roll_shift 9; - round5: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 15; - round5: h_ordering 4, 0, 1, 2, 3; data_index 5; roll_shift 5; - round5: h_ordering 3, 4, 0, 1, 2; data_index 9; roll_shift 11; - round5: h_ordering 2, 3, 4, 0, 1; data_index 7; roll_shift 6; - round5: h_ordering 1, 2, 3, 4, 0; data_index 12; roll_shift 8; - round5: h_ordering 0, 1, 2, 3, 4; data_index 2; roll_shift 13; - round5: h_ordering 4, 0, 1, 2, 3; data_index 10; roll_shift 12; - round5: h_ordering 3, 4, 0, 1, 2; data_index 14; roll_shift 5; - round5: h_ordering 2, 3, 4, 0, 1; data_index 1; roll_shift 12; - round5: h_ordering 1, 2, 3, 4, 0; data_index 3; roll_shift 13; - round5: h_ordering 0, 1, 2, 3, 4; data_index 8; roll_shift 14; - round5: h_ordering 4, 0, 1, 2, 3; data_index 11; roll_shift 11; - round5: h_ordering 3, 4, 0, 1, 2; data_index 6; roll_shift 8; - round5: h_ordering 2, 3, 4, 0, 1; data_index 15; roll_shift 5; - round5: h_ordering 1, 2, 3, 4, 0; data_index 13; roll_shift 6; - - // Porallel Round 1; - par_round1: h_ordering 0, 1, 2, 3, 4; data_index 5; roll_shift 8; - par_round1: h_ordering 4, 0, 1, 2, 3; data_index 14; roll_shift 9; - par_round1: h_ordering 3, 4, 0, 1, 2; data_index 7; roll_shift 9; - par_round1: h_ordering 2, 3, 4, 0, 1; data_index 0; roll_shift 11; - par_round1: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 13; - par_round1: h_ordering 0, 1, 2, 3, 4; data_index 2; roll_shift 15; - par_round1: h_ordering 4, 0, 1, 2, 3; data_index 11; roll_shift 15; - par_round1: h_ordering 3, 4, 0, 1, 2; data_index 4; roll_shift 5; - par_round1: h_ordering 2, 3, 4, 0, 1; data_index 13; roll_shift 7; - par_round1: h_ordering 1, 2, 3, 4, 0; data_index 6; roll_shift 7; - par_round1: h_ordering 0, 1, 2, 3, 4; data_index 15; roll_shift 8; - par_round1: h_ordering 4, 0, 1, 2, 3; data_index 8; roll_shift 11; - par_round1: h_ordering 3, 4, 0, 1, 2; data_index 1; roll_shift 14; - par_round1: h_ordering 2, 3, 4, 0, 1; data_index 10; roll_shift 14; - par_round1: h_ordering 1, 2, 3, 4, 0; data_index 3; roll_shift 12; - par_round1: h_ordering 0, 1, 2, 3, 4; data_index 12; roll_shift 6; - - // Parallel Round 2 - par_round2: h_ordering 4, 0, 1, 2, 3; data_index 6; roll_shift 9; - par_round2: h_ordering 3, 4, 0, 1, 2; data_index 11; roll_shift 13; - par_round2: h_ordering 2, 3, 4, 0, 1; data_index 3; roll_shift 15; - par_round2: h_ordering 1, 2, 3, 4, 0; data_index 7; roll_shift 7; - par_round2: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 12; - par_round2: h_ordering 4, 0, 1, 2, 3; data_index 13; roll_shift 8; - par_round2: h_ordering 3, 4, 0, 1, 2; data_index 5; roll_shift 9; - par_round2: h_ordering 2, 3, 4, 0, 1; data_index 10; roll_shift 11; - par_round2: h_ordering 1, 2, 3, 4, 0; data_index 14; roll_shift 7; - par_round2: h_ordering 0, 1, 2, 3, 4; data_index 15; roll_shift 7; - par_round2: h_ordering 4, 0, 1, 2, 3; data_index 8; roll_shift 12; - par_round2: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 7; - par_round2: h_ordering 2, 3, 4, 0, 1; data_index 4; roll_shift 6; - par_round2: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 15; - par_round2: h_ordering 0, 1, 2, 3, 4; data_index 1; roll_shift 13; - par_round2: h_ordering 4, 0, 1, 2, 3; data_index 2; roll_shift 11; - - // Parallel Round 3 - par_round3: h_ordering 3, 4, 0, 1, 2; data_index 15; roll_shift 9; - par_round3: h_ordering 2, 3, 4, 0, 1; data_index 5; roll_shift 7; - par_round3: h_ordering 1, 2, 3, 4, 0; data_index 1; roll_shift 15; - par_round3: h_ordering 0, 1, 2, 3, 4; data_index 3; roll_shift 11; - par_round3: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 8; - par_round3: h_ordering 3, 4, 0, 1, 2; data_index 14; roll_shift 6; - par_round3: h_ordering 2, 3, 4, 0, 1; data_index 6; roll_shift 6; - par_round3: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 14; - par_round3: h_ordering 0, 1, 2, 3, 4; data_index 11; roll_shift 12; - par_round3: h_ordering 4, 0, 1, 2, 3; data_index 8; roll_shift 13; - par_round3: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 5; - par_round3: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 14; - par_round3: h_ordering 1, 2, 3, 4, 0; data_index 10; roll_shift 13; - par_round3: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 13; - par_round3: h_ordering 4, 0, 1, 2, 3; data_index 4; roll_shift 7; - par_round3: h_ordering 3, 4, 0, 1, 2; data_index 13; roll_shift 5; - - // Parallel Round 4 - par_round4: h_ordering 2, 3, 4, 0, 1; data_index 8; roll_shift 15; - par_round4: h_ordering 1, 2, 3, 4, 0; data_index 6; roll_shift 5; - par_round4: h_ordering 0, 1, 2, 3, 4; data_index 4; roll_shift 8; - par_round4: h_ordering 4, 0, 1, 2, 3; data_index 1; roll_shift 11; - par_round4: h_ordering 3, 4, 0, 1, 2; data_index 3; roll_shift 14; - par_round4: h_ordering 2, 3, 4, 0, 1; data_index 11; roll_shift 14; - par_round4: h_ordering 1, 2, 3, 4, 0; data_index 15; roll_shift 6; - par_round4: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 14; - par_round4: h_ordering 4, 0, 1, 2, 3; data_index 5; roll_shift 6; - par_round4: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 9; - par_round4: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 12; - par_round4: h_ordering 1, 2, 3, 4, 0; data_index 13; roll_shift 9; - par_round4: h_ordering 0, 1, 2, 3, 4; data_index 9; roll_shift 12; - par_round4: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 5; - par_round4: h_ordering 3, 4, 0, 1, 2; data_index 10; roll_shift 15; - par_round4: h_ordering 2, 3, 4, 0, 1; data_index 14; roll_shift 8; - - // Parallel Round 5 - par_round5: h_ordering 1, 2, 3, 4, 0; data_index 12; roll_shift 8; - par_round5: h_ordering 0, 1, 2, 3, 4; data_index 15; roll_shift 5; - par_round5: h_ordering 4, 0, 1, 2, 3; data_index 10; roll_shift 12; - par_round5: h_ordering 3, 4, 0, 1, 2; data_index 4; roll_shift 9; - par_round5: h_ordering 2, 3, 4, 0, 1; data_index 1; roll_shift 12; - par_round5: h_ordering 1, 2, 3, 4, 0; data_index 5; roll_shift 5; - par_round5: h_ordering 0, 1, 2, 3, 4; data_index 8; roll_shift 14; - par_round5: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 6; - par_round5: h_ordering 3, 4, 0, 1, 2; data_index 6; roll_shift 8; - par_round5: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 13; - par_round5: h_ordering 1, 2, 3, 4, 0; data_index 13; roll_shift 6; - par_round5: h_ordering 0, 1, 2, 3, 4; data_index 14; roll_shift 5; - par_round5: h_ordering 4, 0, 1, 2, 3; data_index 0; roll_shift 15; - par_round5: h_ordering 3, 4, 0, 1, 2; data_index 3; roll_shift 13; - par_round5: h_ordering 2, 3, 4, 0, 1; data_index 9; roll_shift 11; - par_round5: h_ordering 1, 2, 3, 4, 0; data_index 11; roll_shift 11; - ); - } -} - -#[cfg(test)] -mod tests { - #[test] - fn test() { - use std::convert::TryFrom; - - use crate::{ripemd160, Hash, HashEngine}; - - #[derive(Clone)] - struct Test { - input: &'static str, - output: Vec, - output_str: &'static str, - } - - #[rustfmt::skip] - let tests = vec![ - // Test messages from FIPS 180-1 - Test { - input: "abc", - output: vec![ - 0x8e, 0xb2, 0x08, 0xf7, - 0xe0, 0x5d, 0x98, 0x7a, - 0x9b, 0x04, 0x4a, 0x8e, - 0x98, 0xc6, 0xb0, 0x87, - 0xf1, 0x5a, 0x0b, 0xfc, - ], - output_str: "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc" - }, - Test { - input: - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - output: vec![ - 0x12, 0xa0, 0x53, 0x38, - 0x4a, 0x9c, 0x0c, 0x88, - 0xe4, 0x05, 0xa0, 0x6c, - 0x27, 0xdc, 0xf4, 0x9a, - 0xda, 0x62, 0xeb, 0x2b, - ], - output_str: "12a053384a9c0c88e405a06c27dcf49ada62eb2b" - }, - // Examples from wikipedia - Test { - input: "The quick brown fox jumps over the lazy dog", - output: vec![ - 0x37, 0xf3, 0x32, 0xf6, - 0x8d, 0xb7, 0x7b, 0xd9, - 0xd7, 0xed, 0xd4, 0x96, - 0x95, 0x71, 0xad, 0x67, - 0x1c, 0xf9, 0xdd, 0x3b, - ], - output_str: "37f332f68db77bd9d7edd4969571ad671cf9dd3b", - }, - Test { - input: "The quick brown fox jumps over the lazy cog", - output: vec![ - 0x13, 0x20, 0x72, 0xdf, - 0x69, 0x09, 0x33, 0x83, - 0x5e, 0xb8, 0xb6, 0xad, - 0x0b, 0x77, 0xe7, 0xb6, - 0xf1, 0x4a, 0xca, 0xd7, - ], - output_str: "132072df690933835eb8b6ad0b77e7b6f14acad7", - }, - ]; - - for mut test in tests { - // Hash through high-level API, check hex encoding/decoding - let hash = ripemd160::Hash::hash(test.input.as_bytes()); - assert_eq!(hash, test.output_str.parse::().expect("parse hex")); - assert_eq!(&hash[..], &test.output[..]); - assert_eq!(&hash.to_string(), &test.output_str); - assert_eq!( - ripemd160::Hash::from_bytes_ref( - <&[u8; 20]>::try_from(&*test.output).expect("known length") - ), - &hash - ); - assert_eq!( - ripemd160::Hash::from_bytes_mut( - <&mut [u8; 20]>::try_from(&mut *test.output).expect("known length") - ), - &hash - ); - - // Hash through engine, checking that we can input byte by byte - let mut engine = ripemd160::Hash::engine(); - for ch in test.input.as_bytes() { - engine.input(&[*ch]); - } - let manual_hash = ripemd160::Hash::from_engine(engine); - assert_eq!(hash, manual_hash); - assert_eq!(hash.as_byte_array(), test.output.as_slice()); - } - } - - #[cfg(feature = "serde")] - #[test] - fn ripemd_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - use crate::{ripemd160, Hash}; - - #[rustfmt::skip] - static HASH_BYTES: [u8; 20] = [ - 0x13, 0x20, 0x72, 0xdf, - 0x69, 0x09, 0x33, 0x83, - 0x5e, 0xb8, 0xb6, 0xad, - 0x0b, 0x77, 0xe7, 0xb6, - 0xf1, 0x4a, 0xca, 0xd7, - ]; - - let hash = ripemd160::Hash::from_slice(&HASH_BYTES).expect("right number of bytes"); - assert_tokens(&hash.compact(), &[Token::BorrowedBytes(&HASH_BYTES[..])]); - assert_tokens(&hash.readable(), &[Token::Str("132072df690933835eb8b6ad0b77e7b6f14acad7")]); - } -} diff --git a/hashes/src/serde_macros.rs b/hashes/src/serde_macros.rs index 2f098c6db..ba94298d0 100644 --- a/hashes/src/serde_macros.rs +++ b/hashes/src/serde_macros.rs @@ -27,11 +27,10 @@ pub mod serde_details { /// Single visitor that accepts every shape a hash can arrive in: an ASCII /// hex string (`visit_str`), a UTF-8 byte slice that decodes as hex - /// (`visit_bytes` of length `2*N` — note `N` is in BYTES per the macro, - /// see `serde_impl!` invocation in `internal_macros.rs`), a raw byte - /// slice of the hash's length-in-bytes (`visit_bytes` of length `N`), - /// or a length-prefixed sequence of `u8` from non-self-describing - /// formats (`visit_seq`, used by bincode). + /// (`visit_bytes` of length `2*N`, where `N` is in bytes as passed to + /// `serde_impl!`), a raw byte slice of the hash's length-in-bytes + /// (`visit_bytes` of length `N`), or a length-prefixed sequence of `u8` + /// from non-self-describing formats (`visit_seq`, used by bincode). /// /// Required to interoperate with serde's `ContentDeserializer`, the /// format-agnostic intermediate buffer serde uses to dispatch @@ -78,10 +77,9 @@ pub mod serde_details { E: de::Error, { // Disambiguate by length. A correctly-sized raw hash byte string - // is exactly `N` bytes (`N` is in bytes per the macro — see - // `serde_impl!` invocation in `internal_macros.rs`); a hex-encoded - // form of that hash is `2*N` ASCII bytes. Any other length is - // rejected. + // is exactly `N` bytes (`N` is in bytes as passed to + // `serde_impl!`); a hex-encoded form of that hash is `2*N` ASCII + // bytes. Any other length is rejected. let raw_len_bytes = ValueT::N; let hex_len_bytes = raw_len_bytes * 2; if v.len() == raw_len_bytes { diff --git a/hashes/src/sha1.rs b/hashes/src/sha1.rs deleted file mode 100644 index 6bcaf0616..000000000 --- a/hashes/src/sha1.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -//! SHA1 implementation. -//! - -use core::ops::Index; -use core::slice::SliceIndex; -use core::{cmp, str}; - -use crate::{Error, HashEngine as _}; - -crate::internal_macros::hash_type! { - 160, - false, - "Output of the SHA1 hash function.", - "crate::util::json_hex_string::len_20" -} - -fn from_engine(mut e: HashEngine) -> Hash { - // pad buffer with a single 1-bit then all 0s, until there are exactly 8 bytes remaining - let data_len = e.length as u64; - - let zeroes = [0; BLOCK_SIZE - 8]; - e.input(&[0x80]); - if e.length % BLOCK_SIZE > zeroes.len() { - e.input(&zeroes); - } - let pad_length = zeroes.len() - (e.length % BLOCK_SIZE); - e.input(&zeroes[..pad_length]); - debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len()); - - e.input(&(8 * data_len).to_be_bytes()); - debug_assert_eq!(e.length % BLOCK_SIZE, 0); - - Hash(e.midstate()) -} - -const BLOCK_SIZE: usize = 64; - -/// Engine to compute SHA1 hash function. -#[derive(Clone)] -pub struct HashEngine { - buffer: [u8; BLOCK_SIZE], - h: [u32; 5], - length: usize, -} - -impl Default for HashEngine { - fn default() -> Self { - HashEngine { - h: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0], - length: 0, - buffer: [0; BLOCK_SIZE], - } - } -} - -impl crate::HashEngine for HashEngine { - type MidState = [u8; 20]; - - #[cfg(not(fuzzing))] - fn midstate(&self) -> [u8; 20] { - let mut ret = [0; 20]; - for (val, ret_bytes) in self.h.iter().zip(ret.as_chunks_mut::<4>().0) { - *ret_bytes = val.to_be_bytes() - } - ret - } - - #[cfg(fuzzing)] - fn midstate(&self) -> [u8; 20] { - let mut ret = [0; 20]; - ret.copy_from_slice(&self.buffer[..20]); - ret - } - - const BLOCK_SIZE: usize = 64; - - fn n_bytes_hashed(&self) -> usize { - self.length - } - - engine_input_impl!(); -} - -impl HashEngine { - // Basic unoptimized algorithm from Wikipedia - fn process_block(&mut self) { - debug_assert_eq!(self.buffer.len(), BLOCK_SIZE); - - let mut w = [0u32; 80]; - for (w_val, buff_bytes) in w.iter_mut().zip(self.buffer.as_chunks::<4>().0) { - *w_val = u32::from_be_bytes(*buff_bytes) - } - for i in 16..80 { - w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1); - } - - let mut a = self.h[0]; - let mut b = self.h[1]; - let mut c = self.h[2]; - let mut d = self.h[3]; - let mut e = self.h[4]; - - for (i, &wi) in w.iter().enumerate() { - let (f, k) = match i { - 0..=19 => ((b & c) | (!b & d), 0x5a827999), - 20..=39 => (b ^ c ^ d, 0x6ed9eba1), - 40..=59 => ((b & c) | (b & d) | (c & d), 0x8f1bbcdc), - 60..=79 => (b ^ c ^ d, 0xca62c1d6), - _ => unreachable!(), - }; - - let new_a = - a.rotate_left(5).wrapping_add(f).wrapping_add(e).wrapping_add(k).wrapping_add(wi); - e = d; - d = c; - c = b.rotate_left(30); - b = a; - a = new_a; - } - - self.h[0] = self.h[0].wrapping_add(a); - self.h[1] = self.h[1].wrapping_add(b); - self.h[2] = self.h[2].wrapping_add(c); - self.h[3] = self.h[3].wrapping_add(d); - self.h[4] = self.h[4].wrapping_add(e); - } -} - -#[cfg(test)] -mod tests { - #[test] - fn test() { - use crate::{sha1, Hash, HashEngine}; - - #[derive(Clone)] - struct Test { - input: &'static str, - output: Vec, - output_str: &'static str, - } - - #[rustfmt::skip] - let tests = vec![ - // Examples from wikipedia - Test { - input: "", - output: vec![ - 0xda, 0x39, 0xa3, 0xee, - 0x5e, 0x6b, 0x4b, 0x0d, - 0x32, 0x55, 0xbf, 0xef, - 0x95, 0x60, 0x18, 0x90, - 0xaf, 0xd8, 0x07, 0x09, - ], - output_str: "da39a3ee5e6b4b0d3255bfef95601890afd80709" - }, - Test { - input: "The quick brown fox jumps over the lazy dog", - output: vec![ - 0x2f, 0xd4, 0xe1, 0xc6, - 0x7a, 0x2d, 0x28, 0xfc, - 0xed, 0x84, 0x9e, 0xe1, - 0xbb, 0x76, 0xe7, 0x39, - 0x1b, 0x93, 0xeb, 0x12, - ], - output_str: "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - }, - Test { - input: "The quick brown fox jumps over the lazy cog", - output: vec![ - 0xde, 0x9f, 0x2c, 0x7f, - 0xd2, 0x5e, 0x1b, 0x3a, - 0xfa, 0xd3, 0xe8, 0x5a, - 0x0b, 0xd1, 0x7d, 0x9b, - 0x10, 0x0d, 0xb4, 0xb3, - ], - output_str: "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3", - }, - ]; - - for test in tests { - // Hash through high-level API, check hex encoding/decoding - let hash = sha1::Hash::hash(test.input.as_bytes()); - assert_eq!(hash, test.output_str.parse::().expect("parse hex")); - assert_eq!(&hash[..], &test.output[..]); - assert_eq!(&hash.to_string(), &test.output_str); - - // Hash through engine, checking that we can input byte by byte - let mut engine = sha1::Hash::engine(); - for ch in test.input.as_bytes() { - engine.input(&[*ch]); - } - let manual_hash = sha1::Hash::from_engine(engine); - assert_eq!(hash, manual_hash); - assert_eq!(hash.as_byte_array(), test.output.as_slice()); - } - } - - #[cfg(feature = "serde")] - #[test] - fn sha1_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - use crate::{sha1, Hash}; - - #[rustfmt::skip] - static HASH_BYTES: [u8; 20] = [ - 0x13, 0x20, 0x72, 0xdf, - 0x69, 0x09, 0x33, 0x83, - 0x5e, 0xb8, 0xb6, 0xad, - 0x0b, 0x77, 0xe7, 0xb6, - 0xf1, 0x4a, 0xca, 0xd7, - ]; - - let hash = sha1::Hash::from_slice(&HASH_BYTES).expect("right number of bytes"); - assert_tokens(&hash.compact(), &[Token::BorrowedBytes(&HASH_BYTES[..])]); - assert_tokens(&hash.readable(), &[Token::Str("132072df690933835eb8b6ad0b77e7b6f14acad7")]); - } -} diff --git a/hashes/src/sha256.rs b/hashes/src/sha256.rs deleted file mode 100644 index fac3b2737..000000000 --- a/hashes/src/sha256.rs +++ /dev/null @@ -1,725 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -//! SHA256 implementation. -//! - -use core::ops::Index; -use core::slice::SliceIndex; -use core::{cmp, str}; - -use crate::{hex, sha256d, Error, HashEngine as _}; - -crate::internal_macros::hash_type! { - 256, - false, - "Output of the SHA256 hash function.", - "crate::util::json_hex_string::len_32" -} - -#[cfg(not(fuzzing))] -fn from_engine(mut e: HashEngine) -> Hash { - // pad buffer with a single 1-bit then all 0s, until there are exactly 8 bytes remaining - let data_len = e.length as u64; - - let zeroes = [0; BLOCK_SIZE - 8]; - e.input(&[0x80]); - if e.length % BLOCK_SIZE > zeroes.len() { - e.input(&zeroes); - } - let pad_length = zeroes.len() - (e.length % BLOCK_SIZE); - e.input(&zeroes[..pad_length]); - debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len()); - - e.input(&(8 * data_len).to_be_bytes()); - debug_assert_eq!(e.length % BLOCK_SIZE, 0); - - Hash(e.midstate().to_byte_array()) -} - -#[cfg(fuzzing)] -fn from_engine(e: HashEngine) -> Hash { - let mut hash = e.midstate().to_byte_array(); - if hash == [0; 32] { - // Assume sha256 is secure and never generate 0-hashes (which represent invalid - // secp256k1 secret keys, causing downstream application breakage). - hash[0] = 1; - } - Hash(hash) -} - -const BLOCK_SIZE: usize = 64; - -/// Engine to compute SHA256 hash function. -#[derive(Clone)] -pub struct HashEngine { - buffer: [u8; BLOCK_SIZE], - h: [u32; 8], - length: usize, -} - -impl Default for HashEngine { - fn default() -> Self { - HashEngine { - h: [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, - 0x5be0cd19, - ], - length: 0, - buffer: [0; BLOCK_SIZE], - } - } -} - -impl crate::HashEngine for HashEngine { - type MidState = Midstate; - - #[cfg(not(fuzzing))] - fn midstate(&self) -> Midstate { - let mut ret = [0; 32]; - for (val, ret_bytes) in self.h.iter().zip(ret.as_chunks_mut::<4>().0) { - *ret_bytes = val.to_be_bytes(); - } - Midstate(ret) - } - - #[cfg(fuzzing)] - fn midstate(&self) -> Midstate { - let mut ret = [0; 32]; - ret.copy_from_slice(&self.buffer[..32]); - Midstate(ret) - } - - const BLOCK_SIZE: usize = 64; - - fn n_bytes_hashed(&self) -> usize { - self.length - } - - engine_input_impl!(); -} - -impl Hash { - /// Iterate the sha256 algorithm to turn a sha256 hash into a sha256d hash - pub fn hash_again(&self) -> sha256d::Hash { - crate::Hash::from_byte_array(::hash(&self.0).0) - } - - /// Computes hash from `bytes` in `const` context. - /// - /// Warning: this function is inefficient. It should be only used in `const` context. - pub const fn const_hash(bytes: &[u8]) -> Self { - Hash(Midstate::const_hash(bytes, true).0) - } -} - -/// Output of the SHA256 hash function. -#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] -pub struct Midstate(pub [u8; 32]); - -crate::internal_macros::arr_newtype_fmt_impl!(Midstate, 32); -serde_impl!(Midstate, 32); -borrow_slice_impl!(Midstate); - -impl> Index for Midstate { - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - &self.0[index] - } -} - -impl str::FromStr for Midstate { - type Err = hex::Error; - fn from_str(s: &str) -> Result { - hex::FromHex::from_hex(s) - } -} - -impl Midstate { - /// Length of the midstate, in bytes. - const LEN: usize = 32; - - /// Flag indicating whether user-visible serializations of this hash - /// should be backward. For some reason Satoshi decided this should be - /// true for `Sha256dHash`, so here we are. - const DISPLAY_BACKWARD: bool = true; - - /// Construct a new [`Midstate`] from the inner value. - pub const fn from_byte_array(inner: [u8; 32]) -> Self { - Midstate(inner) - } - - /// Copies a byte slice into the [`Midstate`] object. - pub fn from_slice(sl: &[u8]) -> Result { - if sl.len() != Self::LEN { - Err(Error::InvalidLength(Self::LEN, sl.len())) - } else { - let mut ret = [0; 32]; - ret.copy_from_slice(sl); - Ok(Midstate(ret)) - } - } - - /// Unwraps the [`Midstate`] and returns the underlying byte array. - pub fn to_byte_array(self) -> [u8; 32] { - self.0 - } - - /// Creates midstate for tagged hashes. - /// - /// Warning: this function is inefficient. It should be only used in `const` context. - /// - /// Computes non-finalized hash of `sha256(tag) || sha256(tag)` for use in - /// [`sha256t`](super::sha256t). It's provided for use with [`sha256t`](crate::sha256t). - pub const fn hash_tag(tag: &[u8]) -> Self { - let hash = Hash::const_hash(tag); - let mut buf = [0u8; 64]; - let mut i = 0usize; - while i < buf.len() { - buf[i] = hash.0[i % hash.0.len()]; - i += 1; - } - Self::const_hash(&buf, false) - } -} - -impl hex::FromHex for Midstate { - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, - { - // DISPLAY_BACKWARD is true - Ok(Midstate::from_byte_array(hex::FromHex::from_byte_iter(iter.rev())?)) - } -} - -macro_rules! Ch( ($x:expr, $y:expr, $z:expr) => ($z ^ ($x & ($y ^ $z))) ); -macro_rules! Maj( ($x:expr, $y:expr, $z:expr) => (($x & $y) | ($z & ($x | $y))) ); -macro_rules! Sigma0( ($x:expr) => ($x.rotate_left(30) ^ $x.rotate_left(19) ^ $x.rotate_left(10)) ); -macro_rules! Sigma1( ($x:expr) => ( $x.rotate_left(26) ^ $x.rotate_left(21) ^ $x.rotate_left(7)) ); -macro_rules! sigma0( ($x:expr) => ($x.rotate_left(25) ^ $x.rotate_left(14) ^ ($x >> 3)) ); -macro_rules! sigma1( ($x:expr) => ($x.rotate_left(15) ^ $x.rotate_left(13) ^ ($x >> 10)) ); - -macro_rules! round( - // first round - ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $k:expr, $w:expr) => ( - let t1 = $h.wrapping_add(Sigma1!($e)).wrapping_add(Ch!($e, $f, $g)).wrapping_add($k).wrapping_add($w); - let t2 = Sigma0!($a).wrapping_add(Maj!($a, $b, $c)); - $d = $d.wrapping_add(t1); - $h = t1.wrapping_add(t2); - ); - // later rounds we reassign $w before doing the first-round computation - ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $k:expr, $w:expr, $w1:expr, $w2:expr, $w3:expr) => ( - $w = $w.wrapping_add(sigma1!($w1)).wrapping_add($w2).wrapping_add(sigma0!($w3)); - round!($a, $b, $c, $d, $e, $f, $g, $h, $k, $w); - ) -); - -impl Midstate { - #[allow(clippy::identity_op)] // more readable - const fn read_u32(bytes: &[u8], index: usize) -> u32 { - ((bytes[index + 0] as u32) << 24) - | ((bytes[index + 1] as u32) << 16) - | ((bytes[index + 2] as u32) << 8) - | ((bytes[index + 3] as u32) << 0) - } - - const fn copy_w(bytes: &[u8], index: usize) -> [u32; 16] { - let mut w = [0u32; 16]; - let mut i = 0; - while i < 16 { - w[i] = Self::read_u32(bytes, index + i * 4); - i += 1; - } - w - } - - const fn const_hash(bytes: &[u8], finalize: bool) -> Self { - let mut state = [ - 0x6a09e667u32, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19, - ]; - - let num_chunks = (bytes.len() + 9).div_ceil(64); - let mut chunk = 0; - #[allow(clippy::precedence)] - while chunk < num_chunks { - if !finalize && chunk + 1 == num_chunks { - break; - } - let mut w = if chunk * 64 + 64 <= bytes.len() { - Self::copy_w(bytes, chunk * 64) - } else { - let mut buf = [0; 64]; - let mut i = 0; - let offset = chunk * 64; - while offset + i < bytes.len() { - buf[i] = bytes[offset + i]; - i += 1; - } - if (bytes.len() % 64 <= 64 - 9) || (chunk + 2 == num_chunks) { - buf[i] = 0x80; - } - #[allow(clippy::identity_op)] // more readable - #[allow(clippy::erasing_op)] - if chunk + 1 == num_chunks { - let bit_len = bytes.len() as u64 * 8; - buf[64 - 8] = ((bit_len >> 8 * 7) & 0xFF) as u8; - buf[64 - 7] = ((bit_len >> 8 * 6) & 0xFF) as u8; - buf[64 - 6] = ((bit_len >> 8 * 5) & 0xFF) as u8; - buf[64 - 5] = ((bit_len >> 8 * 4) & 0xFF) as u8; - buf[64 - 4] = ((bit_len >> 8 * 3) & 0xFF) as u8; - buf[64 - 3] = ((bit_len >> 8 * 2) & 0xFF) as u8; - buf[64 - 2] = ((bit_len >> 8 * 1) & 0xFF) as u8; - buf[64 - 1] = ((bit_len >> 8 * 0) & 0xFF) as u8; - } - Self::copy_w(&buf, 0) - }; - chunk += 1; - - let mut a = state[0]; - let mut b = state[1]; - let mut c = state[2]; - let mut d = state[3]; - let mut e = state[4]; - let mut f = state[5]; - let mut g = state[6]; - let mut h = state[7]; - - round!(a, b, c, d, e, f, g, h, 0x428a2f98, w[0]); - round!(h, a, b, c, d, e, f, g, 0x71374491, w[1]); - round!(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w[2]); - round!(f, g, h, a, b, c, d, e, 0xe9b5dba5, w[3]); - round!(e, f, g, h, a, b, c, d, 0x3956c25b, w[4]); - round!(d, e, f, g, h, a, b, c, 0x59f111f1, w[5]); - round!(c, d, e, f, g, h, a, b, 0x923f82a4, w[6]); - round!(b, c, d, e, f, g, h, a, 0xab1c5ed5, w[7]); - round!(a, b, c, d, e, f, g, h, 0xd807aa98, w[8]); - round!(h, a, b, c, d, e, f, g, 0x12835b01, w[9]); - round!(g, h, a, b, c, d, e, f, 0x243185be, w[10]); - round!(f, g, h, a, b, c, d, e, 0x550c7dc3, w[11]); - round!(e, f, g, h, a, b, c, d, 0x72be5d74, w[12]); - round!(d, e, f, g, h, a, b, c, 0x80deb1fe, w[13]); - round!(c, d, e, f, g, h, a, b, 0x9bdc06a7, w[14]); - round!(b, c, d, e, f, g, h, a, 0xc19bf174, w[15]); - - round!(a, b, c, d, e, f, g, h, 0xe49b69c1, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0xefbe4786, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x0fc19dc6, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x240ca1cc, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x2de92c6f, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x4a7484aa, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x76f988da, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0x983e5152, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0xa831c66d, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0xb00327c8, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0xbf597fc7, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0xc6e00bf3, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xd5a79147, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0x06ca6351, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0x14292967, w[15], w[13], w[8], w[0]); - - round!(a, b, c, d, e, f, g, h, 0x27b70a85, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0x2e1b2138, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x53380d13, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x650a7354, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x766a0abb, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x81c2c92e, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x92722c85, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0xa81a664b, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0xc24b8b70, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0xc76c51a3, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0xd192e819, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xd6990624, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0xf40e3585, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0x106aa070, w[15], w[13], w[8], w[0]); - - round!(a, b, c, d, e, f, g, h, 0x19a4c116, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0x1e376c08, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x2748774c, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x34b0bcb5, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x391c0cb3, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x5b9cca4f, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x682e6ff3, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0x748f82ee, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0x78a5636f, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0x84c87814, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0x8cc70208, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0x90befffa, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xa4506ceb, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0xbef9a3f7, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0xc67178f2, w[15], w[13], w[8], w[0]); - - state[0] = state[0].wrapping_add(a); - state[1] = state[1].wrapping_add(b); - state[2] = state[2].wrapping_add(c); - state[3] = state[3].wrapping_add(d); - state[4] = state[4].wrapping_add(e); - state[5] = state[5].wrapping_add(f); - state[6] = state[6].wrapping_add(g); - state[7] = state[7].wrapping_add(h); - } - let mut output = [0u8; 32]; - let mut i = 0; - #[allow(clippy::identity_op)] // more readable - while i < 8 { - output[i * 4 + 0] = (state[i + 0] >> 24) as u8; - output[i * 4 + 1] = (state[i + 0] >> 16) as u8; - output[i * 4 + 2] = (state[i + 0] >> 8) as u8; - output[i * 4 + 3] = (state[i + 0] >> 0) as u8; - i += 1; - } - Midstate(output) - } -} - -impl HashEngine { - /// Create a new [`HashEngine`] from a [`Midstate`]. - /// - /// # Panics - /// - /// If `length` is not a multiple of the block size. - pub fn from_midstate(midstate: Midstate, length: usize) -> HashEngine { - assert!(length.is_multiple_of(BLOCK_SIZE), "length is no multiple of the block size"); - - let mut ret = [0; 8]; - for (ret_val, midstate_bytes) in ret.iter_mut().zip(midstate[..].as_chunks::<4>().0) { - *ret_val = u32::from_be_bytes(*midstate_bytes); - } - - HashEngine { - buffer: [0; BLOCK_SIZE], - h: ret, - length, - } - } - - // Algorithm copied from libsecp256k1 - fn process_block(&mut self) { - debug_assert_eq!(self.buffer.len(), BLOCK_SIZE); - - let mut w = [0u32; 16]; - for (w_val, buff_bytes) in w.iter_mut().zip(self.buffer.as_chunks::<4>().0) { - *w_val = u32::from_be_bytes(*buff_bytes); - } - - let mut a = self.h[0]; - let mut b = self.h[1]; - let mut c = self.h[2]; - let mut d = self.h[3]; - let mut e = self.h[4]; - let mut f = self.h[5]; - let mut g = self.h[6]; - let mut h = self.h[7]; - - round!(a, b, c, d, e, f, g, h, 0x428a2f98, w[0]); - round!(h, a, b, c, d, e, f, g, 0x71374491, w[1]); - round!(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w[2]); - round!(f, g, h, a, b, c, d, e, 0xe9b5dba5, w[3]); - round!(e, f, g, h, a, b, c, d, 0x3956c25b, w[4]); - round!(d, e, f, g, h, a, b, c, 0x59f111f1, w[5]); - round!(c, d, e, f, g, h, a, b, 0x923f82a4, w[6]); - round!(b, c, d, e, f, g, h, a, 0xab1c5ed5, w[7]); - round!(a, b, c, d, e, f, g, h, 0xd807aa98, w[8]); - round!(h, a, b, c, d, e, f, g, 0x12835b01, w[9]); - round!(g, h, a, b, c, d, e, f, 0x243185be, w[10]); - round!(f, g, h, a, b, c, d, e, 0x550c7dc3, w[11]); - round!(e, f, g, h, a, b, c, d, 0x72be5d74, w[12]); - round!(d, e, f, g, h, a, b, c, 0x80deb1fe, w[13]); - round!(c, d, e, f, g, h, a, b, 0x9bdc06a7, w[14]); - round!(b, c, d, e, f, g, h, a, 0xc19bf174, w[15]); - - round!(a, b, c, d, e, f, g, h, 0xe49b69c1, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0xefbe4786, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x0fc19dc6, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x240ca1cc, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x2de92c6f, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x4a7484aa, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x76f988da, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0x983e5152, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0xa831c66d, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0xb00327c8, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0xbf597fc7, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0xc6e00bf3, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xd5a79147, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0x06ca6351, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0x14292967, w[15], w[13], w[8], w[0]); - - round!(a, b, c, d, e, f, g, h, 0x27b70a85, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0x2e1b2138, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x53380d13, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x650a7354, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x766a0abb, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x81c2c92e, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x92722c85, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0xa81a664b, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0xc24b8b70, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0xc76c51a3, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0xd192e819, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xd6990624, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0xf40e3585, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0x106aa070, w[15], w[13], w[8], w[0]); - - round!(a, b, c, d, e, f, g, h, 0x19a4c116, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0x1e376c08, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x2748774c, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x34b0bcb5, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x391c0cb3, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x5b9cca4f, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x682e6ff3, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0x748f82ee, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0x78a5636f, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0x84c87814, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0x8cc70208, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0x90befffa, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xa4506ceb, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0xbef9a3f7, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0xc67178f2, w[15], w[13], w[8], w[0]); - - self.h[0] = self.h[0].wrapping_add(a); - self.h[1] = self.h[1].wrapping_add(b); - self.h[2] = self.h[2].wrapping_add(c); - self.h[3] = self.h[3].wrapping_add(d); - self.h[4] = self.h[4].wrapping_add(e); - self.h[5] = self.h[5].wrapping_add(f); - self.h[6] = self.h[6].wrapping_add(g); - self.h[7] = self.h[7].wrapping_add(h); - } -} - -#[cfg(test)] -mod tests { - use crate::{sha256, Hash, HashEngine}; - - #[test] - fn test() { - #[derive(Clone)] - struct Test { - input: &'static str, - output: Vec, - output_str: &'static str, - } - - #[rustfmt::skip] - let tests = vec![ - // Examples from wikipedia - Test { - input: "", - output: vec![ - 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, - 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, - 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, - 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, - ], - output_str: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - }, - Test { - input: "The quick brown fox jumps over the lazy dog", - output: vec![ - 0xd7, 0xa8, 0xfb, 0xb3, 0x07, 0xd7, 0x80, 0x94, - 0x69, 0xca, 0x9a, 0xbc, 0xb0, 0x08, 0x2e, 0x4f, - 0x8d, 0x56, 0x51, 0xe4, 0x6d, 0x3c, 0xdb, 0x76, - 0x2d, 0x02, 0xd0, 0xbf, 0x37, 0xc9, 0xe5, 0x92, - ], - output_str: "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", - }, - Test { - input: "The quick brown fox jumps over the lazy dog.", - output: vec![ - 0xef, 0x53, 0x7f, 0x25, 0xc8, 0x95, 0xbf, 0xa7, - 0x82, 0x52, 0x65, 0x29, 0xa9, 0xb6, 0x3d, 0x97, - 0xaa, 0x63, 0x15, 0x64, 0xd5, 0xd7, 0x89, 0xc2, - 0xb7, 0x65, 0x44, 0x8c, 0x86, 0x35, 0xfb, 0x6c, - ], - output_str: "ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c", - }, - ]; - - for test in tests { - // Hash through high-level API, check hex encoding/decoding - let hash = sha256::Hash::hash(test.input.as_bytes()); - assert_eq!(hash, test.output_str.parse::().expect("parse hex")); - assert_eq!(&hash[..], &test.output[..]); - assert_eq!(&hash.to_string(), &test.output_str); - - // Hash through engine, checking that we can input byte by byte - let mut engine = sha256::Hash::engine(); - for ch in test.input.as_bytes() { - engine.input(&[*ch]); - } - let manual_hash = sha256::Hash::from_engine(engine); - assert_eq!(hash, manual_hash); - assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice()); - } - } - - #[test] - #[rustfmt::skip] - fn midstate() { - // Test vector obtained by doing an asset issuance on Elements - let mut engine = sha256::Hash::engine(); - // sha256dhash of outpoint - // 73828cbc65fd68ab78dc86992b76ae50ae2bf8ceedbe8de0483172f0886219f7:0 - engine.input(&[ - 0x9d, 0xd0, 0x1b, 0x56, 0xb1, 0x56, 0x45, 0x14, - 0x3e, 0xad, 0x15, 0x8d, 0xec, 0x19, 0xf8, 0xce, - 0xa9, 0x0b, 0xd0, 0xa9, 0xb2, 0xf8, 0x1d, 0x21, - 0xff, 0xa3, 0xa4, 0xc6, 0x44, 0x81, 0xd4, 0x1c, - ]); - // 32 bytes of zeroes representing "new asset" - engine.input(&[0; 32]); - assert_eq!( - engine.midstate(), - // RPC output - sha256::Midstate::from_byte_array([ - 0x0b, 0xcf, 0xe0, 0xe5, 0x4e, 0x6c, 0xc7, 0xd3, - 0x4f, 0x4f, 0x7c, 0x1d, 0xf0, 0xb0, 0xf5, 0x03, - 0xf2, 0xf7, 0x12, 0x91, 0x2a, 0x06, 0x05, 0xb4, - 0x14, 0xed, 0x33, 0x7f, 0x7f, 0x03, 0x2e, 0x03, - ]) - ); - } - - #[test] - fn engine_with_state() { - let mut engine = sha256::Hash::engine(); - let midstate_engine = sha256::HashEngine::from_midstate(engine.midstate(), 0); - // Fresh engine and engine initialized with fresh state should have same state - assert_eq!(engine.h, midstate_engine.h); - - // Midstate changes after writing 64 bytes - engine.input(&[1; 63]); - assert_eq!(engine.h, midstate_engine.h); - engine.input(&[2; 1]); - assert_ne!(engine.h, midstate_engine.h); - - // Initializing an engine with midstate from another engine should result in - // both engines producing the same hashes - let data_vec = vec![vec![3; 1], vec![4; 63], vec![5; 65], vec![6; 66]]; - for data in data_vec { - let mut engine = engine.clone(); - let mut midstate_engine = - sha256::HashEngine::from_midstate(engine.midstate(), engine.length); - assert_eq!(engine.h, midstate_engine.h); - assert_eq!(engine.length, midstate_engine.length); - engine.input(&data); - midstate_engine.input(&data); - assert_eq!(engine.h, midstate_engine.h); - let hash1 = sha256::Hash::from_engine(engine); - let hash2 = sha256::Hash::from_engine(midstate_engine); - assert_eq!(hash1, hash2); - } - - // Test that a specific midstate results in a specific hash. Midstate was - // obtained by applying sha256 to sha256("MuSig coefficient")||sha256("MuSig - // coefficient"). - #[rustfmt::skip] - static MIDSTATE: [u8; 32] = [ - 0x0f, 0xd0, 0x69, 0x0c, 0xfe, 0xfe, 0xae, 0x97, - 0x99, 0x6e, 0xac, 0x7f, 0x5c, 0x30, 0xd8, 0x64, - 0x8c, 0x4a, 0x05, 0x73, 0xac, 0xa1, 0xa2, 0x2f, - 0x6f, 0x43, 0xb8, 0x01, 0x85, 0xce, 0x27, 0xcd, - ]; - #[rustfmt::skip] - static HASH_EXPECTED: [u8; 32] = [ - 0x18, 0x84, 0xe4, 0x72, 0x40, 0x4e, 0xf4, 0x5a, - 0xb4, 0x9c, 0x4e, 0xa4, 0x9a, 0xe6, 0x23, 0xa8, - 0x88, 0x52, 0x7f, 0x7d, 0x8a, 0x06, 0x94, 0x20, - 0x8f, 0xf1, 0xf7, 0xa9, 0xd5, 0x69, 0x09, 0x59, - ]; - let midstate_engine = - sha256::HashEngine::from_midstate(sha256::Midstate::from_byte_array(MIDSTATE), 64); - let hash = sha256::Hash::from_engine(midstate_engine); - assert_eq!(hash, sha256::Hash(HASH_EXPECTED)); - } - - #[test] - fn const_hash() { - assert_eq!(super::Hash::hash(&[]), super::Hash::const_hash(&[])); - - let mut bytes = Vec::new(); - for i in 0..256 { - bytes.push(i as u8); - assert_eq!( - super::Hash::hash(&bytes), - super::Hash::const_hash(&bytes), - "hashes don't match for length {}", - i + 1 - ); - } - } - - #[test] - fn const_midstate() { - use super::Midstate; - - assert_eq!( - Midstate::hash_tag(b"TapLeaf"), - Midstate([ - 156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, - 243, 147, 108, 71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201, - ]) - ) - } - - #[cfg(feature = "serde")] - #[test] - fn sha256_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - #[rustfmt::skip] - static HASH_BYTES: [u8; 32] = [ - 0xef, 0x53, 0x7f, 0x25, 0xc8, 0x95, 0xbf, 0xa7, - 0x82, 0x52, 0x65, 0x29, 0xa9, 0xb6, 0x3d, 0x97, - 0xaa, 0x63, 0x15, 0x64, 0xd5, 0xd7, 0x89, 0xc2, - 0xb7, 0x65, 0x44, 0x8c, 0x86, 0x35, 0xfb, 0x6c, - ]; - - let hash = sha256::Hash::from_slice(&HASH_BYTES).expect("right number of bytes"); - assert_tokens(&hash.compact(), &[Token::BorrowedBytes(&HASH_BYTES[..])]); - assert_tokens( - &hash.readable(), - &[Token::Str("ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c")], - ); - } - - #[cfg(target_arch = "wasm32")] - mod wasm_tests { - extern crate wasm_bindgen_test; - use self::wasm_bindgen_test::*; - use super::*; - #[wasm_bindgen_test] - fn sha256_tests() { - test(); - midstate(); - engine_with_state(); - } - } -} diff --git a/hashes/src/sha256d.rs b/hashes/src/sha256d.rs deleted file mode 100644 index 8bbd5755f..000000000 --- a/hashes/src/sha256d.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -//! SHA256d implementation (double SHA256). -//! - -use core::ops::Index; -use core::slice::SliceIndex; -use core::str; - -use crate::{sha256, Error}; - -crate::internal_macros::hash_type! { - 256, - true, - "Output of the SHA256d hash function.", - "crate::util::json_hex_string::len_32" -} - -type HashEngine = sha256::HashEngine; - -fn from_engine(e: sha256::HashEngine) -> Hash { - use crate::Hash as _; - - let sha2 = sha256::Hash::from_engine(e); - let sha2d = sha256::Hash::hash(&sha2[..]); - - let mut ret = [0; 32]; - ret.copy_from_slice(&sha2d[..]); - Hash(ret) -} - -#[cfg(test)] -mod tests { - #[test] - fn test() { - use crate::{sha256, sha256d, Hash, HashEngine}; - - #[derive(Clone)] - struct Test { - input: &'static str, - output: Vec, - output_str: &'static str, - } - - #[rustfmt::skip] - let tests = vec![ - // Test vector copied out of rust-dash - Test { - input: "", - output: vec![ - 0x5d, 0xf6, 0xe0, 0xe2, 0x76, 0x13, 0x59, 0xd3, - 0x0a, 0x82, 0x75, 0x05, 0x8e, 0x29, 0x9f, 0xcc, - 0x03, 0x81, 0x53, 0x45, 0x45, 0xf5, 0x5c, 0xf4, - 0x3e, 0x41, 0x98, 0x3f, 0x5d, 0x4c, 0x94, 0x56, - ], - output_str: "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d", - }, - ]; - - for test in tests { - // Hash through high-level API, check hex encoding/decoding - let hash = sha256d::Hash::hash(test.input.as_bytes()); - assert_eq!(hash, test.output_str.parse::().expect("parse hex")); - assert_eq!(&hash[..], &test.output[..]); - assert_eq!(&hash.to_string(), &test.output_str); - - // Hash through engine, checking that we can input byte by byte - let mut engine = sha256d::Hash::engine(); - for ch in test.input.as_bytes() { - engine.input(&[*ch]); - } - let manual_hash = sha256d::Hash::from_engine(engine); - assert_eq!(hash, manual_hash); - - // Hash by computing a sha256 then `hash_again`ing it - let sha2_hash = sha256::Hash::hash(test.input.as_bytes()); - let sha2d_hash = sha2_hash.hash_again(); - assert_eq!(hash, sha2d_hash); - - assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice()); - } - } - - #[cfg(feature = "serde")] - #[test] - fn sha256_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - use crate::{sha256d, Hash}; - - #[rustfmt::skip] - static HASH_BYTES: [u8; 32] = [ - 0xef, 0x53, 0x7f, 0x25, 0xc8, 0x95, 0xbf, 0xa7, - 0x82, 0x52, 0x65, 0x29, 0xa9, 0xb6, 0x3d, 0x97, - 0xaa, 0x63, 0x15, 0x64, 0xd5, 0xd7, 0x89, 0xc2, - 0xb7, 0x65, 0x44, 0x8c, 0x86, 0x35, 0xfb, 0x6c, - ]; - - let hash = sha256d::Hash::from_slice(&HASH_BYTES).expect("right number of bytes"); - assert_tokens(&hash.compact(), &[Token::BorrowedBytes(&HASH_BYTES[..])]); - assert_tokens( - &hash.readable(), - &[Token::Str("6cfb35868c4465b7c289d7d5641563aa973db6a929655282a7bf95c8257f53ef")], - ); - } -} diff --git a/hashes/src/sha256t.rs b/hashes/src/sha256t.rs index d2e1b0c06..29a92172c 100644 --- a/hashes/src/sha256t.rs +++ b/hashes/src/sha256t.rs @@ -1,100 +1,19 @@ -// Bitcoin Hashes Library -// Written in 2019 by -// The rust-dash developers. // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . +// This file is a part of rust-dashcore. Contains portions from rust-bitcoin. +// SPDX-License-Identifier: CC0-1.0 +// See the accompanying file LICENSE or https://creativecommons.org/publicdomain/zero/1.0 // //! SHA256t implementation (tagged SHA256). -//! - -use core::marker::PhantomData; -use core::ops::Index; -use core::slice::SliceIndex; -use core::{cmp, str}; - -use crate::{sha256, Error}; - -type HashEngine = sha256::HashEngine; - -/// Trait representing a tag that can be used as a context for SHA256t hashes. -pub trait Tag { - /// Returns a hash engine that is pre-tagged and is ready to be used for the data. - fn engine() -> sha256::HashEngine; -} - -/// Output of the SHA256t hash function. -#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[repr(transparent)] -pub struct Hash( - #[cfg_attr( - feature = "schemars", - schemars(schema_with = "crate::util::json_hex_string::len_32") - )] - [u8; 32], - #[cfg_attr(feature = "schemars", schemars(skip))] PhantomData, -); - -impl Hash { - fn internal_new(arr: [u8; 32]) -> Self { - Hash(arr, Default::default()) - } - fn internal_engine() -> HashEngine { - T::engine() - } -} - -impl Copy for Hash {} -impl Clone for Hash { - fn clone(&self) -> Self { - *self - } -} -impl PartialEq for Hash { - fn eq(&self, other: &Hash) -> bool { - self.0 == other.0 - } -} -impl Eq for Hash {} -impl Default for Hash { - fn default() -> Self { - Hash([0; 32], PhantomData) - } -} -impl PartialOrd for Hash { - fn partial_cmp(&self, other: &Hash) -> Option { - Some(cmp::Ord::cmp(self, other)) - } -} -impl Ord for Hash { - fn cmp(&self, other: &Hash) -> cmp::Ordering { - cmp::Ord::cmp(&self.0, &other.0) - } -} -impl core::hash::Hash for Hash { - fn hash(&self, h: &mut H) { - self.0.hash(h) - } -} - -crate::internal_macros::hash_trait_impls!(256, true, T: Tag); - -fn from_engine(e: sha256::HashEngine) -> Hash { - use crate::Hash as _; - - Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array()) -} +pub use bitcoin_hashes::sha256t::{Hash, Tag}; /// Macro used to define a newtype tagged hash. /// +/// A copy of upstream's, differing only in expanding to this crate's +/// [`hash_newtype`](crate::hash_newtype): tagged hashes need the `bincode` and +/// any-shape `serde` surface that upstream's does not generate. +/// /// This macro creates two types: /// /// * a tag struct @@ -154,77 +73,39 @@ macro_rules! sha256t_hash_newtype { } } -// Workaround macros being unavailable in attributes. -#[doc(hidden)] -#[macro_export] -macro_rules! sha256t_hash_newtype_tag { - ($vis:vis, $tag:ident, $name:expr, $(#[$($attr:meta)*])*) => { - #[doc = "The tag used for [`"] - #[doc = $name] - #[doc = "`]\n\n"] - $(#[$($attr)*])* - #[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] - $vis struct $tag; - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! sha256t_hash_newtype_tag_constructor { - (hash_str, $value:expr) => { - ($crate::sha256::Midstate::hash_tag($value.as_bytes()), 64) - }; - (hash_bytes, $value:expr) => { - ($crate::sha256::Midstate::hash_tag($value), 64) - }; - (raw, $bytes:expr, $len:expr) => { - ($crate::sha256::Midstate::from_byte_array($bytes), $len) - }; -} - #[cfg(test)] mod tests { - use crate::Hash; - use crate::{sha256, sha256t}; + use crate::{sha256, sha256t, Hash}; + // The TapLeaf midstate. const TEST_MIDSTATE: [u8; 32] = [ 156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, 243, 147, 108, 71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201, ]; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] - #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct TestHashTag; impl sha256t::Tag for TestHashTag { fn engine() -> sha256::HashEngine { - // The TapRoot TapLeaf midstate. let midstate = sha256::Midstate::from_byte_array(TEST_MIDSTATE); sha256::HashEngine::from_midstate(midstate, 64) } } - /// A hash tagged with `$name`. - pub type TestHash = sha256t::Hash; + type TestHash = sha256t::Hash; sha256t_hash_newtype! { - /// Test detailed explanation. struct NewTypeTag = raw(TEST_MIDSTATE, 64); - /// A test hash. #[hash_newtype(backward)] struct NewTypeHash(_); } #[test] fn test_sha256t() { - assert_eq!( - TestHash::hash(&[0]).to_string(), - "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed" - ); - assert_eq!( - NewTypeHash::hash(&[0]).to_string(), - "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed" - ); + let expected = "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"; + assert_eq!(TestHash::hash(&[0]).to_string(), expected); + assert_eq!(NewTypeHash::hash(&[0]).to_string(), expected); } } diff --git a/hashes/src/sha512.rs b/hashes/src/sha512.rs deleted file mode 100644 index 4c02a3865..000000000 --- a/hashes/src/sha512.rs +++ /dev/null @@ -1,377 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2018 by -// Andrew Poelstra -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -// This module is largely copied from the rust-crypto ripemd.rs file; -// while rust-crypto is licensed under Apache, that file specifically -// was written entirely by Andrew Poelstra, who is re-licensing its -// contents here as CC0. - -//! SHA512 implementation. -//! - -use core::ops::Index; -use core::slice::SliceIndex; -use core::{cmp, str}; - -use crate::{Error, HashEngine as _}; - -crate::internal_macros::hash_trait_impls!(512, false); - -pub(crate) const BLOCK_SIZE: usize = 128; - -/// Engine to compute SHA512 hash function. -#[derive(Clone)] -pub struct HashEngine { - pub(crate) h: [u64; 8], - pub(crate) length: usize, - pub(crate) buffer: [u8; BLOCK_SIZE], -} - -impl Default for HashEngine { - #[rustfmt::skip] - fn default() -> Self { - HashEngine { - h: [ - 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, - ], - length: 0, - buffer: [0; BLOCK_SIZE], - } - } -} - -impl crate::HashEngine for HashEngine { - type MidState = [u8; 64]; - - #[cfg(not(fuzzing))] - fn midstate(&self) -> [u8; 64] { - let mut ret = [0; 64]; - for (val, ret_bytes) in self.h.iter().zip(ret.as_chunks_mut::<8>().0) { - *ret_bytes = val.to_be_bytes(); - } - ret - } - - #[cfg(fuzzing)] - fn midstate(&self) -> [u8; 64] { - let mut ret = [0; 64]; - ret.copy_from_slice(&self.buffer[..64]); - ret - } - - const BLOCK_SIZE: usize = 128; - - fn n_bytes_hashed(&self) -> usize { - self.length - } - - engine_input_impl!(); -} - -/// Output of the SHA512 hash function. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[repr(transparent)] -pub struct Hash( - #[cfg_attr( - feature = "schemars", - schemars(schema_with = "crate::util::json_hex_string::len_64") - )] - [u8; 64], -); - -impl Hash { - fn internal_new(arr: [u8; 64]) -> Self { - Hash(arr) - } - - fn internal_engine() -> HashEngine { - Default::default() - } -} - -#[cfg(not(fuzzing))] -pub(crate) fn from_engine(mut e: HashEngine) -> Hash { - // pad buffer with a single 1-bit then all 0s, until there are exactly 16 bytes remaining - let data_len = e.length as u64; - - let zeroes = [0; BLOCK_SIZE - 16]; - e.input(&[0x80]); - if e.length % BLOCK_SIZE > zeroes.len() { - e.input(&zeroes); - } - let pad_length = zeroes.len() - (e.length % BLOCK_SIZE); - e.input(&zeroes[..pad_length]); - debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len()); - - e.input(&[0; 8]); - e.input(&(8 * data_len).to_be_bytes()); - debug_assert_eq!(e.length % BLOCK_SIZE, 0); - - Hash(e.midstate()) -} - -#[cfg(fuzzing)] -pub(crate) fn from_engine(e: HashEngine) -> Hash { - let mut hash = e.midstate(); - hash[0] ^= 0xff; // Make this distinct from SHA-256 - Hash(hash) -} - -macro_rules! Ch( ($x:expr, $y:expr, $z:expr) => ($z ^ ($x & ($y ^ $z))) ); -macro_rules! Maj( ($x:expr, $y:expr, $z:expr) => (($x & $y) | ($z & ($x | $y))) ); -macro_rules! Sigma0( ($x:expr) => ($x.rotate_left(36) ^ $x.rotate_left(30) ^ $x.rotate_left(25)) ); -macro_rules! Sigma1( ($x:expr) => ($x.rotate_left(50) ^ $x.rotate_left(46) ^ $x.rotate_left(23)) ); -macro_rules! sigma0( ($x:expr) => ($x.rotate_left(63) ^ $x.rotate_left(56) ^ ($x >> 7)) ); -macro_rules! sigma1( ($x:expr) => ($x.rotate_left(45) ^ $x.rotate_left(3) ^ ($x >> 6)) ); - -macro_rules! round( - // first round - ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $k:expr, $w:expr) => ( - let t1 = $h.wrapping_add(Sigma1!($e)).wrapping_add(Ch!($e, $f, $g)).wrapping_add($k).wrapping_add($w); - let t2 = Sigma0!($a).wrapping_add(Maj!($a, $b, $c)); - $d = $d.wrapping_add(t1); - $h = t1.wrapping_add(t2); - ); - // later rounds we reassign $w before doing the first-round computation - ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $k:expr, $w:expr, $w1:expr, $w2:expr, $w3:expr) => ( - $w = $w.wrapping_add(sigma1!($w1)).wrapping_add($w2).wrapping_add(sigma0!($w3)); - round!($a, $b, $c, $d, $e, $f, $g, $h, $k, $w); - ) -); - -impl HashEngine { - // Algorithm copied from libsecp256k1 - pub(crate) fn process_block(&mut self) { - debug_assert_eq!(self.buffer.len(), BLOCK_SIZE); - - let mut w = [0u64; 16]; - for (w_val, buff_bytes) in w.iter_mut().zip(self.buffer.as_chunks::<8>().0) { - *w_val = u64::from_be_bytes(*buff_bytes); - } - - let mut a = self.h[0]; - let mut b = self.h[1]; - let mut c = self.h[2]; - let mut d = self.h[3]; - let mut e = self.h[4]; - let mut f = self.h[5]; - let mut g = self.h[6]; - let mut h = self.h[7]; - - round!(a, b, c, d, e, f, g, h, 0x428a2f98d728ae22, w[0]); - round!(h, a, b, c, d, e, f, g, 0x7137449123ef65cd, w[1]); - round!(g, h, a, b, c, d, e, f, 0xb5c0fbcfec4d3b2f, w[2]); - round!(f, g, h, a, b, c, d, e, 0xe9b5dba58189dbbc, w[3]); - round!(e, f, g, h, a, b, c, d, 0x3956c25bf348b538, w[4]); - round!(d, e, f, g, h, a, b, c, 0x59f111f1b605d019, w[5]); - round!(c, d, e, f, g, h, a, b, 0x923f82a4af194f9b, w[6]); - round!(b, c, d, e, f, g, h, a, 0xab1c5ed5da6d8118, w[7]); - round!(a, b, c, d, e, f, g, h, 0xd807aa98a3030242, w[8]); - round!(h, a, b, c, d, e, f, g, 0x12835b0145706fbe, w[9]); - round!(g, h, a, b, c, d, e, f, 0x243185be4ee4b28c, w[10]); - round!(f, g, h, a, b, c, d, e, 0x550c7dc3d5ffb4e2, w[11]); - round!(e, f, g, h, a, b, c, d, 0x72be5d74f27b896f, w[12]); - round!(d, e, f, g, h, a, b, c, 0x80deb1fe3b1696b1, w[13]); - round!(c, d, e, f, g, h, a, b, 0x9bdc06a725c71235, w[14]); - round!(b, c, d, e, f, g, h, a, 0xc19bf174cf692694, w[15]); - - round!(a, b, c, d, e, f, g, h, 0xe49b69c19ef14ad2, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0xefbe4786384f25e3, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x0fc19dc68b8cd5b5, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x240ca1cc77ac9c65, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x2de92c6f592b0275, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x4a7484aa6ea6e483, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x5cb0a9dcbd41fbd4, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x76f988da831153b5, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0x983e5152ee66dfab, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0xa831c66d2db43210, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0xb00327c898fb213f, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0xbf597fc7beef0ee4, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0xc6e00bf33da88fc2, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xd5a79147930aa725, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0x06ca6351e003826f, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0x142929670a0e6e70, w[15], w[13], w[8], w[0]); - - round!(a, b, c, d, e, f, g, h, 0x27b70a8546d22ffc, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0x2e1b21385c26c926, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x4d2c6dfc5ac42aed, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x53380d139d95b3df, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x650a73548baf63de, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x766a0abb3c77b2a8, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x81c2c92e47edaee6, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x92722c851482353b, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0xa2bfe8a14cf10364, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0xa81a664bbc423001, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0xc24b8b70d0f89791, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0xc76c51a30654be30, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0xd192e819d6ef5218, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xd69906245565a910, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0xf40e35855771202a, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0x106aa07032bbd1b8, w[15], w[13], w[8], w[0]); - - round!(a, b, c, d, e, f, g, h, 0x19a4c116b8d2d0c8, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0x1e376c085141ab53, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0x2748774cdf8eeb99, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0x34b0bcb5e19b48a8, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x391c0cb3c5c95a63, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x4ed8aa4ae3418acb, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x5b9cca4f7763e373, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x682e6ff3d6b2b8a3, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0x748f82ee5defb2fc, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0x78a5636f43172f60, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0x84c87814a1f0ab72, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0x8cc702081a6439ec, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0x90befffa23631e28, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0xa4506cebde82bde9, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0xbef9a3f7b2c67915, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0xc67178f2e372532b, w[15], w[13], w[8], w[0]); - - round!(a, b, c, d, e, f, g, h, 0xca273eceea26619c, w[0], w[14], w[9], w[1]); - round!(h, a, b, c, d, e, f, g, 0xd186b8c721c0c207, w[1], w[15], w[10], w[2]); - round!(g, h, a, b, c, d, e, f, 0xeada7dd6cde0eb1e, w[2], w[0], w[11], w[3]); - round!(f, g, h, a, b, c, d, e, 0xf57d4f7fee6ed178, w[3], w[1], w[12], w[4]); - round!(e, f, g, h, a, b, c, d, 0x06f067aa72176fba, w[4], w[2], w[13], w[5]); - round!(d, e, f, g, h, a, b, c, 0x0a637dc5a2c898a6, w[5], w[3], w[14], w[6]); - round!(c, d, e, f, g, h, a, b, 0x113f9804bef90dae, w[6], w[4], w[15], w[7]); - round!(b, c, d, e, f, g, h, a, 0x1b710b35131c471b, w[7], w[5], w[0], w[8]); - round!(a, b, c, d, e, f, g, h, 0x28db77f523047d84, w[8], w[6], w[1], w[9]); - round!(h, a, b, c, d, e, f, g, 0x32caab7b40c72493, w[9], w[7], w[2], w[10]); - round!(g, h, a, b, c, d, e, f, 0x3c9ebe0a15c9bebc, w[10], w[8], w[3], w[11]); - round!(f, g, h, a, b, c, d, e, 0x431d67c49c100d4c, w[11], w[9], w[4], w[12]); - round!(e, f, g, h, a, b, c, d, 0x4cc5d4becb3e42b6, w[12], w[10], w[5], w[13]); - round!(d, e, f, g, h, a, b, c, 0x597f299cfc657e2a, w[13], w[11], w[6], w[14]); - round!(c, d, e, f, g, h, a, b, 0x5fcb6fab3ad6faec, w[14], w[12], w[7], w[15]); - round!(b, c, d, e, f, g, h, a, 0x6c44198c4a475817, w[15], w[13], w[8], w[0]); - - self.h[0] = self.h[0].wrapping_add(a); - self.h[1] = self.h[1].wrapping_add(b); - self.h[2] = self.h[2].wrapping_add(c); - self.h[3] = self.h[3].wrapping_add(d); - self.h[4] = self.h[4].wrapping_add(e); - self.h[5] = self.h[5].wrapping_add(f); - self.h[6] = self.h[6].wrapping_add(g); - self.h[7] = self.h[7].wrapping_add(h); - } -} - -#[cfg(test)] -mod tests { - #[test] - fn test() { - use crate::{sha512, Hash, HashEngine}; - - #[derive(Clone)] - struct Test { - input: &'static str, - output: Vec, - output_str: &'static str, - } - - #[rustfmt::skip] - let tests = vec![ - // Test vectors computed with `sha512sum` - Test { - input: "", - output: vec![ - 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, - 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, - 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, - 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, - 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, - 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, - 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, - 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e, - ], - output_str: "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" - }, - Test { - input: "The quick brown fox jumps over the lazy dog", - output: vec![ - 0x07, 0xe5, 0x47, 0xd9, 0x58, 0x6f, 0x6a, 0x73, - 0xf7, 0x3f, 0xba, 0xc0, 0x43, 0x5e, 0xd7, 0x69, - 0x51, 0x21, 0x8f, 0xb7, 0xd0, 0xc8, 0xd7, 0x88, - 0xa3, 0x09, 0xd7, 0x85, 0x43, 0x6b, 0xbb, 0x64, - 0x2e, 0x93, 0xa2, 0x52, 0xa9, 0x54, 0xf2, 0x39, - 0x12, 0x54, 0x7d, 0x1e, 0x8a, 0x3b, 0x5e, 0xd6, - 0xe1, 0xbf, 0xd7, 0x09, 0x78, 0x21, 0x23, 0x3f, - 0xa0, 0x53, 0x8f, 0x3d, 0xb8, 0x54, 0xfe, 0xe6, - ], - output_str: "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", - }, - Test { - input: "The quick brown fox jumps over the lazy dog.", - output: vec![ - 0x91, 0xea, 0x12, 0x45, 0xf2, 0x0d, 0x46, 0xae, - 0x9a, 0x03, 0x7a, 0x98, 0x9f, 0x54, 0xf1, 0xf7, - 0x90, 0xf0, 0xa4, 0x76, 0x07, 0xee, 0xb8, 0xa1, - 0x4d, 0x12, 0x89, 0x0c, 0xea, 0x77, 0xa1, 0xbb, - 0xc6, 0xc7, 0xed, 0x9c, 0xf2, 0x05, 0xe6, 0x7b, - 0x7f, 0x2b, 0x8f, 0xd4, 0xc7, 0xdf, 0xd3, 0xa7, - 0xa8, 0x61, 0x7e, 0x45, 0xf3, 0xc4, 0x63, 0xd4, - 0x81, 0xc7, 0xe5, 0x86, 0xc3, 0x9a, 0xc1, 0xed, - ], - output_str: "91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed", - }, - ]; - - for test in tests { - // Hash through high-level API, check hex encoding/decoding - let hash = sha512::Hash::hash(test.input.as_bytes()); - assert_eq!(hash, test.output_str.parse::().expect("parse hex")); - assert_eq!(&hash[..], &test.output[..]); - assert_eq!(&hash.to_string(), &test.output_str); - - // Hash through engine, checking that we can input byte by byte - let mut engine = sha512::Hash::engine(); - for ch in test.input.as_bytes() { - engine.input(&[*ch]); - } - let manual_hash = sha512::Hash::from_engine(engine); - assert_eq!(hash, manual_hash); - assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice()); - } - } - - #[cfg(feature = "serde")] - #[test] - fn sha512_serde() { - use serde_test::{assert_tokens, Configure, Token}; - - use crate::{sha512, Hash}; - - #[rustfmt::skip] - static HASH_BYTES: [u8; 64] = [ - 0x8b, 0x41, 0xe1, 0xb7, 0x8a, 0xd1, 0x15, 0x21, - 0x11, 0x3c, 0x52, 0xff, 0x18, 0x2a, 0x1b, 0x8e, - 0x0a, 0x19, 0x57, 0x54, 0xaa, 0x52, 0x7f, 0xcd, - 0x00, 0xa4, 0x11, 0x62, 0x0b, 0x46, 0xf2, 0x0f, - 0xff, 0xfb, 0x80, 0x88, 0xcc, 0xf8, 0x54, 0x97, - 0x12, 0x1a, 0xd4, 0x49, 0x9e, 0x08, 0x45, 0xb8, - 0x76, 0xf6, 0xdd, 0x66, 0x40, 0x08, 0x8a, 0x2f, - 0x0b, 0x2d, 0x8a, 0x60, 0x0b, 0xdf, 0x4c, 0x0c, - ]; - - let hash = sha512::Hash::from_slice(&HASH_BYTES).expect("right number of bytes"); - assert_tokens(&hash.compact(), &[Token::BorrowedBytes(&HASH_BYTES[..])]); - assert_tokens( - &hash.readable(), - &[Token::Str( - "8b41e1b78ad11521113c52ff182a1b8e0a195754aa527fcd00a411620b46f20f\ - fffb8088ccf85497121ad4499e0845b876f6dd6640088a2f0b2d8a600bdf4c0c", - )], - ); - } -} diff --git a/hashes/src/sha512_256.rs b/hashes/src/sha512_256.rs deleted file mode 100644 index 3d7343f36..000000000 --- a/hashes/src/sha512_256.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Bitcoin Hashes Library -// Written in 2022 by -// The rust-dash developers. -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -// This module is largely copied from the rust-crypto ripemd.rs file; -// while rust-crypto is licensed under Apache, that file specifically -// was written entirely by Andrew Poelstra, who is re-licensing its -// contents here as CC0. - -//! SHA512_256 implementation. -//! -//! SHA512/256 is a hash function that uses the sha512 algorithm but it truncates -//! the output to 256 bits. It has different initial constants than sha512 so it -//! produces an entirely different hash compared to sha512. More information at -//! . - -use core::ops::Index; -use core::slice::SliceIndex; -use core::str; - -use crate::sha512::BLOCK_SIZE; -use crate::{sha512, Error}; - -/// Engine to compute SHA512/256 hash function. -/// -/// SHA512/256 is a hash function that uses the sha512 algorithm but it truncates -/// the output to 256 bits. It has different initial constants than sha512 so it -/// produces an entirely different hash compared to sha512. More information at -/// . -#[derive(Clone)] -pub struct HashEngine(sha512::HashEngine); - -impl Default for HashEngine { - #[rustfmt::skip] - fn default() -> Self { - HashEngine(sha512::HashEngine { - h: [ - 0x22312194fc2bf72c, 0x9f555fa3c84c64c2, 0x2393b86b6f53b151, 0x963877195940eabd, - 0x96283ee2a88effe3, 0xbe5e1e2553863992, 0x2b0199fc2c85b8aa, 0x0eb72ddc81c52ca2, - ], - length: 0, - buffer: [0; BLOCK_SIZE], - }) - } -} - -impl crate::HashEngine for HashEngine { - type MidState = [u8; 64]; - - fn midstate(&self) -> [u8; 64] { - self.0.midstate() - } - - const BLOCK_SIZE: usize = sha512::BLOCK_SIZE; - - fn n_bytes_hashed(&self) -> usize { - self.0.length - } - - fn input(&mut self, inp: &[u8]) { - self.0.input(inp); - } -} - -crate::internal_macros::hash_type! { - 256, - false, - "Output of the SHA512/256 hash function.\n\nSHA512/256 is a hash function that uses the sha512 algorithm but it truncates the output to 256 bits. It has different initial constants than sha512 so it produces an entirely different hash compared to sha512. More information at . ", - "crate::util::json_hex_string::len_32" -} - -fn from_engine(e: HashEngine) -> Hash { - let mut ret = [0; 32]; - ret.copy_from_slice(&sha512::from_engine(e.0)[..32]); - Hash(ret) -} - -#[cfg(test)] -mod tests { - #[test] - fn test() { - use crate::{sha512_256, Hash, HashEngine}; - - #[derive(Clone)] - struct Test { - input: &'static str, - output: Vec, - output_str: &'static str, - } - - #[rustfmt::skip] - let tests = vec![ - // Examples from go sha512/256 tests. - Test { - input: "", - output: vec![ - 0xc6, 0x72, 0xb8, 0xd1, 0xef, 0x56, 0xed, 0x28, - 0xab, 0x87, 0xc3, 0x62, 0x2c, 0x51, 0x14, 0x06, - 0x9b, 0xdd, 0x3a, 0xd7, 0xb8, 0xf9, 0x73, 0x74, - 0x98, 0xd0, 0xc0, 0x1e, 0xce, 0xf0, 0x96, 0x7a, - ], - output_str: "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a" - }, - Test { - input: "abcdef", - output: vec![ - 0xe4, 0xfd, 0xcb, 0x11, 0xd1, 0xac, 0x14, 0xe6, - 0x98, 0x74, 0x3a, 0xcd, 0x88, 0x05, 0x17, 0x4c, - 0xea, 0x5d, 0xdc, 0x0d, 0x31, 0x2e, 0x3e, 0x47, - 0xf6, 0x37, 0x20, 0x32, 0x57, 0x1b, 0xad, 0x84, - ], - output_str: "e4fdcb11d1ac14e698743acd8805174cea5ddc0d312e3e47f6372032571bad84", - }, - Test { - input: "Discard medicine more than two years old.", - output: vec![ - 0x69, 0x0c, 0x8a, 0xd3, 0x91, 0x6c, 0xef, 0xd3, - 0xad, 0x29, 0x22, 0x6d, 0x98, 0x75, 0x96, 0x5e, - 0x3e, 0xe9, 0xec, 0x0d, 0x44, 0x82, 0xea, 0xcc, - 0x24, 0x8f, 0x2f, 0xf4, 0xaa, 0x0d, 0x8e, 0x5b, - ], - output_str: "690c8ad3916cefd3ad29226d9875965e3ee9ec0d4482eacc248f2ff4aa0d8e5b", - }, - Test { - input: "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", - output: vec![ - 0xb5, 0xba, 0xf7, 0x47, 0xc3, 0x07, 0xf9, 0x88, - 0x49, 0xec, 0x88, 0x1c, 0xf0, 0xd4, 0x86, 0x05, - 0xae, 0x4e, 0xdd, 0x38, 0x63, 0x72, 0xae, 0xa9, - 0xb2, 0x6e, 0x71, 0xdb, 0x51, 0x7e, 0x65, 0x0b, - ], - output_str: "b5baf747c307f98849ec881cf0d48605ae4edd386372aea9b26e71db517e650b", - }, - Test { - input: "The major problem is with sendmail. -Mark Horton", - output: vec![ - 0x53, 0xed, 0x5f, 0x9b, 0x5c, 0x0b, 0x67, 0x4a, - 0xc0, 0xf3, 0x42, 0x5d, 0x9f, 0x9a, 0x5d, 0x46, - 0x26, 0x55, 0xb0, 0x7c, 0xc9, 0x0f, 0x5d, 0x0f, - 0x69, 0x2e, 0xec, 0x09, 0x38, 0x84, 0xa6, 0x07, - ], - output_str: "53ed5f9b5c0b674ac0f3425d9f9a5d462655b07cc90f5d0f692eec093884a607", - }, - ]; - - for test in tests { - // Hash through high-level API, check hex encoding/decoding - let hash = sha512_256::Hash::hash(test.input.as_bytes()); - assert_eq!(hash, test.output_str.parse::().expect("parse hex")); - assert_eq!(&hash[..], &test.output[..]); - assert_eq!(&hash.to_string(), &test.output_str); - - // Hash through engine, checking that we can input byte by byte - let mut engine = sha512_256::Hash::engine(); - for ch in test.input.as_bytes() { - engine.0.input(&[*ch]); - } - let manual_hash = sha512_256::Hash::from_engine(engine); - assert_eq!(hash, manual_hash); - assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice()); - } - } -} diff --git a/hashes/src/siphash24.rs b/hashes/src/siphash24.rs index 72c6c9340..84b55d3d8 100644 --- a/hashes/src/siphash24.rs +++ b/hashes/src/siphash24.rs @@ -1,54 +1,31 @@ -// Bitcoin Hashes Library -// Written in 2019 by -// The rust-dash developers // -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . +// This file is a part of rust-dashcore. +// Portions written by Steven Roose for rust-siphash. +// SPDX-License-Identifier: CC0-1.0 +// See the accompanying file LICENSE or https://creativecommons.org/publicdomain/zero/1.0 // -// This module is largely copied from the rust-siphash sip.rs file; -// while rust-siphash is licensed under Apache, that file specifically -// was written entirely by Steven Roose, who is re-licensing its -// contents here as CC0. +// `sip_round_scalar`, `load_int_le!` and `u8to64_le` are copied from the +// rust-siphash sip.rs file; while rust-siphash is licensed under Apache, that +// file specifically was written entirely by Steven Roose, who is re-licensing +// its contents here as CC0. The batch interface and its AVX2 and NEON kernels +// are original to rust-dashcore. -//! SipHash 2-4 implementation. +//! SipHash 2-4, batched. //! +//! The hash type itself comes from `bitcoin_hashes`; what lives here is the +//! fixed-length batch interface, which upstream has no equivalent of. SipRound +//! is serial within a message, so the kernels put one message per lane and keep +//! several register-sets in flight to cover the per-message dependency chain. -use core::ops::Index; -use core::slice::SliceIndex; -use core::{cmp, mem, ptr, str}; - -use crate::{Error, Hash as _, HashEngine as _}; +use core::{mem, ptr}; -// Lane-vector types for the per-arch SIMD batchers, so helper signatures stay readable. #[cfg(target_arch = "aarch64")] use core::arch::aarch64::uint64x2_t; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::__m256i; -crate::internal_macros::hash_type! { - 64, - false, - "Output of the SipHash24 hash function.", - "crate::util::json_hex_string::len_8" -} - -#[cfg(not(fuzzing))] -fn from_engine(e: HashEngine) -> Hash { - Hash::from_u64(Hash::from_engine_to_u64(e)) -} - -#[cfg(fuzzing)] -fn from_engine(e: HashEngine) -> Hash { - let state = e.midstate(); - Hash::from_u64(state.v0 ^ state.v1 ^ state.v2 ^ state.v3) -} +pub use bitcoin_hashes::siphash24::{Hash, HashEngine, State}; /// One scalar SipRound over the four state words. The reference the SIMD kernels /// ([`sip_round_avx2`], [`sip_round_neon`]) reproduce lane-for-lane. @@ -91,181 +68,6 @@ macro_rules! load_int_le { }}; } -/// Internal state of the [`HashEngine`]. -#[derive(Debug, Clone)] -pub struct State { - // v0, v2 and v1, v3 show up in pairs in the algorithm, - // and simd implementations of SipHash will use vectors - // of v02 and v13. By placing them in this order in the struct, - // the compiler can pick up on just a few simd optimizations by itself. - v0: u64, - v2: u64, - v1: u64, - v3: u64, -} - -/// Engine to compute the SipHash24 hash function. -#[derive(Debug, Clone)] -pub struct HashEngine { - k0: u64, - k1: u64, - length: usize, // how many bytes we've processed - state: State, // hash State - tail: u64, // unprocessed bytes le - ntail: usize, // how many bytes in tail are valid -} - -impl HashEngine { - /// Creates a new SipHash24 engine with keys. - pub fn with_keys(k0: u64, k1: u64) -> HashEngine { - HashEngine { - k0, - k1, - length: 0, - state: State { - v0: k0 ^ 0x736f6d6570736575, - v1: k1 ^ 0x646f72616e646f6d, - v2: k0 ^ 0x6c7967656e657261, - v3: k1 ^ 0x7465646279746573, - }, - tail: 0, - ntail: 0, - } - } - - /// Creates a new SipHash24 engine. - pub fn new() -> HashEngine { - HashEngine::with_keys(0, 0) - } - - /// Retrieves the keys of this engine. - pub fn keys(&self) -> (u64, u64) { - (self.k0, self.k1) - } - - #[inline] - fn rounds(state: &mut State, n: usize) { - let (mut v0, mut v1, mut v2, mut v3) = (state.v0, state.v1, state.v2, state.v3); - for _ in 0..n { - (v0, v1, v2, v3) = sip_round_scalar(v0, v1, v2, v3); - } - (state.v0, state.v1, state.v2, state.v3) = (v0, v1, v2, v3); - } - - #[inline] - fn c_rounds(state: &mut State) { - HashEngine::rounds(state, 2); - } - - #[inline] - fn d_rounds(state: &mut State) { - HashEngine::rounds(state, 4); - } -} - -impl Default for HashEngine { - fn default() -> Self { - HashEngine::new() - } -} - -impl crate::HashEngine for HashEngine { - type MidState = State; - - fn midstate(&self) -> State { - self.state.clone() - } - - const BLOCK_SIZE: usize = 8; - - #[inline] - fn input(&mut self, msg: &[u8]) { - let length = msg.len(); - self.length += length; - - let mut needed = 0; - - if self.ntail != 0 { - needed = 8 - self.ntail; - self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail); - if length < needed { - self.ntail += length; - return; - } else { - self.state.v3 ^= self.tail; - HashEngine::c_rounds(&mut self.state); - self.state.v0 ^= self.tail; - self.ntail = 0; - } - } - - // Buffered tail is now flushed, process new input. - let len = length - needed; - let left = len & 0x7; - - let mut i = needed; - while i < len - left { - let mi = load_int_le!(msg, i, u64); - - self.state.v3 ^= mi; - HashEngine::c_rounds(&mut self.state); - self.state.v0 ^= mi; - - i += 8; - } - - self.tail = unsafe { u8to64_le(msg, i, left) }; - self.ntail = left; - } - - fn n_bytes_hashed(&self) -> usize { - self.length - } -} - -impl Hash { - /// Hashes the given data with an engine with the provided keys. - pub fn hash_with_keys(k0: u64, k1: u64, data: &[u8]) -> Hash { - let mut engine = HashEngine::with_keys(k0, k1); - engine.input(data); - Hash::from_engine(engine) - } - - /// Hashes the given data directly to u64 with an engine with the provided keys. - pub fn hash_to_u64_with_keys(k0: u64, k1: u64, data: &[u8]) -> u64 { - let mut engine = HashEngine::with_keys(k0, k1); - engine.input(data); - Hash::from_engine_to_u64(engine) - } - - /// Produces a hash as `u64` from the current state of a given engine. - #[inline] - pub fn from_engine_to_u64(e: HashEngine) -> u64 { - let mut state = e.state; - - let b: u64 = ((e.length as u64 & 0xff) << 56) | e.tail; - - state.v3 ^= b; - HashEngine::c_rounds(&mut state); - state.v0 ^= b; - - state.v2 ^= 0xff; - HashEngine::d_rounds(&mut state); - - state.v0 ^ state.v1 ^ state.v2 ^ state.v3 - } - - /// Returns the (little endian) 64-bit integer representation of the hash value. - pub fn as_u64(&self) -> u64 { - u64::from_le_bytes(self.0) - } - - /// Creates a hash from its (little endian) 64-bit integer representation. - pub fn from_u64(hash: u64) -> Hash { - Hash(hash.to_le_bytes()) - } -} - /// Load an u64 using up to 7 bytes of a byte slice. /// /// Unsafe because: unchecked indexing at `start..start+len`. @@ -700,6 +502,7 @@ unsafe fn hash_many_wide_avx2( #[cfg(test)] mod tests { + use crate::{Hash as _, HashEngine as _}; use test_case::test_case; use super::*; @@ -875,4 +678,22 @@ mod tests { check::<32>(count); check::<40>(count); } + + /// The scalar one-shot the SIMD batcher is checked against must itself + /// agree with the upstream engine. + #[test] + fn siphash_matches_upstream_engine() { + const K0: u64 = 0x0706050403020100; + const K1: u64 = 0x0f0e0d0c0b0a0908; + + for len in 0..80usize { + let data: Vec = (0..len).map(|i| (i as u8).wrapping_mul(37)).collect(); + assert_eq!( + siphash(K0, K1, &data), + Hash::hash_to_u64_with_keys(K0, K1, &data), + "mismatch at len {}", + len + ); + } + } } diff --git a/hashes/src/util.rs b/hashes/src/util.rs index c9bb9a060..c567f3a85 100644 --- a/hashes/src/util.rs +++ b/hashes/src/util.rs @@ -15,17 +15,18 @@ #[macro_export] /// Adds hexadecimal formatting implementation of a trait `$imp` to a given type `$ty`. macro_rules! hex_fmt_impl( - ($reverse:expr, $ty:ident) => ( - $crate::hex_fmt_impl!($reverse, $ty, ); + ($reverse:expr, $len:expr, $ty:ident) => ( + $crate::hex_fmt_impl!($reverse, $len, $ty, ); ); - ($reverse:expr, $ty:ident, $($generator:ident: $gent:ident),*) => ( + ($reverse:expr, $len:expr, $ty:ident, $($generator:ident: $gent:ident),*) => ( impl<$($generator: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($generator),*> { #[inline] fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { + let bytes = $crate::Hash::as_byte_array(self); if $reverse { - $crate::_export::_core::fmt::LowerHex::fmt(&self.0.backward_hex(), f) + $crate::hex::fmt_hex_exact!(f, $len, bytes.iter().rev(), $crate::hex::Case::Lower) } else { - $crate::_export::_core::fmt::LowerHex::fmt(&self.0.forward_hex(), f) + $crate::hex::fmt_hex_exact!(f, $len, bytes.iter(), $crate::hex::Case::Lower) } } } @@ -33,10 +34,11 @@ macro_rules! hex_fmt_impl( impl<$($generator: $gent),*> $crate::_export::_core::fmt::UpperHex for $ty<$($generator),*> { #[inline] fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { + let bytes = $crate::Hash::as_byte_array(self); if $reverse { - $crate::_export::_core::fmt::UpperHex::fmt(&self.0.backward_hex(), f) + $crate::hex::fmt_hex_exact!(f, $len, bytes.iter().rev(), $crate::hex::Case::Upper) } else { - $crate::_export::_core::fmt::UpperHex::fmt(&self.0.forward_hex(), f) + $crate::hex::fmt_hex_exact!(f, $len, bytes.iter(), $crate::hex::Case::Upper) } } } @@ -78,35 +80,6 @@ macro_rules! borrow_slice_impl( ) ); -macro_rules! engine_input_impl( - () => ( - #[cfg(not(fuzzing))] - fn input(&mut self, mut inp: &[u8]) { - while !inp.is_empty() { - let buf_idx = self.length % ::BLOCK_SIZE; - let rem_len = ::BLOCK_SIZE - buf_idx; - let write_len = cmp::min(rem_len, inp.len()); - - self.buffer[buf_idx..buf_idx + write_len] - .copy_from_slice(&inp[..write_len]); - self.length += write_len; - if self.length % ::BLOCK_SIZE == 0 { - self.process_block(); - } - inp = &inp[write_len..]; - } - } - - #[cfg(fuzzing)] - fn input(&mut self, inp: &[u8]) { - for c in inp { - self.buffer[0] ^= *c; - } - self.length += inp.len(); - } - ) -); - /// Creates a new newtype around a [`Hash`] type. /// /// The syntax is similar to the usual tuple struct syntax: @@ -202,7 +175,11 @@ macro_rules! hash_newtype { $({ $($type_attrs)* })* } - $crate::hex_fmt_impl!(<$newtype as $crate::Hash>::DISPLAY_BACKWARD, $newtype); + $crate::hex_fmt_impl!( + <$newtype as $crate::Hash>::DISPLAY_BACKWARD, + <$newtype as $crate::Hash>::LEN, + $newtype + ); $crate::serde_impl!($newtype, <$newtype as $crate::Hash>::LEN); $crate::bincode_impl!($newtype, <$newtype as $crate::Hash>::LEN); $crate::borrow_slice_impl!($newtype); @@ -291,16 +268,15 @@ macro_rules! hash_newtype { } impl $crate::_export::_core::str::FromStr for $newtype { - type Err = $crate::hex::Error; + type Err = $crate::hex::HexToArrayError; fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::Err> { - use $crate::hex::{HexIterator, FromHex}; + use $crate::hex::FromHex; - let inner: <$hash as $crate::Hash>::Bytes = if ::DISPLAY_BACKWARD { - FromHex::from_byte_iter(HexIterator::new(s)?.rev())? - } else { - FromHex::from_byte_iter(HexIterator::new(s)?)? - }; - Ok($newtype(<$hash as $crate::Hash>::from_byte_array(inner))) + let mut bytes = <[u8; ::LEN]>::from_hex(s)?; + if ::DISPLAY_BACKWARD { + bytes.reverse(); + } + Ok($newtype(<$hash as $crate::Hash>::from_byte_array(bytes))) } } @@ -326,7 +302,7 @@ macro_rules! hash_newtype { } impl From<$newtype> for [u8; <$hash as $crate::Hash>::LEN] { - fn from(value: $newtype) -> Self { value.0.into() } + fn from(value: $newtype) -> Self { $crate::Hash::to_byte_array(value.0) } } )+ }; @@ -345,7 +321,11 @@ macro_rules! hash_newtype_no_ord { $({ $($type_attrs)* })* } - $crate::hex_fmt_impl!(<$newtype as $crate::Hash>::DISPLAY_BACKWARD, $newtype); + $crate::hex_fmt_impl!( + <$newtype as $crate::Hash>::DISPLAY_BACKWARD, + <$newtype as $crate::Hash>::LEN, + $newtype + ); $crate::serde_impl!($newtype, <$newtype as $crate::Hash>::LEN); $crate::borrow_slice_impl!($newtype); @@ -425,16 +405,15 @@ macro_rules! hash_newtype_no_ord { } impl $crate::_export::_core::str::FromStr for $newtype { - type Err = $crate::hex::Error; + type Err = $crate::hex::HexToArrayError; fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::Err> { - use $crate::hex::{HexIterator, FromHex}; + use $crate::hex::FromHex; - let inner: <$hash as $crate::Hash>::Bytes = if ::DISPLAY_BACKWARD { - FromHex::from_byte_iter(HexIterator::new(s)?.rev())? - } else { - FromHex::from_byte_iter(HexIterator::new(s)?)? - }; - Ok($newtype(<$hash as $crate::Hash>::from_byte_array(inner))) + let mut bytes = <[u8; ::LEN]>::from_hex(s)?; + if ::DISPLAY_BACKWARD { + bytes.reverse(); + } + Ok($newtype(<$hash as $crate::Hash>::from_byte_array(bytes))) } } @@ -460,7 +439,7 @@ macro_rules! hash_newtype_no_ord { } impl From<$newtype> for [u8; <$hash as $crate::Hash>::LEN] { - fn from(value: $newtype) -> Self { value.0.into() } + fn from(value: $newtype) -> Self { $crate::Hash::to_byte_array(value.0) } } )+ }; @@ -578,101 +557,3 @@ macro_rules! hash_newtype_known_attrs { (#[hash_newtype($($unknown:tt)*)]) => { compile_error!(concat!("Unrecognized attribute ", stringify!($($unknown)*))); }; ($($ignore:tt)*) => {}; } - -#[cfg(feature = "schemars")] -pub mod json_hex_string { - use schemars::SchemaGenerator; - use schemars::{json_schema, Schema}; - macro_rules! define_custom_hex { - ($name:ident, $len:expr) => { - pub fn $name(_generator: &mut SchemaGenerator) -> Schema { - // In schemars 1.0, we can use the json_schema! macro to create schemas - json_schema!({ - "type": "string", - "minLength": $len * 2, - "maxLength": $len * 2, - "pattern": "^[0-9a-fA-F]+$" - }) - } - }; - } - define_custom_hex!(len_8, 8); - define_custom_hex!(len_20, 20); - define_custom_hex!(len_32, 32); - define_custom_hex!(len_64, 64); -} - -#[cfg(test)] -mod test { - use crate::{sha256, Hash}; - - #[test] - fn hash_as_ref_array() { - let hash = sha256::Hash::hash(&[3, 50]); - let r = AsRef::<[u8; 32]>::as_ref(&hash); - assert_eq!(r, hash.as_byte_array()); - } - - #[test] - fn hash_as_ref_slice() { - let hash = sha256::Hash::hash(&[3, 50]); - let r = AsRef::<[u8]>::as_ref(&hash); - assert_eq!(r, hash.as_byte_array()); - } - - #[test] - fn hash_borrow() { - use core::borrow::Borrow; - - let hash = sha256::Hash::hash(&[3, 50]); - let borrowed: &[u8] = hash.borrow(); - assert_eq!(borrowed, hash.as_byte_array()); - } - - hash_newtype! { - /// Test hash. - struct TestHash(crate::sha256d::Hash); - } - - #[test] - fn display() { - let want = "0000000000000000000000000000000000000000000000000000000000000000"; - let got = format!("{}", TestHash::all_zeros()); - assert_eq!(got, want) - } - - #[test] - fn display_alternate() { - let want = "0x0000000000000000000000000000000000000000000000000000000000000000"; - let got = format!("{:#}", TestHash::all_zeros()); - assert_eq!(got, want) - } - - #[test] - fn lower_hex() { - let want = "0000000000000000000000000000000000000000000000000000000000000000"; - let got = format!("{:x}", TestHash::all_zeros()); - assert_eq!(got, want) - } - - #[test] - fn lower_hex_alternate() { - let want = "0x0000000000000000000000000000000000000000000000000000000000000000"; - let got = format!("{:#x}", TestHash::all_zeros()); - assert_eq!(got, want) - } - - #[test] - fn inner_hash_as_ref_array() { - let hash = TestHash::all_zeros(); - let r = AsRef::<[u8; 32]>::as_ref(&hash); - assert_eq!(r, hash.as_byte_array()); - } - - #[test] - fn inner_hash_as_ref_slice() { - let hash = TestHash::all_zeros(); - let r = AsRef::<[u8]>::as_ref(&hash); - assert_eq!(r, hash.as_byte_array()); - } -} diff --git a/internals/Cargo.toml b/internals/Cargo.toml index 5917b4254..1005ffc84 100644 --- a/internals/Cargo.toml +++ b/internals/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "dashcore-private" -version = { workspace = true } authors = ["Samuel Westrich ", "The Rust Dash developers"] -license = "CC0-1.0" -repository = "https://github.com/rust-dashcore/rust-dashcore/" -documentation = "https://docs.rs/dash-private" description = "Internal types and macros used by rust-dash ecosystem" categories = ["cryptography::cryptocurrencies"] keywords = ["internal"] readme = "README.md" -edition = "2024" exclude = ["tests", "contrib"] +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [features] default = [] diff --git a/key-wallet-ffi/Cargo.toml b/key-wallet-ffi/Cargo.toml index 29911a4af..daca75d69 100644 --- a/key-wallet-ffi/Cargo.toml +++ b/key-wallet-ffi/Cargo.toml @@ -1,12 +1,14 @@ [package] name = "key-wallet-ffi" -version = { workspace = true } -authors = ["The Dash Core Developers"] -edition = "2021" description = "FFI bindings for key-wallet library" +edition = "2021" keywords = ["dash", "wallet", "ffi", "bindings"] readme = "README.md" -license = "CC0-1.0" +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [lib] name = "key_wallet_ffi" @@ -34,10 +36,10 @@ key-wallet = { path = "../key-wallet" } key-wallet-manager = { path = "../key-wallet-manager" } dashcore = { path = "../dash" } dash-network = { path = "../dash-network", features = ["ffi"] } -secp256k1 = { version = "0.33.1" } +secp256k1 = { workspace = true } tokio = { version = "1.32", features = ["rt-multi-thread", "sync"] } libc = "0.2" -hex = "0.4" +hex = { workspace = true } [build-dependencies] cbindgen = "0.29" @@ -46,4 +48,4 @@ cbindgen = "0.29" dashcore = { path = "../dash", features = ["test-utils"] } key-wallet = { path = "../key-wallet", features = ["test-utils"] } key-wallet-manager = { path = "../key-wallet-manager", features = ["test-utils"] } -hex = "0.4" +hex = { workspace = true } diff --git a/key-wallet-ffi/FFI_API.md b/key-wallet-ffi/FFI_API.md index c6ec21c43..98772a905 100644 --- a/key-wallet-ffi/FFI_API.md +++ b/key-wallet-ffi/FFI_API.md @@ -3788,7 +3788,7 @@ transaction_sighash(tx: *const FFITransaction, input_index: u32, script_pubkey: ``` **Description:** -Calculate signature hash for an input # Safety - `tx` must be a valid pointer to an FFITransaction - `script_pubkey` must be a valid pointer to the script pubkey - `hash_out` must be a valid pointer to a buffer of at least 32 bytes # Returns - 0 on success - -1 on error +Calculate signature hash for an input # Safety - `tx` must be a valid pointer to an FFITransaction - `script_pubkey` must be a valid pointer to the script pubkey - `hash_out` must be a valid pointer to a buffer of at least 32 bytes # Returns - 0 on success - -1 on error, including a `sighash_type` above 0xff **Safety:** - `tx` must be a valid pointer to an FFITransaction - `script_pubkey` must be a valid pointer to the script pubkey - `hash_out` must be a valid pointer to a buffer of at least 32 bytes diff --git a/key-wallet-ffi/src/transaction.rs b/key-wallet-ffi/src/transaction.rs index 91fe71acc..d094cdffa 100644 --- a/key-wallet-ffi/src/transaction.rs +++ b/key-wallet-ffi/src/transaction.rs @@ -8,8 +8,8 @@ use crate::{check_ptr, FFIWalletManager}; use crate::{deref_ptr, deref_ptr_mut, unwrap_or_return}; use dash_network::ffi::FFINetwork; use dashcore::{ - consensus, hashes::Hash, sighash::SighashCache, EcdsaSighashType, Network, OutPoint, Script, - ScriptBuf, Transaction, TxIn, TxOut, Txid, + consensus, hashes::Hash, sighash::SighashCache, Network, OutPoint, Script, ScriptBuf, + Transaction, TxIn, TxOut, Txid, }; use key_wallet::wallet::managed_wallet_info::asset_lock_builder::{ AssetLockFundingType, CreditOutputFunding, @@ -509,7 +509,7 @@ pub unsafe extern "C" fn transaction_destroy(tx: *mut FFITransaction) { /// /// # Returns /// - 0 on success -/// - -1 on error +/// - -1 on error, including a `sighash_type` above 0xff #[no_mangle] pub unsafe extern "C" fn transaction_sighash( tx: *const FFITransaction, @@ -519,7 +519,9 @@ pub unsafe extern "C" fn transaction_sighash( sighash_type: u32, hash_out: *mut u8, ) -> i32 { - if tx.is_null() || script_pubkey.is_null() || hash_out.is_null() { + // The signature carries only the low byte, so a wider flag would sign + // a hash nobody can reproduce. + if tx.is_null() || script_pubkey.is_null() || hash_out.is_null() || sighash_type > 0xff { return -1; } @@ -527,10 +529,9 @@ pub unsafe extern "C" fn transaction_sighash( let script_slice = slice::from_raw_parts(script_pubkey, script_pubkey_len as usize); let script = Script::from_bytes(script_slice); - let sighash_type = EcdsaSighashType::from_consensus(sighash_type); let cache = SighashCache::new(&tx.inner); - match cache.legacy_signature_hash(input_index as usize, script, sighash_type.to_u32()) { + match cache.legacy_signature_hash(input_index as usize, script, sighash_type) { Ok(hash) => { let hash_bytes: &[u8] = hash.as_ref(); ptr::copy_nonoverlapping(hash_bytes.as_ptr(), hash_out, 32); @@ -999,6 +1000,31 @@ mod tests { } } + #[test] + fn sighash_rejects_flags_wider_than_a_byte() { + let tx = FFITransaction { + inner: Transaction { + version: 2, + lock_time: 0, + input: vec![TxIn::default()], + output: vec![TxOut::default()], + special_transaction_payload: None, + }, + }; + let script = [0x51u8]; + let sighash = |flag: u32| { + let mut out = [0u8; 32]; + let ret = + unsafe { transaction_sighash(&tx, 0, script.as_ptr(), 1, flag, out.as_mut_ptr()) }; + (ret, out) + }; + + assert_eq!(sighash(0x01).0, 0); + assert_eq!(sighash(0xff).0, 0); + assert_eq!(sighash(0x100).0, -1); + assert_eq!(sighash(0x181).0, -1); + } + /// Every kind must map to its own preference. A transposed arm here would /// silently fund an asset lock from an account the caller did not name — /// and the build would succeed, so nothing downstream would catch it. diff --git a/key-wallet-manager/Cargo.toml b/key-wallet-manager/Cargo.toml index fdc1df3f9..8b0b73853 100644 --- a/key-wallet-manager/Cargo.toml +++ b/key-wallet-manager/Cargo.toml @@ -1,11 +1,13 @@ [package] name = "key-wallet-manager" -version = { workspace = true } -authors = ["The Dash Core Developers"] -edition = "2021" description = "High-level wallet management for Dash (WalletManager, WalletInterface, events, compact filters)" +edition = "2021" keywords = ["dash", "wallet", "manager", "spv"] -license = "CC0-1.0" +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [features] default = [] @@ -34,16 +36,16 @@ dashcore = { path = "../dash" } async-trait = "0.1" tokio = { version = "1", features = ["macros", "rt", "sync"] } tracing = "0.1" -zeroize = { version = "1.8", features = ["derive"] } -rayon = { version = "1.11", optional = true } +zeroize = { workspace = true } +rayon = { workspace = true, optional = true } bincode = { workspace = true, optional = true } -serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } +serde = { workspace = true, features = ["derive"], optional = true } [dev-dependencies] key-wallet = { path = "../key-wallet", features = ["test-utils", "bincode"] } dashcore = { path = "../dash", features = ["test-utils"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } -hex = "0.4" +hex = { workspace = true } criterion = "0.8.1" [[bench]] diff --git a/key-wallet/Cargo.toml b/key-wallet/Cargo.toml index ea9d1d4a5..28f0b5a9e 100644 --- a/key-wallet/Cargo.toml +++ b/key-wallet/Cargo.toml @@ -1,12 +1,14 @@ [package] name = "key-wallet" -version = { workspace = true } -authors = ["The Dash Core Developers"] -edition = "2021" description = "Key derivation and wallet functionality for Dash" +edition = "2021" keywords = ["dash", "wallet", "bip32", "bip39", "hdwallet"] readme = "README.md" -license = "CC0-1.0" +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [features] default = ["getrandom"] @@ -34,11 +36,10 @@ keep-finalized-transactions = [] internals = { path = "../internals", package = "dashcore-private" } dashcore_hashes = { path = "../hashes" } dashcore = { path="../dash" } -secp256k1 = { version = "0.33.1", default-features = false, features = ["recovery", "std"] } +secp256k1 = { workspace = true, features = ["recovery"] } bip39 = { version = "2.2.0", default-features = false, features = ["std", "chinese-simplified", "chinese-traditional", "czech", "french", "italian", "japanese", "korean", "portuguese", "spanish", "zeroize"] } unicode-normalization = "0.1" -serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } -base58 = { package = "base58ck", version = "0.1.0", default-features = false } +serde = { workspace = true, features = ["derive"], optional = true } bitflags = { version = "2.6", default-features = false } getrandom = { version = "0.4", optional = true } # BIP38 dependencies (optional) @@ -51,15 +52,15 @@ rand = { version = "0.9", default-features = false, features = ["std", "std_rng" bincode = { workspace = true, optional = true } bincode_derive = { workspace = true, optional = true } serde_json = { version = "1.0", optional = true } -hex = { version = "0.4"} -zeroize = { version = "1.8", features = ["derive"] } +hex = { workspace = true } +zeroize = { workspace = true } tracing = "0.1" async-trait = "0.1" [dev-dependencies] criterion = "0.8.1" dashcore = { path="../dash", features = ["test-utils"] } -hex = "0.4" +hex = { workspace = true } key-wallet = { path = ".", features = ["test-utils", "bip38", "serde", "bincode", "eddsa", "bls"] } rand = { version = "0.9", features = ["std", "std_rng", "thread_rng"] } test-case = "3.3" diff --git a/key-wallet/src/account/eddsa_account.rs b/key-wallet/src/account/eddsa_account.rs index 673ff4f14..eef13c5c1 100644 --- a/key-wallet/src/account/eddsa_account.rs +++ b/key-wallet/src/account/eddsa_account.rs @@ -3,23 +3,23 @@ //! This module provides account functionality using Ed25519 keys //! for Platform identity operations. -use super::account_trait::AccountTrait; -use crate::account::AccountType; -use crate::derivation_slip10::{ExtendedEd25519PrivKey, ExtendedEd25519PubKey}; -use crate::error::{Error, Result}; -use crate::{ChildNumber, DerivationPath, Network}; use core::fmt; -use dashcore::eddsa::{EddsaPkBytes, EddsaSkBytes}; -use dashcore::Address; +#[cfg(feature = "bincode")] +use bincode_derive::{Decode, Encode}; +use dashcore::eddsa::{EddsaPkBytes, EddsaPkHash, EddsaPublicKey, EddsaSkBytes}; +use dashcore::Address; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use super::account_trait::AccountTrait; use crate::account::derivation::AccountDerivation; +use crate::account::AccountType; use crate::bip32::{ChainCode, Fingerprint}; +use crate::derivation_slip10::{ExtendedEd25519PrivKey, ExtendedEd25519PubKey}; +use crate::error::{Error, Result}; use crate::managed_account::address_pool::AddressPoolType; -#[cfg(feature = "bincode")] -use bincode_derive::{Decode, Encode}; +use crate::{ChildNumber, DerivationPath, Network}; /// EdDSA (Ed25519) account structure for Platform identity operations #[derive(Debug, Clone)] @@ -64,8 +64,7 @@ impl EdDSAAccount { ) -> Result { // Create an extended public key with default metadata let verifying_key = EddsaPkBytes::from_bytes(ed25519_public_key); - verifying_key - .validate() + EddsaPublicKey::try_from(verifying_key) .map_err(|e| Error::InvalidParameter(format!("Invalid Ed25519 public key: {}", e)))?; let extended_key = ExtendedEd25519PubKey { @@ -339,7 +338,7 @@ impl AccountDerivation for Error { } } -impl From for Error { - fn from(err: base58::Error) -> Self { +impl From for Error { + fn from(err: base58::DecodeCheckError) -> Self { Error::Base58(err) } } @@ -1915,7 +1915,7 @@ impl ExtendedPubKey { impl fmt::Display for ExtendedPrivKey { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(&base58::encode_check(&self.encode()[..])) + fmt.write_str(base58::Base58CkString::encode_unbounded(&self.encode()[..]).as_str()) } } @@ -1930,7 +1930,7 @@ impl FromStr for ExtendedPrivKey { impl fmt::Display for ExtendedPubKey { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(&base58::encode_check(&self.encode()[..])) + fmt.write_str(base58::Base58CkString::encode_unbounded(&self.encode()[..]).as_str()) } } diff --git a/key-wallet/src/derivation_slip10.rs b/key-wallet/src/derivation_slip10.rs index 6314e4fc2..0e725ea4d 100644 --- a/key-wallet/src/derivation_slip10.rs +++ b/key-wallet/src/derivation_slip10.rs @@ -9,15 +9,16 @@ //! - Different serialization format (no xpub/xprv, custom encoding) use core::fmt; -use dashcore::eddsa::{EddsaPkBytes, EddsaSkBytes}; +use std::error; + +use dashcore::eddsa::{EddsaPkBytes, EddsaPublicKey, EddsaSecretKey, EddsaSkBytes}; use dashcore::Network; use dashcore_hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine}; #[cfg(feature = "serde")] use serde; -use std::error; + // Re-export ChainCode, Fingerprint and ChildNumber from bip32 use crate::bip32::{ChainCode, ChildNumber, Fingerprint}; - // Use DerivationPath from bip32 pub use crate::bip32::DerivationPath; @@ -165,7 +166,7 @@ impl ExtendedEd25519PrivKey { /// Get the public key for this private key pub fn public_key(&self) -> Result { - Ok(self.private_key.public_key()) + Ok(EddsaSecretKey::from(self.private_key.clone()).public_key().into()) } /// Get the fingerprint of this key @@ -409,7 +410,7 @@ impl ExtendedEd25519PubKey { let public_key_bytes: [u8; 32] = data[46..78].try_into().map_err(|_| Error::WrongExtendedKeyLength(data.len()))?; let public_key = EddsaPkBytes::from_bytes(public_key_bytes); - public_key.validate().map_err(|e| Error::Ed25519Error(e.to_string()))?; + EddsaPublicKey::try_from(public_key).map_err(|e| Error::Ed25519Error(e.to_string()))?; Ok(ExtendedEd25519PubKey { network, @@ -604,8 +605,7 @@ impl bincode::Decode for ExtendedEd25519PubKey { let child_number = ChildNumber::decode(decoder)?; let public_key_bytes = <[u8; 32]>::decode(decoder)?; let public_key = EddsaPkBytes::from_bytes(public_key_bytes); - public_key - .validate() + EddsaPublicKey::try_from(public_key) .map_err(|e| bincode::error::DecodeError::OtherString(e.to_string()))?; let chain_code = ChainCode::decode(decoder)?; diff --git a/key-wallet/src/managed_account/address_pool.rs b/key-wallet/src/managed_account/address_pool.rs index c83722dd3..f0b426bc9 100644 --- a/key-wallet/src/managed_account/address_pool.rs +++ b/key-wallet/src/managed_account/address_pool.rs @@ -14,6 +14,10 @@ use crate::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey} use crate::error::{Error, Result}; use crate::gap_limit::DEFAULT_EXTERNAL_GAP_LIMIT; use crate::Network; +use dashcore::address::Payload; +#[cfg(feature = "eddsa")] +use dashcore::eddsa::{EddsaPkBytes, EddsaPkHash}; +use dashcore::hashes::{hash160, Hash}; use dashcore::{Address, AddressType, ScriptBuf}; /// Types of public keys used in the address pool @@ -522,11 +526,9 @@ impl AddressPool { } DerivedKey::BLS(public_key_bytes) => { // BLS addresses use Hash160 of the public key bytes - use dashcore::hashes::{hash160, Hash}; let pubkey_hash = hash160::Hash::hash(&public_key_bytes); // Create P2PKH address from the hash - use dashcore::address::Payload; let payload = Payload::PubkeyHash(pubkey_hash.into()); let address = Address::new(self.network, payload); @@ -547,12 +549,8 @@ impl AddressPool { public_key_bytes.len() )) })?; - let node_id = dashcore::eddsa::EddsaPkBytes::from_bytes(*pubkey_arr) - .hash() - .to_canonical_bytes(); - - use dashcore::address::Payload; - use dashcore::hashes::Hash; + let node_id = + EddsaPkHash::from(EddsaPkBytes::from_bytes(*pubkey_arr)).to_canonical_bytes(); let payload = Payload::PubkeyHash(dashcore::PubkeyHash::from_byte_array(node_id)); let address = Address::new(self.network, payload); diff --git a/key-wallet/src/managed_account/managed_account_trait.rs b/key-wallet/src/managed_account/managed_account_trait.rs index a933b97f6..9e08318cb 100644 --- a/key-wallet/src/managed_account/managed_account_trait.rs +++ b/key-wallet/src/managed_account/managed_account_trait.rs @@ -7,6 +7,12 @@ use std::collections::BTreeMap; +#[cfg(feature = "bls")] +use dashcore::bls_sig_utils::{BLSPublicKey, BlsScheme}; +#[cfg(feature = "eddsa")] +use dashcore::eddsa::{EddsaPkBytes, EddsaPublicKey}; +use dashcore::{Address, ScriptBuf, Txid}; + use crate::account::TransactionRecord; #[cfg(feature = "bls")] use crate::derivation_bls_bip32::ExtendedBLSPubKey; @@ -18,9 +24,6 @@ use crate::managed_account::managed_account_type::ManagedAccountType; use crate::AddressInfo; use crate::ExtendedPubKey; use crate::Network; -#[cfg(feature = "bls")] -use dashcore::bls_sig_utils::{BLSPublicKey, BlsScheme}; -use dashcore::{Address, ScriptBuf, Txid}; /// Common trait for "core" managed account types — both funds-bearing /// (`ManagedCoreFundsAccount`) and keys-only (`ManagedCoreKeysAccount`). @@ -439,7 +442,7 @@ pub trait ManagedAccountTrait { &mut self, account_xpriv: crate::derivation_slip10::ExtendedEd25519PrivKey, add_to_state: bool, - ) -> Result<(dashcore::eddsa::EddsaPkBytes, AddressInfo), &'static str> { + ) -> Result<(EddsaPkBytes, AddressInfo), &'static str> { match self.managed_account_type_mut() { ManagedAccountType::ProviderPlatformKeys { addresses, @@ -457,10 +460,11 @@ pub trait ManagedAccountTrait { addresses.mark_index_used(info.index); - let verifying_key = dashcore::eddsa::EddsaPkBytes::from_bytes( + let verifying_key = EddsaPkBytes::from_bytes( pub_key_bytes.try_into().map_err(|_| "Invalid EdDSA public key length")?, ); - verifying_key.validate().map_err(|_| "Failed to deserialize EdDSA public key")?; + EddsaPublicKey::try_from(verifying_key) + .map_err(|_| "Failed to deserialize EdDSA public key")?; Ok((verifying_key, info)) } diff --git a/key-wallet/src/tests/provider_key_derivation_tests.rs b/key-wallet/src/tests/provider_key_derivation_tests.rs index 7b5a8e72e..5a0d05f3d 100644 --- a/key-wallet/src/tests/provider_key_derivation_tests.rs +++ b/key-wallet/src/tests/provider_key_derivation_tests.rs @@ -7,11 +7,15 @@ //! mnemonic. See . use crate::account::derivation::AccountDerivation; +#[cfg(feature = "eddsa")] +use crate::account::eddsa_account::EdDSAAccount; use crate::account::AccountType; use crate::mnemonic::Mnemonic; use crate::wallet::initialization::WalletAccountCreationOptions; use crate::wallet::Wallet; use crate::{ChildNumber, Network}; +#[cfg(feature = "eddsa")] +use dashcore::eddsa::{EddsaPkBytes, EddsaPkHash, EddsaSecretKey, EddsaSkBytes}; const TEST_MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; @@ -110,10 +114,11 @@ fn ed25519_platform_node_keys_match_slip10_reference() { // The stored account key must be the account-level key at m/9'/5'/3'/4'. let expected_account_sk = hex::decode("80035d9c2f89971a9c9fad826bba8be9328f1686ae555e912949c2c32800c379").unwrap(); - let expected_account_pk = dashcore::eddsa::EddsaSkBytes::from_bytes( + let expected_account_pk: EddsaPkBytes = EddsaSecretKey::from(EddsaSkBytes::from_bytes( expected_account_sk.as_slice().try_into().unwrap(), - ) - .public_key(); + )) + .public_key() + .into(); assert_eq!(account.ed25519_public_key.public_key, expected_account_pk); // Platform node key 0 (hardened child 0') via the seed-based signing path. @@ -132,20 +137,18 @@ fn ed25519_platform_node_keys_match_slip10_reference() { #[cfg(feature = "eddsa")] #[test] fn ed25519_platform_node_id_matches_tenderdash_convention() { - use crate::account::eddsa_account::EdDSAAccount; - // Platform node key 0 (m/9'/5'/3'/4'/0') for TEST_SEED — the same key the // SLIP-0010 vector above pins. Public key and node id cross-checked with // an independent Ed25519 implementation (pyca/cryptography). let seed = hex::decode(TEST_SEED_HEX).unwrap(); let sk0 = EdDSAAccount::platform_node_key_at(&seed, Network::Mainnet, 0).unwrap(); - let pubkey = sk0.public_key(); + let pubkey: EddsaPkBytes = EddsaSecretKey::from(sk0).public_key().into(); assert_eq!( hex::encode(pubkey.to_bytes()), "3130c14339391cf26a68d86879e180ee9a16b660f5aa91f560f67c0abe8cf789" ); assert_eq!( - hex::encode(pubkey.hash().to_canonical_bytes()), + hex::encode(EddsaPkHash::from(pubkey).to_canonical_bytes()), "302f2615e6955cce8ed3cff81e8011bfd3a2991f" ); } diff --git a/masternode-seeds-fetcher/Cargo.toml b/masternode-seeds-fetcher/Cargo.toml index d5d70d62d..caf3a8e8b 100644 --- a/masternode-seeds-fetcher/Cargo.toml +++ b/masternode-seeds-fetcher/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "masternode-seeds-fetcher" -version = { workspace = true } -edition = "2024" -authors = ["Dash Core Team"] description = "Fetch the current masternode IP list from the Dash P2P network and write per-network seed files used by dash-spv." -license = "MIT" -repository = "https://github.com/dashpay/rust-dashcore" publish = false +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [[bin]] name = "masternode-seeds-fetcher" @@ -25,7 +26,7 @@ chrono = { version = "0.4.20", default-features = false, features = ["clock"] } tracing = "0.1" tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } rand = "0.9" -futures = "0.3" +futures = { workspace = true } # TLS probing for the Dash Platform HTTP port (Evo nodes). tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] } diff --git a/rpc-client/Cargo.toml b/rpc-client/Cargo.toml index bba0461c5..65d7b18f2 100644 --- a/rpc-client/Cargo.toml +++ b/rpc-client/Cargo.toml @@ -1,18 +1,18 @@ [package] name = "dashcore-rpc" -version = { workspace = true } authors = [ "Steven Roose ", "Jean Pierre Dudey ", "Dawid Ciężarkiewicz ", ] -license = "CC0-1.0" -homepage = "https://github.com/dashevo/rust-dashcore/" -repository = "https://github.com/dashevo/rust-dashcore/" description = "RPC client library for the Dash Core JSON-RPC API." keywords = ["crypto", "dash", "dash-core", "rpc"] readme = "README.md" -edition = "2024" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [lib] name = "dashcore_rpc" @@ -25,7 +25,7 @@ jsonrpc = "0.18.0" tracing = "0.1" # Used for deserialization of JSON. -serde = { version = "1.0.219", features = ["derive"] } +serde = { workspace = true, features = ["std", "derive"] } serde_json = { version="1.0", features=["preserve_order"] } -hex = { version="0.4", features=["serde"]} +hex = { workspace = true, features = ["serde"] } diff --git a/rpc-client/src/error.rs b/rpc-client/src/error.rs index 3a1d24bc4..e6768cc5e 100644 --- a/rpc-client/src/error.rs +++ b/rpc-client/src/error.rs @@ -21,7 +21,7 @@ pub type FixedSizeLen = usize; #[derive(Debug)] pub enum Error { JsonRpc(jsonrpc::error::Error), - Hex(hex::Error), + Hex(hex::HexToBytesError), Json(serde_json::error::Error), BitcoinSerialization(dashcore::consensus::encode::Error), Secp256k1(secp256k1::Error), @@ -38,8 +38,8 @@ impl From for Error { } } -impl From for Error { - fn from(e: hex::Error) -> Error { +impl From for Error { + fn from(e: hex::HexToBytesError) -> Error { Error::Hex(e) } } diff --git a/rpc-integration-test/Cargo.toml b/rpc-integration-test/Cargo.toml index 9f03b536e..fac9c99ac 100644 --- a/rpc-integration-test/Cargo.toml +++ b/rpc-integration-test/Cargo.toml @@ -1,14 +1,17 @@ [package] name = "integration_test" authors = ["Steven Roose "] -license = "CC0-1.0" -edition = "2024" publish = false +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [dependencies] dashcore-rpc = { path = "../rpc-client" } lazy_static = "1.4.0" -hex = "0.4.3" +hex = { workspace = true } dotenvy = "0.15.7" tracing = "0.1" tracing-log = "0.2" diff --git a/rpc-json/Cargo.toml b/rpc-json/Cargo.toml index 693da1b38..6d300c373 100644 --- a/rpc-json/Cargo.toml +++ b/rpc-json/Cargo.toml @@ -1,29 +1,29 @@ [package] name = "dashcore-rpc-json" -version = { workspace = true } authors = [ "Steven Roose ", "Jean Pierre Dudey ", "Dawid Ciężarkiewicz " ] -license = "CC0-1.0" -homepage = "https://github.com/dashevo/rust-dashcore/" -repository = "https://github.com/dashevo/rust-dashcore/" description = "JSON-enabled type structs for dashcore-rpc crate." keywords = [ "crypto", "dash", "dashcore", "rpc" ] readme = "README.md" -edition = "2024" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true [lib] name = "dashcore_rpc_json" path = "src/lib.rs" [dependencies] -serde = { version = "1.0.219", features = ["derive"] } +serde = { workspace = true, features = ["std", "derive"] } serde_json = { version="1.0", features=["preserve_order"] } serde_with = "2.1.0" serde_repr = "0.1" -hex = { version="0.4", features=["serde"]} +hex = { workspace = true, features = ["serde"] } key-wallet = { path = "../key-wallet", default-features = false, features=["serde", "getrandom"] } dashcore = { path = "../dash", features=["secp-recovery", "rand-std", "signer", "serde"] } diff --git a/rpc-json/src/lib.rs b/rpc-json/src/lib.rs index 3889beccc..07f1cae4a 100644 --- a/rpc-json/src/lib.rs +++ b/rpc-json/src/lib.rs @@ -31,7 +31,6 @@ use dashcore::address; use dashcore::address::NetworkUnchecked; use dashcore::block::Version; use dashcore::consensus::encode; -use dashcore::hashes::hex::Error::InvalidChar; use dashcore::hashes::sha256; use dashcore::{ Address, Amount, BlockHash, PrivateKey, ProTxHash, PublicKey, QuorumHash, Script, ScriptBuf, @@ -1699,14 +1698,22 @@ impl serde::Serialize for SigHashType { where S: Serializer, { - serializer.serialize_str(match self.0 { + // Dash Core's RPC only names the standard flags; a non-standard one cannot be spelled. + let name = match self.0 { dashcore::EcdsaSighashType::All => "ALL", dashcore::EcdsaSighashType::None => "NONE", dashcore::EcdsaSighashType::Single => "SINGLE", dashcore::EcdsaSighashType::AllPlusAnyoneCanPay => "ALL|ANYONECANPAY", dashcore::EcdsaSighashType::NonePlusAnyoneCanPay => "NONE|ANYONECANPAY", dashcore::EcdsaSighashType::SinglePlusAnyoneCanPay => "SINGLE|ANYONECANPAY", - }) + dashcore::EcdsaSighashType::NonStandard(n) => { + return Err(serde::ser::Error::custom(format!( + "non-standard sighash type {} has no RPC spelling", + n.to_u32() + ))); + } + }; + serializer.serialize_str(name) } } @@ -2360,8 +2367,8 @@ impl TryFrom for DMNStateDiff { let platform_node_id = platform_node_id .map(|address| { - let address = - hex::decode(address).map_err(|_| encode::Error::Hex(InvalidChar(0)))?; + let address = hex::decode(address) + .map_err(|_| encode::Error::ParseFailed("invalid hex in platform node id"))?; let len = address.len(); address.try_into().map_err(|_| encode::Error::InvalidVectorSize { expected: 20,