Skip to main content

dryoc/classic/
crypto_onetimeauth.rs

1//! # One-time authentication
2//!
3//! Implements one-time authentication using the Poly1305 algorithm, compatible
4//! with libsodium's `crypto_onetimeauth_*` functions.
5//!
6//! # Classic API single-part example
7//!
8//! ```
9//! use base64::Engine as _;
10//! use base64::engine::general_purpose;
11//! use dryoc::classic::crypto_onetimeauth::{
12//!     Mac, crypto_onetimeauth, crypto_onetimeauth_keygen, crypto_onetimeauth_verify,
13//! };
14//!
15//! let key = crypto_onetimeauth_keygen();
16//! let mut mac = Mac::default();
17//!
18//! crypto_onetimeauth(&mut mac, b"Data to authenticate", &key);
19//!
20//! // This should be valid
21//! crypto_onetimeauth_verify(&mac, b"Data to authenticate", &key).expect("failed to authenticate");
22//!
23//! // This should not be valid
24//! crypto_onetimeauth_verify(&mac, b"Invalid data", &key).expect_err("should not authenticate");
25//! ```
26//!
27//! # Classic API multi-part example
28//!
29//! ```
30//! use base64::Engine as _;
31//! use base64::engine::general_purpose;
32//! use dryoc::classic::crypto_onetimeauth::{
33//!     Mac, crypto_onetimeauth_final, crypto_onetimeauth_init, crypto_onetimeauth_keygen,
34//!     crypto_onetimeauth_update, crypto_onetimeauth_verify,
35//! };
36//!
37//! let key = crypto_onetimeauth_keygen();
38//! let mut mac = Mac::default();
39//!
40//! let mut state = crypto_onetimeauth_init(&key);
41//! crypto_onetimeauth_update(&mut state, b"Multi-part");
42//! crypto_onetimeauth_update(&mut state, b"data");
43//! crypto_onetimeauth_final(state, &mut mac);
44//!
45//! // This should be valid
46//! crypto_onetimeauth_verify(&mac, b"Multi-partdata", &key).expect("failed to authenticate");
47//!
48//! // This should not be valid
49//! crypto_onetimeauth_verify(&mac, b"Invalid data", &key).expect_err("should not authenticate");
50//! ```
51use subtle::ConstantTimeEq;
52
53use crate::constants::{
54    CRYPTO_ONETIMEAUTH_BYTES, CRYPTO_ONETIMEAUTH_KEYBYTES, CRYPTO_ONETIMEAUTH_POLY1305_BYTES,
55    CRYPTO_ONETIMEAUTH_POLY1305_KEYBYTES,
56};
57use crate::error::Error;
58use crate::poly1305::Poly1305;
59use crate::types::*;
60struct OnetimeauthPoly1305State {
61    mac: Poly1305,
62}
63
64/// Key type for use with one-time authentication.
65pub type Key = [u8; CRYPTO_ONETIMEAUTH_POLY1305_KEYBYTES];
66/// Message authentication code type for use with one-time authentication.
67pub type Mac = [u8; CRYPTO_ONETIMEAUTH_POLY1305_BYTES];
68
69fn crypto_onetimeauth_poly1305(output: &mut Mac, message: &[u8], key: &Key) {
70    let mut poly1305 = Poly1305::new(key);
71    poly1305.update(message);
72    poly1305.finalize(output)
73}
74fn crypto_onetimeauth_poly1305_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
75    let mut poly1305 = Poly1305::new(key);
76    poly1305.update(input);
77    let computed_mac = poly1305.finalize_to_array();
78
79    if mac.ct_eq(&computed_mac).unwrap_u8() == 1 {
80        Ok(())
81    } else {
82        Err(Error::AuthenticationFailed)
83    }
84}
85
86fn crypto_onetimeauth_poly1305_init(key: &Key) -> OnetimeauthPoly1305State {
87    OnetimeauthPoly1305State {
88        mac: Poly1305::new(key),
89    }
90}
91
92fn crypto_onetimeauth_poly1305_update(state: &mut OnetimeauthPoly1305State, input: &[u8]) {
93    state.mac.update(input)
94}
95fn crypto_onetimeauth_poly1305_final(
96    mut state: OnetimeauthPoly1305State,
97    output: &mut [u8; CRYPTO_ONETIMEAUTH_POLY1305_BYTES],
98) {
99    state.mac.finalize(output)
100}
101
102/// Authenticates `message` using `key`, and places the result into
103/// `mac`. `key` should only be used once.
104///
105/// Equivalent to libsodium's `crypto_onetimeauth`.
106pub fn crypto_onetimeauth(mac: &mut Mac, message: &[u8], key: &Key) {
107    crypto_onetimeauth_poly1305(mac, message, key)
108}
109
110/// Verifies that `mac` is the correct authenticator for `message` using `key`.
111/// Returns `Ok(())` if the message authentication code is valid.
112///
113/// Equivalent to libsodium's `crypto_onetimeauth_verify`.
114///
115/// # Errors
116///
117/// Returns an error if `mac` is not valid for `input` under `key`.
118pub fn crypto_onetimeauth_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
119    crypto_onetimeauth_poly1305_verify(mac, input, key)
120}
121
122/// Internal state for [`crypto_onetimeauth`].
123pub struct OnetimeauthState {
124    state: OnetimeauthPoly1305State,
125}
126
127/// Generates a random key using
128/// [`copy_randombytes`](crate::rng::copy_randombytes), suitable for use with
129/// [`crypto_onetimeauth_init`] and [`crypto_onetimeauth`]. The key should only
130/// be used once.
131///
132/// Equivalent to libsodium's `crypto_onetimeauth_keygen`.
133pub fn crypto_onetimeauth_keygen() -> Key {
134    Key::generate()
135}
136
137/// Initializes the incremental Poly1305-based one-time authentication.
138///
139/// Initialize the incremental interface for Poly1305-based one-time
140/// authentication, using `key`. Returns a state struct which is required for
141/// subsequent calls to [`crypto_onetimeauth_update`] and
142/// [`crypto_onetimeauth_final`]. The key should only be used once.
143///
144/// Equivalent to libsodium's `crypto_onetimeauth_init`.
145pub fn crypto_onetimeauth_init(key: &[u8; CRYPTO_ONETIMEAUTH_KEYBYTES]) -> OnetimeauthState {
146    OnetimeauthState {
147        state: crypto_onetimeauth_poly1305_init(key),
148    }
149}
150
151/// Updates `state` for the one-time authentication function, based on `input`.
152///
153/// Equivalent to libsodium's `crypto_onetimeauth_update`.
154pub fn crypto_onetimeauth_update(state: &mut OnetimeauthState, input: &[u8]) {
155    crypto_onetimeauth_poly1305_update(&mut state.state, input)
156}
157
158/// Finalizes the message authentication code for `state`, and places the result
159/// into `output`.
160///
161/// Equivalent to libsodium's `crypto_onetimeauth_final`.
162pub fn crypto_onetimeauth_final(
163    state: OnetimeauthState,
164    output: &mut [u8; CRYPTO_ONETIMEAUTH_BYTES],
165) {
166    crypto_onetimeauth_poly1305_final(state.state, output)
167}
168
169#[cfg(all(test, dryoc_native_tests))]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn test_onetimeauth() {
175        use sodiumoxide::crypto::onetimeauth;
176
177        use crate::rng::copy_randombytes;
178
179        for _ in 0..20 {
180            let mut key = [0u8; 32];
181            copy_randombytes(&mut key);
182            let mut input = [0u8; 1024];
183            copy_randombytes(&mut input);
184
185            let so_mac = onetimeauth::authenticate(
186                &input,
187                &onetimeauth::poly1305::Key::from_slice(&key).expect("so key failed"),
188            );
189
190            let mut mac = [0u8; CRYPTO_ONETIMEAUTH_BYTES];
191            crypto_onetimeauth(&mut mac, &input, &key);
192
193            assert_eq!(so_mac.0, mac);
194
195            crypto_onetimeauth_verify(&mac, &input, &key).expect("verify failed");
196        }
197    }
198
199    #[test]
200    fn test_onetimeauth_incremental() {
201        use sodiumoxide::crypto::onetimeauth;
202
203        use crate::rng::copy_randombytes;
204
205        for _ in 0..20 {
206            let mut key = [0u8; 32];
207            copy_randombytes(&mut key);
208            let mut input = [0u8; 1024];
209            copy_randombytes(&mut input);
210
211            let so_mac = onetimeauth::authenticate(
212                &input,
213                &onetimeauth::poly1305::Key::from_slice(&key).expect("so key failed"),
214            );
215
216            let mut mac = [0u8; CRYPTO_ONETIMEAUTH_BYTES];
217            let mut state = crypto_onetimeauth_init(&key);
218            crypto_onetimeauth_update(&mut state, &input);
219            crypto_onetimeauth_final(state, &mut mac);
220
221            assert_eq!(so_mac.0, mac);
222
223            crypto_onetimeauth_verify(&mac, &input, &key).expect("verify failed");
224        }
225    }
226}