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