Skip to main content

dryoc/classic/
crypto_auth_hmacsha256.rs

1//! # HMAC-SHA-256 authentication
2//!
3//! Implements libsodium's `crypto_auth_hmacsha256_*` functions.
4//!
5//! HMAC-SHA-256 authenticates a message with a shared secret key and writes a
6//! 32-byte tag. Verification recomputes the tag and compares it in constant
7//! time. The message is not encrypted, and the same key must be available to
8//! both the sender and verifier.
9//!
10//! ```
11//! use dryoc::classic::crypto_auth_hmacsha256::*;
12//!
13//! let key = crypto_auth_hmacsha256_keygen();
14//! let message = b"What's past is prologue.";
15//!
16//! let mut mac = Mac::default();
17//! crypto_auth_hmacsha256(&mut mac, message, &key);
18//! crypto_auth_hmacsha256_verify(&mac, message, &key).expect("verify failed");
19//! crypto_auth_hmacsha256_verify(&mac, b"invalid", &key).expect_err("verify should fail");
20//! ```
21//!
22//! The incremental interface produces the same MAC as the one-shot interface:
23//!
24//! ```
25//! use dryoc::classic::crypto_auth_hmacsha256::*;
26//!
27//! let key = crypto_auth_hmacsha256_keygen();
28//! let mut one_shot = Mac::default();
29//! crypto_auth_hmacsha256(&mut one_shot, b"Parting is such sweet sorrow.", &key);
30//!
31//! let mut state = crypto_auth_hmacsha256_init(&key);
32//! crypto_auth_hmacsha256_update(&mut state, b"Parting is such ");
33//! crypto_auth_hmacsha256_update(&mut state, b"sweet sorrow.");
34//! let mut streaming = Mac::default();
35//! crypto_auth_hmacsha256_final(state, &mut streaming);
36//!
37//! assert_eq!(one_shot, streaming);
38//! ```
39
40use crate::classic::crypto_auth_hmac_impl::{
41    HmacState, hmac, hmac_final, hmac_init, hmac_keygen, hmac_update, hmac_verify,
42};
43use crate::constants::{CRYPTO_AUTH_HMACSHA256_BYTES, CRYPTO_AUTH_HMACSHA256_KEYBYTES};
44use crate::error::Error;
45use crate::sha256::Sha256;
46
47/// Key for HMAC-SHA-256 message authentication.
48pub type Key = [u8; CRYPTO_AUTH_HMACSHA256_KEYBYTES];
49/// Message authentication code type for HMAC-SHA-256.
50pub type Mac = [u8; CRYPTO_AUTH_HMACSHA256_BYTES];
51
52/// Internal state for HMAC-SHA-256.
53pub struct HmacSha256State(HmacState<Sha256, 64, CRYPTO_AUTH_HMACSHA256_BYTES>);
54
55/// Authenticates `message` using `key`, and places the result into `mac`.
56pub fn crypto_auth_hmacsha256(mac: &mut Mac, message: &[u8], key: &Key) {
57    hmac::<Sha256, CRYPTO_AUTH_HMACSHA256_KEYBYTES, 64, CRYPTO_AUTH_HMACSHA256_BYTES>(
58        mac, message, key,
59    );
60}
61
62/// Verifies that `mac` is the correct authenticator for `message` using `key`.
63///
64/// # Errors
65///
66/// Returns an error if `mac` is not valid for `input` under `key`.
67pub fn crypto_auth_hmacsha256_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
68    hmac_verify::<Sha256, CRYPTO_AUTH_HMACSHA256_KEYBYTES, 64, CRYPTO_AUTH_HMACSHA256_BYTES>(
69        mac, input, key,
70    )
71}
72
73/// Generates a random key for HMAC-SHA-256.
74pub fn crypto_auth_hmacsha256_keygen() -> Key {
75    hmac_keygen()
76}
77
78/// Initializes the incremental interface for HMAC-SHA-256.
79pub fn crypto_auth_hmacsha256_init(key: &[u8]) -> HmacSha256State {
80    HmacSha256State(hmac_init::<Sha256, 64, CRYPTO_AUTH_HMACSHA256_BYTES>(key))
81}
82
83/// Updates `state` for HMAC-SHA-256 with `input`.
84pub fn crypto_auth_hmacsha256_update(state: &mut HmacSha256State, input: &[u8]) {
85    hmac_update(&mut state.0, input);
86}
87
88/// Finalizes HMAC-SHA-256 and places the result into `output`.
89pub fn crypto_auth_hmacsha256_final(state: HmacSha256State, output: &mut Mac) {
90    hmac_final(state.0, output);
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    fn compute_hmac(key: &[u8], message: &[u8]) -> Mac {
98        let mut mac = Mac::default();
99        let mut state = crypto_auth_hmacsha256_init(key);
100        crypto_auth_hmacsha256_update(&mut state, message);
101        crypto_auth_hmacsha256_final(state, &mut mac);
102        mac
103    }
104
105    fn assert_hmac(key: &[u8], message: &[u8], expected_hex: &str) {
106        let mac = compute_hmac(key, message);
107        let expected = hex::decode(expected_hex).expect("hex failed");
108        assert_eq!(mac.as_slice(), expected.as_slice());
109    }
110
111    #[test]
112    fn test_rfc4231_case_1() {
113        let key = [0x0bu8; 20];
114        assert_hmac(
115            &key,
116            b"Hi There",
117            "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
118        );
119    }
120
121    #[test]
122    fn test_rfc4231_short_key_case_2() {
123        assert_hmac(
124            b"Jefe",
125            b"what do ya want for nothing?",
126            "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
127        );
128    }
129
130    #[test]
131    fn test_rfc4231_long_message_case_3() {
132        let key = [0xaau8; 20];
133        let message = [0xddu8; 50];
134        assert_hmac(
135            &key,
136            &message,
137            "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
138        );
139    }
140
141    #[test]
142    fn test_rfc4231_case_4() {
143        let key =
144            hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").expect("hex failed");
145        let message = [0xcdu8; 50];
146        assert_hmac(
147            &key,
148            &message,
149            "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
150        );
151    }
152
153    #[test]
154    fn test_rfc4231_long_key_case_6() {
155        let key = [0xaau8; 131];
156        assert_hmac(
157            &key,
158            b"Test Using Larger Than Block-Size Key - Hash Key First",
159            "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
160        );
161    }
162
163    #[test]
164    fn test_rfc4231_long_key_and_message_case_7() {
165        let key = [0xaau8; 131];
166        assert_hmac(
167            &key,
168            b"This is a test using a larger than block-size key and a larger than block-size data. \
169              The key needs to be hashed before being used by the HMAC algorithm.",
170            "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
171        );
172    }
173
174    #[test]
175    fn test_one_shot_matches_incremental_for_keybytes_key() {
176        let key = [0x0bu8; CRYPTO_AUTH_HMACSHA256_KEYBYTES];
177        let message = b"message";
178        let mut one_shot = Mac::default();
179        crypto_auth_hmacsha256(&mut one_shot, message, &key);
180        assert_eq!(one_shot, compute_hmac(&key, message));
181    }
182
183    #[cfg(dryoc_native_tests)]
184    #[test]
185    fn test_libsodium_compatibility() {
186        use sodiumoxide::crypto::auth::hmacsha256;
187
188        let key = crypto_auth_hmacsha256_keygen();
189        let message = b"message to authenticate";
190        let so_key = hmacsha256::Key::from_slice(&key).expect("key failed");
191        let so_mac = hmacsha256::authenticate(message, &so_key);
192
193        let mut mac = Mac::default();
194        crypto_auth_hmacsha256(&mut mac, message, &key);
195        assert_eq!(mac.as_slice(), so_mac.as_ref());
196        crypto_auth_hmacsha256_verify(&mac, message, &key).expect("verify failed");
197
198        let mut state = crypto_auth_hmacsha256_init(&key);
199        crypto_auth_hmacsha256_update(&mut state, b"message ");
200        crypto_auth_hmacsha256_update(&mut state, b"to authenticate");
201        let mut state_mac = Mac::default();
202        crypto_auth_hmacsha256_final(state, &mut state_mac);
203        assert_eq!(state_mac.as_slice(), so_mac.as_ref());
204    }
205}