Skip to main content

dryoc/classic/
crypto_auth.rs

1//! # Secret-key authentication
2//!
3//! Implements secret-key authentication using HMAC-SHA512-256, compatible
4//! with libsodium's `crypto_auth_*` functions.
5//!
6//! # Classic API single-part example
7//!
8//! ```
9//! use dryoc::classic::crypto_auth::{Mac, crypto_auth, crypto_auth_keygen, crypto_auth_verify};
10//!
11//! let key = crypto_auth_keygen();
12//! let mut mac = Mac::default();
13//!
14//! crypto_auth(&mut mac, b"Data to authenticate", &key);
15//!
16//! // This should be valid
17//! crypto_auth_verify(&mac, b"Data to authenticate", &key).expect("failed to authenticate");
18//!
19//! // This should not be valid
20//! crypto_auth_verify(&mac, b"Invalid data", &key).expect_err("should not authenticate");
21//! ```
22//!
23//! # Classic API multi-part example
24//!
25//! ```
26//! use dryoc::classic::crypto_auth::{
27//!     Mac, crypto_auth_final, crypto_auth_init, crypto_auth_keygen, crypto_auth_update,
28//!     crypto_auth_verify,
29//! };
30//!
31//! let key = crypto_auth_keygen();
32//! let mut mac = Mac::default();
33//!
34//! let mut state = crypto_auth_init(&key);
35//! crypto_auth_update(&mut state, b"Multi-part");
36//! crypto_auth_update(&mut state, b"data");
37//! crypto_auth_final(state, &mut mac);
38//!
39//! // This should be valid
40//! crypto_auth_verify(&mac, b"Multi-partdata", &key).expect("failed to authenticate");
41//!
42//! // This should not be valid
43//! crypto_auth_verify(&mac, b"Invalid data", &key).expect_err("should not authenticate");
44//! ```
45use super::crypto_auth_hmacsha512256::{
46    HmacSha512256State, crypto_auth_hmacsha512256, crypto_auth_hmacsha512256_final,
47    crypto_auth_hmacsha512256_init, crypto_auth_hmacsha512256_keygen,
48    crypto_auth_hmacsha512256_update, crypto_auth_hmacsha512256_verify,
49};
50use crate::constants::{CRYPTO_AUTH_BYTES, CRYPTO_AUTH_KEYBYTES};
51use crate::error::Error;
52
53/// Key for secret-key message authentication.
54pub type Key = [u8; CRYPTO_AUTH_KEYBYTES];
55/// Message authentication code type for use with secret-key authentication.
56pub type Mac = [u8; CRYPTO_AUTH_BYTES];
57
58/// Authenticates `message` using `key`, and places the result into
59/// `mac`.
60///
61/// Equivalent to libsodium's `crypto_auth`.
62pub fn crypto_auth(mac: &mut Mac, message: &[u8], key: &Key) {
63    crypto_auth_hmacsha512256(mac, message, key)
64}
65
66/// Verifies that `mac` is the correct authenticator for `message` using `key`.
67/// Returns `Ok(())` if the message authentication code is valid.
68///
69/// Equivalent to libsodium's `crypto_auth_verify`.
70///
71/// # Errors
72///
73/// Returns an error if `mac` is not valid for `input` under `key`.
74pub fn crypto_auth_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
75    crypto_auth_hmacsha512256_verify(mac, input, key)
76}
77
78/// Internal state for [`crypto_auth`].
79pub struct AuthState {
80    state: HmacSha512256State,
81}
82
83/// Generates a random key using
84/// [`copy_randombytes`](crate::rng::copy_randombytes), suitable for use with
85/// [`crypto_auth_init`] and [`crypto_auth`].
86///
87/// Equivalent to libsodium's `crypto_auth_keygen`.
88pub fn crypto_auth_keygen() -> Key {
89    crypto_auth_hmacsha512256_keygen()
90}
91
92/// Initialize the incremental interface for HMAC-SHA512-256 secret-key.
93///
94/// Initializes the incremental interface for HMAC-SHA512-256 secret-key
95/// authentication, using `key`. Returns a state struct which is required for
96/// subsequent calls to [`crypto_auth_update`] and
97/// [`crypto_auth_final`].
98pub fn crypto_auth_init(key: &Key) -> AuthState {
99    AuthState {
100        state: crypto_auth_hmacsha512256_init(key),
101    }
102}
103
104/// Updates `state` for the secret-key authentication function, based on
105/// `input`.
106pub fn crypto_auth_update(state: &mut AuthState, input: &[u8]) {
107    crypto_auth_hmacsha512256_update(&mut state.state, input)
108}
109
110/// Finalizes the message authentication code for `state`, and places the result
111/// into `output`.
112pub fn crypto_auth_final(state: AuthState, output: &mut [u8; CRYPTO_AUTH_BYTES]) {
113    crypto_auth_hmacsha512256_final(state.state, output)
114}
115
116#[cfg(all(test, dryoc_native_tests))]
117mod tests {
118    use rand::TryRng;
119
120    use super::*;
121
122    #[test]
123    fn test_crypto_auth() {
124        use rand::rngs::SysRng;
125        use sodiumoxide::crypto::auth;
126        use sodiumoxide::crypto::auth::Key as SOKey;
127
128        use crate::rng::copy_randombytes;
129
130        for _ in 0..20 {
131            let mlen = (SysRng.try_next_u32().unwrap() % 5000) as usize;
132            let mut message = vec![0u8; mlen];
133            copy_randombytes(&mut message);
134            let key = crypto_auth_keygen();
135
136            let so_tag =
137                auth::authenticate(&message, &SOKey::from_slice(&key).expect("key failed"));
138
139            let mut mac = Mac::default();
140            crypto_auth(&mut mac, &message, &key);
141
142            assert_eq!(mac, so_tag.0);
143
144            crypto_auth_verify(&mac, &message, &key).expect("verify failed");
145            crypto_auth_verify(&mac, b"invalid message", &key)
146                .expect_err("verify should have failed");
147        }
148    }
149}