//! sha256 crypto digest util //! //! ```rust //! //! use sha256::{digest, try_digest}; //! //! //sha256 digest String //! let input = String::from("hello"); //! let val = digest(input); //! assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"); //! //! //sha256 digest &str //! let input = "hello"; //! let val = digest(input); //! assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"); //! //! //sha256 digest &mut &str //! let mut input = "hello"; //! let val = digest(&mut input); //! assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"); //! //! //sha256 digest char //! let mut input = "π"; //! let val = digest(input); //! assert_eq!(val,"2617fcb92baa83a96341de050f07a3186657090881eae6b833f66a035600f35a"); //! //! //sha256 digest bytes //! let input = b"hello"; //! let val = digest(input); //! assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"); //! //! //sha256 digest file //! use std::path::Path; //! let input = Path::new("./foo.file"); //! let val = try_digest(input).unwrap(); //! assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1"); //! //! ``` #[cfg(feature = "native_openssl")] use crate::openssl_sha256::OpenSslSha256; #[cfg(feature = "async")] pub use async_digest::*; use sha2::digest::Output; use sha2::{Digest, Sha256}; use std::fmt::Debug; use std::fs; use std::io; use std::io::{BufReader, Read}; use std::path::Path; /// sha256 digest string /// /// # Examples /// /// ```rust /// use sha256::digest; /// let input = "hello"; /// let val = digest(input); /// assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824") /// ``` /// pub fn digest(input: D) -> String { input.digest() } /// sha256 digest file /// /// # Examples /// /// ```rust /// use sha256::try_digest; /// use std::path::Path; /// let input = Path::new("./foo.file"); /// let val = try_digest(input).unwrap(); /// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1") /// ``` pub fn try_digest(input: D) -> Result { input.digest() } /// sha256 digest bytes /// /// # Examples /// /// ```rust /// use sha256::digest_bytes; /// let input = b"hello"; /// let val = digest_bytes(input); /// assert_eq!(val,"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824") /// ``` /// #[deprecated(since = "1.1.0", note = "Use new function `digest()` instead")] pub fn digest_bytes(input: &[u8]) -> String { __digest__(input) } /// sha256 digest file /// /// # Examples /// /// ```rust /// use sha256::digest_file; /// use std::path::Path; /// let input = Path::new("./foo.file"); /// let val = digest_file(input).unwrap(); /// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1") /// ``` #[deprecated(since = "1.1.0", note = "Use new function `try_digest()` instead")] pub fn digest_file>(path: P) -> Result { let bytes = fs::read(path)?; Ok(__digest__(&bytes)) } pub trait Sha256Digest { fn digest(self) -> String; } #[async_trait::async_trait] pub trait TrySha256Digest { type Error: Debug; fn digest(self) -> Result; #[cfg(feature = "async")] async fn async_digest(self) -> Result; #[cfg(feature = "native_openssl")] async fn async_openssl_digest(self) -> Result; } impl Sha256Digest for &[u8; N] { fn digest(self) -> String { __digest__(self) } } impl Sha256Digest for &[u8] { fn digest(self) -> String { __digest__(self) } } impl Sha256Digest for &Vec { fn digest(self) -> String { __digest__(self) } } impl Sha256Digest for Vec { fn digest(self) -> String { __digest__(&self) } } impl Sha256Digest for String { fn digest(self) -> String { __digest__(self.as_bytes()) } } impl Sha256Digest for &str { fn digest(self) -> String { __digest__(self.as_bytes()) } } impl Sha256Digest for char { fn digest(self) -> String { __digest__(self.encode_utf8(&mut [0; 4]).as_bytes()) } } impl Sha256Digest for &mut &str { fn digest(self) -> String { __digest__(self.as_bytes()) } } impl Sha256Digest for &String { fn digest(self) -> String { __digest__(self.as_bytes()) } } #[async_trait::async_trait] impl

