use bc_ur::prelude::*;
use crate::{AuthenticationTag, Digest, DigestProvider, Nonce, tags};
#[derive(Clone, Eq, PartialEq)]
pub struct EncryptedMessage {
ciphertext: Vec<u8>,
aad: Vec<u8>, nonce: Nonce,
auth: AuthenticationTag,
}
impl EncryptedMessage {
pub fn new(
ciphertext: impl AsRef<[u8]>,
aad: impl AsRef<[u8]>,
nonce: Nonce,
auth: AuthenticationTag,
) -> Self {
Self {
ciphertext: ciphertext.as_ref().to_vec(),
aad: aad.as_ref().to_vec(),
nonce,
auth,
}
}
pub fn ciphertext(&self) -> &[u8] { &self.ciphertext }
pub fn aad(&self) -> &[u8] { &self.aad }
pub fn nonce(&self) -> &Nonce { &self.nonce }
pub fn authentication_tag(&self) -> &AuthenticationTag { &self.auth }
pub fn aad_cbor(&self) -> Option<CBOR> {
if self.aad.is_empty() {
None
} else {
CBOR::try_from_data(self.aad()).ok()
}
}
pub fn aad_digest(&self) -> Option<Digest> {
self.aad_cbor().and_then(|cbor| Digest::try_from(cbor).ok())
}
pub fn has_digest(&self) -> bool { self.aad_digest().is_some() }
}
impl std::fmt::Debug for EncryptedMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EncryptedMessage")
.field("ciphertext", &hex::encode(&self.ciphertext))
.field("aad", &hex::encode(&self.aad))
.field("nonce", &self.nonce)
.field("auth", &self.auth)
.finish()
}
}
impl AsRef<EncryptedMessage> for EncryptedMessage {
fn as_ref(&self) -> &EncryptedMessage { self }
}
impl DigestProvider for EncryptedMessage {
fn digest(&self) -> Digest { self.aad_digest().unwrap() }
}
impl CBORTagged for EncryptedMessage {
fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_ENCRYPTED]) }
}
impl From<EncryptedMessage> for CBOR {
fn from(value: EncryptedMessage) -> Self { value.tagged_cbor() }
}
impl TryFrom<CBOR> for EncryptedMessage {
type Error = dcbor::Error;
fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
Self::from_tagged_cbor(cbor)
}
}
impl CBORTaggedEncodable for EncryptedMessage {
fn untagged_cbor(&self) -> CBOR {
let mut a = vec![
CBOR::to_byte_string(&self.ciphertext),
CBOR::to_byte_string(self.nonce.data()),
CBOR::to_byte_string(self.auth.data()),
];
if !self.aad.is_empty() {
a.push(CBOR::to_byte_string(&self.aad));
}
a.into()
}
}
impl CBORTaggedDecodable for EncryptedMessage {
fn from_untagged_cbor(cbor: CBOR) -> dcbor::Result<Self> {
match cbor.as_case() {
CBORCase::Array(elements) => {
if elements.len() < 3 {
return Err(
"EncryptedMessage must have at least 3 elements".into(),
);
}
let ciphertext =
CBOR::try_into_byte_string(elements[0].clone())?;
let nonce_data =
CBOR::try_into_byte_string(elements[1].clone())?;
let nonce = Nonce::from_data_ref(nonce_data)?;
let auth_data =
CBOR::try_into_byte_string(elements[2].clone())?;
let auth = AuthenticationTag::from_data_ref(auth_data)?;
let aad = if elements.len() > 3 {
CBOR::try_into_byte_string(elements[3].clone())?
} else {
Vec::new()
};
Ok(Self::new(ciphertext, aad, nonce, auth))
}
_ => Err("EncryptedMessage must be an array".into()),
}
}
}