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