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}