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//! # Rustaceous API example, one-time interface
13//!
14//! ```
15//! use dryoc::auth::*;
16//! use dryoc::types::*;
17//!
18//! // Generate a random key
19//! let key = Key::gen();
20//!
21//! // Compute the mac in one shot. Here we clone the key for the purpose of this
22//! // example, but normally you would not do this as you never want to re-use a
23//! // key.
24//! let mac = Auth::compute_to_vec(key.clone(), b"Data to authenticate");
25//!
26//! // Verify the mac
27//! Auth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed");
28//! ```
29//!
30//! # Rustaceous API example, incremental interface
31//!
32//! ```
33//! use dryoc::auth::*;
34//! use dryoc::types::*;
35//!
36//! // Generate a random key
37//! let key = Key::gen();
38//!
39//! // Initialize the MAC, clone the key (don't do this)
40//! let mut mac = Auth::new(key.clone());
41//! mac.update(b"Multi-part");
42//! mac.update(b"data");
43//! let mac = mac.finalize_to_vec();
44//!
45//! // Verify it's correct, clone the key (don't do this)
46//! let mut verify_mac = Auth::new(key.clone());
47//! verify_mac.update(b"Multi-part");
48//! verify_mac.update(b"data");
49//! verify_mac.verify(&mac).expect("verify failed");
50//!
51//! // Check that invalid data fails, consume the key
52//! let mut verify_mac = Auth::new(key);
53//! verify_mac.update(b"Multi-part");
54//! verify_mac.update(b"bad data");
55//! verify_mac
56//!     .verify(&mac)
57//!     .expect_err("verify should have failed");
58//! ```
59
60use subtle::ConstantTimeEq;
61
62use crate::classic::crypto_auth::{
63    AuthState, crypto_auth, crypto_auth_final, crypto_auth_init, crypto_auth_update,
64    crypto_auth_verify,
65};
66use crate::constants::{CRYPTO_AUTH_BYTES, CRYPTO_AUTH_KEYBYTES};
67use crate::error::Error;
68use crate::types::*;
69
70/// Stack-allocated key for secret-key authentication.
71pub type Key = StackByteArray<CRYPTO_AUTH_KEYBYTES>;
72/// Stack-allocated message authentication code for secret-key authentication.
73pub type Mac = StackByteArray<CRYPTO_AUTH_BYTES>;
74
75#[cfg(any(feature = "nightly", all(doc, not(doctest))))]
76#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))]
77pub mod protected {
78    //! #  Protected memory type aliases for [`Auth`]
79    //!
80    //! This mod provides re-exports of type aliases for protected memory usage
81    //! with [`Auth`]. These type aliases are provided for
82    //! convenience.
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::gen_readonly_locked().expect("gen failed");
92    //! let input =
93    //!     HeapBytes::from_slice_into_readonly_locked(b"super secret input").expect("input failed");
94    //! // Compute the message authentication code, consuming 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 the generic hash algorithm,
101    /// for use with protected memory.
102    pub type Key = HeapByteArray<CRYPTO_AUTH_KEYBYTES>;
103    /// Heap-allocated, page-aligned hash output for the generic hash algorithm,
104    /// for use with protected memory.
105    pub type Mac = HeapByteArray<CRYPTO_AUTH_BYTES>;
106}
107
108/// secret-key authentication implementation based on Poly1305, compatible with
109/// libsodium's `crypto_Auth_*` functions.
110pub struct Auth {
111    state: AuthState,
112}
113
114impl Auth {
115    /// Single-part interface for [`Auth`]. Computes (and returns) the
116    /// message authentication code for `input` using `key`. The `key` is
117    /// consumed to prevent accidental re-use of the same key.
118    pub fn compute<
119        Key: ByteArray<CRYPTO_AUTH_KEYBYTES>,
120        Input: Bytes,
121        Output: NewByteArray<CRYPTO_AUTH_BYTES>,
122    >(
123        key: Key,
124        input: &Input,
125    ) -> Output {
126        let mut output = Output::new_byte_array();
127        crypto_auth(output.as_mut_array(), input.as_slice(), key.as_array());
128        output
129    }
130
131    /// Convience wrapper around [`Auth::compute`]. Returns the message
132    /// authentication code as a [`Vec`]. The `key` is
133    /// consumed to prevent accidental re-use of the same key.
134    pub fn compute_to_vec<Key: ByteArray<CRYPTO_AUTH_KEYBYTES>, Input: Bytes>(
135        key: Key,
136        input: &Input,
137    ) -> Vec<u8> {
138        Self::compute(key, input)
139    }
140
141    /// Verifies the message authentication code `other_mac` matches the
142    /// expected code for `key` and `input`. The `key` is
143    /// consumed to prevent accidental re-use of the same key.
144    pub fn compute_and_verify<
145        OtherMac: ByteArray<CRYPTO_AUTH_BYTES>,
146        Key: ByteArray<CRYPTO_AUTH_KEYBYTES>,
147        Input: Bytes,
148    >(
149        other_mac: &OtherMac,
150        key: Key,
151        input: &Input,
152    ) -> Result<(), Error> {
153        crypto_auth_verify(other_mac.as_array(), input.as_slice(), key.as_array())
154    }
155
156    /// Returns a new secret-key authenticator for `key`. The `key` is
157    /// consumed to prevent accidental re-use of the same key.
158    pub fn new<Key: ByteArray<CRYPTO_AUTH_KEYBYTES>>(key: Key) -> Self {
159        Self {
160            state: crypto_auth_init(key.as_array()),
161        }
162    }
163
164    /// Updates the secret-key authenticator at `self` with `input`.
165    pub fn update<Input: Bytes>(&mut self, input: &Input) {
166        crypto_auth_update(&mut self.state, input.as_slice())
167    }
168
169    /// Finalizes this secret-key authenticator, returning the message
170    /// authentication code.
171    pub fn finalize<Output: NewByteArray<CRYPTO_AUTH_BYTES>>(self) -> Output {
172        let mut output = Output::new_byte_array();
173        crypto_auth_final(self.state, output.as_mut_array());
174        output
175    }
176
177    /// Finalizes this secret-key authenticator, returning the message
178    /// authentication code as a [`Vec`]. Convenience wrapper around
179    /// [`Auth::finalize`].
180    pub fn finalize_to_vec(self) -> Vec<u8> {
181        self.finalize()
182    }
183
184    /// Finalizes this authenticator, and verifies that the computed code
185    /// matches `other_mac` using a constant-time comparison.
186    pub fn verify<OtherMac: ByteArray<CRYPTO_AUTH_BYTES>>(
187        self,
188        other_mac: &OtherMac,
189    ) -> Result<(), Error> {
190        let computed_mac: Mac = self.finalize();
191
192        if other_mac
193            .as_array()
194            .ct_eq(computed_mac.as_array())
195            .unwrap_u8()
196            == 1
197        {
198            Ok(())
199        } else {
200            Err(dryoc_error!("authentication codes do not match"))
201        }
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208
209    #[test]
210    fn test_single_part() {
211        let key = Key::gen();
212        let mac = Auth::compute_to_vec(key.clone(), b"Data to authenticate");
213
214        Auth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed");
215    }
216
217    #[test]
218    fn test_multi_part() {
219        let key = Key::gen();
220
221        let mut mac = Auth::new(key.clone());
222        mac.update(b"Multi-part");
223        mac.update(b"data");
224        let mac = mac.finalize_to_vec();
225
226        let mut verify_mac = Auth::new(key.clone());
227        verify_mac.update(b"Multi-part");
228        verify_mac.update(b"data");
229        verify_mac.verify(&mac).expect("verify failed");
230
231        let mut verify_mac = Auth::new(key);
232        verify_mac.update(b"Multi-part");
233        verify_mac.update(b"bad data");
234        verify_mac
235            .verify(&mac)
236            .expect_err("verify should have failed");
237    }
238}