use cbor2::Cbor;
use crate::{
header::{decode_protected, encode_protected},
iana, tag, util, Encryptor, Error, Header, Value,
};
#[derive(Clone, Debug, PartialEq, Cbor)]
struct Encrypt0Wire(
#[serde(with = "serde_bytes")] Vec<u8>,
Header,
#[serde(with = "serde_bytes")] Option<Vec<u8>>,
);
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Encrypt0Message {
pub protected: Header,
pub unprotected: Header,
pub payload: Option<Vec<u8>>,
ciphertext: Vec<u8>,
protected_raw: Vec<u8>,
encrypted: bool,
}
impl Encrypt0Message {
pub fn new(payload: Option<Vec<u8>>) -> Self {
Encrypt0Message {
payload,
..Default::default()
}
}
fn to_be_encrypted(protected_raw: &[u8], external_aad: &[u8]) -> Vec<u8> {
util::encode_structure(vec![
Value::from("Encrypt0"),
Value::Bytes(protected_raw.to_vec()),
Value::Bytes(external_aad.to_vec()),
])
}
fn iv(&self, encryptor: &dyn Encryptor) -> Result<Vec<u8>, Error> {
let iv = self
.unprotected
.get_bytes(iana::HeaderParameterIV)?
.ok_or_else(|| Error::Custom("missing IV in unprotected header".into()))?;
if iv.len() != encryptor.nonce_size() {
return Err(Error::Custom(format!(
"IV size mismatch, expected {}, got {}",
encryptor.nonce_size(),
iv.len()
)));
}
Ok(iv.to_vec())
}
pub fn encrypt(
&mut self,
encryptor: &dyn Encryptor,
external_aad: Option<&[u8]>,
) -> Result<(), Error> {
util::ensure_protected_alg(&mut self.protected, encryptor.alg())?;
util::ensure_unprotected_kid(&mut self.unprotected, encryptor.kid());
let iv = self.iv(encryptor)?;
self.protected_raw = encode_protected(&self.protected);
let aad = Self::to_be_encrypted(&self.protected_raw, external_aad.unwrap_or(&[]));
let plaintext = self.payload.clone().unwrap_or_default();
self.ciphertext = encryptor.encrypt(&iv, &plaintext, &aad)?;
self.encrypted = true;
Ok(())
}
pub fn encrypt_and_encode(
&mut self,
encryptor: &dyn Encryptor,
external_aad: Option<&[u8]>,
) -> Result<Vec<u8>, Error> {
self.encrypt(encryptor, external_aad)?;
self.to_vec()
}
pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
if !self.encrypted {
return Err(Error::Custom(
"Encrypt0Message must be encrypted before encoding".into(),
));
}
let wire = Encrypt0Wire(
self.protected_raw.clone(),
self.unprotected.clone(),
Some(self.ciphertext.clone()),
);
Ok(tag::with_tag(
tag::ENCRYPT0_PREFIX,
&cbor2::to_canonical_vec(&wire)?,
))
}
pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
let body = tag::untag(data, tag::ENCRYPT0_PREFIX);
let wire: Encrypt0Wire = cbor2::from_slice(body)?;
let protected = decode_protected(&wire.0)?;
let ciphertext = wire
.2
.ok_or_else(|| Error::Custom("COSE_Encrypt0 has no ciphertext".into()))?;
Ok(Encrypt0Message {
protected,
unprotected: wire.1,
payload: None,
ciphertext,
protected_raw: wire.0,
encrypted: true,
})
}
pub fn decrypt(
&mut self,
encryptor: &dyn Encryptor,
external_aad: Option<&[u8]>,
) -> Result<&[u8], Error> {
if !self.encrypted {
return Err(Error::Custom(
"Encrypt0Message must be decoded before decrypting".into(),
));
}
util::check_protected_alg(&self.protected, encryptor.alg())?;
let iv = self.iv(encryptor)?;
let aad = Self::to_be_encrypted(&self.protected_raw, external_aad.unwrap_or(&[]));
let plaintext = encryptor.decrypt(&iv, &self.ciphertext, &aad)?;
self.payload = Some(plaintext);
Ok(self.payload.as_deref().unwrap())
}
pub fn decrypt_and_decode(
encryptor: &dyn Encryptor,
data: &[u8],
external_aad: Option<&[u8]>,
) -> Result<Self, Error> {
let mut msg = Self::from_slice(data)?;
msg.decrypt(encryptor, external_aad)?;
Ok(msg)
}
pub fn ciphertext(&self) -> &[u8] {
&self.ciphertext
}
pub const TAG: u64 = iana::CBORTagCOSEEncrypt0;
}