TrySha256Digest for P where P: AsRef + Send, { type Error = io::Error; fn digest(self) -> Result { let f = fs::File::open(self)?; let reader = BufReader::new(f); let sha = Sha256::new(); calc(reader, sha) } #[cfg(feature = "async")] async fn async_digest(self) -> Result { let f = tokio::fs::File::open(self).await?; let reader = tokio::io::BufReader::new(f); let sha = Sha256::new(); async_calc(reader, sha).await } #[cfg(all(feature = "async", feature = "native_openssl"))] async fn async_openssl_digest(self) -> Result { let f = tokio::fs::File::open(self).await?; let reader = tokio::io::BufReader::new(f); let sha = OpenSslSha256::new(); async_calc(reader, sha).await } } fn __digest__(data: &[u8]) -> String { hex::encode(Sha256::digest(data)) } trait CalculatorInput { fn read_inner(&mut self, buf: &mut [u8]) -> std::io::Result; } impl CalculatorInput for T where T: Read, { fn read_inner(&mut self, buf: &mut [u8]) -> io::Result { self.read(buf) } } pub trait CalculatorSelector { type FinishType: AsRef<[u8]>; fn update_inner(&mut self, data: &[u8]); fn finish_inner(self) -> Self::FinishType; } impl CalculatorSelector for Sha256 { type FinishType = Output; fn update_inner(&mut self, data: &[u8]) { self.update(data) } fn finish_inner(self) -> Self::FinishType { self.finalize() } } fn calc(mut input: I, mut selector: S) -> io::Result where I: CalculatorInput, S: CalculatorSelector, { let mut buf = [0u8; 1024]; loop { let len = input.read_inner(&mut buf)?; if len == 0 { break; } selector.update_inner(&buf[0..len]); } let hash = selector.finish_inner(); Ok(hex::encode(hash)) } #[cfg(feature = "async")] pub mod async_digest { use crate::{CalculatorSelector, TrySha256Digest}; use bytes::BytesMut; use std::io; /// sha256 digest file /// /// # Examples /// /// ```rust /// use sha256::{try_async_digest}; /// use std::path::Path; /// let input = Path::new("./foo.file"); /// tokio_test::block_on(async{ /// let val = try_async_digest(input).await.unwrap(); /// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1") /// }); /// /// ``` pub async fn try_async_digest(input: D) -> Result { input.async_digest().await } /// sha256 digest file /// /// # Examples /// /// ```rust /// use sha256::{try_async_openssl_digest}; /// use std::path::Path; /// let input = Path::new("./foo.file"); /// tokio_test::block_on(async{ /// let val = try_async_openssl_digest(input).await.unwrap(); /// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1") /// }); /// ``` #[cfg(feature = "native_openssl")] pub async fn try_async_openssl_digest( input: D, ) -> Result { input.async_openssl_digest().await } #[async_trait::async_trait] pub trait AsyncCalculatorInput { async fn read_inner(&mut self, buf: &mut BytesMut) -> io::Result; } pub async fn async_calc(mut input: I, mut selector: S) -> io::Result where I: AsyncCalculatorInput, S: CalculatorSelector, { let mut buf = BytesMut::with_capacity(1024); loop { buf.clear(); let len = input.read_inner(&mut buf).await?; if len == 0 { break; } selector.update_inner(&buf[0..len]); } let hash = selector.finish_inner(); Ok(hex::encode(hash)) } #[async_trait::async_trait] impl AsyncCalculatorInput for tokio::io::BufReader where R: tokio::io::AsyncRead + Unpin + Send, { async fn read_inner(&mut self, buf: &mut BytesMut) -> io::Result { use tokio::io::AsyncReadExt; self.read_buf(buf).await } } } #[cfg(feature = "native_openssl")] mod openssl_sha256 { use crate::CalculatorSelector; pub type OpenSslSha256 = openssl::sha::Sha256; impl CalculatorSelector for OpenSslSha256 { type FinishType = [u8; 32]; fn update_inner(&mut self, data: &[u8]) { self.update(data) } fn finish_inner(self) -> Self::FinishType { self.finish() } } } #[cfg(test)] mod tests { use super::*; #[cfg(feature = "native_openssl")] #[test] fn test_openssl() { let input = Path::new("./foo.file"); let f = fs::File::open(input).unwrap(); let reader = BufReader::new(f); let sha = openssl::sha::Sha256::new(); assert_eq!( "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1", calc(reader, sha).unwrap() ); } #[test] fn test_sha256() { let input = Path::new("./foo.file"); let f = fs::File::open(input).unwrap(); let reader = BufReader::new(f); let sha = Sha256::new(); assert_eq!( "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1", calc(reader, sha).unwrap() ); } #[cfg(all(feature = "async", feature = "native_openssl"))] #[tokio::test] async fn test_async_openssl() { let input = Path::new("./foo.file"); let f = tokio::fs::File::open(input).await.unwrap(); let reader = tokio::io::BufReader::new(f); let sha = openssl::sha::Sha256::new(); assert_eq!( "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1", async_calc(reader, sha).await.unwrap() ); } #[cfg(feature = "async")] #[tokio::test] async fn test_async() { let input = Path::new("./foo.file"); let f = tokio::fs::File::open(input).await.unwrap(); let reader = tokio::io::BufReader::new(f); let sha = Sha256::new(); assert_eq!( "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1", async_calc(reader, sha).await.unwrap() ); } #[cfg(all(feature = "async", feature = "native_openssl"))] #[tokio::test] async fn test_try_async_openssl_digest() { let hash = try_async_openssl_digest("./foo.file").await.unwrap(); assert_eq!( "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1", hash ); } #[cfg(feature = "async")] #[tokio::test] async fn test_try_async_digest() { let hash = try_async_digest("./foo.file").await.unwrap(); assert_eq!( "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1", hash ); } #[cfg(feature = "async")] #[tokio::test] async fn test_async_parity() { let bytes = (0..0x1000).map(|v| (v % 256) as u8).collect::>(); let val = digest(&bytes); let async_res = { let bytes = &bytes; // We want to force Poll::Pending on reads during async_calc, which may break parity // between sync and async hashing. let (client, mut server) = tokio::io::duplex(64); let reader = tokio::io::BufReader::new(client); let sha = Sha256::new(); use tokio::io::AsyncWriteExt; tokio::join! { async_calc(reader, sha), async move { server.write_all(&bytes[..]).await.unwrap(); core::mem::drop(server); } } .0 .unwrap() }; let sync_res = { let reader = BufReader::new(&*bytes); let sha = Sha256::new(); calc(reader, sha).unwrap() }; assert_eq!(val, async_res); assert_eq!(async_res, sync_res); } #[cfg(all(feature = "async", feature = "native_openssl"))] #[tokio::test] async fn test_async_parity_openssl() { let bytes = (0..0x1000).map(|v| (v % 256) as u8).collect::>(); let val = digest(&bytes); let async_res = { let bytes = &bytes; // We want to force Poll::Pending on reads during async_calc, which may break parity // between sync and async hashing. let (client, mut server) = tokio::io::duplex(64); let reader = tokio::io::BufReader::new(client); let sha = OpenSslSha256::new(); use tokio::io::AsyncWriteExt; tokio::join! { async_calc(reader, sha), async move { server.write_all(&bytes[..]).await.unwrap(); core::mem::drop(server); } } .0 .unwrap() }; let sync_res = { let reader = BufReader::new(&*bytes); let sha = OpenSslSha256::new(); calc(reader, sha).unwrap() }; assert_eq!(val, async_res); assert_eq!(async_res, sync_res); } }