coins_core/
hashes.rs

1//! Holds utilites for working with cryptographic digests, and disambiguating
2//! digests via marker traits.
3//!
4//! We want to wrap hashes in marked newtypes in order to prevent
5//! type-confusion between TXIDs, sighashes, and other digests with the same
6//! length.
7
8use crate::ser::{ByteFormat, SerError, SerResult};
9use digest::{
10    core_api::{BlockSizeUser, OutputSizeUser},
11    HashMarker, Output,
12};
13use std::io::Write;
14
15// Useful re-exports
16pub use digest::Digest;
17pub use generic_array::GenericArray;
18pub use ripemd::Ripemd160;
19pub use sha2::Sha256;
20pub use sha3::Sha3_256;
21
22/// Output of a Digest function
23pub type DigestOutput<D> = GenericArray<u8, <D as OutputSizeUser>::OutputSize>;
24
25/// Convenience interface for hash function outputs, particularly marked digest outputs
26pub trait MarkedDigestOutput:
27    Default + Copy + AsRef<[u8]> + AsMut<[u8]> + ByteFormat<Error = SerError>
28{
29    /// Returns the number of bytes in the digest
30    fn size(&self) -> usize;
31
32    /// Return a clone in opposite byte order
33    fn reversed(&self) -> Self {
34        let mut reversed = Self::default();
35        let mut digest_bytes = self.as_slice().to_vec();
36        digest_bytes.reverse();
37        reversed
38            .as_mut()
39            .copy_from_slice(&digest_bytes[..self.size()]);
40        reversed
41    }
42
43    /// Deserialize to BE hex
44    fn from_be_hex(be: &str) -> SerResult<Self> {
45        Ok(Self::deserialize_hex(be)?.reversed())
46    }
47
48    /// Convert to BE hex
49    fn to_be_hex(&self) -> String {
50        self.reversed().serialize_hex()
51    }
52
53    /// Use as a mutable slice
54    fn as_mut_slice(&mut self) -> &mut [u8] {
55        self.as_mut()
56    }
57
58    /// Use as a slice
59    fn as_slice(&self) -> &[u8] {
60        self.as_ref()
61    }
62}
63
64/// A marked digest
65pub trait MarkedDigest<D>: Digest + Default + Write
66where
67    D: MarkedDigestOutput,
68{
69    /// Produce a marked digest from the hasher
70    fn finalize_marked(self) -> D;
71
72    /// Shortcut to produce a marked digest
73    fn digest_marked(data: &[u8]) -> D;
74}
75
76#[derive(Clone, Default)]
77/// A `Digest` implementation that performs Bitcoin style double-sha256
78pub struct Hash256(sha2::Sha256);
79
80impl std::io::Write for Hash256 {
81    fn flush(&mut self) -> std::io::Result<()> {
82        Ok(())
83    }
84
85    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
86        self.update(buf);
87        Ok(buf.len())
88    }
89}
90
91impl HashMarker for Hash256 {}
92
93impl BlockSizeUser for Hash256 {
94    type BlockSize = <Sha256 as BlockSizeUser>::BlockSize;
95}
96
97impl OutputSizeUser for Hash256 {
98    type OutputSize = <Sha256 as digest::OutputSizeUser>::OutputSize;
99}
100
101impl digest::FixedOutput for Hash256 {
102    fn finalize_into(self, out: &mut GenericArray<u8, Self::OutputSize>) {
103        let mut hasher = sha2::Sha256::default();
104        hasher.update(self.0.finalize());
105        Digest::finalize_into(hasher, out)
106    }
107}
108
109impl digest::FixedOutputReset for Hash256 {
110    fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
111        let other = self.clone();
112        other.finalize_into(out);
113        self.0.reset();
114    }
115}
116
117impl digest::Reset for Hash256 {
118    fn reset(&mut self) {
119        Digest::reset(&mut self.0);
120    }
121}
122
123impl digest::Update for Hash256 {
124    fn update(&mut self, data: &[u8]) {
125        Digest::update(&mut self.0, data);
126    }
127}
128
129#[derive(Clone, Default)]
130/// A `Digest` implementation that performs Bitcoin style double-sha256
131pub struct Hash160(sha2::Sha256);
132
133impl std::io::Write for Hash160 {
134    fn flush(&mut self) -> std::io::Result<()> {
135        Ok(())
136    }
137
138    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
139        self.update(buf);
140        Ok(buf.len())
141    }
142}
143
144impl HashMarker for Hash160 {}
145
146impl BlockSizeUser for Hash160 {
147    type BlockSize = <Ripemd160 as BlockSizeUser>::BlockSize;
148}
149
150impl OutputSizeUser for Hash160 {
151    type OutputSize = <Ripemd160 as digest::OutputSizeUser>::OutputSize;
152}
153
154impl digest::FixedOutput for Hash160 {
155    fn finalize_into(self, out: &mut GenericArray<u8, Self::OutputSize>) {
156        let mut hasher = ripemd::Ripemd160::default();
157        hasher.update(self.0.finalize());
158        Digest::finalize_into(hasher, out)
159    }
160}
161
162impl digest::FixedOutputReset for Hash160 {
163    fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
164        let other = self.clone();
165        other.finalize_into(out);
166        self.0.reset();
167    }
168}
169
170impl digest::Reset for Hash160 {
171    fn reset(&mut self) {
172        Digest::reset(&mut self.0);
173    }
174}
175
176impl digest::Update for Hash160 {
177    fn update(&mut self, data: &[u8]) {
178        Digest::update(&mut self.0, data);
179    }
180}
181
182marked_digest!(
183    /// A bitcoin-style Hash160
184    Hash160Digest,
185    Hash160
186);
187
188marked_digest!(
189    /// A bitcoin-style Hash256
190    Hash256Digest,
191    Hash256
192);