1pub const SHA1_LEN: usize = 20;
20pub const SHA256_LEN: usize = 32;
22
23#[must_use]
27pub fn sha1(data: &[u8]) -> [u8; SHA1_LEN] {
28 #[cfg(all(not(windows), not(feature = "rustls"), feature = "openssl"))]
29 {
30 tls_openssl::sha::sha1(data)
31 }
32 #[cfg(all(not(windows), feature = "rustls"))]
33 {
34 use sha1::Digest;
35 sha1::Sha1::digest(data).into()
36 }
37 #[cfg(windows)]
38 {
39 cng::hash(cng::BCRYPT_SHA1_ALG_HANDLE, data)
40 }
41}
42
43#[must_use]
45pub fn sha256(data: &[u8]) -> [u8; SHA256_LEN] {
46 #[cfg(all(not(windows), not(feature = "rustls"), feature = "openssl"))]
47 {
48 tls_openssl::sha::sha256(data)
49 }
50 #[cfg(all(not(windows), feature = "rustls"))]
51 {
52 use sha2::Digest;
53 sha2::Sha256::digest(data).into()
54 }
55 #[cfg(windows)]
56 {
57 cng::hash(cng::BCRYPT_SHA256_ALG_HANDLE, data)
58 }
59}
60
61#[cfg(windows)]
62pub(crate) mod cng {
63 use std::ptr;
64
65 pub(crate) use windows_sys::Win32::Security::Cryptography::{
66 BCRYPT_ALG_HANDLE, BCRYPT_SHA1_ALG_HANDLE, BCRYPT_SHA256_ALG_HANDLE,
67 BCRYPT_SHA384_ALG_HANDLE, BCRYPT_SHA512_ALG_HANDLE,
68 };
69 use windows_sys::Win32::Security::Cryptography::{
70 BCRYPT_HASH_HANDLE, BCryptCreateHash, BCryptDestroyHash, BCryptFinishHash, BCryptHashData,
71 };
72
73 struct Hash(BCRYPT_HASH_HANDLE);
75
76 impl Drop for Hash {
77 fn drop(&mut self) {
78 unsafe { BCryptDestroyHash(self.0) };
80 }
81 }
82
83 fn check(status: i32, op: &str) {
84 assert!(status >= 0, "{op} failed: NTSTATUS {status:#010x}");
85 }
86
87 pub(crate) fn hash<const N: usize>(alg: BCRYPT_ALG_HANDLE, data: &[u8]) -> [u8; N] {
92 let mut handle = ptr::null_mut();
93 check(
96 unsafe {
97 BCryptCreateHash(alg, &raw mut handle, ptr::null_mut(), 0, ptr::null(), 0, 0)
98 },
99 "BCryptCreateHash",
100 );
101 let hash = Hash(handle);
102 for chunk in data.chunks(u32::MAX as usize) {
103 check(
106 unsafe {
107 BCryptHashData(
108 hash.0,
109 chunk.as_ptr(),
110 u32::try_from(chunk.len()).unwrap_or(u32::MAX),
111 0,
112 )
113 },
114 "BCryptHashData",
115 );
116 }
117 let mut out = [0; N];
118 let len = u32::try_from(N).expect("digest size fits in u32");
119 check(
121 unsafe { BCryptFinishHash(hash.0, out.as_mut_ptr(), len, 0) },
122 "BCryptFinishHash",
123 );
124 out
125 }
126}
127
128#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
130pub enum Algorithm {
131 Sha1,
133 Sha256,
135}
136
137impl Algorithm {
138 #[must_use]
143 pub const fn for_hex_thumbprint(thumbprint: &str) -> Self {
144 if thumbprint.len() == SHA1_LEN * 2 {
145 Algorithm::Sha1
146 } else {
147 Algorithm::Sha256
148 }
149 }
150
151 #[must_use]
153 pub const fn digest_len(self) -> usize {
154 match self {
155 Algorithm::Sha1 => SHA1_LEN,
156 Algorithm::Sha256 => SHA256_LEN,
157 }
158 }
159
160 #[must_use]
162 pub fn digest(self, data: &[u8]) -> Vec<u8> {
163 match self {
164 Algorithm::Sha1 => sha1(data).to_vec(),
165 Algorithm::Sha256 => sha256(data).to_vec(),
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
179 fn nist_vectors() {
180 assert_eq!(
181 hex::encode(sha1(b"")),
182 "da39a3ee5e6b4b0d3255bfef95601890afd80709"
183 );
184 assert_eq!(
185 hex::encode(sha1(b"abc")),
186 "a9993e364706816aba3e25717850c26c9cd0d89d"
187 );
188 assert_eq!(
189 hex::encode(sha256(b"")),
190 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
191 );
192 assert_eq!(
193 hex::encode(sha256(b"abc")),
194 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
195 );
196 }
197
198 #[test]
203 fn algorithm() {
204 assert_eq!(
205 Algorithm::for_hex_thumbprint(&"0".repeat(40)),
206 Algorithm::Sha1
207 );
208 assert_eq!(
209 Algorithm::for_hex_thumbprint(&"0".repeat(64)),
210 Algorithm::Sha256
211 );
212 assert_eq!(Algorithm::for_hex_thumbprint("abc"), Algorithm::Sha256);
213 assert_eq!(Algorithm::Sha1.digest_len(), 20);
214 assert_eq!(Algorithm::Sha256.digest_len(), 32);
215 assert_eq!(Algorithm::Sha1.digest(b"abc"), sha1(b"abc").to_vec());
216 assert_eq!(Algorithm::Sha256.digest(b"abc"), sha256(b"abc").to_vec());
217 }
218}