cardano_serialization_lib/chain_crypto/
digest.rs

1//! module to provide some handy interfaces atop the hashes so we have
2//! the common interfaces for the project to work with.
3
4use std::convert::TryFrom;
5use std::hash::{Hash, Hasher};
6use std::str::FromStr;
7use std::{error, fmt, result};
8
9use cryptoxide::blake2b::Blake2b;
10use cryptoxide::digest::Digest as _;
11use cryptoxide::sha3;
12use hex::FromHexError;
13
14use crate::typed_bytes::ByteSlice;
15
16use crate::chain_crypto::bech32::{self, Bech32};
17use crate::chain_crypto::hash::{Blake2b256, Sha3_256};
18
19#[derive(Debug, PartialEq, Clone)]
20pub enum Error {
21    InvalidDigestSize { got: usize, expected: usize },
22    InvalidHexEncoding(FromHexError),
23}
24impl fmt::Display for Error {
25    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26        match self {
27            Error::InvalidDigestSize { got: sz, expected } => write!(
28                f,
29                "invalid digest size, expected {} but received {} bytes.",
30                expected, sz
31            ),
32            Error::InvalidHexEncoding(_) => write!(f, "invalid hex encoding for digest value"),
33        }
34    }
35}
36
37impl error::Error for Error {
38    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
39        match self {
40            Error::InvalidDigestSize {
41                got: _,
42                expected: _,
43            } => None,
44            Error::InvalidHexEncoding(err) => Some(err),
45        }
46    }
47}
48
49impl From<FromHexError> for Error {
50    fn from(err: FromHexError) -> Self {
51        Error::InvalidHexEncoding(err)
52    }
53}
54
55#[derive(Debug, Copy, Clone)]
56pub struct TryFromSliceError(());
57
58impl fmt::Display for TryFromSliceError {
59    #[inline]
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        fmt::Display::fmt("could not convert slice to digest", f)
62    }
63}
64
65pub trait DigestAlg {
66    const HASH_SIZE: usize;
67    type DigestData: Clone + PartialEq + Hash + Send + AsRef<[u8]>;
68    type DigestContext: Clone;
69
70    fn try_from_slice(slice: &[u8]) -> Result<Self::DigestData, Error>;
71    fn new() -> Self::DigestContext;
72    fn append_data(ctx: &mut Self::DigestContext, data: &[u8]);
73    fn finalize(ctx: Self::DigestContext) -> Self::DigestData;
74}
75
76impl DigestAlg for Blake2b256 {
77    const HASH_SIZE: usize = 32;
78    type DigestData = [u8; Self::HASH_SIZE];
79    type DigestContext = Blake2b;
80
81    fn try_from_slice(slice: &[u8]) -> Result<Self::DigestData, Error> {
82        if slice.len() == Self::HASH_SIZE {
83            let mut out = [0u8; Self::HASH_SIZE];
84            out.copy_from_slice(slice);
85            Ok(out)
86        } else {
87            Err(Error::InvalidDigestSize {
88                expected: Self::HASH_SIZE,
89                got: slice.len(),
90            })
91        }
92    }
93
94    fn new() -> Self::DigestContext {
95        Blake2b::new(Self::HASH_SIZE)
96    }
97
98    fn append_data(ctx: &mut Self::DigestContext, data: &[u8]) {
99        ctx.input(data)
100    }
101
102    fn finalize(mut ctx: Self::DigestContext) -> Self::DigestData {
103        let mut out: Self::DigestData = [0; Self::HASH_SIZE];
104        ctx.result(&mut out);
105        out
106    }
107}
108
109impl DigestAlg for Sha3_256 {
110    const HASH_SIZE: usize = 32;
111    type DigestData = [u8; Self::HASH_SIZE];
112    type DigestContext = sha3::Sha3_256;
113
114    fn try_from_slice(slice: &[u8]) -> Result<Self::DigestData, Error> {
115        if slice.len() == Self::HASH_SIZE {
116            let mut out = [0u8; Self::HASH_SIZE];
117            out.copy_from_slice(slice);
118            Ok(out)
119        } else {
120            Err(Error::InvalidDigestSize {
121                expected: Self::HASH_SIZE,
122                got: slice.len(),
123            })
124        }
125    }
126
127    fn new() -> Self::DigestContext {
128        sha3::Sha3_256::new()
129    }
130
131    fn append_data(ctx: &mut Self::DigestContext, data: &[u8]) {
132        ctx.input(data)
133    }
134
135    fn finalize(mut ctx: Self::DigestContext) -> Self::DigestData {
136        let mut out: Self::DigestData = [0; Self::HASH_SIZE];
137        ctx.result(&mut out);
138        out
139    }
140}
141
142/// A Digest Context for the H digest algorithm
143pub struct Context<H: DigestAlg>(H::DigestContext);
144
145impl<H: DigestAlg> Clone for Context<H> {
146    fn clone(&self) -> Self {
147        Self(self.0.clone())
148    }
149}
150
151impl<H: DigestAlg> Context<H> {
152    /// Create a new digest context
153    pub fn new() -> Self {
154        Self(H::new())
155    }
156
157    /// Append data in the context
158    pub fn append_data(&mut self, data: &[u8]) {
159        H::append_data(&mut self.0, data)
160    }
161
162    /// Finalize a context and create a digest
163    pub fn finalize(self) -> Digest<H> {
164        Digest(H::finalize(self.0))
165    }
166}
167
168pub struct Digest<H: DigestAlg>(H::DigestData);
169
170impl<H: DigestAlg> Clone for Digest<H> {
171    fn clone(&self) -> Self {
172        Self(self.0.clone())
173    }
174}
175
176macro_rules! define_from_instances {
177    ($hash_ty:ty, $hash_size:expr, $bech32_hrp:expr) => {
178        impl From<Digest<$hash_ty>> for [u8; $hash_size] {
179            fn from(digest: Digest<$hash_ty>) -> Self {
180                digest.0
181            }
182        }
183
184        impl<'a> From<&'a Digest<$hash_ty>> for &'a [u8; $hash_size] {
185            fn from(digest: &'a Digest<$hash_ty>) -> Self {
186                &digest.0
187            }
188        }
189
190        impl From<[u8; $hash_size]> for Digest<$hash_ty> {
191            fn from(bytes: [u8; $hash_size]) -> Self {
192                Digest(bytes)
193            }
194        }
195        impl From<$hash_ty> for Digest<$hash_ty> {
196            fn from(bytes: $hash_ty) -> Self {
197                let out: [u8; $hash_size] = bytes.into();
198                out.into()
199            }
200        }
201        impl Bech32 for Digest<$hash_ty> {
202            const BECH32_HRP: &'static str = $bech32_hrp;
203
204            fn try_from_bech32_str(bech32_str: &str) -> bech32::Result<Self> {
205                let bytes = bech32::try_from_bech32_to_bytes::<Self>(bech32_str)?;
206                Digest::try_from(&bytes[..]).map_err(bech32::Error::data_invalid)
207            }
208
209            fn to_bech32_str(&self) -> String {
210                bech32::to_bech32_from_bytes::<Self>(self.as_ref())
211            }
212        }
213    };
214}
215
216define_from_instances!(Sha3_256, 32, "sha3");
217define_from_instances!(Blake2b256, 32, "blake2b");
218
219unsafe impl<H: DigestAlg> Send for Digest<H> {}
220
221impl<H: DigestAlg> PartialEq for Digest<H> {
222    fn eq(&self, other: &Self) -> bool {
223        self.0 == other.0
224    }
225}
226
227impl<H: DigestAlg> Eq for Digest<H> {}
228
229impl<H: DigestAlg> PartialOrd for Digest<H> {
230    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
231        Some(self.cmp(other))
232    }
233}
234
235impl<H: DigestAlg> Ord for Digest<H> {
236    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
237        self.as_ref().cmp(other.as_ref())
238    }
239}
240
241impl<H: DigestAlg> AsRef<[u8]> for Digest<H> {
242    fn as_ref(&self) -> &[u8] {
243        self.0.as_ref()
244    }
245}
246
247impl<H: DigestAlg> Hash for Digest<H> {
248    fn hash<HA: Hasher>(&self, state: &mut HA) {
249        self.0.hash(state)
250    }
251}
252
253impl<H: DigestAlg> TryFrom<&[u8]> for Digest<H> {
254    type Error = Error;
255    fn try_from(slice: &[u8]) -> Result<Digest<H>, Self::Error> {
256        <H as DigestAlg>::try_from_slice(slice).map(Digest)
257    }
258}
259
260impl<H: DigestAlg> FromStr for Digest<H> {
261    type Err = Error;
262    fn from_str(s: &str) -> result::Result<Digest<H>, Self::Err> {
263        let bytes = hex::decode(s)?;
264        Digest::try_from(&bytes[..])
265    }
266}
267
268impl<H: DigestAlg> fmt::Display for Digest<H> {
269    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270        write!(f, "{}", hex::encode(self.as_ref()))
271    }
272}
273impl<H: DigestAlg> fmt::Debug for Digest<H> {
274    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275        f.write_str(concat!(stringify!($hash_ty), "(0x"))?;
276        write!(f, "{}", hex::encode(self.as_ref()))?;
277        f.write_str(")")
278    }
279}
280
281impl<H: DigestAlg> Digest<H> {
282    /// Get the digest of a slice of data
283    pub fn digest(slice: &[u8]) -> Self {
284        let mut ctx = Context::new();
285        ctx.append_data(slice);
286        ctx.finalize()
287    }
288}
289
290use std::marker::PhantomData;
291
292/// A typed version of Digest
293pub struct DigestOf<H: DigestAlg, T> {
294    inner: Digest<H>,
295    marker: PhantomData<T>,
296}
297
298unsafe impl<H: DigestAlg, T> Send for DigestOf<H, T> {}
299
300impl<H: DigestAlg, T> Clone for DigestOf<H, T> {
301    fn clone(&self) -> Self {
302        DigestOf {
303            inner: self.inner.clone(),
304            marker: self.marker.clone(),
305        }
306    }
307}
308
309impl<H: DigestAlg, T> From<DigestOf<H, T>> for Digest<H> {
310    fn from(d: DigestOf<H, T>) -> Self {
311        d.inner
312    }
313}
314
315impl<H: DigestAlg, T> From<Digest<H>> for DigestOf<H, T> {
316    fn from(d: Digest<H>) -> Self {
317        DigestOf {
318            inner: d,
319            marker: PhantomData,
320        }
321    }
322}
323
324impl<H: DigestAlg, T> PartialEq for DigestOf<H, T> {
325    fn eq(&self, other: &Self) -> bool {
326        &self.inner == &other.inner
327    }
328}
329
330impl<H: DigestAlg, T> Eq for DigestOf<H, T> {}
331
332impl<H: DigestAlg, T> PartialOrd for DigestOf<H, T> {
333    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
334        self.inner.partial_cmp(&other.inner)
335    }
336}
337
338impl<H: DigestAlg, T> Ord for DigestOf<H, T> {
339    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
340        self.inner.cmp(&other.inner)
341    }
342}
343
344impl<H: DigestAlg, T> AsRef<[u8]> for DigestOf<H, T> {
345    fn as_ref(&self) -> &[u8] {
346        self.inner.as_ref()
347    }
348}
349
350impl<H: DigestAlg, T> Hash for DigestOf<H, T> {
351    fn hash<HA: Hasher>(&self, state: &mut HA) {
352        self.inner.hash(state)
353    }
354}
355
356impl<H: DigestAlg, T> TryFrom<&[u8]> for DigestOf<H, T> {
357    type Error = Error;
358    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
359        Digest::<H>::try_from(slice).map(|d| d.into())
360    }
361}
362
363impl<H: DigestAlg, T> FromStr for DigestOf<H, T> {
364    type Err = Error;
365    fn from_str(s: &str) -> result::Result<Self, Self::Err> {
366        Digest::<H>::from_str(s).map(|d| d.into())
367    }
368}
369
370impl<H: DigestAlg, T> fmt::Display for DigestOf<H, T> {
371    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372        self.inner.fmt(f)
373    }
374}
375impl<H: DigestAlg, T> fmt::Debug for DigestOf<H, T> {
376    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
377        self.inner.fmt(f)
378    }
379}
380
381impl<H: DigestAlg, T> DigestOf<H, T> {
382    /// Coerce a digest of T, to a digest of U
383    pub fn coerce<U>(&self) -> DigestOf<H, U> {
384        DigestOf {
385            inner: self.inner.clone(),
386            marker: PhantomData,
387        }
388    }
389
390    pub fn digest_byteslice<'a>(byteslice: &ByteSlice<'a, T>) -> Self {
391        let mut ctx = Context::new();
392        ctx.append_data(byteslice.as_slice());
393        DigestOf {
394            inner: ctx.finalize(),
395            marker: PhantomData,
396        }
397    }
398
399    /// Get the digest of object T, given its AsRef<[u8]> implementation
400    pub fn digest(obj: &T) -> Self
401    where
402        T: AsRef<[u8]>,
403    {
404        let mut ctx = Context::new();
405        ctx.append_data(obj.as_ref());
406        DigestOf {
407            inner: ctx.finalize(),
408            marker: PhantomData,
409        }
410    }
411
412    /// Get the digest of object T, given its serialization function in closure
413    pub fn digest_with<F>(obj: &T, f: F) -> Self
414    where
415        F: FnOnce(&T) -> &[u8],
416    {
417        let mut ctx = Context::new();
418        ctx.append_data(f(obj));
419        DigestOf {
420            inner: ctx.finalize(),
421            marker: PhantomData,
422        }
423    }
424}
425
426macro_rules! typed_define_from_instances {
427    ($hash_ty:ty, $hash_size:expr, $bech32_hrp:expr) => {
428        impl<T> From<DigestOf<$hash_ty, T>> for [u8; $hash_size] {
429            fn from(digest: DigestOf<$hash_ty, T>) -> Self {
430                digest.inner.into()
431            }
432        }
433        impl<'a, T> From<&'a DigestOf<$hash_ty, T>> for &'a [u8; $hash_size] {
434            fn from(digest: &'a DigestOf<$hash_ty, T>) -> Self {
435                (&digest.inner).into()
436            }
437        }
438
439        impl<T> From<[u8; $hash_size]> for DigestOf<$hash_ty, T> {
440            fn from(bytes: [u8; $hash_size]) -> Self {
441                Digest::from(bytes).into()
442            }
443        }
444
445        impl<T> From<$hash_ty> for DigestOf<$hash_ty, T> {
446            fn from(bytes: $hash_ty) -> Self {
447                let out: [u8; $hash_size] = bytes.into();
448                out.into()
449            }
450        }
451        impl<T> Bech32 for DigestOf<$hash_ty, T> {
452            const BECH32_HRP: &'static str = $bech32_hrp;
453
454            fn try_from_bech32_str(bech32_str: &str) -> bech32::Result<Self> {
455                let bytes = bech32::try_from_bech32_to_bytes::<Self>(bech32_str)?;
456                Digest::try_from(&bytes[..])
457                    .map_err(bech32::Error::data_invalid)
458                    .map(|d| d.into())
459            }
460
461            fn to_bech32_str(&self) -> String {
462                bech32::to_bech32_from_bytes::<Self>(self.inner.as_ref())
463            }
464        }
465    };
466}
467
468typed_define_from_instances!(Sha3_256, 32, "sha3");
469typed_define_from_instances!(Blake2b256, 32, "blake2b");