use bc_components::{Digest, DigestProvider, Nonce, SymmetricKey, tags};
use dcbor::prelude::*;
use crate::{Envelope, Error, Result, base::envelope::EnvelopeCase};
impl Envelope {
pub fn encrypt_subject(&self, key: &SymmetricKey) -> Result<Self> {
self.encrypt_subject_opt(key, None)
}
#[doc(hidden)]
pub fn encrypt_subject_opt(
&self,
key: &SymmetricKey,
test_nonce: Option<Nonce>,
) -> Result<Self> {
let result: Self;
let original_digest: Digest;
match self.case() {
EnvelopeCase::Node {
subject,
assertions,
digest: envelope_digest,
} => {
if subject.is_encrypted() {
return Err(Error::AlreadyEncrypted);
}
let encoded_cbor = subject.tagged_cbor().to_cbor_data();
let digest = subject.digest();
let encrypted_message =
key.encrypt_with_digest(encoded_cbor, digest, test_nonce);
let encrypted_subject =
Self::new_with_encrypted(encrypted_message).unwrap();
result = Self::new_with_unchecked_assertions(
encrypted_subject,
assertions.clone(),
);
original_digest = *envelope_digest;
}
EnvelopeCase::Leaf { cbor, digest } => {
let encoded_cbor = CBOR::to_tagged_value(
tags::TAG_ENVELOPE,
CBOR::to_tagged_value(tags::TAG_LEAF, cbor.clone()),
)
.to_cbor_data();
let encrypted_message =
key.encrypt_with_digest(encoded_cbor, *digest, test_nonce);
result = Self::new_with_encrypted(encrypted_message).unwrap();
original_digest = *digest;
}
EnvelopeCase::Wrapped { digest, .. } => {
let encoded_cbor = self.tagged_cbor().to_cbor_data();
let encrypted_message =
key.encrypt_with_digest(encoded_cbor, *digest, test_nonce);
result = Self::new_with_encrypted(encrypted_message).unwrap();
original_digest = *digest;
}
EnvelopeCase::KnownValue { value, digest } => {
let encoded_cbor = CBOR::to_tagged_value(
tags::TAG_ENVELOPE,
value.untagged_cbor(),
)
.to_cbor_data();
let encrypted_message =
key.encrypt_with_digest(encoded_cbor, *digest, test_nonce);
result = Self::new_with_encrypted(encrypted_message).unwrap();
original_digest = *digest;
}
EnvelopeCase::Assertion(assertion) => {
let digest = assertion.digest();
let encoded_cbor = CBOR::to_tagged_value(
tags::TAG_ENVELOPE,
assertion.clone(),
)
.to_cbor_data();
let encrypted_message =
key.encrypt_with_digest(encoded_cbor, digest, test_nonce);
result = Self::new_with_encrypted(encrypted_message).unwrap();
original_digest = digest;
}
EnvelopeCase::Encrypted { .. } => {
return Err(Error::AlreadyEncrypted);
}
#[cfg(feature = "compress")]
EnvelopeCase::Compressed(compressed) => {
let digest = compressed.digest();
let encoded_cbor = CBOR::to_tagged_value(
tags::TAG_ENVELOPE,
compressed.tagged_cbor(),
)
.to_cbor_data();
let encrypted_message =
key.encrypt_with_digest(encoded_cbor, digest, test_nonce);
result = Self::new_with_encrypted(encrypted_message).unwrap();
original_digest = digest;
}
EnvelopeCase::Elided { .. } => {
return Err(Error::AlreadyElided);
}
}
assert_eq!(result.digest(), original_digest);
Ok(result)
}
pub fn decrypt_subject(&self, key: &SymmetricKey) -> Result<Self> {
match self.subject().case() {
EnvelopeCase::Encrypted(message) => {
let encoded_cbor = key.decrypt(message)?;
let subject_digest =
message.aad_digest().ok_or(Error::MissingDigest)?;
let cbor = CBOR::try_from_data(encoded_cbor)?;
let result_subject = Self::from_tagged_cbor(cbor)?;
if result_subject.digest() != subject_digest {
return Err(Error::InvalidDigest);
}
match self.case() {
EnvelopeCase::Node { assertions, digest, .. } => {
let result = Self::new_with_unchecked_assertions(
result_subject,
assertions.clone(),
);
if result.digest() != *digest {
return Err(Error::InvalidDigest);
}
Ok(result)
}
_ => Ok(result_subject),
}
}
_ => Err(Error::NotEncrypted),
}
}
}
impl Envelope {
pub fn encrypt(&self, key: &SymmetricKey) -> Envelope {
self.wrap().encrypt_subject(key).unwrap()
}
pub fn decrypt(&self, key: &SymmetricKey) -> Result<Envelope> {
self.decrypt_subject(key)?.try_unwrap()
}
}