rust_auth_utils/
hash.rs

1// based on https://github.com/better-auth/utils/blob/main/src/hash.ts
2
3use crate::base64::{Base64, Base64Url};
4use crate::types::{EncodingFormat, SHAFamily};
5use sha1::Sha1;
6use sha2::{Digest, Sha256, Sha384, Sha512};
7
8pub struct Hash {
9    algorithm: SHAFamily,
10    encoding: Option<EncodingFormat>,
11}
12
13impl Hash {
14    pub fn new(algorithm: SHAFamily, encoding: Option<EncodingFormat>) -> Self {
15        Self {
16            algorithm,
17            encoding,
18        }
19    }
20
21    pub async fn digest(
22        &self,
23        input: impl AsRef<[u8]>,
24    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
25        let data = input.as_ref();
26
27        let hash = match self.algorithm {
28            SHAFamily::SHA1 => {
29                let mut hasher = Sha1::new();
30                hasher.update(data);
31                hasher.finalize().to_vec()
32            }
33            SHAFamily::SHA256 => {
34                let mut hasher = Sha256::new();
35                hasher.update(data);
36                hasher.finalize().to_vec()
37            }
38            SHAFamily::SHA384 => {
39                let mut hasher = Sha384::new();
40                hasher.update(data);
41                hasher.finalize().to_vec()
42            }
43            SHAFamily::SHA512 => {
44                let mut hasher = Sha512::new();
45                hasher.update(data);
46                hasher.finalize().to_vec()
47            }
48        };
49
50        match self.encoding {
51            Some(EncodingFormat::Hex) => Ok(hash
52                .iter()
53                .map(|b| format!("{:02x}", b))
54                .collect::<String>()
55                .into_bytes()),
56            Some(EncodingFormat::Base64) => Ok(Base64::encode(&hash, None).into_bytes()),
57            Some(EncodingFormat::Base64Url) => {
58                Ok(Base64Url::encode(&hash, Some(true)).into_bytes())
59            }
60            Some(EncodingFormat::Base64UrlNoPad) => {
61                Ok(Base64Url::encode(&hash, Some(false)).into_bytes())
62            }
63            Some(EncodingFormat::None) | None => Ok(hash),
64        }
65    }
66
67    pub async fn digest_string(
68        &self,
69        input: impl AsRef<str>,
70    ) -> Result<String, Box<dyn std::error::Error>> {
71        let bytes = self.digest(input.as_ref().as_bytes()).await?;
72        Ok(String::from_utf8(bytes)?)
73    }
74}
75
76pub fn create_hash(algorithm: SHAFamily, encoding: Option<EncodingFormat>) -> Hash {
77    Hash::new(algorithm, encoding)
78}