Skip to main content

dryoc/classic/
crypto_auth_hmacsha512256.rs

1//! # HMAC-SHA-512-256 authentication
2//!
3//! Implements libsodium's `crypto_auth_hmacsha512256_*` functions.
4//!
5//! HMAC-SHA-512-256 is HMAC-SHA-512 with a 32-byte truncated output. This is
6//! libsodium's default `crypto_auth` construction. It authenticates a public
7//! message with a shared secret key; it does not hide the message contents.
8//!
9//! ```
10//! use dryoc::classic::crypto_auth_hmacsha512256::*;
11//!
12//! let key = crypto_auth_hmacsha512256_keygen();
13//! let message = b"No legacy is so rich as honesty.";
14//!
15//! let mut mac = Mac::default();
16//! crypto_auth_hmacsha512256(&mut mac, message, &key);
17//! crypto_auth_hmacsha512256_verify(&mac, message, &key).expect("verify failed");
18//! crypto_auth_hmacsha512256_verify(&mac, b"invalid", &key).expect_err("verify should fail");
19//! ```
20//!
21//! The incremental interface produces the same truncated HMAC-SHA-512 MAC as
22//! the one-shot interface:
23//!
24//! ```
25//! use dryoc::classic::crypto_auth_hmacsha512256::*;
26//!
27//! let key = crypto_auth_hmacsha512256_keygen();
28//! let mut one_shot = Mac::default();
29//! crypto_auth_hmacsha512256(
30//!     &mut one_shot,
31//!     b"Small cheer and great welcome makes a merry feast.",
32//!     &key,
33//! );
34//!
35//! let mut state = crypto_auth_hmacsha512256_init(&key);
36//! crypto_auth_hmacsha512256_update(&mut state, b"Small cheer and great welcome ");
37//! crypto_auth_hmacsha512256_update(&mut state, b"makes a merry feast.");
38//! let mut streaming = Mac::default();
39//! crypto_auth_hmacsha512256_final(state, &mut streaming);
40//!
41//! assert_eq!(one_shot, streaming);
42//! ```
43
44use subtle::ConstantTimeEq;
45use zeroize::Zeroize;
46
47use crate::classic::crypto_auth_hmac_impl::hmac_keygen;
48use crate::classic::crypto_auth_hmacsha512::{
49    HmacSha512State, crypto_auth_hmacsha512_final, crypto_auth_hmacsha512_init,
50    crypto_auth_hmacsha512_update,
51};
52use crate::constants::{
53    CRYPTO_AUTH_HMACSHA512_BYTES, CRYPTO_AUTH_HMACSHA512256_BYTES,
54    CRYPTO_AUTH_HMACSHA512256_KEYBYTES,
55};
56use crate::error::Error;
57
58/// Key for HMAC-SHA-512-256 message authentication.
59pub type Key = [u8; CRYPTO_AUTH_HMACSHA512256_KEYBYTES];
60/// Message authentication code type for HMAC-SHA-512-256.
61pub type Mac = [u8; CRYPTO_AUTH_HMACSHA512256_BYTES];
62/// Internal state for HMAC-SHA-512-256.
63pub type HmacSha512256State = HmacSha512State;
64
65/// Authenticates `message` using `key`, and places the result into `mac`.
66pub fn crypto_auth_hmacsha512256(mac: &mut Mac, message: &[u8], key: &Key) {
67    let mut state = crypto_auth_hmacsha512256_init(key);
68    crypto_auth_hmacsha512256_update(&mut state, message);
69    crypto_auth_hmacsha512256_final(state, mac);
70}
71
72/// Verifies that `mac` is the correct authenticator for `message` using `key`.
73///
74/// # Errors
75///
76/// Returns an error if `mac` is not valid for `input` under `key`.
77pub fn crypto_auth_hmacsha512256_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
78    let mut computed_mac = Mac::default();
79    crypto_auth_hmacsha512256(&mut computed_mac, input, key);
80    let valid = mac.ct_eq(&computed_mac).unwrap_u8();
81    computed_mac.zeroize();
82    if valid == 1 {
83        Ok(())
84    } else {
85        Err(Error::AuthenticationFailed)
86    }
87}
88
89/// Generates a random key for HMAC-SHA-512-256.
90pub fn crypto_auth_hmacsha512256_keygen() -> Key {
91    hmac_keygen()
92}
93
94/// Initializes the incremental interface for HMAC-SHA-512-256.
95pub fn crypto_auth_hmacsha512256_init(key: &[u8]) -> HmacSha512256State {
96    crypto_auth_hmacsha512_init(key)
97}
98
99/// Updates `state` for HMAC-SHA-512-256 with `input`.
100pub fn crypto_auth_hmacsha512256_update(state: &mut HmacSha512256State, input: &[u8]) {
101    crypto_auth_hmacsha512_update(state, input);
102}
103
104/// Finalizes HMAC-SHA-512-256 and places the truncated result into `output`.
105pub fn crypto_auth_hmacsha512256_final(state: HmacSha512256State, output: &mut Mac) {
106    let mut full_output = [0u8; CRYPTO_AUTH_HMACSHA512_BYTES];
107    crypto_auth_hmacsha512_final(state, &mut full_output);
108    output.copy_from_slice(&full_output[..CRYPTO_AUTH_HMACSHA512256_BYTES]);
109    full_output.zeroize();
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    fn compute_hmac(key: &[u8], message: &[u8]) -> Mac {
117        let mut mac = Mac::default();
118        let mut state = crypto_auth_hmacsha512256_init(key);
119        crypto_auth_hmacsha512256_update(&mut state, message);
120        crypto_auth_hmacsha512256_final(state, &mut mac);
121        mac
122    }
123
124    fn assert_hmac(key: &[u8], message: &[u8], expected_hex: &str) {
125        let mac = compute_hmac(key, message);
126        let expected = hex::decode(expected_hex).expect("hex failed");
127        assert_eq!(mac.as_slice(), expected.as_slice());
128    }
129
130    #[test]
131    fn test_rfc4231_case_1_truncated() {
132        let key = [0x0bu8; 20];
133        assert_hmac(
134            &key,
135            b"Hi There",
136            "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde",
137        );
138    }
139
140    #[test]
141    fn test_rfc4231_short_key_case_2_truncated() {
142        assert_hmac(
143            b"Jefe",
144            b"what do ya want for nothing?",
145            "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554",
146        );
147    }
148
149    #[test]
150    fn test_rfc4231_long_key_case_6_truncated() {
151        let key = [0xaau8; 131];
152        assert_hmac(
153            &key,
154            b"Test Using Larger Than Block-Size Key - Hash Key First",
155            "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352",
156        );
157    }
158
159    #[test]
160    fn test_rfc4231_long_key_and_message_case_7_truncated() {
161        let key = [0xaau8; 131];
162        assert_hmac(
163            &key,
164            b"This is a test using a larger than block-size key and a larger than block-size data. \
165              The key needs to be hashed before being used by the HMAC algorithm.",
166            "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944",
167        );
168    }
169
170    #[test]
171    fn test_one_shot_matches_incremental_for_keybytes_key() {
172        let key = [0x0bu8; CRYPTO_AUTH_HMACSHA512256_KEYBYTES];
173        let message = b"message";
174        let mut one_shot = Mac::default();
175        crypto_auth_hmacsha512256(&mut one_shot, message, &key);
176        assert_eq!(one_shot, compute_hmac(&key, message));
177    }
178
179    #[cfg(dryoc_native_tests)]
180    #[test]
181    fn test_libsodium_compatibility() {
182        use sodiumoxide::crypto::auth::hmacsha512256;
183
184        let key = crypto_auth_hmacsha512256_keygen();
185        let message = b"message to authenticate";
186        let so_key = hmacsha512256::Key::from_slice(&key).expect("key failed");
187        let so_mac = hmacsha512256::authenticate(message, &so_key);
188
189        let mut mac = Mac::default();
190        crypto_auth_hmacsha512256(&mut mac, message, &key);
191        assert_eq!(mac.as_slice(), so_mac.as_ref());
192        crypto_auth_hmacsha512256_verify(&mac, message, &key).expect("verify failed");
193
194        let mut state = crypto_auth_hmacsha512256_init(&key);
195        crypto_auth_hmacsha512256_update(&mut state, b"message ");
196        crypto_auth_hmacsha512256_update(&mut state, b"to authenticate");
197        let mut state_mac = Mac::default();
198        crypto_auth_hmacsha512256_final(state, &mut state_mac);
199        assert_eq!(state_mac.as_slice(), so_mac.as_ref());
200    }
201}