Skip to main content

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}