use subtle::ConstantTimeEq;
use crate::classic::crypto_onetimeauth::{
crypto_onetimeauth, crypto_onetimeauth_final, crypto_onetimeauth_init,
crypto_onetimeauth_update, crypto_onetimeauth_verify, OnetimeauthState,
};
use crate::constants::{CRYPTO_ONETIMEAUTH_BYTES, CRYPTO_ONETIMEAUTH_KEYBYTES};
use crate::error::Error;
use crate::types::*;
pub type Key = StackByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>;
pub type Mac = StackByteArray<CRYPTO_ONETIMEAUTH_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_ONETIMEAUTH_KEYBYTES>;
pub type Mac = HeapByteArray<CRYPTO_ONETIMEAUTH_BYTES>;
}
pub struct OnetimeAuth {
state: OnetimeauthState,
}
impl OnetimeAuth {
pub fn compute<
Key: ByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>,
Input: Bytes,
Output: NewByteArray<CRYPTO_ONETIMEAUTH_BYTES>,
>(
key: Key,
input: &Input,
) -> Output {
let mut output = Output::new_byte_array();
crypto_onetimeauth(output.as_mut_array(), input.as_slice(), key.as_array());
output
}
pub fn compute_to_vec<Key: ByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>, Input: Bytes>(
key: Key,
input: &Input,
) -> Vec<u8> {
Self::compute(key, input)
}
pub fn compute_and_verify<
OtherMac: ByteArray<CRYPTO_ONETIMEAUTH_BYTES>,
Key: ByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>,
Input: Bytes,
>(
other_mac: &OtherMac,
key: Key,
input: &Input,
) -> Result<(), Error> {
crypto_onetimeauth_verify(other_mac.as_array(), input.as_slice(), key.as_array())
}
pub fn new<Key: ByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>>(key: Key) -> Self {
Self {
state: crypto_onetimeauth_init(key.as_array()),
}
}
pub fn update<Input: Bytes>(&mut self, input: &Input) {
crypto_onetimeauth_update(&mut self.state, input.as_slice())
}
pub fn finalize<Output: NewByteArray<CRYPTO_ONETIMEAUTH_BYTES>>(self) -> Output {
let mut output = Output::new_byte_array();
crypto_onetimeauth_final(self.state, output.as_mut_array());
output
}
pub fn finalize_to_vec(self) -> Vec<u8> {
self.finalize()
}
pub fn verify<OtherMac: ByteArray<CRYPTO_ONETIMEAUTH_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 = OnetimeAuth::compute_to_vec(key.clone(), b"Data to authenticate");
OnetimeAuth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed");
}
#[test]
fn test_multi_part() {
let key = Key::gen();
let mut mac = OnetimeAuth::new(key.clone());
mac.update(b"Multi-part");
mac.update(b"data");
let mac = mac.finalize_to_vec();
let mut verify_mac = OnetimeAuth::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 = OnetimeAuth::new(key);
verify_mac.update(b"Multi-part");
verify_mac.update(b"bad data");
verify_mac
.verify(&mac)
.expect_err("verify should have failed");
}
}