libsodium_rs/crypto_auth/mod.rs
1//! # Secret-Key Authentication
2//!
3//! This module provides functions for message authentication using secret keys.
4//! It allows you to compute an authentication tag for a message and a secret key,
5//! and provides a way to verify that a given tag is valid for a given message and key.
6//!
7//! ## Purpose
8//!
9//! The authentication function is deterministic: the same (message, key) tuple will always
10//! produce the same output. Even if the message is public, knowing the key is required to
11//! compute a valid tag. Therefore, the key should remain confidential. The tag, however,
12//! can be public.
13//!
14//! Typical use cases include:
15//! - A prepares a message, adds an authentication tag, and sends it to B
16//! - A doesn't store the message
17//! - Later on, B sends the message and the authentication tag back to A
18//! - A uses the authentication tag to verify that it created this message
19//!
20//! This operation does not encrypt the message. It only computes and verifies an
21//! authentication tag.
22//!
23//! ## Algorithm
24//!
25//! The default implementation uses HMAC-SHA512-256.
26//!
27//! ## Usage Example
28//!
29//! ```
30//! use libsodium_rs as sodium;
31//! use sodium::crypto_auth;
32//! use sodium::ensure_init;
33//!
34//! // Initialize libsodium
35//! ensure_init().expect("Failed to initialize libsodium");
36//!
37//! // Generate a random key
38//! let key = crypto_auth::Key::generate().unwrap();
39//!
40//! // Message to authenticate
41//! let message = b"Hello, world!";
42//!
43//! // Compute authentication tag
44//! let tag = crypto_auth::auth(message, &key).unwrap();
45//!
46//! // Verify the tag
47//! let verification = crypto_auth::verify(&tag, message, &key);
48//! assert!(verification);
49//!
50//! // Verification fails with a different message
51//! let wrong_message = b"Modified message";
52//! let verification = crypto_auth::verify(&tag, wrong_message, &key);
53//! assert!(!verification);
54//! ```
55
56use crate::{Result, SodiumError};
57use std::convert::TryFrom;
58
59/// Number of bytes in an authentication tag (32 bytes)
60pub const BYTES: usize = libsodium_sys::crypto_auth_BYTES as usize;
61/// Number of bytes in an authentication key (32 bytes)
62pub const KEYBYTES: usize = libsodium_sys::crypto_auth_KEYBYTES as usize;
63
64/// A secret key for authentication
65///
66/// This key is used to compute and verify authentication tags. It should be kept secret,
67/// as anyone with the key can create valid authentication tags.
68#[derive(Debug, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
69pub struct Key([u8; KEYBYTES]);
70
71impl Key {
72 /// Generates a new random key for authentication
73 ///
74 /// This function generates a cryptographically secure random key that can be used
75 /// for message authentication. The key is `KEYBYTES` (32) bytes long.
76 ///
77 /// # Returns
78 /// * `Result<Self>` - A new randomly generated key
79 ///
80 /// # Example
81 /// ```
82 /// use libsodium_rs as sodium;
83 /// use sodium::crypto_auth;
84 /// use sodium::ensure_init;
85 ///
86 /// // Initialize libsodium
87 /// ensure_init().expect("Failed to initialize libsodium");
88 ///
89 /// // Generate a random key
90 /// let key = crypto_auth::Key::generate().unwrap();
91 /// ```
92 pub fn generate() -> Result<Self> {
93 let mut key = [0u8; KEYBYTES];
94 unsafe {
95 libsodium_sys::crypto_auth_keygen(key.as_mut_ptr());
96 }
97 Ok(Key(key))
98 }
99
100 /// Creates a key from a byte slice
101 ///
102 /// This function creates a key from an existing byte slice, which must be exactly
103 /// `KEYBYTES` (32) bytes long. This is useful when you have an existing key or
104 /// when you need to derive a key from another source.
105 ///
106 /// # Arguments
107 /// * `slice` - Byte slice of exactly `KEYBYTES` (32) bytes length
108 ///
109 /// # Returns
110 /// * `Result<Self>` - A new key or an error if the input is invalid
111 ///
112 /// # Errors
113 /// Returns an error if the input is not exactly `KEYBYTES` bytes long
114 ///
115 /// # Example
116 /// ```
117 /// use libsodium_rs as sodium;
118 /// use sodium::crypto_auth;
119 /// use sodium::ensure_init;
120 ///
121 /// // Initialize libsodium
122 /// ensure_init().expect("Failed to initialize libsodium");
123 ///
124 /// // Create a key from existing bytes
125 /// let key_bytes = [0x42; crypto_auth::KEYBYTES]; // In a real application, use a proper key
126 /// let key = crypto_auth::Key::from_slice(&key_bytes).unwrap();
127 /// ```
128 pub fn from_slice(slice: &[u8]) -> Result<Self> {
129 if slice.len() != KEYBYTES {
130 return Err(SodiumError::InvalidInput(format!(
131 "key must be exactly {KEYBYTES} bytes"
132 )));
133 }
134 let mut key = [0u8; KEYBYTES];
135 key.copy_from_slice(slice);
136 Ok(Key(key))
137 }
138
139 /// Returns a reference to the key as a byte slice
140 ///
141 /// This method provides access to the raw bytes of the key, which can be useful
142 /// when you need to pass the key to other functions or store it securely.
143 ///
144 /// # Returns
145 /// * `&[u8]` - Reference to the key bytes
146 ///
147 /// # Example
148 /// ```
149 /// use libsodium_rs as sodium;
150 /// use sodium::crypto_auth;
151 /// use sodium::ensure_init;
152 ///
153 /// // Initialize libsodium
154 /// ensure_init().expect("Failed to initialize libsodium");
155 ///
156 /// // Generate a random key
157 /// let key = crypto_auth::Key::generate().unwrap();
158 ///
159 /// // Get the raw bytes of the key
160 /// let key_bytes = key.as_bytes();
161 /// assert_eq!(key_bytes.len(), crypto_auth::KEYBYTES);
162 /// ```
163 pub fn as_bytes(&self) -> &[u8] {
164 &self.0
165 }
166}
167
168impl AsRef<[u8]> for Key {
169 fn as_ref(&self) -> &[u8] {
170 &self.0
171 }
172}
173
174impl TryFrom<&[u8]> for Key {
175 type Error = SodiumError;
176
177 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
178 Self::from_slice(slice)
179 }
180}
181
182impl From<[u8; KEYBYTES]> for Key {
183 fn from(bytes: [u8; KEYBYTES]) -> Self {
184 Self(bytes)
185 }
186}
187
188impl From<Key> for [u8; KEYBYTES] {
189 fn from(key: Key) -> [u8; KEYBYTES] {
190 key.0
191 }
192}
193
194/// An authentication tag
195///
196/// This represents an authentication tag computed for a message using a secret key.
197/// The tag can be publicly shared and later verified to ensure the authenticity of
198/// a message.
199#[derive(Debug, Clone, Eq, PartialEq)]
200pub struct Tag([u8; BYTES]);
201
202impl Tag {
203 /// Creates a tag from a byte slice
204 ///
205 /// This function creates a tag from an existing byte slice, which must be exactly
206 /// `BYTES` (32) bytes long. This is useful when you receive a tag from another party
207 /// and need to verify it.
208 ///
209 /// # Arguments
210 /// * `slice` - Byte slice of exactly `BYTES` (32) bytes length
211 ///
212 /// # Returns
213 /// * `Result<Self>` - A new tag or an error if the input is invalid
214 ///
215 /// # Errors
216 /// Returns an error if the input is not exactly `BYTES` bytes long
217 ///
218 /// # Example
219 /// ```
220 /// use libsodium_rs as sodium;
221 /// use sodium::crypto_auth;
222 /// use sodium::ensure_init;
223 ///
224 /// // Initialize libsodium
225 /// ensure_init().expect("Failed to initialize libsodium");
226 ///
227 /// // Create a tag from existing bytes
228 /// let tag_bytes = [0x42; crypto_auth::BYTES]; // In a real application, this would be a real tag
229 /// let tag = crypto_auth::Tag::from_slice(&tag_bytes).unwrap();
230 /// ```
231 pub fn from_slice(slice: &[u8]) -> Result<Self> {
232 if slice.len() != BYTES {
233 return Err(SodiumError::InvalidInput(format!(
234 "tag must be exactly {BYTES} bytes"
235 )));
236 }
237 let mut tag = [0u8; BYTES];
238 tag.copy_from_slice(slice);
239 Ok(Tag(tag))
240 }
241
242 /// Returns a reference to the tag as a byte slice
243 ///
244 /// This method provides access to the raw bytes of the tag, which can be useful
245 /// when you need to transmit the tag or store it.
246 ///
247 /// # Returns
248 /// * `&[u8]` - Reference to the tag bytes
249 ///
250 /// # Example
251 /// ```
252 /// use libsodium_rs as sodium;
253 /// use sodium::crypto_auth;
254 /// use sodium::ensure_init;
255 ///
256 /// // Initialize libsodium
257 /// ensure_init().expect("Failed to initialize libsodium");
258 ///
259 /// // Generate a random key
260 /// let key = crypto_auth::Key::generate().unwrap();
261 ///
262 /// // Compute authentication tag for a message
263 /// let message = b"Hello, world!";
264 /// let tag = crypto_auth::auth(message, &key).unwrap();
265 ///
266 /// // Get the raw bytes of the tag
267 /// let tag_bytes = tag.as_bytes();
268 /// assert_eq!(tag_bytes.len(), crypto_auth::BYTES);
269 /// ```
270 pub fn as_bytes(&self) -> &[u8] {
271 &self.0
272 }
273}
274
275impl AsRef<[u8]> for Tag {
276 fn as_ref(&self) -> &[u8] {
277 &self.0
278 }
279}
280
281impl TryFrom<&[u8]> for Tag {
282 type Error = SodiumError;
283
284 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
285 Self::from_slice(slice)
286 }
287}
288
289impl From<[u8; BYTES]> for Tag {
290 fn from(bytes: [u8; BYTES]) -> Self {
291 Self(bytes)
292 }
293}
294
295impl From<Tag> for [u8; BYTES] {
296 fn from(tag: Tag) -> [u8; BYTES] {
297 tag.0
298 }
299}
300
301/// Computes an authentication tag for a message using a secret key
302///
303/// This function computes an authentication tag for a message and a secret key. The tag
304/// is deterministic: the same (message, key) tuple will always produce the same output.
305/// The tag can be later verified using the `verify` function.
306///
307/// The default implementation uses HMAC-SHA512-256.
308///
309/// # Arguments
310/// * `message` - Message to authenticate
311/// * `key` - Secret key for authentication
312///
313/// # Returns
314/// * `Result<Tag>` - Authentication tag or an error
315///
316/// # Example
317/// ```
318/// use libsodium_rs as sodium;
319/// use sodium::crypto_auth;
320/// use sodium::ensure_init;
321///
322/// // Initialize libsodium
323/// ensure_init().expect("Failed to initialize libsodium");
324///
325/// // Generate a random key
326/// let key = crypto_auth::Key::generate().unwrap();
327///
328/// // Message to authenticate
329/// let message = b"Hello, world!";
330///
331/// // Compute authentication tag
332/// let tag = crypto_auth::auth(message, &key).unwrap();
333/// ```
334pub fn auth(message: &[u8], key: &Key) -> Result<Tag> {
335 let mut tag = [0u8; BYTES];
336 let result = unsafe {
337 libsodium_sys::crypto_auth(
338 tag.as_mut_ptr(),
339 message.as_ptr(),
340 message.len() as u64,
341 key.as_bytes().as_ptr(),
342 )
343 };
344
345 if result != 0 {
346 return Err(SodiumError::OperationError("authentication failed".into()));
347 }
348
349 Ok(Tag(tag))
350}
351
352/// Verifies that a tag is valid for a message and key
353///
354/// This function verifies that a given authentication tag is valid for a given message
355/// and key. It returns `true` if the verification passes, or `false` if the
356/// verification fails (indicating that the message may have been tampered with or that
357/// the wrong key was used).
358///
359/// # Arguments
360/// * `tag` - Authentication tag to verify
361/// * `message` - Message that was authenticated
362/// * `key` - Secret key used for authentication
363///
364/// # Returns
365/// * `bool` - `true` if verification passes, `false` otherwise
366///
367/// # Example
368/// ```
369/// use libsodium_rs as sodium;
370/// use sodium::crypto_auth;
371/// use sodium::ensure_init;
372///
373/// // Initialize libsodium
374/// ensure_init().expect("Failed to initialize libsodium");
375///
376/// // Generate a random key
377/// let key = crypto_auth::Key::generate().unwrap();
378///
379/// // Message to authenticate
380/// let message = b"Hello, world!";
381///
382/// // Compute authentication tag
383/// let tag = crypto_auth::auth(message, &key).unwrap();
384///
385/// // Verify the tag (should succeed)
386/// assert!(crypto_auth::verify(&tag, message, &key));
387///
388/// // Verify with wrong message (should fail)
389/// let wrong_message = b"Modified message";
390/// assert!(!crypto_auth::verify(&tag, wrong_message, &key));
391///
392/// // Verify with wrong key (should fail)
393/// let wrong_key = crypto_auth::Key::generate().unwrap();
394/// assert!(!crypto_auth::verify(&tag, message, &wrong_key));
395/// ```
396pub fn verify(tag: &Tag, message: &[u8], key: &Key) -> bool {
397 let result = unsafe {
398 libsodium_sys::crypto_auth_verify(
399 tag.as_bytes().as_ptr(),
400 message.as_ptr(),
401 message.len() as u64,
402 key.as_bytes().as_ptr(),
403 )
404 };
405
406 result == 0
407}
408
409// Export submodules
410/// HMAC-SHA-256 authentication functions
411pub mod hmacsha256;
412/// HMAC-SHA-512 authentication functions
413pub mod hmacsha512;
414/// HMAC-SHA-512-256 authentication functions (truncated SHA-512)
415pub mod hmacsha512256;
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420
421 #[test]
422 fn test_key_generation() {
423 let key = Key::generate().unwrap();
424 assert_eq!(key.as_bytes().len(), KEYBYTES);
425 }
426
427 #[test]
428 fn test_key_from_slice() {
429 let bytes = vec![0u8; KEYBYTES];
430 let key = Key::from_slice(&bytes).unwrap();
431 assert_eq!(key.as_bytes(), bytes.as_slice());
432
433 // Test invalid key size
434 assert!(Key::from_slice(&[0u8; KEYBYTES + 1]).is_err());
435 }
436
437 #[test]
438 fn test_tag_from_slice() {
439 let bytes = vec![0u8; BYTES];
440 let tag = Tag::from_slice(&bytes).unwrap();
441 assert_eq!(tag.as_bytes(), bytes.as_slice());
442
443 // Test invalid tag size
444 assert!(Tag::from_slice(&[0u8; BYTES + 1]).is_err());
445 }
446
447 #[test]
448 fn test_auth_and_verify() {
449 let key = Key::generate().unwrap();
450 let message = b"test message";
451
452 // Create authentication tag
453 let tag = auth(message, &key).unwrap();
454
455 // Verify the tag
456 assert!(verify(&tag, message, &key));
457
458 // Verify with wrong message
459 assert!(!verify(&tag, b"wrong message", &key));
460
461 // Verify with wrong key
462 let wrong_key = Key::generate().unwrap();
463 assert!(!verify(&tag, message, &wrong_key));
464 }
465
466 #[test]
467 fn test_key_as_ref() {
468 let key = Key::generate().unwrap();
469 let key_ref: &[u8] = key.as_ref();
470 assert_eq!(key_ref.len(), KEYBYTES);
471 assert_eq!(key_ref, key.as_bytes());
472 }
473
474 #[test]
475 fn test_key_try_from_slice() {
476 // Valid slice
477 let bytes = vec![0x42; KEYBYTES];
478 let key = Key::try_from(bytes.as_slice()).unwrap();
479 assert_eq!(key.as_bytes(), bytes.as_slice());
480
481 // Invalid slice - too short
482 let short_bytes = vec![0x42; KEYBYTES - 1];
483 assert!(Key::try_from(short_bytes.as_slice()).is_err());
484
485 // Invalid slice - too long
486 let long_bytes = vec![0x42; KEYBYTES + 1];
487 assert!(Key::try_from(long_bytes.as_slice()).is_err());
488 }
489
490 #[test]
491 fn test_key_from_bytes() {
492 let bytes = [0x42; KEYBYTES];
493 let key = Key::from(bytes);
494 assert_eq!(key.as_bytes(), &bytes);
495 }
496
497 #[test]
498 fn test_key_into_bytes() {
499 let original_bytes = [0x42; KEYBYTES];
500 let key = Key::from(original_bytes);
501 let bytes: [u8; KEYBYTES] = key.into();
502 assert_eq!(bytes, original_bytes);
503 }
504
505 #[test]
506 fn test_tag_as_ref() {
507 let key = Key::generate().unwrap();
508 let message = b"test message";
509 let tag = auth(message, &key).unwrap();
510 let tag_ref: &[u8] = tag.as_ref();
511 assert_eq!(tag_ref.len(), BYTES);
512 assert_eq!(tag_ref, tag.as_bytes());
513 }
514
515 #[test]
516 fn test_tag_try_from_slice() {
517 // Valid slice
518 let bytes = vec![0x42; BYTES];
519 let tag = Tag::try_from(bytes.as_slice()).unwrap();
520 assert_eq!(tag.as_bytes(), bytes.as_slice());
521
522 // Invalid slice - too short
523 let short_bytes = vec![0x42; BYTES - 1];
524 assert!(Tag::try_from(short_bytes.as_slice()).is_err());
525
526 // Invalid slice - too long
527 let long_bytes = vec![0x42; BYTES + 1];
528 assert!(Tag::try_from(long_bytes.as_slice()).is_err());
529 }
530
531 #[test]
532 fn test_tag_from_bytes() {
533 let bytes = [0x42; BYTES];
534 let tag = Tag::from(bytes);
535 assert_eq!(tag.as_bytes(), &bytes);
536 }
537
538 #[test]
539 fn test_tag_into_bytes() {
540 let original_bytes = [0x42; BYTES];
541 let tag = Tag::from(original_bytes);
542 let bytes: [u8; BYTES] = tag.into();
543 assert_eq!(bytes, original_bytes);
544 }
545
546 #[test]
547 fn test_key_tag_roundtrip() {
548 // Test Key roundtrip
549 let key_bytes = [0x42; KEYBYTES];
550 let key = Key::from(key_bytes);
551 let key_bytes_out: [u8; KEYBYTES] = key.into();
552 assert_eq!(key_bytes, key_bytes_out);
553 let key_from_bytes = Key::from(key_bytes_out);
554 assert_eq!(key_from_bytes.as_bytes(), &key_bytes);
555
556 // Test Tag roundtrip with real tag
557 let key = Key::generate().unwrap();
558 let message = b"test message";
559 let tag = auth(message, &key).unwrap();
560 let tag_bytes: [u8; BYTES] = tag.clone().into();
561 let tag_from_bytes = Tag::from(tag_bytes);
562 assert_eq!(tag.as_bytes(), tag_from_bytes.as_bytes());
563
564 // Verify the reconstructed tag still works
565 assert!(verify(&tag_from_bytes, message, &key));
566 }
567}