bc_envelope/extension/recipient.rs
1//! Public key encryption extension for Gordian Envelope.
2//!
3//! This module implements public key encryption for Gordian Envelope, allowing encrypted
4//! content to be selectively shared with one or more recipients. Each recipient needs
5//! their own public/private key pair, and only recipients with the corresponding private
6//! key can decrypt the envelope's content.
7//!
8//! The recipient extension builds on the basic envelope encryption capabilities by adding:
9//!
10//! - **Multiple Recipients** - A single envelope can be encrypted to multiple recipients
11//! - **Content Key Distribution** - Uses public key cryptography to securely distribute the
12//! symmetric key that encrypts the actual content
13//! - **Privacy** - Recipients can decrypt the envelope independently without revealing
14//! their identity or access to other recipients
15//!
16//! # How It Works
17//!
18//! The envelope's subject is encrypted using a random symmetric key (the "content key"),
19//! and then this content key is encrypted to each recipient's public key using a `SealedMessage`.
20//! Each encrypted content key is attached to the envelope with a `hasRecipient` assertion.
21//!
22//! When recipients want to decrypt the envelope, they use their private key to decrypt
23//! the content key from the appropriate `SealedMessage`, and then use that content key to
24//! decrypt the envelope's subject.
25//!
26//! # Basic Usage
27//!
28//! ```
29//! # #[cfg(feature = "recipient")]
30//! # {
31//! use bc_envelope::prelude::*;
32//! use bc_components::{Encrypter, SymmetricKey, PublicKeysProvider};
33//!
34//! // Create some test keys (in a real application, these would be proper asymmetric keys)
35//! let alice_keys = bc_components::PrivateKeyBase::new();
36//! let bob_keys = bc_components::PrivateKeyBase::new();
37//!
38//! // Create an envelope with some content
39//! let envelope = Envelope::new("Confidential message");
40//!
41//! // Encrypt to Bob (generates a random content key internally)
42//! let encrypted = envelope
43//! .encrypt_subject_to_recipient(&bob_keys.public_keys())
44//! .unwrap();
45//!
46//! // Bob can decrypt it with his private key
47//! let decrypted = encrypted
48//! .decrypt_subject_to_recipient(&bob_keys)
49//! .unwrap();
50//!
51//! assert_eq!(decrypted.extract_subject::<String>().unwrap(), "Confidential message");
52//!
53//! // Alice can't decrypt it because it wasn't encrypted to her
54//! assert!(encrypted.clone()
55//! .decrypt_subject_to_recipient(&alice_keys)
56//! .is_err());
57//! # }
58//! ```
59//!
60//! # Multiple Recipients
61//!
62//! You can encrypt an envelope to multiple recipients, and each can independently decrypt it:
63//!
64//! ```
65//! # #[cfg(feature = "recipient")]
66//! # {
67//! use bc_envelope::prelude::*;
68//! use bc_components::{SymmetricKey, PublicKeysProvider};
69//!
70//! // Create keys for multiple recipients
71//! let alice_keys = bc_components::PrivateKeyBase::new();
72//! let bob_keys = bc_components::PrivateKeyBase::new();
73//! let carol_keys = bc_components::PrivateKeyBase::new();
74//!
75//! // Create and encrypt the envelope to both Bob and Carol
76//! let envelope = Envelope::new("Shared secret");
77//! let encrypted = envelope.encrypt_subject_to_recipients(
78//! &[&bob_keys.public_keys(), &carol_keys.public_keys()]
79//! ).unwrap();
80//!
81//! // Bob can decrypt it
82//! let bob_decrypted = encrypted.clone()
83//! .decrypt_subject_to_recipient(&bob_keys)
84//! .unwrap();
85//!
86//! // Carol can decrypt it
87//! let carol_decrypted = encrypted.clone()
88//! .decrypt_subject_to_recipient(&carol_keys)
89//! .unwrap();
90//!
91//! // Alice can't decrypt it
92//! assert!(encrypted
93//! .decrypt_subject_to_recipient(&alice_keys)
94//! .is_err());
95//! # }
96//! ```
97//!
98//! # Usage with Custom Keys
99//!
100//! If you need more control over the encryption process, you can use the lower-level methods
101//! to encrypt with a specific content key:
102//!
103//! ```
104//! # #[cfg(feature = "recipient")]
105//! # {
106//! use bc_envelope::prelude::*;
107//! use bc_components::{SymmetricKey, PublicKeysProvider};
108//!
109//! // Create keys for the recipient
110//! let bob_keys = bc_components::PrivateKeyBase::new();
111//!
112//! // Create the envelope and a specific content key
113//! let envelope = Envelope::new("Secret message");
114//! let content_key = SymmetricKey::new();
115//!
116//! // Encrypt the envelope's subject with the content key
117//! let encrypted = envelope
118//! .encrypt_subject(&content_key)
119//! .unwrap()
120//! .add_recipient(&bob_keys.public_keys(), &content_key);
121//!
122//! // Bob can decrypt it
123//! let decrypted = encrypted
124//! .decrypt_subject_to_recipient(&bob_keys)
125//! .unwrap();
126//! # }
127//! ```
128//!
129//! # Combining with Signatures
130//!
131//! Recipient encryption works well with envelope signatures, allowing you to create
132//! authenticated encrypted messages:
133//!
134//! ```
135//! # #[cfg(all(feature = "recipient", feature = "signature"))]
136//! # {
137//! use bc_envelope::prelude::*;
138//! use bc_components::PublicKeysProvider;
139//!
140//! // Create keys for sender and recipient
141//! let alice_keys = bc_components::PrivateKeyBase::new();
142//! let bob_keys = bc_components::PrivateKeyBase::new();
143//!
144//! // Alice signs and encrypts a message to Bob
145//! let envelope = Envelope::new("From Alice to Bob")
146//! .add_signature(&alice_keys)
147//! .encrypt_subject_to_recipient(&bob_keys.public_keys())
148//! .unwrap();
149//!
150//! // Bob receives it, verifies the signature, and decrypts it
151//! let message = envelope
152//! .verify_signature_from(&alice_keys.public_keys())
153//! .unwrap()
154//! .decrypt_subject_to_recipient(&bob_keys)
155//! .unwrap()
156//! .extract_subject::<String>()
157//! .unwrap();
158//!
159//! assert_eq!(message, "From Alice to Bob");
160//! # }
161//! ```
162
163use crate::{ Envelope, Error };
164#[cfg(feature = "known_value")]
165use known_values;
166
167#[cfg(feature = "encrypt")]
168use bc_components::Decrypter;
169
170use anyhow::{ bail, Result };
171use bc_components::{ SealedMessage, SymmetricKey, Nonce, Encrypter };
172use dcbor::prelude::*;
173
174/// Support for public key encryption.
175impl Envelope {
176 /// Returns a new envelope with an added `hasRecipient: SealedMessage` assertion.
177 ///
178 /// This method adds a recipient to an already-encrypted envelope. It creates a `hasRecipient`
179 /// assertion containing a `SealedMessage` that holds the content key encrypted to the
180 /// recipient's public key.
181 ///
182 /// # Parameters
183 /// * `recipient` - The public keys of the recipient that will be able to decrypt the envelope
184 /// * `content_key` - The symmetric key that was used to encrypt the envelope's subject
185 ///
186 /// # Returns
187 /// A new envelope with the recipient assertion added
188 ///
189 /// # Example
190 ///
191 /// ```
192 /// # #[cfg(feature = "recipient")]
193 /// # {
194 /// use bc_envelope::prelude::*;
195 /// use bc_components::{SymmetricKey, PublicKeysProvider};
196 ///
197 /// let bob_keys = bc_components::PrivateKeyBase::new();
198 ///
199 /// // Create and encrypt the envelope
200 /// let content_key = SymmetricKey::new();
201 /// let envelope = Envelope::new("Secret message")
202 /// .encrypt_subject(&content_key)
203 /// .unwrap()
204 /// .add_recipient(&bob_keys.public_keys(), &content_key);
205 ///
206 /// // Format of the envelope shows the hasRecipient assertion
207 /// assert!(envelope.format().contains("hasRecipient"));
208 /// # }
209 /// ```
210 pub fn add_recipient(&self, recipient: &dyn Encrypter, content_key: &SymmetricKey) -> Self {
211 self.add_recipient_opt(recipient, content_key, None::<&Nonce>)
212 }
213
214 /// Version of `add_recipient` that accepts an optional test nonce for deterministic testing.
215 ///
216 /// This is an internal method primarily used for testing. In production code, use
217 /// `add_recipient` instead, which will generate a cryptographically secure random nonce.
218 #[doc(hidden)]
219 pub fn add_recipient_opt(
220 &self,
221 recipient: &dyn Encrypter,
222 content_key: &SymmetricKey,
223 test_nonce: Option<&Nonce>
224 ) -> Self {
225 let assertion = Self::make_has_recipient(recipient, content_key, test_nonce);
226 self.add_assertion_envelope(assertion).unwrap()
227 }
228
229 /// Returns all `SealedMessage`s from the envelope's `hasRecipient` assertions.
230 ///
231 /// This method extracts all the `SealedMessage` objects attached to the envelope
232 /// as `hasRecipient` assertions. Each `SealedMessage` contains the content key
233 /// encrypted to a particular recipient's public key.
234 ///
235 /// # Returns
236 /// A vector of `SealedMessage` objects, one for each recipient
237 ///
238 /// # Errors
239 /// Returns an error if any `hasRecipient` assertion does not have a `SealedMessage`
240 /// as its object, or if the object is obscured (elided or encrypted).
241 ///
242 /// # Example
243 ///
244 /// ```
245 /// # #[cfg(feature = "recipient")]
246 /// # {
247 /// use bc_envelope::prelude::*;
248 /// use bc_components::{SymmetricKey, PublicKeysProvider};
249 ///
250 /// let bob_keys = bc_components::PrivateKeyBase::new();
251 /// let carol_keys = bc_components::PrivateKeyBase::new();
252 ///
253 /// // Create envelope with multiple recipients
254 /// let content_key = SymmetricKey::new();
255 /// let envelope = Envelope::new("Secret message")
256 /// .encrypt_subject(&content_key)
257 /// .unwrap()
258 /// .add_recipient(&bob_keys.public_keys(), &content_key)
259 /// .add_recipient(&carol_keys.public_keys(), &content_key);
260 ///
261 /// // Get all recipient sealed messages
262 /// let sealed_messages = envelope.recipients().unwrap();
263 /// assert_eq!(sealed_messages.len(), 2);
264 /// # }
265 /// ```
266 pub fn recipients(&self) -> Result<Vec<SealedMessage>> {
267 self.assertions_with_predicate(known_values::HAS_RECIPIENT)
268 .into_iter()
269 .filter(|assertion| { !assertion.as_object().unwrap().is_obscured() })
270 .map(|assertion| { assertion.as_object().unwrap().extract_subject::<SealedMessage>() })
271 .collect()
272 }
273
274 /// Encrypts the envelope's subject and adds recipient assertions for multiple recipients.
275 ///
276 /// This is a convenience method that handles the complete process of:
277 /// 1. Generating a random symmetric key (the content key)
278 /// 2. Encrypting the envelope's subject with this key
279 /// 3. Encrypting the content key to each recipient's public key
280 /// 4. Adding a `hasRecipient` assertion for each recipient
281 ///
282 /// # Parameters
283 /// * `recipients` - An array of public keys, one for each potential recipient
284 ///
285 /// # Returns
286 /// A new envelope with encrypted subject and recipient assertions
287 ///
288 /// # Errors
289 /// Returns an error if the envelope's subject is already encrypted or cannot
290 /// be encrypted for any other reason.
291 ///
292 /// # Example
293 ///
294 /// ```
295 /// # #[cfg(feature = "encrypt")]
296 /// # {
297 /// use bc_envelope::prelude::*;
298 /// use bc_components::PublicKeysProvider;
299 ///
300 /// let bob_keys = bc_components::PrivateKeyBase::new();
301 /// let carol_keys = bc_components::PrivateKeyBase::new();
302 ///
303 /// // Create and encrypt the envelope to both Bob and Carol
304 /// let envelope = Envelope::new("Shared secret");
305 /// let encrypted = envelope.encrypt_subject_to_recipients(
306 /// &[&bob_keys.public_keys(), &carol_keys.public_keys()]
307 /// ).unwrap();
308 ///
309 /// // The envelope is now encrypted
310 /// assert_eq!(encrypted.format(), "ENCRYPTED [\n 'hasRecipient': SealedMessage\n 'hasRecipient': SealedMessage\n]");
311 /// # }
312 /// ```
313 #[cfg(feature = "encrypt")]
314 pub fn encrypt_subject_to_recipients(&self, recipients: &[&dyn Encrypter]) -> Result<Self> {
315 self.encrypt_subject_to_recipients_opt(recipients, None::<&Nonce>)
316 }
317
318 /// Version of `encrypt_subject_to_recipients` that accepts an optional test nonce.
319 ///
320 /// This is an internal method primarily used for testing. In production code, use
321 /// `encrypt_subject_to_recipients` instead, which will generate a cryptographically
322 /// secure random nonce for the content key encryption.
323 #[cfg(feature = "encrypt")]
324 #[doc(hidden)]
325 pub fn encrypt_subject_to_recipients_opt(
326 &self,
327 recipients: &[&dyn Encrypter],
328 test_nonce: Option<&Nonce>
329 ) -> Result<Self> {
330 let content_key = SymmetricKey::new();
331 let mut e = self.encrypt_subject(&content_key)?;
332 for recipient in recipients {
333 e = e.add_recipient_opt(*recipient, &content_key, test_nonce);
334 }
335 Ok(e)
336 }
337
338 /// Encrypts the envelope's subject and adds a recipient assertion for a single recipient.
339 ///
340 /// This is a convenience method that handles the complete process of:
341 /// 1. Generating a random symmetric key (the content key)
342 /// 2. Encrypting the envelope's subject with this key
343 /// 3. Encrypting the content key to the recipient's public key
344 /// 4. Adding a `hasRecipient` assertion for the recipient
345 ///
346 /// # Parameters
347 /// * `recipient` - The public keys of the recipient who will be able to decrypt the envelope
348 ///
349 /// # Returns
350 /// A new envelope with encrypted subject and recipient assertion
351 ///
352 /// # Errors
353 /// Returns an error if the envelope's subject is already encrypted or cannot
354 /// be encrypted for any other reason.
355 ///
356 /// # Example
357 ///
358 /// ```
359 /// # #[cfg(feature = "encrypt")]
360 /// # {
361 /// use bc_envelope::prelude::*;
362 /// use bc_components::PublicKeysProvider;
363 ///
364 /// let bob_keys = bc_components::PrivateKeyBase::new();
365 ///
366 /// // Create and encrypt the envelope to Bob
367 /// let envelope = Envelope::new("Secret message");
368 /// let encrypted = envelope.encrypt_subject_to_recipient(&bob_keys.public_keys()).unwrap();
369 ///
370 /// // The envelope is now encrypted with a recipient
371 /// assert!(encrypted.format().contains("hasRecipient"));
372 /// # }
373 /// ```
374 #[cfg(feature = "encrypt")]
375 pub fn encrypt_subject_to_recipient(&self, recipient: &dyn Encrypter) -> Result<Self> {
376 self.encrypt_subject_to_recipient_opt(recipient, None::<&Nonce>)
377 }
378
379 /// Version of `encrypt_subject_to_recipient` that accepts an optional test nonce.
380 ///
381 /// This is an internal method primarily used for testing. In production code, use
382 /// `encrypt_subject_to_recipient` instead, which will generate a cryptographically
383 /// secure random nonce for the content key encryption.
384 #[cfg(feature = "encrypt")]
385 #[doc(hidden)]
386 pub fn encrypt_subject_to_recipient_opt(
387 &self,
388 recipient: &dyn Encrypter,
389 test_nonce: Option<&Nonce>
390 ) -> Result<Self> {
391 self.encrypt_subject_to_recipients_opt(&[recipient], test_nonce)
392 }
393
394 /// Find and decrypt the first sealed message that can be decrypted with the given private key.
395 ///
396 /// This internal helper method tries to decrypt each sealed message with the provided
397 /// private key, returning the first successful decryption. It's used during the
398 /// recipient decryption process to find the content key that was encrypted for
399 /// this particular recipient.
400 #[cfg(feature = "encrypt")]
401 fn first_plaintext_in_sealed_messages(
402 sealed_messages: &[SealedMessage],
403 private_key: &dyn Decrypter
404 ) -> Result<Vec<u8>> {
405 for sealed_message in sealed_messages {
406 let a = sealed_message.decrypt(private_key).ok();
407 if let Some(plaintext) = a {
408 return Ok(plaintext);
409 }
410 }
411 bail!(Error::UnknownRecipient)
412 }
413
414 /// Decrypts an envelope's subject using the recipient's private key.
415 ///
416 /// This method:
417 /// 1. Finds and extracts all `SealedMessage` objects from `hasRecipient` assertions
418 /// 2. Tries to decrypt each one with the provided private key until successful
419 /// 3. Extracts the content key from the decrypted message
420 /// 4. Uses the content key to decrypt the envelope's subject
421 ///
422 /// # Parameters
423 /// * `recipient` - The private key of the recipient trying to decrypt the envelope
424 ///
425 /// # Returns
426 /// A new envelope with decrypted subject
427 ///
428 /// # Errors
429 /// Returns an error if:
430 /// - No `hasRecipient` assertions containing `SealedMessage` objects are found
431 /// - None of the sealed messages can be decrypted with the provided private key
432 /// - The envelope's subject cannot be decrypted with the extracted content key
433 ///
434 /// # Example
435 ///
436 /// ```
437 /// # #[cfg(feature = "encrypt")]
438 /// # {
439 /// use bc_envelope::prelude::*;
440 /// use bc_components::PublicKeysProvider;
441 ///
442 /// let bob_keys = bc_components::PrivateKeyBase::new();
443 ///
444 /// // Create and encrypt the envelope to Bob
445 /// let envelope = Envelope::new("Secret message")
446 /// .encrypt_subject_to_recipient(&bob_keys.public_keys())
447 /// .unwrap();
448 ///
449 /// // Bob decrypts it with his private key
450 /// let decrypted = envelope
451 /// .decrypt_subject_to_recipient(&bob_keys)
452 /// .unwrap();
453 ///
454 /// assert_eq!(decrypted.extract_subject::<String>().unwrap(), "Secret message");
455 /// # }
456 /// ```
457 #[cfg(feature = "encrypt")]
458 pub fn decrypt_subject_to_recipient(&self, recipient: &dyn Decrypter) -> Result<Self> {
459 let sealed_messages = self.clone().recipients()?;
460 let content_key_data = Self::first_plaintext_in_sealed_messages(
461 &sealed_messages,
462 recipient
463 )?;
464 let content_key = SymmetricKey::from_tagged_cbor_data(content_key_data)?;
465 self.decrypt_subject(&content_key)
466 }
467
468 /// Creates a `hasRecipient: SealedMessage` assertion envelope.
469 ///
470 /// This internal helper method creates an assertion envelope with:
471 /// - The predicate set to the known value `hasRecipient`
472 /// - The object set to a `SealedMessage` containing the content key encrypted to the recipient's public key
473 ///
474 /// # Parameters
475 /// * `recipient` - The public keys of the recipient
476 /// * `content_key` - The symmetric key that was used to encrypt the subject
477 /// * `test_nonce` - Optional nonce for deterministic testing
478 ///
479 /// # Returns
480 /// An assertion envelope containing the sealed message
481 fn make_has_recipient(
482 recipient: &dyn Encrypter,
483 content_key: &SymmetricKey,
484 test_nonce: Option<&Nonce>
485 ) -> Self {
486 let sealed_message = SealedMessage::new_opt(
487 content_key.to_cbor_data(),
488 recipient,
489 None::<Vec<u8>>,
490 test_nonce
491 );
492 Self::new_assertion(known_values::HAS_RECIPIENT, sealed_message)
493 }
494}
495
496/// Convenience methods for recipient-based encryption and decryption.
497///
498/// These methods provide simplified versions of the recipient encryption and decryption
499/// operations with a more intuitive API for common use cases.
500#[cfg(feature = "recipient")]
501impl Envelope {
502 /// Wraps and encrypts an envelope to a single recipient.
503 ///
504 /// This is a convenience method that:
505 /// 1. Wraps the envelope (preserving its assertions in the wrap)
506 /// 2. Encrypts the resulting envelope to the recipient
507 ///
508 /// This method is simpler than calling `wrap_envelope()` and then
509 /// `encrypt_subject_to_recipient()` separately, and it handles error unwrapping.
510 ///
511 /// # Parameters
512 /// * `recipient` - The public keys of the recipient who will be able to decrypt the envelope
513 ///
514 /// # Returns
515 /// A new envelope that wraps and encrypts the original envelope to the recipient
516 ///
517 /// # Example
518 ///
519 /// ```
520 /// # #[cfg(feature = "recipient")]
521 /// # {
522 /// use bc_envelope::prelude::*;
523 /// use bc_components::PublicKeysProvider;
524 ///
525 /// let bob_keys = bc_components::PrivateKeyBase::new();
526 ///
527 /// // Create an envelope with some assertions
528 /// let envelope = Envelope::new("Alice")
529 /// .add_assertion("knows", "Bob")
530 /// .add_assertion("age", 30);
531 ///
532 /// // Wrap and encrypt it to Bob in one step
533 /// let encrypted = envelope.encrypt_to_recipient(&bob_keys.public_keys());
534 ///
535 /// // The format shows it's encrypted but doesn't reveal the content
536 /// assert_eq!(encrypted.format(), "ENCRYPTED [\n 'hasRecipient': SealedMessage\n]");
537 /// # }
538 /// ```
539 pub fn encrypt_to_recipient(&self, recipient: &dyn Encrypter) -> Envelope {
540 self.wrap_envelope().encrypt_subject_to_recipient(recipient).unwrap()
541 }
542
543 /// Decrypts an envelope that was encrypted to a recipient and unwraps it.
544 ///
545 /// This is a convenience method that:
546 /// 1. Decrypts the envelope using the recipient's private key
547 /// 2. Unwraps the resulting envelope to reveal the original content
548 ///
549 /// This method is simpler than calling `decrypt_subject_to_recipient()` and then
550 /// `unwrap_envelope()` separately.
551 ///
552 /// # Parameters
553 /// * `recipient` - The private key of the recipient trying to decrypt the envelope
554 ///
555 /// # Returns
556 /// The original, unwrapped envelope
557 ///
558 /// # Errors
559 /// Returns an error if:
560 /// - The envelope cannot be decrypted with the recipient's private key
561 /// - The decrypted envelope is not a wrapped envelope
562 ///
563 /// # Example
564 ///
565 /// ```
566 /// # #[cfg(feature = "recipient")]
567 /// # {
568 /// use bc_envelope::prelude::*;
569 /// use bc_components::PublicKeysProvider;
570 ///
571 /// let bob_keys = bc_components::PrivateKeyBase::new();
572 ///
573 /// // Create an envelope with assertions and encrypt it to Bob
574 /// let original = Envelope::new("Alice")
575 /// .add_assertion("knows", "Bob")
576 /// .add_assertion("age", 30);
577 ///
578 /// let encrypted = original.encrypt_to_recipient(&bob_keys.public_keys());
579 ///
580 /// // Bob decrypts it with his private key and unwraps it
581 /// let decrypted = encrypted
582 /// .decrypt_to_recipient(&bob_keys)
583 /// .unwrap();
584 ///
585 /// // The decrypted envelope should match the original
586 /// assert!(decrypted.is_identical_to(&original));
587 /// # }
588 /// ```
589 pub fn decrypt_to_recipient(&self, recipient: &dyn Decrypter) -> Result<Envelope> {
590 self.decrypt_subject_to_recipient(recipient)?.unwrap_envelope()
591 }
592}