libsodium_rs/crypto_onetimeauth.rs
1//! # One-Time Authentication (Poly1305)
2//!
3//! This module provides functions for computing and verifying one-time authentication tags
4//! using the Poly1305 algorithm. Poly1305 is a cryptographic message authentication code (MAC)
5//! that can be used to verify the integrity and authenticity of a message.
6//!
7//! ## About Poly1305
8//!
9//! Poly1305 is a state-of-the-art message authentication code designed by Daniel J. Bernstein.
10//! It computes a 16-byte (128-bit) authentication tag for a message and a 32-byte key.
11//! The algorithm is based on evaluation of a polynomial modulo the prime 2^130 - 5.
12//!
13//! ## Security Properties
14//!
15//! - **High security**: Poly1305 offers 128-bit security against forgery attempts
16//! - **Constant-time**: The implementation is designed to take the same amount of time regardless of input
17//! - **Deterministic**: The same message and key always produce the same tag
18//! - **Efficient**: Poly1305 is extremely fast on modern processors
19//!
20//! ## Security Considerations
21//!
22//! - **CRITICAL**: Each key must be used **only once**. Reusing a key for multiple messages
23//! completely compromises security. The name "one-time" is not a suggestion but a strict requirement.
24//! - Poly1305 is designed to be used in conjunction with a cipher, typically in an AEAD construction.
25//! - For most applications, you should use higher-level AEAD constructions like `crypto_secretbox`
26//! or `crypto_box` instead of using this module directly.
27//! - Poly1305 by itself does not provide confidentiality (encryption) - it only provides authenticity.
28//!
29//! ## When to Use This Module
30//!
31//! You should only use this module directly if you:
32//! - Understand the security implications of one-time authentication
33//! - Have a specific need for standalone authentication tags
34//! - Can guarantee that each key will be used only once
35//! - Have a secure mechanism for key generation and management
36//!
37//! ## Example
38//!
39//! ```rust
40//! use libsodium_rs as sodium;
41//! use sodium::crypto_onetimeauth;
42//! use sodium::ensure_init;
43//!
44//! fn main() -> Result<(), Box<dyn std::error::Error>> {
45//! ensure_init()?;
46//!
47//! // Generate a random key (should only be used once)
48//! let key = crypto_onetimeauth::Key::generate();
49//! let message = b"Hello, world!";
50//!
51//! // Compute the authentication tag
52//! let tag = crypto_onetimeauth::onetimeauth(message, &key);
53//!
54//! // Verify the tag
55//! assert!(crypto_onetimeauth::verify(&tag, message, &key));
56//!
57//! // For incremental authentication
58//! let mut state = crypto_onetimeauth::State::new(&key);
59//! state.update(b"Hello, ");
60//! state.update(b"world!");
61//! let tag2 = state.finalize();
62//!
63//! assert_eq!(tag.as_bytes(), tag2.as_bytes());
64//! Ok(())
65//! }
66//! ```
67//!
68
69use crate::{Result, SodiumError};
70use std::convert::TryFrom;
71use std::fmt;
72
73/// Number of bytes in a key
74pub const KEYBYTES: usize = libsodium_sys::crypto_onetimeauth_KEYBYTES as usize;
75/// Number of bytes in a tag
76pub const BYTES: usize = libsodium_sys::crypto_onetimeauth_BYTES as usize;
77
78/// A key for Poly1305 one-time authentication
79///
80/// This key is used for computing and verifying Poly1305 authentication tags.
81/// It must be exactly 32 bytes long.
82///
83/// ## Security Warning
84///
85/// This key should be used only once for a single message.
86/// Reusing the same key for multiple messages completely compromises security.
87/// This is why Poly1305 is called a "one-time" authenticator.
88///
89/// ## Key Generation
90///
91/// Keys should be generated using a cryptographically secure random number generator.
92/// The `generate()` method provides a convenient way to create a secure random key.
93///
94/// ## Usage Pattern
95///
96/// 1. Generate a new key for each message to be authenticated
97/// 2. Use the key to compute an authentication tag
98/// 3. Never reuse the key for another message
99/// 4. Store or transmit the key securely alongside the message and tag
100#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
101pub struct Key([u8; KEYBYTES]);
102
103/// A Poly1305 authentication tag
104///
105/// This tag is used to verify the authenticity of a message. It is always 16 bytes (128 bits) long.
106///
107/// ## Security Properties
108///
109/// - The tag provides 128-bit security against forgery attempts
110/// - Tags are deterministic: the same message and key always produce the same tag
111/// - Verification is constant-time to prevent timing attacks
112///
113/// ## Usage
114///
115/// - The tag should be transmitted or stored alongside the message
116/// - The recipient can verify the tag to ensure the message hasn't been tampered with
117/// - The tag does not need to be kept secret, but the key used to create it does
118#[derive(Debug, Clone, Eq, PartialEq)]
119pub struct Tag([u8; BYTES]);
120
121impl Key {
122 /// Generate a new key from bytes
123 ///
124 /// # Arguments
125 /// * `bytes` - The bytes to create the key from
126 ///
127 /// # Returns
128 /// * `Result<Self>` - The key or an error if the input is invalid
129 ///
130 /// # Errors
131 /// Returns an error if the input is not exactly `KEYBYTES` bytes long
132 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
133 if bytes.len() != KEYBYTES {
134 return Err(SodiumError::InvalidInput(format!(
135 "key must be exactly {} bytes, got {}",
136 KEYBYTES,
137 bytes.len()
138 )));
139 }
140
141 let mut key = [0u8; KEYBYTES];
142 key.copy_from_slice(bytes);
143 Ok(Key(key))
144 }
145
146 /// Generate a new random key
147 ///
148 /// # Returns
149 /// * `Self` - A randomly generated key
150 ///
151 /// # Panics
152 /// This function will panic if the random number generator fails, which should never happen
153 pub fn generate() -> Self {
154 let bytes = crate::random::bytes(KEYBYTES);
155 // This unwrap is safe because we know the length is correct
156 Key::from_bytes(&bytes).unwrap()
157 }
158
159 /// Get the bytes of the key
160 ///
161 /// # Returns
162 /// * `&[u8]` - A reference to the key bytes
163 pub fn as_bytes(&self) -> &[u8] {
164 &self.0
165 }
166}
167
168impl Tag {
169 /// Create a tag from bytes
170 ///
171 /// # Arguments
172 /// * `bytes` - The bytes to create the tag from
173 ///
174 /// # Returns
175 /// * `Result<Self>` - The tag or an error if the input is invalid
176 ///
177 /// # Errors
178 /// Returns an error if the input is not exactly `BYTES` bytes long
179 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
180 if bytes.len() != BYTES {
181 return Err(SodiumError::InvalidInput(format!(
182 "tag must be exactly {} bytes, got {}",
183 BYTES,
184 bytes.len()
185 )));
186 }
187
188 let mut tag = [0u8; BYTES];
189 tag.copy_from_slice(bytes);
190 Ok(Tag(tag))
191 }
192
193 /// Get the bytes of the tag
194 ///
195 /// # Returns
196 /// * `&[u8]` - A reference to the tag bytes
197 pub fn as_bytes(&self) -> &[u8] {
198 &self.0
199 }
200}
201
202/// Poly1305 state for incremental authentication
203///
204/// This structure allows computing a Poly1305 authentication tag incrementally,
205/// which is useful when the entire message is not available at once or when
206/// processing large messages in chunks to avoid excessive memory usage.
207///
208/// ## Memory Efficiency
209///
210/// Using the incremental API allows processing messages of any size without
211/// needing to load the entire message into memory at once. This is particularly
212/// useful for:
213///
214/// - Large files that don't fit in memory
215/// - Streaming data from a network or other source
216/// - Memory-constrained environments
217///
218/// ## Security Note
219///
220/// The security properties of Poly1305 are maintained when using the incremental API.
221/// The same tag will be produced regardless of how the message is chunked, as long as
222/// the chunks are processed in the correct order.
223pub struct State {
224 state: Box<libsodium_sys::crypto_onetimeauth_state>,
225}
226
227impl State {
228 /// Creates a new Poly1305 authentication state
229 ///
230 /// # Arguments
231 /// * `key` - The key to use for authentication
232 ///
233 /// # Returns
234 /// * `Self` - The initialized state
235 ///
236 /// # Panics
237 /// This function should never panic with valid inputs. The key validity is ensured by the Key type.
238 pub fn new(key: &Key) -> Self {
239 let mut state: Box<libsodium_sys::crypto_onetimeauth_state> =
240 Box::new(unsafe { std::mem::zeroed() });
241 let result = unsafe {
242 libsodium_sys::crypto_onetimeauth_init(state.as_mut(), key.as_bytes().as_ptr())
243 };
244
245 // This should never fail with a valid key, which is guaranteed by the Key type
246 debug_assert_eq!(result, 0, "Poly1305 state initialization failed");
247
248 State { state }
249 }
250
251 /// Updates the authentication state with more input data
252 ///
253 /// # Arguments
254 /// * `input` - The data to include in the authentication
255 ///
256 /// This function cannot fail with valid inputs, and we validate inputs through the State constructor.
257 pub fn update(&mut self, input: &[u8]) {
258 unsafe {
259 // This call cannot fail with valid inputs
260 libsodium_sys::crypto_onetimeauth_update(
261 self.state.as_mut(),
262 input.as_ptr(),
263 input.len() as u64,
264 );
265 }
266 }
267
268 /// Finalizes the authentication computation and returns the tag
269 ///
270 /// # Returns
271 /// * `Tag` - The authentication tag
272 ///
273 /// This function cannot fail with valid inputs, and we validate inputs through the State constructor.
274 pub fn finalize(&mut self) -> Tag {
275 let mut tag = [0u8; BYTES];
276 unsafe {
277 // This call cannot fail with valid inputs
278 libsodium_sys::crypto_onetimeauth_final(self.state.as_mut(), tag.as_mut_ptr());
279 }
280
281 Tag(tag)
282 }
283}
284
285/// Computes a Poly1305 one-time authentication tag for the input data
286///
287/// This function computes a 16-byte (128-bit) authentication tag for the given input data
288/// using the provided key. It provides a convenient one-shot interface for authenticating
289/// data that is already available in memory.
290///
291/// ## Security Properties
292///
293/// - **Forgery resistance**: It is computationally infeasible to create a valid tag for a
294/// message without knowing the key
295/// - **Collision resistance**: It is computationally infeasible to find two different messages
296/// that produce the same tag with the same key
297///
298/// ## Use Cases
299///
300/// - **Message authentication**: Verify that a message hasn't been tampered with
301/// - **API request signing**: Authenticate requests to an API
302/// - **Component of authenticated encryption**: Used in combination with encryption
303///
304/// ## Arguments
305/// * `input` - The data to authenticate
306/// * `key` - The key to use for authentication (should be used only once)
307///
308/// ## Returns
309/// * `Tag` - The 16-byte authentication tag
310///
311/// ## Example
312///
313/// ```rust
314/// use libsodium_rs as sodium;
315/// use sodium::crypto_onetimeauth;
316/// use sodium::ensure_init;
317///
318/// fn main() -> Result<(), Box<dyn std::error::Error>> {
319/// ensure_init()?;
320///
321/// // Generate a random key (should only be used once)
322/// let key = crypto_onetimeauth::Key::generate();
323/// let message = b"Message to authenticate";
324///
325/// // Compute the authentication tag
326/// let tag = crypto_onetimeauth::onetimeauth(message, &key);
327///
328/// // Later, verify the tag
329/// assert!(crypto_onetimeauth::verify(&tag, message, &key));
330/// Ok(())
331/// }
332/// ```
333///
334/// ## Security Considerations
335/// - **CRITICAL**: Each key must be used only once. Reusing a key for multiple messages
336/// completely compromises security.
337/// - For most applications, you should use higher-level AEAD constructions like `crypto_secretbox`
338/// or `crypto_box` instead of using this function directly.
339/// - This function does not provide confidentiality (encryption) - it only provides authenticity.
340pub fn onetimeauth(input: &[u8], key: &Key) -> Tag {
341 let mut tag = [0u8; BYTES];
342
343 unsafe {
344 // This call cannot fail with valid inputs, and we validate inputs through the Key type
345 libsodium_sys::crypto_onetimeauth(
346 tag.as_mut_ptr(),
347 input.as_ptr(),
348 input.len() as u64,
349 key.as_bytes().as_ptr(),
350 );
351 }
352
353 Tag(tag)
354}
355
356/// Verifies a Poly1305 one-time authentication tag
357///
358/// This function verifies that a Poly1305 authentication tag is valid for the given
359/// input data and key. It returns a boolean indicating whether the tag is valid.
360///
361/// ## Constant-Time Verification
362///
363/// This function performs verification in constant time to prevent timing attacks.
364/// This means the time taken to verify a tag does not depend on the content of the
365/// tag or the validity of the tag, making it resistant to timing side-channel attacks.
366///
367/// ## Security Considerations
368///
369/// - The key used for verification must be the same key used to create the tag
370/// - Even if verification fails, the key should not be reused for another message
371/// - Failed verification indicates the message may have been tampered with or corrupted
372///
373/// ## Arguments
374/// * `tag` - The tag to verify
375/// * `input` - The data that was authenticated
376/// * `key` - The key that was used for authentication
377///
378/// ## Returns
379/// * `bool` - `true` if the tag is valid, `false` otherwise
380///
381/// ## Example
382///
383/// ```rust
384/// use libsodium_rs as sodium;
385/// use sodium::crypto_onetimeauth;
386/// use sodium::ensure_init;
387///
388/// fn main() -> Result<(), Box<dyn std::error::Error>> {
389/// ensure_init()?;
390///
391/// // Generate a random key (should only be used once)
392/// let key = crypto_onetimeauth::Key::generate();
393/// let message = b"Message to authenticate";
394/// let tag = crypto_onetimeauth::onetimeauth(message, &key);
395///
396/// // Verify the tag with the original message
397/// assert!(crypto_onetimeauth::verify(&tag, message, &key));
398///
399/// // Verification should fail with a different message
400/// let tampered_message = b"Tampered message";
401/// assert!(!crypto_onetimeauth::verify(&tag, tampered_message, &key));
402/// Ok(())
403/// }
404/// ```
405pub fn verify(tag: &Tag, input: &[u8], key: &Key) -> bool {
406 let result = unsafe {
407 libsodium_sys::crypto_onetimeauth_verify(
408 tag.as_bytes().as_ptr(),
409 input.as_ptr(),
410 input.len() as u64,
411 key.as_bytes().as_ptr(),
412 )
413 };
414
415 result == 0
416}
417
418// Add implementation of Display for Tag for easier debugging
419impl fmt::Display for Tag {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 write!(f, "Tag({:?})", self.0)
422 }
423}
424
425// Add implementation of TryFrom for Key for more idiomatic conversions
426impl TryFrom<&[u8]> for Key {
427 type Error = SodiumError;
428
429 fn try_from(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
430 Key::from_bytes(bytes)
431 }
432}
433
434// Add implementation of TryFrom for Tag for more idiomatic conversions
435impl TryFrom<&[u8]> for Tag {
436 type Error = SodiumError;
437
438 fn try_from(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
439 Tag::from_bytes(bytes)
440 }
441}
442
443// Add AsRef<[u8]> implementation for Key
444impl AsRef<[u8]> for Key {
445 fn as_ref(&self) -> &[u8] {
446 &self.0
447 }
448}
449
450// Add AsRef<[u8]> implementation for Tag
451impl AsRef<[u8]> for Tag {
452 fn as_ref(&self) -> &[u8] {
453 &self.0
454 }
455}
456
457// Add From<[u8; KEYBYTES]> for Key
458impl From<[u8; KEYBYTES]> for Key {
459 fn from(bytes: [u8; KEYBYTES]) -> Self {
460 Key(bytes)
461 }
462}
463
464// Add From<[u8; BYTES]> for Tag
465impl From<[u8; BYTES]> for Tag {
466 fn from(bytes: [u8; BYTES]) -> Self {
467 Tag(bytes)
468 }
469}
470
471// Add From<Key> for [u8; KEYBYTES]
472impl From<Key> for [u8; KEYBYTES] {
473 fn from(key: Key) -> Self {
474 key.0
475 }
476}
477
478// Add From<Tag> for [u8; BYTES]
479impl From<Tag> for [u8; BYTES] {
480 fn from(tag: Tag) -> Self {
481 tag.0
482 }
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488
489 #[test]
490 fn test_onetimeauth() {
491 // Generate a random key
492 let key = Key::generate();
493 let message = b"Hello, World!";
494
495 // Compute the authentication tag
496 let tag = onetimeauth(message, &key);
497 assert_eq!(tag.as_bytes().len(), BYTES);
498
499 // Verify the tag with correct inputs
500 assert!(verify(&tag, message, &key));
501
502 // Verify with wrong message - should fail
503 let wrong_message = b"Wrong message";
504 assert!(!verify(&tag, wrong_message, &key));
505
506 // Verify with wrong key - should fail
507 let wrong_key = Key::generate();
508 assert!(!verify(&tag, message, &wrong_key));
509 }
510
511 #[test]
512 fn test_onetimeauth_incremental() {
513 // Generate a random key
514 let key = Key::generate();
515 let message1 = b"Hello, ";
516 let message2 = b"World!";
517
518 // Compute tag in one go for comparison
519 let full_message = b"Hello, World!";
520 let expected_tag = onetimeauth(full_message, &key);
521
522 // Compute tag incrementally
523 let mut state = State::new(&key);
524 state.update(message1);
525 state.update(message2);
526 let incremental_tag = state.finalize();
527
528 // Both methods should produce the same tag
529 assert_eq!(expected_tag.as_bytes(), incremental_tag.as_bytes());
530 }
531
532 #[test]
533 fn test_key_trait_implementations() {
534 // Test From<[u8; KEYBYTES]> for Key
535 let key_bytes = [42u8; KEYBYTES];
536 let key = Key::from(key_bytes);
537 assert_eq!(key.as_bytes(), &key_bytes);
538
539 // Test From<Key> for [u8; KEYBYTES]
540 let key_bytes_back: [u8; KEYBYTES] = key.clone().into();
541 assert_eq!(key_bytes_back, key_bytes);
542
543 // Test AsRef<[u8]> for Key
544 let key_ref: &[u8] = key.as_ref();
545 assert_eq!(key_ref, &key_bytes);
546 assert_eq!(key_ref.len(), KEYBYTES);
547 }
548
549 #[test]
550 fn test_tag_trait_implementations() {
551 // Test From<[u8; BYTES]> for Tag
552 let tag_bytes = [99u8; BYTES];
553 let tag = Tag::from(tag_bytes);
554 assert_eq!(tag.as_bytes(), &tag_bytes);
555
556 // Test From<Tag> for [u8; BYTES]
557 let tag_bytes_back: [u8; BYTES] = tag.clone().into();
558 assert_eq!(tag_bytes_back, tag_bytes);
559
560 // Test AsRef<[u8]> for Tag
561 let tag_ref: &[u8] = tag.as_ref();
562 assert_eq!(tag_ref, &tag_bytes);
563 assert_eq!(tag_ref.len(), BYTES);
564 }
565
566 #[test]
567 fn test_key_tag_conversions_with_real_crypto() {
568 // Generate a real key and test conversions
569 let key = Key::generate();
570 let key_bytes: [u8; KEYBYTES] = key.clone().into();
571 let key_restored = Key::from(key_bytes);
572 assert_eq!(key.as_bytes(), key_restored.as_bytes());
573
574 // Test with real crypto operations
575 let message = b"Test message for trait conversions";
576 let tag = onetimeauth(message, &key_restored);
577
578 // Convert tag to bytes and back
579 let tag_bytes: [u8; BYTES] = tag.clone().into();
580 let tag_restored = Tag::from(tag_bytes);
581
582 // Verify the restored tag works correctly
583 assert!(verify(&tag_restored, message, &key_restored));
584 }
585}