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}