bc_components/encapsulation/
sealed_message.rs

1use crate::{ tags, Decrypter, EncapsulationCiphertext, EncryptedMessage, Encrypter, Nonce };
2use bc_ur::prelude::*;
3use anyhow::Result;
4
5use super::EncapsulationScheme;
6
7/// A sealed message that can only be decrypted by the intended recipient.
8///
9/// `SealedMessage` provides a public key encryption mechanism where a message is
10/// encrypted with a symmetric key, and that key is then encapsulated using the
11/// recipient's public key. This ensures that only the recipient can decrypt the
12/// message by first decapsulating the shared secret using their private key.
13///
14/// Features:
15/// - Anonymous sender: The sender's identity is not revealed in the sealed message
16/// - Authenticated encryption: Message integrity and authenticity are guaranteed
17/// - Forward secrecy: Each message uses a different ephemeral key
18/// - Post-quantum security options: Can use ML-KEM for quantum-resistant encryption
19///
20/// The structure internally contains:
21/// - An `EncryptedMessage` containing the actual encrypted data
22/// - An `EncapsulationCiphertext` containing the encapsulated shared secret
23#[derive(Clone, PartialEq, Debug)]
24pub struct SealedMessage {
25    /// The encrypted message content
26    message: EncryptedMessage,
27    /// The encapsulated key used to encrypt the message
28    encapsulated_key: EncapsulationCiphertext,
29}
30
31impl SealedMessage {
32    /// Creates a new `SealedMessage` from the given plaintext and recipient.
33    ///
34    /// This method performs the following steps:
35    /// 1. Generates a new shared secret key and encapsulates it for the recipient
36    /// 2. Encrypts the plaintext using that shared secret
37    ///
38    /// # Parameters
39    ///
40    /// * `plaintext` - The message data to encrypt
41    /// * `recipient` - The recipient who will be able to decrypt the message
42    ///
43    /// # Returns
44    ///
45    /// A new `SealedMessage` containing the encrypted message and encapsulated key
46    ///
47    /// # Example
48    ///
49    /// ```
50    /// use bc_components::{EncapsulationScheme, SealedMessage};
51    ///
52    /// // Generate a keypair for the recipient
53    /// let (recipient_private_key, recipient_public_key) = EncapsulationScheme::default().keypair();
54    ///
55    /// // Create a sealed message for the recipient
56    /// let plaintext = b"For your eyes only";
57    /// let sealed_message = SealedMessage::new(plaintext, &recipient_public_key);
58    ///
59    /// // The recipient can decrypt the message
60    /// let decrypted = sealed_message.decrypt(&recipient_private_key).unwrap();
61    /// assert_eq!(decrypted, plaintext);
62    /// ```
63    pub fn new(plaintext: impl Into<Vec<u8>>, recipient: &dyn Encrypter) -> Self {
64        Self::new_with_aad(plaintext, recipient, None::<Vec<u8>>)
65    }
66
67    /// Creates a new `SealedMessage` with additional authenticated data (AAD).
68    ///
69    /// AAD is data that is authenticated but not encrypted. It can be used to
70    /// bind the encrypted message to some context.
71    ///
72    /// # Parameters
73    ///
74    /// * `plaintext` - The message data to encrypt
75    /// * `recipient` - The recipient who will be able to decrypt the message
76    /// * `aad` - Additional authenticated data that will be bound to the encryption
77    ///
78    /// # Returns
79    ///
80    /// A new `SealedMessage` containing the encrypted message and encapsulated key
81    ///
82    /// # Example
83    ///
84    /// ```
85    /// use bc_components::{EncapsulationScheme, SealedMessage};
86    ///
87    /// // Generate a keypair for the recipient
88    /// let (recipient_private_key, recipient_public_key) = EncapsulationScheme::default().keypair();
89    ///
90    /// // Create a sealed message with additional authenticated data
91    /// let plaintext = b"For your eyes only";
92    /// let aad = b"Message ID: 12345";
93    /// let sealed_message = SealedMessage::new_with_aad(plaintext, &recipient_public_key, Some(aad));
94    ///
95    /// // The recipient can decrypt the message
96    /// let decrypted = sealed_message.decrypt(&recipient_private_key).unwrap();
97    /// assert_eq!(decrypted, plaintext);
98    /// ```
99    pub fn new_with_aad(
100        plaintext: impl Into<Vec<u8>>,
101        recipient: &dyn Encrypter,
102        aad: Option<impl Into<Vec<u8>>>
103    ) -> Self {
104        Self::new_opt(plaintext, recipient, aad, None::<Nonce>)
105    }
106
107    /// Creates a new `SealedMessage` with options for testing.
108    ///
109    /// This method is similar to `new_with_aad` but allows specifying a test nonce,
110    /// which is useful for deterministic testing.
111    ///
112    /// # Parameters
113    ///
114    /// * `plaintext` - The message data to encrypt
115    /// * `recipient` - The recipient who will be able to decrypt the message
116    /// * `aad` - Additional authenticated data that will be bound to the encryption
117    /// * `test_nonce` - Optional nonce for deterministic encryption (testing only)
118    ///
119    /// # Returns
120    ///
121    /// A new `SealedMessage` containing the encrypted message and encapsulated key
122    pub fn new_opt(
123        plaintext: impl Into<Vec<u8>>,
124        recipient: &dyn Encrypter,
125        aad: Option<impl Into<Vec<u8>>>,
126        test_nonce: Option<impl AsRef<Nonce>>
127    ) -> Self {
128        let (shared_key, encapsulated_key) = recipient.encapsulate_new_shared_secret();
129        let message = shared_key.encrypt(plaintext, aad, test_nonce);
130        Self {
131            message,
132            encapsulated_key,
133        }
134    }
135
136    /// Decrypts the message using the recipient's private key.
137    ///
138    /// This method performs the following steps:
139    /// 1. Decapsulates the shared secret using the recipient's private key
140    /// 2. Uses the shared secret to decrypt the message
141    ///
142    /// # Parameters
143    ///
144    /// * `private_key` - The private key of the intended recipient
145    ///
146    /// # Returns
147    ///
148    /// A `Result` containing the decrypted message data if successful,
149    /// or an error if decryption fails
150    ///
151    /// # Errors
152    ///
153    /// Returns an error if:
154    /// - The private key doesn't match the one used for encapsulation
155    /// - The decapsulation process fails
156    /// - The decryption process fails (e.g., message tampering)
157    ///
158    /// # Example
159    ///
160    /// ```
161    /// use bc_components::{EncapsulationScheme, SealedMessage};
162    ///
163    /// // Generate keypairs for different users
164    /// let (alice_private_key, _) = EncapsulationScheme::default().keypair();
165    /// let (bob_private_key, bob_public_key) = EncapsulationScheme::default().keypair();
166    ///
167    /// // Alice sends a message to Bob
168    /// let plaintext = b"Secret message for Bob";
169    /// let sealed_message = SealedMessage::new(plaintext, &bob_public_key);
170    ///
171    /// // Bob can decrypt the message
172    /// let decrypted = sealed_message.decrypt(&bob_private_key).unwrap();
173    /// assert_eq!(decrypted, plaintext);
174    ///
175    /// // Alice cannot decrypt the message she sent
176    /// assert!(sealed_message.decrypt(&alice_private_key).is_err());
177    /// ```
178    pub fn decrypt(&self, private_key: &dyn Decrypter) -> Result<Vec<u8>> {
179        let shared_key = private_key.decapsulate_shared_secret(&self.encapsulated_key)?;
180        shared_key.decrypt(&self.message)
181    }
182
183    /// Returns the encapsulation scheme used for this sealed message.
184    ///
185    /// # Returns
186    ///
187    /// The encapsulation scheme (X25519, MLKEM512, MLKEM768, or MLKEM1024)
188    /// that was used to create this sealed message.
189    ///
190    /// # Example
191    ///
192    /// ```
193    /// use bc_components::{EncapsulationScheme, SealedMessage};
194    ///
195    /// // Generate a keypair using ML-KEM768
196    /// let (_, public_key) = EncapsulationScheme::MLKEM768.keypair();
197    ///
198    /// // Create a sealed message
199    /// let sealed_message = SealedMessage::new(b"Quantum-resistant message", &public_key);
200    ///
201    /// // Check the encapsulation scheme
202    /// assert_eq!(sealed_message.encapsulation_scheme(), EncapsulationScheme::MLKEM768);
203    /// ```
204    pub fn encapsulation_scheme(&self) -> EncapsulationScheme {
205        self.encapsulated_key.encapsulation_scheme()
206    }
207}
208
209/// Implementation of `AsRef` trait for `SealedMessage`.
210impl AsRef<SealedMessage> for SealedMessage {
211    fn as_ref(&self) -> &SealedMessage {
212        self
213    }
214}
215
216/// Implementation of CBOR tagging for `SealedMessage`.
217impl CBORTagged for SealedMessage {
218    fn cbor_tags() -> Vec<Tag> {
219        tags_for_values(&[tags::TAG_SEALED_MESSAGE])
220    }
221}
222
223/// Conversion from `SealedMessage` to CBOR for serialization.
224impl From<SealedMessage> for CBOR {
225    fn from(value: SealedMessage) -> Self {
226        value.tagged_cbor()
227    }
228}
229
230/// Conversion from CBOR to `SealedMessage` for deserialization.
231impl TryFrom<CBOR> for SealedMessage {
232    type Error = dcbor::Error;
233
234    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
235        Self::from_tagged_cbor(cbor)
236    }
237}
238
239/// Implementation of CBOR encoding for `SealedMessage`.
240impl CBORTaggedEncodable for SealedMessage {
241    fn untagged_cbor(&self) -> CBOR {
242        let message: CBOR = self.message.clone().into();
243        let ephemeral_public_key: CBOR = self.encapsulated_key.clone().into();
244        [message, ephemeral_public_key].into()
245    }
246}
247
248/// Implementation of CBOR decoding for `SealedMessage`.
249impl CBORTaggedDecodable for SealedMessage {
250    fn from_untagged_cbor(cbor: CBOR) -> dcbor::Result<Self> {
251        match cbor.as_case() {
252            CBORCase::Array(elements) => {
253                if elements.len() != 2 {
254                    return Err("SealedMessage must have two elements".into());
255                }
256                let message = elements[0].clone().try_into()?;
257                let ephemeral_public_key = elements[1].clone().try_into()?;
258                Ok(Self {
259                    message,
260                    encapsulated_key: ephemeral_public_key,
261                })
262            }
263            _ => return Err("SealedMessage must be an array".into()),
264        }
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use crate::{ EncapsulationScheme, SealedMessage };
271
272    #[test]
273    fn test_sealed_message_x25519() {
274        let plaintext = b"Some mysteries aren't meant to be solved.";
275
276        let encapsulation = EncapsulationScheme::X25519;
277        let (alice_private_key, _) = encapsulation.keypair();
278        let (bob_private_key, bob_public_key) = encapsulation.keypair();
279        let (carol_private_key, _) = encapsulation.keypair();
280
281        // Alice constructs a message for Bob's eyes only.
282        let sealed_message = SealedMessage::new(plaintext, &bob_public_key);
283
284        // Bob decrypts and reads the message.
285        assert_eq!(sealed_message.decrypt(&bob_private_key).unwrap(), plaintext);
286
287        // No one else can decrypt the message, not even the sender.
288        assert!(sealed_message.decrypt(&alice_private_key).is_err());
289        assert!(sealed_message.decrypt(&carol_private_key).is_err());
290    }
291
292    #[test]
293    fn test_sealed_message_mlkem512() {
294        let plaintext = b"Some mysteries aren't meant to be solved.";
295
296        let encapsulation = EncapsulationScheme::MLKEM512;
297        let (alice_private_key, _) = encapsulation.keypair();
298        let (bob_private_key, bob_public_key) = encapsulation.keypair();
299        let (carol_private_key, _) = encapsulation.keypair();
300
301        // Alice constructs a message for Bob's eyes only.
302        let sealed_message = SealedMessage::new(plaintext, &bob_public_key);
303
304        // Bob decrypts and reads the message.
305        assert_eq!(sealed_message.decrypt(&bob_private_key).unwrap(), plaintext);
306
307        // No one else can decrypt the message, not even the sender.
308        assert!(sealed_message.decrypt(&alice_private_key).is_err());
309        assert!(sealed_message.decrypt(&carol_private_key).is_err());
310    }
311}