rs_hmac/lib.rs
1//! # HMAC `rs_hmac` - Hash-based Message Authentication Code
2//!
3//! This HMAC implementation is a specific type of message authentication code (MAC) involving a cryptographic hash
4//! function in combination with a secret cryptographic key. It was developed by the National Institute of Standards and
5//! Technology (NIST).
6//!
7//! The HMAC function implemented in this crate is compatible with any hash function present in the encompassing
8//! project.
9//!
10//! HMAC is suitable for a range of cryptographic purposes, including verifying the data integrity and the authenticity
11//! of a message. It is typically used in data communications and is crucial for many protocols to ensure data hasn't
12//! been tampered with during transmission.
13//!
14//! ## Usage
15//!
16//! The crate provides a straightforward API. Users can create a new HMAC instance, update it with input data, and
17//! finalize to get the resultant MAC.
18//!
19//! ### Example
20//!
21//! Here is an example of how to use the HMAC function with SHA3-256 in Rust:
22//!
23//! ```rust
24//! # use std::hash::Hasher;
25//! # use rs_hmac::Hmac;
26//! # use rs_sha3_256::Sha3_256State;
27//! let mut hmac = Hmac::<Sha3_256State, 32>::new(b"my secret and secure key");
28//! hmac.write(b"hello world");
29//! let result = hmac.finish();
30//! assert_eq!(result, 0xE50FF3D282C9C0F9);
31//! ```
32//!
33//! ## Use Cases
34//!
35//! HMAC is recommended for a variety of tasks, including:
36//!
37//! - Ensuring data integrity and authenticity in data communications.
38//! - Authenticating API requests.
39//! - Secure password storage.
40//!
41//! [NIST](https://www.nist.gov/) recommends HMAC for ensuring data integrity and authenticity, particularly when it is
42//! crucial to verify that data has not been tampered with during communication.
43//!
44
45#![no_std]
46
47use core::hash::{Hash, Hasher};
48pub use rs_hasher_ctx::HasherContext;
49use rs_hasher_ctx::{ByteArrayWrapper, GenericHasher};
50use rs_internal_hasher::{HashAlgorithm, LenPad};
51use rs_internal_state::BytesLen;
52
53const INNER_PAD: u8 = 0x36;
54const OUTER_PAD: u8 = 0x5c;
55
56/// `Hmac<H: Default + HashAlgorithm, const OUTPUT_SIZE: usize>` is a generic struct that provides the HMAC (Hash-based
57/// Message Authentication Code) in Rust.
58///
59/// In the context of cryptographic hashing, HMAC is an algorithm that combines a specified Hash function (`H`) and a
60/// secret cryptographic key to convert input data into a fixed-size sequence of bytes. The HMAC is responsible for
61/// maintaining the internal state of the hashing process and providing methods to add more data and retrieve the
62/// resultant Message Authentication Code (MAC).
63///
64/// The `Hmac` struct is implemented using any hash function available in this crate, making it a flexible choice for
65/// HMAC operations.
66///
67/// ## Examples
68///
69/// The following examples demonstrate using `Hmac` with both `Hash` and `Hasher`, and illustrate the difference between
70/// these approaches:
71///
72///```rust
73/// # use std::hash::{Hash, Hasher};
74/// # use rs_hmac::Hmac;
75/// use rs_sha3_512::Sha3_512State;
76/// let data = b"hello";
77/// let key = b"my secret and secure key";
78///
79/// // Using Hash
80/// let mut hmac_hash = Hmac::<Sha3_512State, 64>::new(key);
81/// data.hash(&mut hmac_hash);
82/// let result_via_hash = hmac_hash.finish();
83///
84/// // Using Hasher
85/// let mut hmac_hasher = Hmac::<Sha3_512State, 64>::new(key);
86/// hmac_hasher.write(data);
87/// let result_via_hasher = hmac_hasher.finish();
88///
89/// // Simulating the Hash inners
90/// let mut hmac_simulate = Hmac::<Sha3_512State, 64>::new(key);
91/// hmac_simulate.write_usize(data.len());
92/// hmac_simulate.write(data);
93/// let simulated_hash_result = hmac_simulate.finish();
94///
95/// assert_ne!(result_via_hash, result_via_hasher);
96/// assert_eq!(result_via_hash, simulated_hash_result);
97///```
98///
99/// Note that in the context of HMAC, the hash operation applies a different set of operations than the hasher's
100/// `write`, which results in different outcomes.
101#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
102pub struct Hmac<H: Default + HashAlgorithm, const OUTPUT_SIZE: usize> {
103 inner_hasher: GenericHasher<H, OUTPUT_SIZE>,
104 outer_hasher: GenericHasher<H, OUTPUT_SIZE>,
105}
106
107impl<H, const OUTPUT_SIZE: usize> Hmac<H, OUTPUT_SIZE>
108where
109 H: BytesLen + Default + HashAlgorithm<Output = ByteArrayWrapper<OUTPUT_SIZE>>,
110 <H as HashAlgorithm>::Output: From<H>,
111 ByteArrayWrapper<OUTPUT_SIZE>: From<H>,
112{
113 /// Creates a new HMAC context with the given key.
114 ///
115 /// This method initializes a new HMAC context using the provided key. If the key is longer than the block size
116 /// of the hash algorithm, it is hashed, and the hash is used as the key. If the key is shorter than the block
117 /// size of the hash algorithm, it is padded with zeros.
118 ///
119 /// The key is split into two halves - one for the inner hash and one for the outer hash. The inner hash is padded
120 /// with `0x36` and the outer hash is padded with `0x5c`.
121 ///
122 /// # Arguments
123 ///
124 /// * `key` - A byte slice that holds the key.
125 ///
126 /// # Returns
127 ///
128 /// A new HMAC context.
129 ///
130 /// # Example
131 ///
132 /// ```
133 /// # use std::hash::Hasher;
134 /// use rs_hasher_ctx::HasherContext;
135 /// use rs_hmac::Hmac;
136 /// use rs_sha3_384::Sha3_384State;
137 ///
138 /// let key = b"my secret and secure key";
139 /// let mut hmac = Hmac::<Sha3_384State, 48>::new(key);
140 ///
141 /// hmac.write(b"data");
142 /// let u64result = hmac.finish();
143 /// let bytes_result = HasherContext::finish(&mut hmac);
144 ///
145 /// assert_eq!(u64result, 0xCD06EBA676832C12);
146 /// assert_eq!(
147 /// bytes_result,
148 /// [
149 /// 0xC1, 0x4C, 0x21, 0xF6, 0x5A, 0xCA, 0x45, 0x19, 0x91, 0xF3, 0xAB, 0x87, 0x76, 0x6E, 0x7C, 0xDF, 0xC4, 0x50,
150 /// 0x6A, 0x18, 0xE7, 0x08, 0xA4, 0x64, 0xFA, 0x0E, 0xCD, 0x4C, 0xC0, 0x97, 0x32, 0xFA, 0x5F, 0x8A, 0x8A, 0x33,
151 /// 0x26, 0x0F, 0xE2, 0x65, 0x9C, 0xC3, 0xBF, 0x6E, 0xD1, 0x5E, 0x16, 0xEB
152 /// ]
153 /// );
154 /// ```
155 pub fn new(key: &[u8]) -> Self {
156 let mut inner_key = H::Padding::default();
157 let mut outer_key = H::Padding::default();
158
159 if key.len() > H::Padding::len() {
160 let mut hasher: GenericHasher<H, OUTPUT_SIZE> = GenericHasher::default();
161 hasher.write(key);
162 let bytes_output: ByteArrayWrapper<OUTPUT_SIZE> = HasherContext::finish(&mut hasher).into();
163
164 inner_key.as_mut()[..H::len()].clone_from_slice(bytes_output.as_ref());
165 outer_key.as_mut()[..H::len()].clone_from_slice(bytes_output.as_ref());
166 } else {
167 inner_key.as_mut()[..key.len()].clone_from_slice(key);
168 outer_key.as_mut()[..key.len()].clone_from_slice(key);
169 }
170
171 for (i, o) in inner_key.as_mut().iter_mut().zip(outer_key.as_mut().iter_mut()) {
172 *i ^= INNER_PAD;
173 *o ^= OUTER_PAD;
174 }
175
176 let mut inner_hasher = GenericHasher::default();
177 let mut outer_hasher = GenericHasher::default();
178
179 inner_hasher.write(inner_key.as_ref());
180 outer_hasher.write(outer_key.as_ref());
181
182 Self {
183 inner_hasher,
184 outer_hasher,
185 }
186 }
187
188 /// Computes the HMAC of a message with a key.
189 ///
190 /// This method calculates the HMAC of the provided message using the key from the HMAC context.
191 ///
192 /// # Arguments
193 ///
194 /// * `key` - A byte slice that holds the key.
195 /// * `msg` - A byte slice that holds the message.
196 ///
197 /// # Returns
198 ///
199 /// The HMAC of the message as a byte array wrapper.
200 ///
201 /// # Example
202 ///
203 /// ```
204 /// use rs_hmac::Hmac;
205 /// use rs_sha1::{Sha1Hasher, Sha1State};
206 ///
207 /// let key = b"key";
208 /// let msg = b"The quick brown fox jumps over the lazy dog";
209 /// let resulting_sha1state = Hmac::<Sha1State, 20>::digest(key, msg);
210 ///
211 /// assert_eq!(format!("{:02x}", resulting_sha1state), "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
212 /// ```
213 pub fn digest(key: &[u8], msg: &[u8]) -> ByteArrayWrapper<OUTPUT_SIZE> {
214 let mut hmac = Self::new(key);
215 hmac.write(msg);
216 HasherContext::finish(&mut hmac)
217 }
218}
219
220impl<H, const OUTPUT_SIZE: usize> Hasher for Hmac<H, OUTPUT_SIZE>
221where
222 H: Default + HashAlgorithm,
223 <H as HashAlgorithm>::Output: From<H>,
224 ByteArrayWrapper<OUTPUT_SIZE>: From<H>,
225{
226 fn finish(&self) -> u64 {
227 Hasher::finish(&self.inner_hasher)
228 }
229
230 fn write(&mut self, bytes: &[u8]) {
231 self.inner_hasher.write(bytes)
232 }
233}
234
235impl<H, const OUTPUT_SIZE: usize> HasherContext<OUTPUT_SIZE> for Hmac<H, OUTPUT_SIZE>
236where
237 H: Default + HashAlgorithm,
238 <H as HashAlgorithm>::Output: From<H>,
239 ByteArrayWrapper<OUTPUT_SIZE>: From<H>,
240{
241 type Output = ByteArrayWrapper<OUTPUT_SIZE>;
242
243 fn finish(&mut self) -> Self::Output {
244 let inner_result: ByteArrayWrapper<OUTPUT_SIZE> = HasherContext::finish(&mut self.inner_hasher).into();
245
246 self.outer_hasher.write(inner_result.as_ref());
247 HasherContext::finish(&mut self.outer_hasher).into()
248 }
249}