use cbor2::Cbor;
use crate::{
header::{decode_protected, encode_protected},
iana, tag, util, Error, Header, Macer, Recipient, Value,
};
#[derive(Clone, Debug, PartialEq, Cbor)]
struct MacWire(
#[serde(with = "serde_bytes")] Vec<u8>,
Header,
#[serde(with = "serde_bytes")] Option<Vec<u8>>,
#[serde(with = "serde_bytes")] Vec<u8>,
Vec<Recipient>,
);
#[derive(Clone, Debug, Default, PartialEq)]
pub struct MacMessage {
pub protected: Header,
pub unprotected: Header,
pub payload: Option<Vec<u8>>,
pub recipients: Vec<Recipient>,
tag: Vec<u8>,
protected_raw: Vec<u8>,
computed: bool,
}
impl MacMessage {
pub fn new(payload: Option<Vec<u8>>) -> Self {
MacMessage {
payload,
..Default::default()
}
}
fn to_be_maced(
protected_raw: &[u8],
external_aad: &[u8],
payload: &Option<Vec<u8>>,
) -> Vec<u8> {
util::encode_structure(vec![
Value::from("MAC"),
Value::Bytes(protected_raw.to_vec()),
Value::Bytes(external_aad.to_vec()),
util::payload_value(payload),
])
}
pub fn compute(&mut self, macer: &dyn Macer, external_aad: Option<&[u8]>) -> Result<(), Error> {
util::ensure_protected_alg(&mut self.protected, macer.alg())?;
util::ensure_unprotected_kid(&mut self.unprotected, macer.kid());
self.protected_raw = encode_protected(&self.protected);
let tbm = Self::to_be_maced(
&self.protected_raw,
external_aad.unwrap_or(&[]),
&self.payload,
);
self.tag = macer.mac_create(&tbm)?;
self.computed = true;
Ok(())
}
pub fn compute_and_encode(
&mut self,
macer: &dyn Macer,
external_aad: Option<&[u8]>,
) -> Result<Vec<u8>, Error> {
self.compute(macer, external_aad)?;
self.to_vec()
}
pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
if !self.computed {
return Err(Error::Custom(
"MacMessage must be computed before encoding".into(),
));
}
if self.recipients.is_empty() {
return Err(Error::Custom("MacMessage has no recipients".into()));
}
let wire = MacWire(
self.protected_raw.clone(),
self.unprotected.clone(),
self.payload.clone(),
self.tag.clone(),
self.recipients.clone(),
);
Ok(tag::with_tag(
tag::MAC_PREFIX,
&cbor2::to_canonical_vec(&wire)?,
))
}
pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
let body = tag::untag(data, tag::MAC_PREFIX);
let wire: MacWire = cbor2::from_slice(body)?;
if wire.4.is_empty() {
return Err(Error::Custom("MacMessage has no recipients".into()));
}
let protected = decode_protected(&wire.0)?;
Ok(MacMessage {
protected,
unprotected: wire.1,
payload: wire.2,
recipients: wire.4,
tag: wire.3,
protected_raw: wire.0,
computed: true,
})
}
pub fn verify(&self, macer: &dyn Macer, external_aad: Option<&[u8]>) -> Result<(), Error> {
if !self.computed {
return Err(Error::Custom(
"MacMessage must be decoded before verifying".into(),
));
}
util::check_protected_alg(&self.protected, macer.alg())?;
let tbm = Self::to_be_maced(
&self.protected_raw,
external_aad.unwrap_or(&[]),
&self.payload,
);
macer.mac_verify(&tbm, &self.tag)
}
pub fn verify_and_decode(
macer: &dyn Macer,
data: &[u8],
external_aad: Option<&[u8]>,
) -> Result<Self, Error> {
let msg = Self::from_slice(data)?;
msg.verify(macer, external_aad)?;
Ok(msg)
}
pub fn tag(&self) -> &[u8] {
&self.tag
}
pub const TAG: u64 = iana::CBORTagCOSEMac;
}