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