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}