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
168use anyhow::{Result, bail};
169#[cfg(feature = "encrypt")]
170use bc_components::Decrypter;
171use bc_components::{Encrypter, Nonce, SealedMessage, SymmetricKey};
172use dcbor::prelude::*;
173#[cfg(feature = "known_value")]
174use known_values;
175
176use crate::{Envelope, Error};
177
178/// Support for public key encryption.
179impl Envelope {
180    /// Returns a new envelope with an added `hasRecipient: SealedMessage`
181    /// assertion.
182    ///
183    /// This method adds a recipient to an already-encrypted envelope. It
184    /// creates a `hasRecipient` assertion containing a `SealedMessage` that
185    /// holds the content key encrypted to the recipient's public key.
186    ///
187    /// # Parameters
188    /// * `recipient` - The public keys of the recipient that will be able to
189    ///   decrypt the envelope
190    /// * `content_key` - The symmetric key that was used to encrypt the
191    ///   envelope's subject
192    ///
193    /// # Returns
194    /// A new envelope with the recipient assertion added
195    ///
196    /// # Example
197    ///
198    /// ```
199    /// # #[cfg(feature = "recipient")]
200    /// # {
201    /// use bc_components::{PublicKeysProvider, SymmetricKey};
202    /// use bc_envelope::prelude::*;
203    ///
204    /// let bob_keys = bc_components::PrivateKeyBase::new();
205    ///
206    /// // Create and encrypt the envelope
207    /// let content_key = SymmetricKey::new();
208    /// let envelope = Envelope::new("Secret message")
209    ///     .encrypt_subject(&content_key)
210    ///     .unwrap()
211    ///     .add_recipient(&bob_keys.public_keys(), &content_key);
212    ///
213    /// // Format of the envelope shows the hasRecipient assertion
214    /// assert!(envelope.format().contains("hasRecipient"));
215    /// # }
216    /// ```
217    pub fn add_recipient(
218        &self,
219        recipient: &dyn Encrypter,
220        content_key: &SymmetricKey,
221    ) -> Self {
222        self.add_recipient_opt(recipient, content_key, None::<&Nonce>)
223    }
224
225    /// Version of `add_recipient` that accepts an optional test nonce for
226    /// deterministic testing.
227    ///
228    /// This is an internal method primarily used for testing. In production
229    /// code, use `add_recipient` instead, which will generate a
230    /// cryptographically secure random nonce.
231    #[doc(hidden)]
232    pub fn add_recipient_opt(
233        &self,
234        recipient: &dyn Encrypter,
235        content_key: &SymmetricKey,
236        test_nonce: Option<&Nonce>,
237    ) -> Self {
238        let assertion =
239            Self::make_has_recipient(recipient, content_key, test_nonce);
240        self.add_assertion_envelope(assertion).unwrap()
241    }
242
243    /// Returns all `SealedMessage`s from the envelope's `hasRecipient`
244    /// assertions.
245    ///
246    /// This method extracts all the `SealedMessage` objects attached to the
247    /// envelope as `hasRecipient` assertions. Each `SealedMessage` contains
248    /// the content key encrypted to a particular recipient's public key.
249    ///
250    /// # Returns
251    /// A vector of `SealedMessage` objects, one for each recipient
252    ///
253    /// # Errors
254    /// Returns an error if any `hasRecipient` assertion does not have a
255    /// `SealedMessage` as its object, or if the object is obscured (elided
256    /// or encrypted).
257    ///
258    /// # Example
259    ///
260    /// ```
261    /// # #[cfg(feature = "recipient")]
262    /// # {
263    /// use bc_components::{PublicKeysProvider, SymmetricKey};
264    /// use bc_envelope::prelude::*;
265    ///
266    /// let bob_keys = bc_components::PrivateKeyBase::new();
267    /// let carol_keys = bc_components::PrivateKeyBase::new();
268    ///
269    /// // Create envelope with multiple recipients
270    /// let content_key = SymmetricKey::new();
271    /// let envelope = Envelope::new("Secret message")
272    ///     .encrypt_subject(&content_key)
273    ///     .unwrap()
274    ///     .add_recipient(&bob_keys.public_keys(), &content_key)
275    ///     .add_recipient(&carol_keys.public_keys(), &content_key);
276    ///
277    /// // Get all recipient sealed messages
278    /// let sealed_messages = envelope.recipients().unwrap();
279    /// assert_eq!(sealed_messages.len(), 2);
280    /// # }
281    /// ```
282    pub fn recipients(&self) -> Result<Vec<SealedMessage>> {
283        self.assertions_with_predicate(known_values::HAS_RECIPIENT)
284            .into_iter()
285            .filter(|assertion| !assertion.as_object().unwrap().is_obscured())
286            .map(|assertion| {
287                assertion
288                    .as_object()
289                    .unwrap()
290                    .extract_subject::<SealedMessage>()
291            })
292            .collect()
293    }
294
295    /// Encrypts the envelope's subject and adds recipient assertions for
296    /// multiple recipients.
297    ///
298    /// This is a convenience method that handles the complete process of:
299    /// 1. Generating a random symmetric key (the content key)
300    /// 2. Encrypting the envelope's subject with this key
301    /// 3. Encrypting the content key to each recipient's public key
302    /// 4. Adding a `hasRecipient` assertion for each recipient
303    ///
304    /// # Parameters
305    /// * `recipients` - An array of public keys, one for each potential
306    ///   recipient
307    ///
308    /// # Returns
309    /// A new envelope with encrypted subject and recipient assertions
310    ///
311    /// # Errors
312    /// Returns an error if the envelope's subject is already encrypted or
313    /// cannot be encrypted for any other reason.
314    ///
315    /// # Example
316    ///
317    /// ```
318    /// # #[cfg(feature = "encrypt")]
319    /// # {
320    /// use bc_envelope::prelude::*;
321    /// use bc_components::PublicKeysProvider;
322    ///
323    /// let bob_keys = bc_components::PrivateKeyBase::new();
324    /// let carol_keys = bc_components::PrivateKeyBase::new();
325    ///
326    /// // Create and encrypt the envelope to both Bob and Carol
327    /// let envelope = Envelope::new("Shared secret");
328    /// let encrypted = envelope.encrypt_subject_to_recipients(
329    ///     &[&bob_keys.public_keys(), &carol_keys.public_keys()]
330    /// ).unwrap();
331    ///
332    /// // The envelope is now encrypted
333    /// assert_eq!(encrypted.format(), "ENCRYPTED [\n    'hasRecipient': SealedMessage\n    'hasRecipient': SealedMessage\n]");
334    /// # }
335    /// ```
336    #[cfg(feature = "encrypt")]
337    pub fn encrypt_subject_to_recipients(
338        &self,
339        recipients: &[&dyn Encrypter],
340    ) -> Result<Self> {
341        self.encrypt_subject_to_recipients_opt(recipients, None::<&Nonce>)
342    }
343
344    /// Version of `encrypt_subject_to_recipients` that accepts an optional test
345    /// nonce.
346    ///
347    /// This is an internal method primarily used for testing. In production
348    /// code, use `encrypt_subject_to_recipients` instead, which will
349    /// generate a cryptographically secure random nonce for the content key
350    /// encryption.
351    #[cfg(feature = "encrypt")]
352    #[doc(hidden)]
353    pub fn encrypt_subject_to_recipients_opt(
354        &self,
355        recipients: &[&dyn Encrypter],
356        test_nonce: Option<&Nonce>,
357    ) -> Result<Self> {
358        let content_key = SymmetricKey::new();
359        let mut e = self.encrypt_subject(&content_key)?;
360        for recipient in recipients {
361            e = e.add_recipient_opt(*recipient, &content_key, test_nonce);
362        }
363        Ok(e)
364    }
365
366    /// Encrypts the envelope's subject and adds a recipient assertion for a
367    /// single recipient.
368    ///
369    /// This is a convenience method that handles the complete process of:
370    /// 1. Generating a random symmetric key (the content key)
371    /// 2. Encrypting the envelope's subject with this key
372    /// 3. Encrypting the content key to the recipient's public key
373    /// 4. Adding a `hasRecipient` assertion for the recipient
374    ///
375    /// # Parameters
376    /// * `recipient` - The public keys of the recipient who will be able to
377    ///   decrypt the envelope
378    ///
379    /// # Returns
380    /// A new envelope with encrypted subject and recipient assertion
381    ///
382    /// # Errors
383    /// Returns an error if the envelope's subject is already encrypted or
384    /// cannot be encrypted for any other reason.
385    ///
386    /// # Example
387    ///
388    /// ```
389    /// # #[cfg(feature = "encrypt")]
390    /// # {
391    /// use bc_components::PublicKeysProvider;
392    /// use bc_envelope::prelude::*;
393    ///
394    /// let bob_keys = bc_components::PrivateKeyBase::new();
395    ///
396    /// // Create and encrypt the envelope to Bob
397    /// let envelope = Envelope::new("Secret message");
398    /// let encrypted = envelope
399    ///     .encrypt_subject_to_recipient(&bob_keys.public_keys())
400    ///     .unwrap();
401    ///
402    /// // The envelope is now encrypted with a recipient
403    /// assert!(encrypted.format().contains("hasRecipient"));
404    /// # }
405    /// ```
406    #[cfg(feature = "encrypt")]
407    pub fn encrypt_subject_to_recipient(
408        &self,
409        recipient: &dyn Encrypter,
410    ) -> Result<Self> {
411        self.encrypt_subject_to_recipient_opt(recipient, None::<&Nonce>)
412    }
413
414    /// Version of `encrypt_subject_to_recipient` that accepts an optional test
415    /// nonce.
416    ///
417    /// This is an internal method primarily used for testing. In production
418    /// code, use `encrypt_subject_to_recipient` instead, which will
419    /// generate a cryptographically secure random nonce for the content key
420    /// encryption.
421    #[cfg(feature = "encrypt")]
422    #[doc(hidden)]
423    pub fn encrypt_subject_to_recipient_opt(
424        &self,
425        recipient: &dyn Encrypter,
426        test_nonce: Option<&Nonce>,
427    ) -> Result<Self> {
428        self.encrypt_subject_to_recipients_opt(&[recipient], test_nonce)
429    }
430
431    /// Find and decrypt the first sealed message that can be decrypted with the
432    /// given private key.
433    ///
434    /// This internal helper method tries to decrypt each sealed message with
435    /// the provided private key, returning the first successful decryption.
436    /// It's used during the recipient decryption process to find the
437    /// content key that was encrypted for this particular recipient.
438    #[cfg(feature = "encrypt")]
439    fn first_plaintext_in_sealed_messages(
440        sealed_messages: &[SealedMessage],
441        private_key: &dyn Decrypter,
442    ) -> Result<Vec<u8>> {
443        for sealed_message in sealed_messages {
444            let a = sealed_message.decrypt(private_key).ok();
445            if let Some(plaintext) = a {
446                return Ok(plaintext);
447            }
448        }
449        bail!(Error::UnknownRecipient)
450    }
451
452    /// Decrypts an envelope's subject using the recipient's private key.
453    ///
454    /// This method:
455    /// 1. Finds and extracts all `SealedMessage` objects from `hasRecipient`
456    ///    assertions
457    /// 2. Tries to decrypt each one with the provided private key until
458    ///    successful
459    /// 3. Extracts the content key from the decrypted message
460    /// 4. Uses the content key to decrypt the envelope's subject
461    ///
462    /// # Parameters
463    /// * `recipient` - The private key of the recipient trying to decrypt the
464    ///   envelope
465    ///
466    /// # Returns
467    /// A new envelope with decrypted subject
468    ///
469    /// # Errors
470    /// Returns an error if:
471    /// - No `hasRecipient` assertions containing `SealedMessage` objects are
472    ///   found
473    /// - None of the sealed messages can be decrypted with the provided private
474    ///   key
475    /// - The envelope's subject cannot be decrypted with the extracted content
476    ///   key
477    ///
478    /// # Example
479    ///
480    /// ```
481    /// # #[cfg(feature = "encrypt")]
482    /// # {
483    /// use bc_components::PublicKeysProvider;
484    /// use bc_envelope::prelude::*;
485    ///
486    /// let bob_keys = bc_components::PrivateKeyBase::new();
487    ///
488    /// // Create and encrypt the envelope to Bob
489    /// let envelope = Envelope::new("Secret message")
490    ///     .encrypt_subject_to_recipient(&bob_keys.public_keys())
491    ///     .unwrap();
492    ///
493    /// // Bob decrypts it with his private key
494    /// let decrypted = envelope.decrypt_subject_to_recipient(&bob_keys).unwrap();
495    ///
496    /// assert_eq!(
497    ///     decrypted.extract_subject::<String>().unwrap(),
498    ///     "Secret message"
499    /// );
500    /// # }
501    /// ```
502    #[cfg(feature = "encrypt")]
503    pub fn decrypt_subject_to_recipient(
504        &self,
505        recipient: &dyn Decrypter,
506    ) -> Result<Self> {
507        let sealed_messages = self.clone().recipients()?;
508        let content_key_data = Self::first_plaintext_in_sealed_messages(
509            &sealed_messages,
510            recipient,
511        )?;
512        let content_key =
513            SymmetricKey::from_tagged_cbor_data(content_key_data)?;
514        self.decrypt_subject(&content_key)
515    }
516
517    /// Creates a `hasRecipient: SealedMessage` assertion envelope.
518    ///
519    /// This internal helper method creates an assertion envelope with:
520    /// - The predicate set to the known value `hasRecipient`
521    /// - The object set to a `SealedMessage` containing the content key
522    ///   encrypted to the recipient's public key
523    ///
524    /// # Parameters
525    /// * `recipient` - The public keys of the recipient
526    /// * `content_key` - The symmetric key that was used to encrypt the subject
527    /// * `test_nonce` - Optional nonce for deterministic testing
528    ///
529    /// # Returns
530    /// An assertion envelope containing the sealed message
531    fn make_has_recipient(
532        recipient: &dyn Encrypter,
533        content_key: &SymmetricKey,
534        test_nonce: Option<&Nonce>,
535    ) -> Self {
536        let sealed_message = SealedMessage::new_opt(
537            content_key.to_cbor_data(),
538            recipient,
539            None::<Vec<u8>>,
540            test_nonce,
541        );
542        Self::new_assertion(known_values::HAS_RECIPIENT, sealed_message)
543    }
544}
545
546/// Convenience methods for recipient-based encryption and decryption.
547///
548/// These methods provide simplified versions of the recipient encryption and
549/// decryption operations with a more intuitive API for common use cases.
550#[cfg(feature = "recipient")]
551impl Envelope {
552    /// Wraps and encrypts an envelope to a single recipient.
553    ///
554    /// This is a convenience method that:
555    /// 1. Wraps the envelope (preserving its assertions in the wrap)
556    /// 2. Encrypts the resulting envelope to the recipient
557    ///
558    /// This method is simpler than calling `wrap()` and then
559    /// `encrypt_subject_to_recipient()` separately, and it handles error
560    /// unwrapping.
561    ///
562    /// # Parameters
563    /// * `recipient` - The public keys of the recipient who will be able to
564    ///   decrypt the envelope
565    ///
566    /// # Returns
567    /// A new envelope that wraps and encrypts the original envelope to the
568    /// recipient
569    ///
570    /// # Example
571    ///
572    /// ```
573    /// # #[cfg(feature = "recipient")]
574    /// # {
575    /// use bc_components::PublicKeysProvider;
576    /// use bc_envelope::prelude::*;
577    ///
578    /// let bob_keys = bc_components::PrivateKeyBase::new();
579    ///
580    /// // Create an envelope with some assertions
581    /// let envelope = Envelope::new("Alice")
582    ///     .add_assertion("knows", "Bob")
583    ///     .add_assertion("age", 30);
584    ///
585    /// // Wrap and encrypt it to Bob in one step
586    /// let encrypted = envelope.encrypt_to_recipient(&bob_keys.public_keys());
587    ///
588    /// // The format shows it's encrypted but doesn't reveal the content
589    /// assert_eq!(
590    ///     encrypted.format(),
591    ///     "ENCRYPTED [\n    'hasRecipient': SealedMessage\n]"
592    /// );
593    /// # }
594    /// ```
595    pub fn encrypt_to_recipient(&self, recipient: &dyn Encrypter) -> Envelope {
596        self.wrap().encrypt_subject_to_recipient(recipient).unwrap()
597    }
598
599    /// Decrypts an envelope that was encrypted to a recipient and unwraps it.
600    ///
601    /// This is a convenience method that:
602    /// 1. Decrypts the envelope using the recipient's private key
603    /// 2. Unwraps the resulting envelope to reveal the original content
604    ///
605    /// This method is simpler than calling `decrypt_subject_to_recipient()` and
606    /// then `try_unwrap()` separately.
607    ///
608    /// # Parameters
609    /// * `recipient` - The private key of the recipient trying to decrypt the
610    ///   envelope
611    ///
612    /// # Returns
613    /// The original, unwrapped envelope
614    ///
615    /// # Errors
616    /// Returns an error if:
617    /// - The envelope cannot be decrypted with the recipient's private key
618    /// - The decrypted envelope is not a wrapped envelope
619    ///
620    /// # Example
621    ///
622    /// ```
623    /// # #[cfg(feature = "recipient")]
624    /// # {
625    /// use bc_components::PublicKeysProvider;
626    /// use bc_envelope::prelude::*;
627    ///
628    /// let bob_keys = bc_components::PrivateKeyBase::new();
629    ///
630    /// // Create an envelope with assertions and encrypt it to Bob
631    /// let original = Envelope::new("Alice")
632    ///     .add_assertion("knows", "Bob")
633    ///     .add_assertion("age", 30);
634    ///
635    /// let encrypted = original.encrypt_to_recipient(&bob_keys.public_keys());
636    ///
637    /// // Bob decrypts it with his private key and unwraps it
638    /// let decrypted = encrypted.decrypt_to_recipient(&bob_keys).unwrap();
639    ///
640    /// // The decrypted envelope should match the original
641    /// assert!(decrypted.is_identical_to(&original));
642    /// # }
643    /// ```
644    pub fn decrypt_to_recipient(
645        &self,
646        recipient: &dyn Decrypter,
647    ) -> Result<Envelope> {
648        self.decrypt_subject_to_recipient(recipient)?.try_unwrap()
649    }
650}