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}