sha256/
lib.rs

1//! sha256 crypto digest util
2//!
3//! ```rust
4//!
5//! use sha256::{digest, try_digest};
6//!
7//! //sha256 digest String
8//! let input = String::from("hello");
9//! let val = digest(input);
10//! assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824");
11//!
12//! //sha256 digest &str
13//! let input = "hello";
14//! let val = digest(input);
15//! assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824");
16//!
17//! //sha256 digest &mut &str
18//! let mut input = "hello";
19//! let val = digest(&mut input);
20//! assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824");
21//!
22//! //sha256 digest char
23//! let mut input = "π";
24//! let val = digest(input);
25//! assert_eq!(val,"2617fcb92baa83a96341de050f07a3186657090881eae6b833f66a035600f35a");
26//!
27//! //sha256 digest bytes
28//! let input = b"hello";
29//! let val = digest(input);
30//! assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824");
31//!
32//! //sha256 digest file
33//! use std::path::Path;
34//! let input = Path::new("./foo.file");
35//! let val = try_digest(input).unwrap();
36//! assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1");
37//!
38//! ```
39
40#[cfg(feature = "native_openssl")]
41use crate::openssl_sha256::OpenSslSha256;
42
43#[cfg(feature = "async")]
44pub use async_digest::*;
45use sha2::digest::Output;
46use sha2::{Digest, Sha256};
47use std::fmt::Debug;
48use std::fs;
49use std::io;
50use std::io::{BufReader, Read};
51use std::path::Path;
52
53/// sha256 digest string
54///
55/// # Examples
56///
57/// ```rust
58/// use sha256::digest;
59/// let input = "hello";
60/// let val = digest(input);
61/// assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824")
62/// ```
63///
64pub fn digest<D: Sha256Digest>(input: D) -> String {
65    input.digest()
66}
67
68/// sha256 digest file
69///
70/// # Examples
71///
72/// ```rust
73/// use sha256::try_digest;
74/// use std::path::Path;
75/// let input = Path::new("./foo.file");
76/// let val = try_digest(input).unwrap();
77/// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1")
78/// ```
79pub fn try_digest<D: TrySha256Digest>(input: D) -> Result<String, D::Error> {
80    input.digest()
81}
82
83/// sha256 digest bytes
84///
85/// # Examples
86///
87/// ```rust
88/// use sha256::digest_bytes;
89/// let input = b"hello";
90/// let val = digest_bytes(input);
91/// assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824")
92/// ```
93///
94#[deprecated(since = "1.1.0", note = "Use new function `digest()` instead")]
95pub fn digest_bytes(input: &[u8]) -> String {
96    __digest__(input)
97}
98
99/// sha256 digest file
100///
101/// # Examples
102///
103/// ```rust
104/// use sha256::digest_file;
105/// use std::path::Path;
106/// let input = Path::new("./foo.file");
107/// let val = digest_file(input).unwrap();
108/// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1")
109/// ```
110#[deprecated(since = "1.1.0", note = "Use new function `try_digest()` instead")]
111pub fn digest_file<P: AsRef<Path>>(path: P) -> Result<String, io::Error> {
112    let bytes = fs::read(path)?;
113    Ok(__digest__(&bytes))
114}
115
116pub trait Sha256Digest {
117    fn digest(self) -> String;
118}
119
120#[async_trait::async_trait]
121pub trait TrySha256Digest {
122    type Error: Debug;
123
124    fn digest(self) -> Result<String, Self::Error>;
125
126    #[cfg(feature = "async")]
127    async fn async_digest(self) -> Result<String, Self::Error>;
128
129    #[cfg(feature = "native_openssl")]
130    async fn async_openssl_digest(self) -> Result<String, Self::Error>;
131}
132
133impl<const N: usize> Sha256Digest for &[u8; N] {
134    fn digest(self) -> String {
135        __digest__(self)
136    }
137}
138
139impl Sha256Digest for &[u8] {
140    fn digest(self) -> String {
141        __digest__(self)
142    }
143}
144
145impl Sha256Digest for &Vec<u8> {
146    fn digest(self) -> String {
147        __digest__(self)
148    }
149}
150
151impl Sha256Digest for Vec<u8> {
152    fn digest(self) -> String {
153        __digest__(&self)
154    }
155}
156
157impl Sha256Digest for String {
158    fn digest(self) -> String {
159        __digest__(self.as_bytes())
160    }
161}
162
163impl Sha256Digest for &str {
164    fn digest(self) -> String {
165        __digest__(self.as_bytes())
166    }
167}
168
169impl Sha256Digest for char {
170    fn digest(self) -> String {
171        __digest__(self.encode_utf8(&mut [0; 4]).as_bytes())
172    }
173}
174
175impl Sha256Digest for &mut &str {
176    fn digest(self) -> String {
177        __digest__(self.as_bytes())
178    }
179}
180
181impl Sha256Digest for &String {
182    fn digest(self) -> String {
183        __digest__(self.as_bytes())
184    }
185}
186
187#[async_trait::async_trait]
188impl<P> TrySha256Digest for P
189where
190    P: AsRef<Path> + Send,
191{
192    type Error = io::Error;
193
194    fn digest(self) -> Result<String, Self::Error> {
195        let f = fs::File::open(self)?;
196        let reader = BufReader::new(f);
197        let sha = Sha256::new();
198        calc(reader, sha)
199    }
200
201    #[cfg(feature = "async")]
202    async fn async_digest(self) -> Result<String, Self::Error> {
203        let f = tokio::fs::File::open(self).await?;
204        let reader = tokio::io::BufReader::new(f);
205        let sha = Sha256::new();
206        async_calc(reader, sha).await
207    }
208
209    #[cfg(all(feature = "async", feature = "native_openssl"))]
210    async fn async_openssl_digest(self) -> Result<String, Self::Error> {
211        let f = tokio::fs::File::open(self).await?;
212        let reader = tokio::io::BufReader::new(f);
213        let sha = OpenSslSha256::new();
214        async_calc(reader, sha).await
215    }
216}
217
218fn __digest__(data: &[u8]) -> String {
219    hex::encode(Sha256::digest(data))
220}
221
222trait CalculatorInput {
223    fn read_inner(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;
224}
225
226impl<T> CalculatorInput for T
227where
228    T: Read,
229{
230    fn read_inner(&mut self, buf: &mut [u8]) -> io::Result<usize> {
231        self.read(buf)
232    }
233}
234
235pub trait CalculatorSelector {
236    type FinishType: AsRef<[u8]>;
237    fn update_inner(&mut self, data: &[u8]);
238    fn finish_inner(self) -> Self::FinishType;
239}
240
241impl CalculatorSelector for Sha256 {
242    type FinishType = Output<Sha256>;
243
244    fn update_inner(&mut self, data: &[u8]) {
245        self.update(data)
246    }
247
248    fn finish_inner(self) -> Self::FinishType {
249        self.finalize()
250    }
251}
252
253fn calc<I, S>(mut input: I, mut selector: S) -> io::Result<String>
254where
255    I: CalculatorInput,
256    S: CalculatorSelector,
257{
258    let mut buf = [0u8; 1024];
259    loop {
260        let len = input.read_inner(&mut buf)?;
261        if len == 0 {
262            break;
263        }
264        selector.update_inner(&buf[0..len]);
265    }
266    let hash = selector.finish_inner();
267    Ok(hex::encode(hash))
268}
269
270#[cfg(feature = "async")]
271pub mod async_digest {
272    use crate::{CalculatorSelector, TrySha256Digest};
273    use bytes::BytesMut;
274    use std::io;
275
276    /// sha256 digest file
277    ///
278    /// # Examples
279    ///
280    /// ```rust
281    /// use sha256::{try_async_digest};
282    /// use std::path::Path;
283    /// let input = Path::new("./foo.file");
284    /// tokio_test::block_on(async{
285    /// let val = try_async_digest(input).await.unwrap();
286    /// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1")
287    /// });
288    ///
289    /// ```
290    pub async fn try_async_digest<D: TrySha256Digest>(input: D) -> Result<String, D::Error> {
291        input.async_digest().await
292    }
293
294    /// sha256 digest file
295    ///
296    /// # Examples
297    ///
298    /// ```rust
299    /// use sha256::{try_async_openssl_digest};
300    /// use std::path::Path;
301    /// let input = Path::new("./foo.file");
302    /// tokio_test::block_on(async{
303    /// let val = try_async_openssl_digest(input).await.unwrap();
304    /// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1")
305    /// });
306    /// ```
307    #[cfg(feature = "native_openssl")]
308    pub async fn try_async_openssl_digest<D: TrySha256Digest>(
309        input: D,
310    ) -> Result<String, D::Error> {
311        input.async_openssl_digest().await
312    }
313
314    #[async_trait::async_trait]
315    pub trait AsyncCalculatorInput {
316        async fn read_inner(&mut self, buf: &mut BytesMut) -> io::Result<usize>;
317    }
318
319    pub async fn async_calc<I, S>(mut input: I, mut selector: S) -> io::Result<String>
320    where
321        I: AsyncCalculatorInput,
322        S: CalculatorSelector,
323    {
324        let mut buf = BytesMut::with_capacity(1024);
325        loop {
326            buf.clear();
327            let len = input.read_inner(&mut buf).await?;
328            if len == 0 {
329                break;
330            }
331            selector.update_inner(&buf[0..len]);
332        }
333        let hash = selector.finish_inner();
334        Ok(hex::encode(hash))
335    }
336
337    #[async_trait::async_trait]
338    impl<R> AsyncCalculatorInput for tokio::io::BufReader<R>
339    where
340        R: tokio::io::AsyncRead + Unpin + Send,
341    {
342        async fn read_inner(&mut self, buf: &mut BytesMut) -> io::Result<usize> {
343            use tokio::io::AsyncReadExt;
344
345            self.read_buf(buf).await
346        }
347    }
348}
349
350#[cfg(feature = "native_openssl")]
351mod openssl_sha256 {
352    use crate::CalculatorSelector;
353
354    pub type OpenSslSha256 = openssl::sha::Sha256;
355
356    impl CalculatorSelector for OpenSslSha256 {
357        type FinishType = [u8; 32];
358
359        fn update_inner(&mut self, data: &[u8]) {
360            self.update(data)
361        }
362
363        fn finish_inner(self) -> Self::FinishType {
364            self.finish()
365        }
366    }
367}
368
369#[cfg(test)]
370mod tests {
371    use super::*;
372
373    #[cfg(feature = "native_openssl")]
374    #[test]
375    fn test_openssl() {
376        let input = Path::new("./foo.file");
377        let f = fs::File::open(input).unwrap();
378        let reader = BufReader::new(f);
379        let sha = openssl::sha::Sha256::new();
380        assert_eq!(
381            "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
382            calc(reader, sha).unwrap()
383        );
384    }
385
386    #[test]
387    fn test_sha256() {
388        let input = Path::new("./foo.file");
389        let f = fs::File::open(input).unwrap();
390        let reader = BufReader::new(f);
391        let sha = Sha256::new();
392        assert_eq!(
393            "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
394            calc(reader, sha).unwrap()
395        );
396    }
397
398    #[cfg(all(feature = "async", feature = "native_openssl"))]
399    #[tokio::test]
400    async fn test_async_openssl() {
401        let input = Path::new("./foo.file");
402        let f = tokio::fs::File::open(input).await.unwrap();
403        let reader = tokio::io::BufReader::new(f);
404        let sha = openssl::sha::Sha256::new();
405        assert_eq!(
406            "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
407            async_calc(reader, sha).await.unwrap()
408        );
409    }
410
411    #[cfg(feature = "async")]
412    #[tokio::test]
413    async fn test_async() {
414        let input = Path::new("./foo.file");
415        let f = tokio::fs::File::open(input).await.unwrap();
416        let reader = tokio::io::BufReader::new(f);
417        let sha = Sha256::new();
418        assert_eq!(
419            "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
420            async_calc(reader, sha).await.unwrap()
421        );
422    }
423
424    #[cfg(all(feature = "async", feature = "native_openssl"))]
425    #[tokio::test]
426    async fn test_try_async_openssl_digest() {
427        let hash = try_async_openssl_digest("./foo.file").await.unwrap();
428        assert_eq!(
429            "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
430            hash
431        );
432    }
433
434    #[cfg(feature = "async")]
435    #[tokio::test]
436    async fn test_try_async_digest() {
437        let hash = try_async_digest("./foo.file").await.unwrap();
438        assert_eq!(
439            "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
440            hash
441        );
442    }
443
444    #[cfg(feature = "async")]
445    #[tokio::test]
446    async fn test_async_parity() {
447        let bytes = (0..0x1000).map(|v| (v % 256) as u8).collect::<Vec<_>>();
448        let val = digest(&bytes);
449
450        let async_res = {
451            let bytes = &bytes;
452            // We want to force Poll::Pending on reads during async_calc, which may break parity
453            // between sync and async hashing.
454            let (client, mut server) = tokio::io::duplex(64);
455            let reader = tokio::io::BufReader::new(client);
456            let sha = Sha256::new();
457
458            use tokio::io::AsyncWriteExt;
459            tokio::join! {
460                async_calc(reader, sha),
461                async move {
462                    server.write_all(&bytes[..]).await.unwrap();
463                    core::mem::drop(server);
464                }
465            }
466            .0
467            .unwrap()
468        };
469
470        let sync_res = {
471            let reader = BufReader::new(&*bytes);
472            let sha = Sha256::new();
473            calc(reader, sha).unwrap()
474        };
475        assert_eq!(val, async_res);
476        assert_eq!(async_res, sync_res);
477    }
478
479    #[cfg(all(feature = "async", feature = "native_openssl"))]
480    #[tokio::test]
481    async fn test_async_parity_openssl() {
482        let bytes = (0..0x1000).map(|v| (v % 256) as u8).collect::<Vec<_>>();
483        let val = digest(&bytes);
484
485        let async_res = {
486            let bytes = &bytes;
487            // We want to force Poll::Pending on reads during async_calc, which may break parity
488            // between sync and async hashing.
489            let (client, mut server) = tokio::io::duplex(64);
490            let reader = tokio::io::BufReader::new(client);
491            let sha = OpenSslSha256::new();
492
493            use tokio::io::AsyncWriteExt;
494            tokio::join! {
495                async_calc(reader, sha),
496                async move {
497                    server.write_all(&bytes[..]).await.unwrap();
498                    core::mem::drop(server);
499                }
500            }
501            .0
502            .unwrap()
503        };
504
505        let sync_res = {
506            let reader = BufReader::new(&*bytes);
507            let sha = OpenSslSha256::new();
508            calc(reader, sha).unwrap()
509        };
510        assert_eq!(val, async_res);
511        assert_eq!(async_res, sync_res);
512    }
513}