wolf_crypto/mac/hmac/
digest.rs

1use core::fmt;
2use crate::mac::hmac::algo::{self, Digest as _, HexDigest as _};
3use crate::ct;
4use crate::hex;
5
6/// Utility wrapper around the final `HMAC` hash.
7#[must_use]
8#[repr(transparent)]
9#[derive(Copy, Clone)]
10pub struct Digest<D: algo::Digest> {
11    raw: D
12}
13
14impl<D: algo::Digest> fmt::Debug for Digest<D> {
15    /// Writes "Digest { ... }" to the provided formatter.
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        f.write_str("Digest { ... }")
18    }
19}
20
21impl<D: algo::Digest + Copy> Digest<D> {
22    /// Create a new `Digest` instance.
23    pub const fn new(digest: D) -> Self {
24        Self { raw: digest }
25    }
26
27    /// Unwraps this `Digest` returning the raw byte array.
28    ///
29    /// # Note
30    ///
31    /// Comparing `Digest`s should always be in constant-time, do not use `into_inner` prior to
32    /// checking equivalence between `Digest`s. The `Digest` type's `PartialEq` implementations
33    /// are all in constant-time. Either leverage these, or use this crate's [`ct_eq`] function.
34    ///
35    /// [`ct_eq`]: crate::ct_eq
36    #[must_use]
37    pub const fn into_inner(self) -> D {
38        self.raw
39    }
40
41    /// Hex-encodes the underlying digest in constant-time, returning the [`HexDigest`] type.
42    pub fn hex_encode(&self) -> HexDigest<D::Hex> {
43        let mut out = D::Hex::zeroes();
44        hex::encode_into(self.as_ref(), out.as_mut()).unwrap(/* infallible */);
45        HexDigest::new(out)
46    }
47}
48
49impl<D: algo::Digest> AsRef<[u8]> for Digest<D> {
50    #[inline]
51    fn as_ref(&self) -> &[u8] { self.raw.as_ref() }
52}
53
54impl<D: algo::Digest> AsRef<D> for Digest<D> {
55    #[inline]
56    fn as_ref(&self) -> &D { &self.raw }
57}
58
59impl<D: algo::Digest> PartialEq for Digest<D> {
60    /// Constant-Time Equivalence.
61    fn eq(&self, other: &Self) -> bool {
62        ct::ct_eq(self.raw, other.raw)
63    }
64}
65
66impl<D: algo::Digest> Eq for Digest<D> {}
67
68impl<D: algo::Digest> PartialEq<[u8]> for Digest<D> {
69    /// Constant-Time Equivalence.
70    fn eq(&self, other: &[u8]) -> bool {
71        ct::ct_eq(self.raw, other)
72    }
73}
74
75impl<D: algo::Digest, T> PartialEq<&T> for Digest<D> where Self: PartialEq<T> {
76    /// Constant-Time Equivalence.
77    #[inline]
78    fn eq(&self, other: &&T) -> bool {
79        self.eq(other)
80    }
81}
82
83impl<D: algo::Digest, T> PartialEq<&mut T> for Digest<D> where Self: PartialEq<T> {
84    /// Constant-Time Equivalence.
85    #[inline]
86    fn eq(&self, other: &&mut T) -> bool {
87        self.eq(other)
88    }
89}
90
91/// Utility wrapper around the hex-encoded final `HMAC` hash.
92#[repr(transparent)]
93#[must_use]
94#[derive(Copy, Clone)]
95pub struct HexDigest<D: algo::HexDigest> {
96    raw: D
97}
98
99impl<D: algo::HexDigest> HexDigest<D> {
100    /// Create a new `HexDigest` instance
101    ///
102    /// # Arguments
103    ///
104    /// * `digest` - The hex-encoded digest.
105    // new is not public as we cannot guarantee the provided data is properly hex-encoded.
106    const fn new(digest: D) -> Self {
107        Self { raw: digest }
108    }
109
110    /// Decodes the `HexDigest` into a raw [`Digest`] in constant-time.
111    pub fn decode(&self) -> Digest<D::Digest> {
112        let mut output = D::Digest::zeroes();
113        hex::decode_into(self.raw.as_ref(), output.as_mut())
114            .unwrap(/* This is infallible as this type may not be constructed without correct hex
115                       encoding */);
116        Digest::new(output)
117    }
118
119    /// Returns the underlying hex-encoded digest as a `&str`.
120    #[inline]
121    pub fn as_str(&self) -> &str {
122        // SAFETY: See `ct` module's hex encode documentation.
123        unsafe { core::str::from_utf8_unchecked(self.raw.as_ref()) }
124    }
125
126    /// Unwraps this `HexDigest` returning the hex-encoded byte array.
127    ///
128    /// # Note
129    ///
130    /// Comparing `HexDigest`s should always be in constant-time, do not use `into_inner` prior to
131    /// checking equivalence between `HexDigest`s. The `HexDigest` type's `PartialEq`
132    /// implementations are all in constant-time. Either leverage these, or use this crate's
133    /// [`ct_eq`] function.
134    ///
135    /// [`ct_eq`]: crate::ct_eq
136    #[must_use]
137    pub const fn into_inner(self) -> D {
138        self.raw
139    }
140}
141
142impl<D: algo::HexDigest> AsRef<[u8]> for HexDigest<D> {
143    #[inline]
144    fn as_ref(&self) -> &[u8] { self.raw.as_ref() }
145}
146
147impl<D: algo::HexDigest> AsRef<D> for HexDigest<D> {
148    #[inline]
149    fn as_ref(&self) -> &D { &self.raw }
150}
151
152impl<D: algo::HexDigest> PartialEq for HexDigest<D> {
153    /// Constant-Time Equivalence.
154    fn eq(&self, other: &Self) -> bool {
155        ct::ct_eq(self.raw, other.raw)
156    }
157}
158
159impl<D: algo::HexDigest> Eq for HexDigest<D> {}
160
161impl<D: algo::HexDigest> PartialEq<[u8]> for HexDigest<D> {
162    /// Constant-Time Equivalence.
163    fn eq(&self, other: &[u8]) -> bool {
164        ct::ct_eq(self.raw, other)
165    }
166}
167
168impl<D: algo::HexDigest, T> PartialEq<&T> for HexDigest<D> where Self: PartialEq<T> {
169    /// Constant-Time Equivalence.
170    #[inline]
171    fn eq(&self, other: &&T) -> bool {
172        self.eq(other)
173    }
174}
175
176impl<D: algo::HexDigest, T> PartialEq<&mut T> for HexDigest<D> where Self: PartialEq<T> {
177    /// Constant-Time Equivalence.
178    #[inline]
179    fn eq(&self, other: &&mut T) -> bool {
180        self.eq(other)
181    }
182}
183
184impl<D: algo::HexDigest> From<HexDigest<D>> for Digest<D::Digest> {
185    fn from(value: HexDigest<D>) -> Self {
186        value.decode()
187    }
188}
189
190impl<D: algo::Digest> From<Digest<D>> for HexDigest<D::Hex> {
191    fn from(value: Digest<D>) -> Self {
192        value.hex_encode()
193    }
194}