Skip to main content

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}