1#![forbid(unsafe_code)]
4
5use core::{
6 fmt::{self, Debug},
7 ops::{Deref, DerefMut},
8};
9
10use generic_array::{ArrayLength, GenericArray, IntoArrayLength};
11use sha3_utils::{encode_string, right_encode_bytes};
12use subtle::{Choice, ConstantTimeEq};
13use typenum::{
14 generic_const_mappings::Const,
15 type_operators::{IsGreaterOrEqual, IsLess},
16 Unsigned, U32, U65536,
17};
18
19pub trait Hash: Clone {
35 type DigestSize: ArrayLength + IsGreaterOrEqual<U32> + IsLess<U65536> + 'static;
39 const DIGEST_SIZE: usize = Self::DigestSize::USIZE;
41
42 fn new() -> Self;
44
45 fn update(&mut self, data: &[u8]);
47
48 fn digest(self) -> Digest<Self::DigestSize>;
50
51 fn hash(data: &[u8]) -> Digest<Self::DigestSize>
57 where
58 Self: Sized,
59 {
60 let mut h = Self::new();
61 h.update(data);
62 h.digest()
63 }
64}
65
66#[derive(Clone, Default)]
68#[repr(transparent)]
69pub struct Digest<N: ArrayLength>(GenericArray<u8, N>);
70
71impl<N: ArrayLength> Digest<N> {
72 #[inline]
74 pub const fn new(digest: GenericArray<u8, N>) -> Self {
75 Self(digest)
76 }
77
78 #[inline]
80 pub const fn from_array<const U: usize>(digest: [u8; U]) -> Self
81 where
82 Const<U>: IntoArrayLength<ArrayLength = N>,
83 {
84 Self::new(GenericArray::from_array(digest))
85 }
86
87 #[inline]
89 #[allow(clippy::len_without_is_empty)]
90 pub const fn len(&self) -> usize {
91 N::USIZE
92 }
93
94 #[inline]
96 pub const fn as_bytes(&self) -> &[u8] {
97 self.0.as_slice()
98 }
99
100 #[inline]
102 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
103 &mut self.0
104 }
105
106 #[inline]
108 pub fn into_array(self) -> GenericArray<u8, N> {
109 self.0
110 }
111}
112
113impl<N: ArrayLength> Copy for Digest<N> where N::ArrayType<u8>: Copy {}
114
115impl<N: ArrayLength> Deref for Digest<N> {
116 type Target = [u8];
117
118 #[inline]
119 fn deref(&self) -> &Self::Target {
120 &self.0
121 }
122}
123
124impl<N: ArrayLength> DerefMut for Digest<N> {
125 #[inline]
126 fn deref_mut(&mut self) -> &mut Self::Target {
127 &mut self.0
128 }
129}
130
131impl<N: ArrayLength> Debug for Digest<N> {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 f.debug_tuple("Digest").field(&self.0).finish()
134 }
135}
136
137#[cfg(any(test, feature = "test_util"))]
140impl<N: ArrayLength> Eq for Digest<N> {}
141
142#[cfg(any(test, feature = "test_util"))]
145impl<N: ArrayLength> PartialEq for Digest<N> {
146 fn eq(&self, other: &Self) -> bool {
147 bool::from(ConstantTimeEq::ct_eq(self, other))
159 }
160}
161
162impl<N: ArrayLength> ConstantTimeEq for Digest<N> {
163 #[inline]
164 fn ct_eq(&self, other: &Self) -> Choice {
165 self.as_bytes().ct_eq(other.as_bytes())
166 }
167}
168
169pub fn tuple_hash<H, I>(s: I) -> Digest<H::DigestSize>
182where
183 H: Hash,
184 I: IntoIterator<Item: AsRef<[u8]>>,
185{
186 let mut h = H::new();
193 s.into_iter().for_each(|v| {
194 for part in &encode_string(v.as_ref()) {
195 h.update(part);
196 }
197 });
198 h.update(right_encode_bytes(H::DIGEST_SIZE).as_bytes());
199 h.digest()
200}
201
202#[cfg(test)]
203mod test {
204 use super::*;
205
206 macro_rules! test_tuple_hash {
207 ($name:ident, $hash:ty) => {
208 #[test]
209 fn $name() {
210 assert_eq!(
211 tuple_hash::<$hash, _>(["abc"].iter()),
212 tuple_hash::<$hash, _>(["abc"])
213 );
214 assert_eq!(tuple_hash::<$hash, _>([""]), tuple_hash::<$hash, _>([""]));
215
216 assert_ne!(
217 tuple_hash::<$hash, _>(["a", "b", "c"]),
218 tuple_hash::<$hash, _>(["abc"])
219 );
220 assert_ne!(
221 tuple_hash::<$hash, _>(["a", ""]),
222 tuple_hash::<$hash, _>(["a"])
223 );
224 }
225 };
226 }
227 macro_rules! tuple_hash_tests {
228 () => {
229 use super::*;
230 test_tuple_hash!(test_sha256, Sha256);
231 test_tuple_hash!(test_sha384, Sha384);
232 test_tuple_hash!(test_sha512, Sha512);
233 };
234 }
235
236 #[cfg(feature = "bearssl")]
237 mod bearssl {
238 use crate::bearssl::{Sha256, Sha384, Sha512};
239 tuple_hash_tests!();
240 }
241
242 mod rust {
243 use crate::rust::{Sha256, Sha384, Sha512};
244 tuple_hash_tests!();
245 }
246}