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}