Skip to main content

dryoc/classic/
crypto_auth_hmacsha512.rs

1//! # HMAC-SHA-512 authentication
2//!
3//! Implements libsodium's `crypto_auth_hmacsha512_*` functions.
4//!
5//! HMAC-SHA-512 authenticates a message with a shared secret key and writes a
6//! 64-byte tag. Use it when a protocol specifically requires HMAC-SHA-512.
7//! Verification fails if either the message or the tag has been changed.
8//!
9//! ```
10//! use dryoc::classic::crypto_auth_hmacsha512::*;
11//!
12//! let key = crypto_auth_hmacsha512_keygen();
13//! let message = b"One touch of nature makes the whole world kin.";
14//!
15//! let mut mac: Mac = [0u8; 64];
16//! crypto_auth_hmacsha512(&mut mac, message, &key);
17//! crypto_auth_hmacsha512_verify(&mac, message, &key).expect("verify failed");
18//! crypto_auth_hmacsha512_verify(&mac, b"invalid", &key).expect_err("verify should fail");
19//! ```
20//!
21//! The incremental interface produces the same MAC as the one-shot interface:
22//!
23//! ```
24//! use dryoc::classic::crypto_auth_hmacsha512::*;
25//!
26//! let key = crypto_auth_hmacsha512_keygen();
27//! let mut one_shot: Mac = [0u8; 64];
28//! crypto_auth_hmacsha512(
29//!     &mut one_shot,
30//!     b"How far that little candle throws his beams!",
31//!     &key,
32//! );
33//!
34//! let mut state = crypto_auth_hmacsha512_init(&key);
35//! crypto_auth_hmacsha512_update(&mut state, b"How far that little candle ");
36//! crypto_auth_hmacsha512_update(&mut state, b"throws his beams!");
37//! let mut streaming: Mac = [0u8; 64];
38//! crypto_auth_hmacsha512_final(state, &mut streaming);
39//!
40//! assert_eq!(one_shot, streaming);
41//! ```
42
43use crate::classic::crypto_auth_hmac_impl::{
44    HmacState, hmac, hmac_final, hmac_init, hmac_keygen, hmac_update, hmac_verify,
45};
46use crate::constants::{CRYPTO_AUTH_HMACSHA512_BYTES, CRYPTO_AUTH_HMACSHA512_KEYBYTES};
47use crate::error::Error;
48use crate::sha512::Sha512;
49
50/// Key for HMAC-SHA-512 message authentication.
51pub type Key = [u8; CRYPTO_AUTH_HMACSHA512_KEYBYTES];
52/// Message authentication code type for HMAC-SHA-512.
53pub type Mac = [u8; CRYPTO_AUTH_HMACSHA512_BYTES];
54
55/// Internal state for HMAC-SHA-512.
56pub struct HmacSha512State(HmacState<Sha512, 128, CRYPTO_AUTH_HMACSHA512_BYTES>);
57
58/// Authenticates `message` using `key`, and places the result into `mac`.
59pub fn crypto_auth_hmacsha512(mac: &mut Mac, message: &[u8], key: &Key) {
60    hmac::<Sha512, CRYPTO_AUTH_HMACSHA512_KEYBYTES, 128, CRYPTO_AUTH_HMACSHA512_BYTES>(
61        mac, message, key,
62    );
63}
64
65/// Verifies that `mac` is the correct authenticator for `message` using `key`.
66///
67/// # Errors
68///
69/// Returns an error if `mac` is not valid for `input` under `key`.
70pub fn crypto_auth_hmacsha512_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
71    hmac_verify::<Sha512, CRYPTO_AUTH_HMACSHA512_KEYBYTES, 128, CRYPTO_AUTH_HMACSHA512_BYTES>(
72        mac, input, key,
73    )
74}
75
76/// Generates a random key for HMAC-SHA-512.
77pub fn crypto_auth_hmacsha512_keygen() -> Key {
78    hmac_keygen()
79}
80
81/// Initializes the incremental interface for HMAC-SHA-512.
82pub fn crypto_auth_hmacsha512_init(key: &[u8]) -> HmacSha512State {
83    HmacSha512State(hmac_init::<Sha512, 128, CRYPTO_AUTH_HMACSHA512_BYTES>(key))
84}
85
86/// Updates `state` for HMAC-SHA-512 with `input`.
87pub fn crypto_auth_hmacsha512_update(state: &mut HmacSha512State, input: &[u8]) {
88    hmac_update(&mut state.0, input);
89}
90
91/// Finalizes HMAC-SHA-512 and places the result into `output`.
92pub fn crypto_auth_hmacsha512_final(state: HmacSha512State, output: &mut Mac) {
93    hmac_final(state.0, output);
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    fn compute_hmac(key: &[u8], message: &[u8]) -> Mac {
101        let mut mac = [0u8; CRYPTO_AUTH_HMACSHA512_BYTES];
102        let mut state = crypto_auth_hmacsha512_init(key);
103        crypto_auth_hmacsha512_update(&mut state, message);
104        crypto_auth_hmacsha512_final(state, &mut mac);
105        mac
106    }
107
108    fn assert_hmac(key: &[u8], message: &[u8], expected_hex: &str) {
109        let mac = compute_hmac(key, message);
110        let expected = hex::decode(expected_hex).expect("hex failed");
111        assert_eq!(mac.as_slice(), expected.as_slice());
112    }
113
114    #[test]
115    fn test_rfc4231_case_1() {
116        let key = [0x0bu8; 20];
117        assert_hmac(
118            &key,
119            b"Hi There",
120            concat!(
121                "87aa7cdea5ef619d4ff0b4241a1d6cb0",
122                "2379f4e2ce4ec2787ad0b30545e17cde",
123                "daa833b7d6b8a702038b274eaea3f4e4",
124                "be9d914eeb61f1702e696c203a126854",
125            ),
126        );
127    }
128
129    #[test]
130    fn test_rfc4231_short_key_case_2() {
131        assert_hmac(
132            b"Jefe",
133            b"what do ya want for nothing?",
134            concat!(
135                "164b7a7bfcf819e2e395fbe73b56e0a3",
136                "87bd64222e831fd610270cd7ea250554",
137                "9758bf75c05a994a6d034f65f8f0e6fd",
138                "caeab1a34d4a6b4b636e070a38bce737",
139            ),
140        );
141    }
142
143    #[test]
144    fn test_rfc4231_long_message_case_3() {
145        let key = [0xaau8; 20];
146        let message = [0xddu8; 50];
147        assert_hmac(
148            &key,
149            &message,
150            concat!(
151                "fa73b0089d56a284efb0f0756c890be9",
152                "b1b5dbdd8ee81a3655f83e33b2279d39",
153                "bf3e848279a722c806b485a47e67c807",
154                "b946a337bee8942674278859e13292fb",
155            ),
156        );
157    }
158
159    #[test]
160    fn test_rfc4231_long_key_case_6() {
161        let key = [0xaau8; 131];
162        assert_hmac(
163            &key,
164            b"Test Using Larger Than Block-Size Key - Hash Key First",
165            concat!(
166                "80b24263c7c1a3ebb71493c1dd7be8b4",
167                "9b46d1f41b4aeec1121b013783f8f352",
168                "6b56d037e05f2598bd0fd2215d6a1e52",
169                "95e64f73f63f0aec8b915a985d786598",
170            ),
171        );
172    }
173
174    #[test]
175    fn test_rfc4231_long_key_and_message_case_7() {
176        let key = [0xaau8; 131];
177        assert_hmac(
178            &key,
179            b"This is a test using a larger than block-size key and a larger than block-size data. \
180              The key needs to be hashed before being used by the HMAC algorithm.",
181            concat!(
182                "e37b6a775dc87dbaa4dfa9f96e5e3ffd",
183                "debd71f8867289865df5a32d20cdc944",
184                "b6022cac3c4982b10d5eeb55c3e4de15",
185                "134676fb6de0446065c97440fa8c6a58",
186            ),
187        );
188    }
189
190    #[test]
191    fn test_rfc4231_case_4() {
192        let key =
193            hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").expect("hex failed");
194        let message = [0xcdu8; 50];
195        assert_hmac(
196            &key,
197            &message,
198            concat!(
199                "b0ba465637458c6990e5a8c5f61d4af7",
200                "e576d97ff94b872de76f8050361ee3db",
201                "a91ca5c11aa25eb4d679275cc5788063",
202                "a5f19741120c4f2de2adebeb10a298dd",
203            ),
204        );
205    }
206
207    #[test]
208    fn test_rfc4231_case_1_matches_one_shot_for_keybytes_key() {
209        let key = [0x0bu8; CRYPTO_AUTH_HMACSHA512_KEYBYTES];
210        let message = b"Hi There";
211        let mut one_shot = [0u8; CRYPTO_AUTH_HMACSHA512_BYTES];
212        crypto_auth_hmacsha512(&mut one_shot, message, &key);
213        assert_eq!(one_shot, compute_hmac(&key, message));
214    }
215
216    #[cfg(dryoc_native_tests)]
217    #[test]
218    fn test_libsodium_compatibility() {
219        use sodiumoxide::crypto::auth::hmacsha512;
220
221        let key = crypto_auth_hmacsha512_keygen();
222        let message = b"message to authenticate";
223        let so_key = hmacsha512::Key::from_slice(&key).expect("key failed");
224        let so_mac = hmacsha512::authenticate(message, &so_key);
225
226        let mut mac = [0u8; CRYPTO_AUTH_HMACSHA512_BYTES];
227        crypto_auth_hmacsha512(&mut mac, message, &key);
228        assert_eq!(mac.as_slice(), so_mac.as_ref());
229        crypto_auth_hmacsha512_verify(&mac, message, &key).expect("verify failed");
230
231        let mut state = crypto_auth_hmacsha512_init(&key);
232        crypto_auth_hmacsha512_update(&mut state, b"message ");
233        crypto_auth_hmacsha512_update(&mut state, b"to authenticate");
234        let mut state_mac = [0u8; CRYPTO_AUTH_HMACSHA512_BYTES];
235        crypto_auth_hmacsha512_final(state, &mut state_mac);
236        assert_eq!(state_mac.as_slice(), so_mac.as_ref());
237    }
238}