use std::sync::Arc;
use tink_core::{utils::wrap_err, TinkError};
use tink_proto::OutputPrefixType;
const MAX_INT: usize = usize::MAX >> 1;
pub fn new(h: &tink_core::keyset::Handle) -> Result<Box<dyn tink_core::Mac>, TinkError> {
new_with_key_manager(h, None)
}
fn new_with_key_manager(
h: &tink_core::keyset::Handle,
km: Option<Arc<dyn tink_core::registry::KeyManager>>,
) -> Result<Box<dyn tink_core::Mac>, TinkError> {
let ps = h
.primitives_with_key_manager(km)
.map_err(|e| wrap_err("mac::factory: cannot obtain primitive set", e))?;
let ret = WrappedMac::new(ps)?;
Ok(Box::new(ret))
}
#[derive(Clone)]
struct WrappedMac {
ps: tink_core::primitiveset::TypedPrimitiveSet<Box<dyn tink_core::Mac>>,
}
impl WrappedMac {
fn new(ps: tink_core::primitiveset::PrimitiveSet) -> Result<WrappedMac, TinkError> {
let entry = match &ps.primary {
None => return Err("mac::factory: no primary primitive".into()),
Some(p) => p,
};
match entry.primitive {
tink_core::Primitive::Mac(_) => {}
_ => return Err("mac::factory: not a Mac primitive".into()),
};
for (_, primitives) in ps.entries.iter() {
for p in primitives {
match p.primitive {
tink_core::Primitive::Mac(_) => {}
_ => return Err("mac::factory: not a Mac primitive".into()),
};
}
}
Ok(WrappedMac { ps: ps.into() })
}
}
impl tink_core::Mac for WrappedMac {
fn compute_mac(&self, data: &[u8]) -> Result<Vec<u8>, TinkError> {
let primary = match &self.ps.primary {
Some(p) => p,
None => return Err("mac::factory: no primary primitive".into()),
};
let mac = if primary.prefix_type == OutputPrefixType::Legacy {
if data.len() >= MAX_INT {
return Err("mac::factory: data too long".into());
}
let mut local_data = Vec::with_capacity(data.len() + 1);
local_data.extend_from_slice(data);
local_data.push(0u8);
primary.primitive.compute_mac(&local_data)?
} else {
primary.primitive.compute_mac(data)?
};
let mut ret = Vec::with_capacity(primary.prefix.len() + mac.len());
ret.extend_from_slice(&primary.prefix);
ret.extend_from_slice(&mac);
Ok(ret)
}
fn verify_mac(&self, mac: &[u8], data: &[u8]) -> Result<(), TinkError> {
let prefix_size = tink_core::cryptofmt::NON_RAW_PREFIX_SIZE;
if mac.len() <= prefix_size {
return Err("mac::factory: invalid mac".into());
}
let prefix = &mac[..prefix_size];
let mac_no_prefix = &mac[prefix_size..];
if let Some(entries) = self.ps.entries_for_prefix(prefix) {
for entry in entries {
let result = if entry.prefix_type == OutputPrefixType::Legacy {
if data.len() >= MAX_INT {
return Err("mac::factory: data too long".into());
}
let mut local_data = Vec::with_capacity(data.len() + 1);
local_data.extend_from_slice(data);
local_data.push(0u8);
entry.primitive.verify_mac(mac_no_prefix, &local_data)
} else {
entry.primitive.verify_mac(mac_no_prefix, data)
};
if result.is_ok() {
return Ok(());
}
}
}
if let Some(entries) = self.ps.raw_entries() {
for entry in entries {
let result = if entry.prefix_type == OutputPrefixType::Legacy {
let mut local_data = Vec::with_capacity(data.len() + 1);
local_data.extend_from_slice(data);
local_data.push(tink_core::cryptofmt::LEGACY_START_BYTE);
entry.primitive.verify_mac(mac, &local_data)
} else {
entry.primitive.verify_mac(mac, data)
};
if result.is_ok() {
return Ok(());
}
}
}
Err("mac::factory: decryption failed".into())
}
}