winget_types/shared/sha_256.rs
1use core::fmt;
2
3use heapless::String;
4use sha2::{Sha256, digest::Output};
5
6// 256 bits / 4 bits per hex character
7const SHA256_LEN: usize = 256 / 0xF_u8.count_ones() as usize;
8
9#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Sha256String(String<SHA256_LEN>);
12
13impl Sha256String {
14 /// Creates a `Sha256String` from a Sha256 digest.
15 ///
16 /// # Examples
17 ///
18 /// ```
19 /// use winget_types::{sha2::{Digest, Sha256}, Sha256String};
20 ///
21 /// // Digest some data manually
22 /// let sha256_digest = Sha256::digest("abc");
23 ///
24 /// assert_eq!(
25 /// Sha256String::from_digest(&sha256_digest).as_str(),
26 /// "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD"
27 /// );
28 /// ```
29 #[must_use]
30 pub fn from_digest(digest: &Output<Sha256>) -> Self {
31 let mut encode_buf = [0; SHA256_LEN];
32
33 Self(
34 base16ct::upper::encode_str(digest, &mut encode_buf)
35 .unwrap_or_else(|_| unreachable!("SHA256 digests should always be 32 bytes long"))
36 .parse::<String<SHA256_LEN>>()
37 .unwrap_or_else(|_| {
38 unreachable!("Sha256 hashes should always be {SHA256_LEN} bytes long")
39 }),
40 )
41 }
42
43 /// Creates a `Sha256String` by hashing data from a reader.
44 ///
45 /// This will repeatedly read the data into a buffer of length 4096.
46 ///
47 /// # Errors
48 ///
49 /// Returns the propagated `Err` from [`io::read`].
50 ///
51 /// # Examples
52 ///
53 /// [`File`]s implement `Read`:
54 ///
55 /// [`File`]: std::fs::File
56 /// [`io::read`]: std::io::Read::read
57 ///
58 /// ```no_run
59 /// use std::io;
60 /// use std::fs::File;
61 ///
62 /// use winget_types::Sha256String;
63 ///
64 /// fn main() -> io::Result<()> {
65 /// let mut f = File::open("foo.txt")?;
66 ///
67 /// let sha256_string = Sha256String::hash_from_reader(f)?;
68 ///
69 /// println!("File SHA256 hash: {sha256_string}");
70 /// Ok(())
71 /// }
72 /// ```
73 #[cfg(feature = "std")]
74 pub fn hash_from_reader<R: std::io::Read>(mut reader: R) -> std::io::Result<Self> {
75 use sha2::{Digest, Sha256};
76
77 let mut hasher = Sha256::new();
78 let mut buffer = [0; 1 << 12];
79
80 loop {
81 let count = reader.read(&mut buffer)?;
82 if count == 0 {
83 break;
84 }
85 hasher.update(&buffer[..count]);
86 }
87
88 Ok(Self::from_digest(&hasher.finalize()))
89 }
90
91 /// Extracts a string slice containing the entire `Sha256String`.
92 #[must_use]
93 #[inline]
94 pub fn as_str(&self) -> &str {
95 self.0.as_str()
96 }
97}
98
99impl Default for Sha256String {
100 fn default() -> Self {
101 Self(core::iter::repeat_n('0', SHA256_LEN).collect::<_>())
102 }
103}
104
105impl fmt::Display for Sha256String {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 self.0.fmt(f)
108 }
109}