Skip to main content

dryoc/
auth.rs

1//! # Secret-key message authentication
2//!
3//! [`Auth`] implements libsodium's secret-key authentication, based on
4//! HMAC-SHA512-256.
5//!
6//! Use [`Auth`] to authenticate messages when:
7//!
8//! * you want to authenticate arbitrary messages
9//! * you have a pre-shared key between both parties
10//! * (optionally) you want to share the authentication tag publicly
11//!
12//! The same HMAC key can authenticate multiple messages. Keep the key secret,
13//! and use separate keys when protocols require domain separation.
14//!
15//! # Rustaceous API example, single-part interface
16//!
17//! ```
18//! use dryoc::auth::*;
19//! use dryoc::types::*;
20//!
21//! // Generate a random key
22//! let key = Key::generate();
23//!
24//! // Compute the MAC in one shot. This API takes ownership of the key, so clone
25//! // it when the same key is also needed for verification.
26//! let mac = Auth::compute_to_vec(key.clone(), b"Data to authenticate");
27//!
28//! // Verify the MAC
29//! Auth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed");
30//! ```
31//!
32//! # Rustaceous API example, incremental interface
33//!
34//! ```
35//! use dryoc::auth::*;
36//! use dryoc::types::*;
37//!
38//! // Generate a random key
39//! let key = Key::generate();
40//!
41//! // Initialize the MAC
42//! let mut mac = Auth::new(key.clone());
43//! mac.update(b"Multi-part");
44//! mac.update(b"data");
45//! let mac = mac.finalize_to_vec();
46//!
47//! // Verify the MAC
48//! let mut verify_mac = Auth::new(key.clone());
49//! verify_mac.update(b"Multi-part");
50//! verify_mac.update(b"data");
51//! verify_mac.verify(&mac).expect("verify failed");
52//!
53//! // Check that invalid data fails
54//! let mut verify_mac = Auth::new(key);
55//! verify_mac.update(b"Multi-part");
56//! verify_mac.update(b"bad data");
57//! verify_mac
58//!     .verify(&mac)
59//!     .expect_err("verify should have failed");
60//! ```
61
62use subtle::ConstantTimeEq;
63
64use crate::classic::crypto_auth::{
65    AuthState, crypto_auth, crypto_auth_final, crypto_auth_init, crypto_auth_update,
66    crypto_auth_verify,
67};
68use crate::constants::{CRYPTO_AUTH_BYTES, CRYPTO_AUTH_KEYBYTES};
69use crate::error::Error;
70use crate::types::*;
71
72/// Stack-allocated key for secret-key authentication.
73pub type Key = StackByteArray<CRYPTO_AUTH_KEYBYTES>;
74/// Stack-allocated message authentication code for secret-key authentication.
75pub type Mac = StackByteArray<CRYPTO_AUTH_BYTES>;
76
77#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
78#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
79pub mod protected {
80    //! # Protected memory type aliases for [`Auth`]
81    //!
82    //! Protected-memory aliases for authentication keys and codes.
83    //!
84    //! ## Example
85    //!
86    //! ```
87    //! use dryoc::auth::Auth;
88    //! use dryoc::auth::protected::*;
89    //!
90    //! // Create a randomly generated key, lock it, protect it as read-only
91    //! let key = Key::generate_readonly_locked().expect("generate failed");
92    //! let input =
93    //!     HeapBytes::from_slice_into_readonly_locked(b"super secret input").expect("input failed");
94    //! // Compute the message authentication code. This takes ownership of the key.
95    //! let mac: Locked<Mac> = Auth::compute(key, &input);
96    //! ```
97    use super::*;
98    pub use crate::protected::*;
99
100    /// Heap-allocated, page-aligned secret key for authentication with
101    /// protected memory.
102    pub type Key = HeapByteArray<CRYPTO_AUTH_KEYBYTES>;
103    /// Heap-allocated, page-aligned authentication code for use with protected
104    /// memory.
105    pub type Mac = HeapByteArray<CRYPTO_AUTH_BYTES>;
106}
107
108/// Secret-key authentication implementation based on libsodium's
109/// HMAC-SHA512-256 `crypto_auth_*` functions.
110pub struct Auth {
111    state: AuthState,
112}
113
114impl Auth {
115    /// Computes the message authentication code for `input` using `key`.
116    ///
117    /// This function takes ownership of `key`, but HMAC keys may be reused for
118    /// multiple messages. Clone the key first when it is needed again.
119    pub fn compute<
120        Key: ByteArray<CRYPTO_AUTH_KEYBYTES>,
121        Input: Bytes,
122        Output: NewByteArray<CRYPTO_AUTH_BYTES>,
123    >(
124        key: Key,
125        input: &Input,
126    ) -> Output {
127        let mut output = Output::new_byte_array();
128        crypto_auth(output.as_mut_array(), input.as_slice(), key.as_array());
129        output
130    }
131
132    /// Computes the message authentication code and returns it as a [`Vec`].
133    ///
134    /// This is a convenience wrapper around [`Auth::compute`].
135    pub fn compute_to_vec<Key: ByteArray<CRYPTO_AUTH_KEYBYTES>, Input: Bytes>(
136        key: Key,
137        input: &Input,
138    ) -> Vec<u8> {
139        Self::compute(key, input)
140    }
141
142    /// Verifies that `other_mac` authenticates `input` under `key`.
143    ///
144    /// # Errors
145    ///
146    /// Returns an error if `other_mac` does not match the authentication code
147    /// computed from `key` and `input`.
148    pub fn compute_and_verify<
149        OtherMac: ByteArray<CRYPTO_AUTH_BYTES>,
150        Key: ByteArray<CRYPTO_AUTH_KEYBYTES>,
151        Input: Bytes,
152    >(
153        other_mac: &OtherMac,
154        key: Key,
155        input: &Input,
156    ) -> Result<(), Error> {
157        crypto_auth_verify(other_mac.as_array(), input.as_slice(), key.as_array())
158    }
159
160    /// Returns a new incremental authenticator for `key`.
161    ///
162    /// This function takes ownership of `key`, but HMAC keys may be reused for
163    /// multiple messages. Clone the key first when it is needed again.
164    pub fn new<Key: ByteArray<CRYPTO_AUTH_KEYBYTES>>(key: Key) -> Self {
165        Self {
166            state: crypto_auth_init(key.as_array()),
167        }
168    }
169
170    /// Updates the secret-key authenticator at `self` with `input`.
171    pub fn update<Input: Bytes>(&mut self, input: &Input) {
172        crypto_auth_update(&mut self.state, input.as_slice())
173    }
174
175    /// Finalizes this secret-key authenticator, returning the message
176    /// authentication code.
177    pub fn finalize<Output: NewByteArray<CRYPTO_AUTH_BYTES>>(self) -> Output {
178        let mut output = Output::new_byte_array();
179        crypto_auth_final(self.state, output.as_mut_array());
180        output
181    }
182
183    /// Finalizes this secret-key authenticator, returning the message
184    /// authentication code as a [`Vec`]. Convenience wrapper around
185    /// [`Auth::finalize`].
186    pub fn finalize_to_vec(self) -> Vec<u8> {
187        self.finalize()
188    }
189
190    /// Finalizes this authenticator, and verifies that the computed code
191    /// matches `other_mac` using a constant-time comparison.
192    ///
193    /// # Errors
194    ///
195    /// Returns an error if `other_mac` does not match the authentication code
196    /// computed from the data passed to [`Auth::update`].
197    pub fn verify<OtherMac: ByteArray<CRYPTO_AUTH_BYTES>>(
198        self,
199        other_mac: &OtherMac,
200    ) -> Result<(), Error> {
201        let computed_mac: Mac = self.finalize();
202
203        if other_mac
204            .as_array()
205            .ct_eq(computed_mac.as_array())
206            .unwrap_u8()
207            == 1
208        {
209            Ok(())
210        } else {
211            Err(Error::AuthenticationFailed)
212        }
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219
220    #[test]
221    fn test_single_part() {
222        let key = Key::generate();
223        let mac = Auth::compute_to_vec(key.clone(), b"Data to authenticate");
224
225        Auth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed");
226    }
227
228    #[test]
229    fn test_multi_part() {
230        let key = Key::generate();
231
232        let mut mac = Auth::new(key.clone());
233        mac.update(b"Multi-part");
234        mac.update(b"data");
235        let mac = mac.finalize_to_vec();
236
237        let mut verify_mac = Auth::new(key.clone());
238        verify_mac.update(b"Multi-part");
239        verify_mac.update(b"data");
240        verify_mac.verify(&mac).expect("verify failed");
241
242        let mut verify_mac = Auth::new(key);
243        verify_mac.update(b"Multi-part");
244        verify_mac.update(b"bad data");
245        verify_mac
246            .verify(&mac)
247            .expect_err("verify should have failed");
248    }
249}