use subtle::ConstantTimeEq;
use crate::classic::crypto_auth::{
crypto_auth, crypto_auth_final, crypto_auth_init, crypto_auth_update, crypto_auth_verify,
AuthState,
};
use crate::constants::{CRYPTO_AUTH_BYTES, CRYPTO_AUTH_KEYBYTES};
use crate::error::Error;
use crate::types::*;
pub type Key = StackByteArray<CRYPTO_AUTH_KEYBYTES>;
pub type Mac = StackByteArray<CRYPTO_AUTH_BYTES>;
#[cfg(any(feature = "nightly", all(doc, not(doctest))))]
#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))]
pub mod protected {
use super::*;
pub use crate::protected::*;
pub use crate::types::*;
pub type Key = HeapByteArray<CRYPTO_AUTH_KEYBYTES>;
pub type Mac = HeapByteArray<CRYPTO_AUTH_BYTES>;
}
pub struct Auth {
state: AuthState,
}
impl Auth {
pub fn compute<
Key: ByteArray<CRYPTO_AUTH_KEYBYTES>,
Input: Bytes,
Output: NewByteArray<CRYPTO_AUTH_BYTES>,
>(
key: Key,
input: &Input,
) -> Output {
let mut output = Output::new_byte_array();
crypto_auth(output.as_mut_array(), input.as_slice(), key.as_array());
output
}
pub fn compute_to_vec<Key: ByteArray<CRYPTO_AUTH_KEYBYTES>, Input: Bytes>(
key: Key,
input: &Input,
) -> Vec<u8> {
Self::compute(key, input)
}
pub fn compute_and_verify<
OtherMac: ByteArray<CRYPTO_AUTH_BYTES>,
Key: ByteArray<CRYPTO_AUTH_KEYBYTES>,
Input: Bytes,
>(
other_mac: &OtherMac,
key: Key,
input: &Input,
) -> Result<(), Error> {
crypto_auth_verify(other_mac.as_array(), input.as_slice(), key.as_array())
}
pub fn new<Key: ByteArray<CRYPTO_AUTH_KEYBYTES>>(key: Key) -> Self {
Self {
state: crypto_auth_init(key.as_array()),
}
}
pub fn update<Input: Bytes>(&mut self, input: &Input) {
crypto_auth_update(&mut self.state, input.as_slice())
}
pub fn finalize<Output: NewByteArray<CRYPTO_AUTH_BYTES>>(self) -> Output {
let mut output = Output::new_byte_array();
crypto_auth_final(self.state, output.as_mut_array());
output
}
pub fn finalize_to_vec(self) -> Vec<u8> {
self.finalize()
}
pub fn verify<OtherMac: ByteArray<CRYPTO_AUTH_BYTES>>(
self,
other_mac: &OtherMac,
) -> Result<(), Error> {
let computed_mac: Mac = self.finalize();
if other_mac
.as_array()
.ct_eq(computed_mac.as_array())
.unwrap_u8()
== 1
{
Ok(())
} else {
Err(dryoc_error!("authentication codes do not match"))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_single_part() {
let key = Key::gen();
let mac = Auth::compute_to_vec(key.clone(), b"Data to authenticate");
Auth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed");
}
#[test]
fn test_multi_part() {
let key = Key::gen();
let mut mac = Auth::new(key.clone());
mac.update(b"Multi-part");
mac.update(b"data");
let mac = mac.finalize_to_vec();
let mut verify_mac = Auth::new(key.clone());
verify_mac.update(b"Multi-part");
verify_mac.update(b"data");
verify_mac.verify(&mac).expect("verify failed");
let mut verify_mac = Auth::new(key);
verify_mac.update(b"Multi-part");
verify_mac.update(b"bad data");
verify_mac
.verify(&mac)
.expect_err("verify should have failed");
}
}