use bc_ur::prelude::*;
use super::EncapsulationScheme;
use crate::{
Decrypter, EncapsulationCiphertext, EncryptedMessage, Encrypter, Nonce,
Result, tags,
};
#[derive(Clone, PartialEq, Debug)]
pub struct SealedMessage {
message: EncryptedMessage,
encapsulated_key: EncapsulationCiphertext,
}
impl SealedMessage {
pub fn new(plaintext: impl AsRef<[u8]>, recipient: &dyn Encrypter) -> Self {
Self::new_with_aad(plaintext, recipient, None::<Vec<u8>>)
}
pub fn new_with_aad(
plaintext: impl AsRef<[u8]>,
recipient: &dyn Encrypter,
aad: Option<impl AsRef<[u8]>>,
) -> Self {
Self::new_opt(plaintext, recipient, aad, None::<Nonce>)
}
pub fn new_opt(
plaintext: impl AsRef<[u8]>,
recipient: &dyn Encrypter,
aad: Option<impl AsRef<[u8]>>,
test_nonce: Option<Nonce>,
) -> Self {
let (shared_key, encapsulated_key) =
recipient.encapsulate_new_shared_secret();
let message = shared_key.encrypt(plaintext, aad, test_nonce);
Self { message, encapsulated_key }
}
pub fn decrypt(&self, private_key: &dyn Decrypter) -> Result<Vec<u8>> {
let shared_key =
private_key.decapsulate_shared_secret(&self.encapsulated_key)?;
shared_key.decrypt(&self.message)
}
pub fn encapsulation_scheme(&self) -> EncapsulationScheme {
self.encapsulated_key.encapsulation_scheme()
}
}
impl AsRef<SealedMessage> for SealedMessage {
fn as_ref(&self) -> &SealedMessage { self }
}
impl CBORTagged for SealedMessage {
fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_SEALED_MESSAGE]) }
}
impl From<SealedMessage> for CBOR {
fn from(value: SealedMessage) -> Self { value.tagged_cbor() }
}
impl TryFrom<CBOR> for SealedMessage {
type Error = dcbor::Error;
fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
Self::from_tagged_cbor(cbor)
}
}
impl CBORTaggedEncodable for SealedMessage {
fn untagged_cbor(&self) -> CBOR {
let message: CBOR = self.message.clone().into();
let ephemeral_public_key: CBOR = self.encapsulated_key.clone().into();
[message, ephemeral_public_key].into()
}
}
impl CBORTaggedDecodable for SealedMessage {
fn from_untagged_cbor(cbor: CBOR) -> dcbor::Result<Self> {
match cbor.as_case() {
CBORCase::Array(elements) => {
if elements.len() != 2 {
return Err("SealedMessage must have two elements".into());
}
let message = elements[0].clone().try_into()?;
let ephemeral_public_key = elements[1].clone().try_into()?;
Ok(Self { message, encapsulated_key: ephemeral_public_key })
}
_ => Err("SealedMessage must be an array".into()),
}
}
}
#[cfg(test)]
mod tests {
use crate::{EncapsulationScheme, SealedMessage};
#[test]
fn test_sealed_message_x25519() {
let plaintext = b"Some mysteries aren't meant to be solved.";
let encapsulation = EncapsulationScheme::X25519;
let (alice_private_key, _) = encapsulation.keypair();
let (bob_private_key, bob_public_key) = encapsulation.keypair();
let (carol_private_key, _) = encapsulation.keypair();
let sealed_message = SealedMessage::new(plaintext, &bob_public_key);
assert_eq!(
sealed_message.decrypt(&bob_private_key).unwrap(),
plaintext
);
assert!(sealed_message.decrypt(&alice_private_key).is_err());
assert!(sealed_message.decrypt(&carol_private_key).is_err());
}
#[test]
#[cfg(feature = "pqcrypto")]
fn test_sealed_message_mlkem512() {
let plaintext = b"Some mysteries aren't meant to be solved.";
let encapsulation = EncapsulationScheme::MLKEM512;
let (alice_private_key, _) = encapsulation.keypair();
let (bob_private_key, bob_public_key) = encapsulation.keypair();
let (carol_private_key, _) = encapsulation.keypair();
let sealed_message = SealedMessage::new(plaintext, &bob_public_key);
assert_eq!(
sealed_message.decrypt(&bob_private_key).unwrap(),
plaintext
);
assert!(sealed_message.decrypt(&alice_private_key).is_err());
assert!(sealed_message.decrypt(&carol_private_key).is_err());
}
}