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(dryoc_error!("authentication codes do not match"))
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`.
114pub fn crypto_onetimeauth_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
115 crypto_onetimeauth_poly1305_verify(mac, input, key)
116}
117
118/// Internal state for [`crypto_onetimeauth`].
119pub struct OnetimeauthState {
120 state: OnetimeauthPoly1305State,
121}
122
123/// Generates a random key using
124/// [`copy_randombytes`](crate::rng::copy_randombytes), suitable for use with
125/// [`crypto_onetimeauth_init`] and [`crypto_onetimeauth`]. The key should only
126/// be used once.
127///
128/// Equivalent to libsodium's `crypto_onetimeauth_keygen`.
129pub fn crypto_onetimeauth_keygen() -> Key {
130 Key::gen()
131}
132
133/// Initializes the incremental Poly1305-based one-time authentication.
134///
135/// Initialize the incremental interface for Poly1305-based one-time
136/// authentication, using `key`. Returns a state struct which is required for
137/// subsequent calls to [`crypto_onetimeauth_update`] and
138/// [`crypto_onetimeauth_final`]. The key should only be used once.
139///
140/// Equivalent to libsodium's `crypto_onetimeauth_init`.
141pub fn crypto_onetimeauth_init(key: &[u8; CRYPTO_ONETIMEAUTH_KEYBYTES]) -> OnetimeauthState {
142 OnetimeauthState {
143 state: crypto_onetimeauth_poly1305_init(key),
144 }
145}
146
147/// Updates `state` for the one-time authentication function, based on `input`.
148///
149/// Equivalent to libsodium's `crypto_onetimeauth_update`.
150pub fn crypto_onetimeauth_update(state: &mut OnetimeauthState, input: &[u8]) {
151 crypto_onetimeauth_poly1305_update(&mut state.state, input)
152}
153
154/// Finalizes the message authentication code for `state`, and places the result
155/// into `output`.
156///
157/// Equivalent to libsodium's `crypto_onetimeauth_final`.
158pub fn crypto_onetimeauth_final(
159 state: OnetimeauthState,
160 output: &mut [u8; CRYPTO_ONETIMEAUTH_BYTES],
161) {
162 crypto_onetimeauth_poly1305_final(state.state, output)
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_onetimeauth() {
171 use sodiumoxide::crypto::onetimeauth;
172
173 use crate::rng::copy_randombytes;
174
175 for _ in 0..20 {
176 let mut key = [0u8; 32];
177 copy_randombytes(&mut key);
178 let mut input = [0u8; 1024];
179 copy_randombytes(&mut input);
180
181 let so_mac = onetimeauth::authenticate(
182 &input,
183 &onetimeauth::poly1305::Key::from_slice(&key).expect("so key failed"),
184 );
185
186 let mut mac = [0u8; CRYPTO_ONETIMEAUTH_BYTES];
187 crypto_onetimeauth(&mut mac, &input, &key);
188
189 assert_eq!(so_mac.0, mac);
190
191 crypto_onetimeauth_verify(&mac, &input, &key).expect("verify failed");
192 }
193 }
194
195 #[test]
196 fn test_onetimeauth_incremental() {
197 use sodiumoxide::crypto::onetimeauth;
198
199 use crate::rng::copy_randombytes;
200
201 for _ in 0..20 {
202 let mut key = [0u8; 32];
203 copy_randombytes(&mut key);
204 let mut input = [0u8; 1024];
205 copy_randombytes(&mut input);
206
207 let so_mac = onetimeauth::authenticate(
208 &input,
209 &onetimeauth::poly1305::Key::from_slice(&key).expect("so key failed"),
210 );
211
212 let mut mac = [0u8; CRYPTO_ONETIMEAUTH_BYTES];
213 let mut state = crypto_onetimeauth_init(&key);
214 crypto_onetimeauth_update(&mut state, &input);
215 crypto_onetimeauth_final(state, &mut mac);
216
217 assert_eq!(so_mac.0, mac);
218
219 crypto_onetimeauth_verify(&mac, &input, &key).expect("verify failed");
220 }
221 }
222}