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}