cardano_serialization_lib/chain_crypto/
digest.rs1use 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
142pub 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 pub fn new() -> Self {
154 Self(H::new())
155 }
156
157 pub fn append_data(&mut self, data: &[u8]) {
159 H::append_data(&mut self.0, data)
160 }
161
162 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 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
292pub 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 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 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 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");