use super::BlockCipher;
use super::gcm::Gcm;
#[derive(Clone)]
pub struct Gmac<C: BlockCipher> {
gcm: Gcm<C>,
nonce: [u8; 12],
#[cfg(feature = "alloc")]
data: alloc::vec::Vec<u8>,
#[cfg(not(feature = "alloc"))]
data: GmacBuf,
}
#[cfg(not(feature = "alloc"))]
#[derive(Clone)]
struct GmacBuf {
bytes: [u8; 1024],
len: usize,
}
impl<C: BlockCipher> Gmac<C> {
pub fn new(cipher: C, nonce: &[u8; 12]) -> Self {
Gmac {
gcm: Gcm::new(cipher),
nonce: *nonce,
#[cfg(feature = "alloc")]
data: alloc::vec::Vec::new(),
#[cfg(not(feature = "alloc"))]
data: GmacBuf {
bytes: [0u8; 1024],
len: 0,
},
}
}
pub fn update(&mut self, data: &[u8]) {
#[cfg(feature = "alloc")]
{
self.data.extend_from_slice(data);
}
#[cfg(not(feature = "alloc"))]
{
let end = self.data.len + data.len();
assert!(
end <= self.data.bytes.len(),
"GMAC message exceeds 1024 bytes; enable the `alloc` feature"
);
self.data.bytes[self.data.len..end].copy_from_slice(data);
self.data.len = end;
}
}
#[inline]
fn message(&self) -> &[u8] {
#[cfg(feature = "alloc")]
{
&self.data
}
#[cfg(not(feature = "alloc"))]
{
&self.data.bytes[..self.data.len]
}
}
pub fn finalize(self) -> [u8; 16] {
let mut empty: [u8; 0] = [];
self.gcm.encrypt(&self.nonce, self.message(), &mut empty)
}
pub fn finalize_into(self, out: &mut [u8]) {
let tag = self.finalize();
let n = out.len().min(16);
out[..n].copy_from_slice(&tag[..n]);
}
}
impl<C: BlockCipher> Drop for Gmac<C> {
fn drop(&mut self) {
for b in self.nonce.iter_mut() {
*b = 0;
}
let _ = core::hint::black_box(&self.nonce);
}
}
#[cfg(feature = "hash")]
impl<C: BlockCipher + Clone> crate::hash::Mac for Gmac<C> {
const OUTPUT_LEN: Option<usize> = Some(16);
fn update(&mut self, data: &[u8]) {
Gmac::update(self, data);
}
fn finalize_into(self, out: &mut [u8]) {
Gmac::finalize_into(self, out);
}
}
pub type AesGmac128 = Gmac<super::Aes128>;
pub type AesGmac256 = Gmac<super::Aes256>;
#[cfg(test)]
mod tests {
use super::*;
use crate::cipher::{Aes128, Aes256};
use crate::test_util::from_hex;
fn gmac128(key: &str, nonce: &str, data: &[u8]) -> [u8; 16] {
let mut m = AesGmac128::new(Aes128::new(&from_hex::<16>(key)), &from_hex::<12>(nonce));
m.update(data);
m.finalize()
}
#[test]
fn nist_aes128_gmac() {
let tag = gmac128(
"11754cd72aec309bf52f7687212e8957",
"3c819d9a9bed087615030b65",
&[],
);
assert_eq!(tag, from_hex::<16>("250327c674aaf477aef2675748cf6971"));
}
#[test]
fn nist_aes128_gmac_with_aad() {
let aad = from_hex::<16>("7a43ec1d9c0a5a78a0b16533a6213cab");
let tag = gmac128(
"77be63708971c4e240d1cb79e8d77feb",
"e0e00f19fed7ba0136a797f3",
&aad,
);
assert_eq!(tag, from_hex::<16>("209fcc8d3675ed938e9c7166709dd946"));
}
#[test]
fn nist_aes256_gmac() {
let mut m = AesGmac256::new(
Aes256::new(&from_hex::<32>(
"b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4",
)),
&from_hex::<12>("516c33929df5a3284ff463d7"),
);
m.update(&[]);
assert_eq!(
m.finalize(),
from_hex::<16>("bdc1ac884d332457a1d2664f168c76f0")
);
}
#[test]
fn streaming_matches_oneshot() {
let key = "11754cd72aec309bf52f7687212e8957";
let nonce = "3c819d9a9bed087615030b65";
let data =
from_hex::<32>("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72");
let oneshot = gmac128(key, nonce, &data);
let mut m = AesGmac128::new(Aes128::new(&from_hex::<16>(key)), &from_hex::<12>(nonce));
m.update(&data[..7]);
m.update(&data[7..16]);
m.update(&data[16..]);
assert_eq!(m.finalize(), oneshot);
}
#[cfg(feature = "hash")]
#[test]
fn mac_trait_verify() {
use crate::hash::Mac;
let key = from_hex::<16>("11754cd72aec309bf52f7687212e8957");
let nonce = from_hex::<12>("3c819d9a9bed087615030b65");
let mut m = AesGmac128::new(Aes128::new(&key), &nonce);
Mac::update(&mut m, &[]);
let expected = from_hex::<16>("250327c674aaf477aef2675748cf6971");
assert!(bool::from(Mac::verify(m, &expected)));
let mut m = AesGmac128::new(Aes128::new(&key), &nonce);
Mac::update(&mut m, &[]);
let mut bad = expected;
bad[0] ^= 1;
assert!(!bool::from(Mac::verify(m.clone(), &bad)));
assert!(!bool::from(Mac::verify(m.clone(), &expected[..8])));
assert!(!bool::from(Mac::verify(m.clone(), &[])));
let mut long = [0u8; 17];
long[..16].copy_from_slice(&expected);
assert!(!bool::from(Mac::verify(m, &long)));
}
}