bc_envelope/
seal.rs

1#![cfg(all(feature = "signature", feature = "recipient"))]
2
3//! # Envelope Sealing and Unsealing
4//!
5//! This module provides convenience functions for combining signing and encryption
6//! operations in a single step, creating secure, authenticated envelopes.
7//!
8//! ## Sealing
9//!
10//! Sealing an envelope:
11//! 1. Signs the envelope with the sender's private key
12//! 2. Encrypts the signed envelope to the recipient's public key
13//!
14//! This creates a secure container where:
15//! - The recipient can verify who sent the envelope (authentication)
16//! - Only the intended recipient can decrypt the content (confidentiality)
17//! - The signature ensures the content hasn't been modified (integrity)
18//!
19//! ## Unsealing
20//!
21//! Unsealing performs these operations in reverse:
22//! 1. Decrypts the envelope using the recipient's private key
23//! 2. Verifies the signature using the sender's public key
24
25use crate::Envelope;
26use anyhow::Result;
27
28use bc_components::{Signer, Verifier, Encrypter, Decrypter, SigningOptions};
29
30impl Envelope {
31    /// Seals an envelope by signing it with the sender's key and then encrypting it to the recipient.
32    ///
33    /// This is a convenience method that combines signing and encryption in one step.
34    ///
35    /// # Arguments
36    ///
37    /// * `sender` - The private key used to sign the envelope
38    /// * `recipient` - The public key used to encrypt the envelope
39    ///
40    /// # Returns
41    ///
42    /// A new envelope that has been signed and encrypted
43    ///
44    /// # Example
45    ///
46    /// ```
47    /// # #[cfg(all(feature = "signature", feature = "recipient"))]
48    /// # {
49    /// use bc_envelope::prelude::*;
50    /// use bc_components::{SignatureScheme, EncapsulationScheme};
51    ///
52    /// // Create an envelope
53    /// let envelope = Envelope::new("Confidential message");
54    ///
55    /// // Generate keys for sender and recipient using specific schemes
56    /// let (sender_private, _) = SignatureScheme::Ed25519.keypair();
57    /// let (_, recipient_public) = EncapsulationScheme::X25519.keypair();
58    ///
59    /// // Seal the envelope
60    /// let sealed_envelope = envelope.seal(&sender_private, &recipient_public);
61    /// # }
62    /// ```
63    pub fn seal(
64        &self,
65        sender: &dyn Signer,
66        recipient: &dyn Encrypter,
67    ) -> Envelope {
68        self.sign(sender).encrypt_to_recipient(recipient)
69    }
70
71    /// Seals an envelope by signing it with the sender's key and then encrypting it to the recipient,
72    /// with optional signing options.
73    ///
74    /// This is a convenience method that combines signing and encryption in one step.
75    ///
76    /// # Arguments
77    ///
78    /// * `sender` - The private key used to sign the envelope
79    /// * `recipient` - The public key used to encrypt the envelope
80    /// * `options` - Optional signing options to control how the signature is created
81    ///
82    /// # Returns
83    ///
84    /// A new envelope that has been signed and encrypted
85    ///
86    /// # Example
87    ///
88    /// ```
89    /// # #[cfg(all(feature = "signature", feature = "recipient"))]
90    /// # fn main() -> Result<(), anyhow::Error> {
91    /// use bc_envelope::prelude::*;
92    /// use bc_components::{SignatureScheme, EncapsulationScheme, SigningOptions};
93    ///
94    /// // Create an envelope
95    /// let envelope = Envelope::new("Confidential message");
96    ///
97    /// // Generate keys for sender and recipient using specific schemes
98    /// let (sender_private, _) = SignatureScheme::Ed25519.keypair();
99    /// let (_, recipient_public) = EncapsulationScheme::X25519.keypair();
100    ///
101    /// // Create signing options for SSH
102    /// let options = SigningOptions::Ssh {
103    ///     namespace: "test".to_string(),
104    ///     hash_alg: ssh_key::HashAlg::Sha512,
105    /// };
106    ///
107    /// // Seal the envelope with options
108    /// let sealed_envelope = envelope.seal_opt(&sender_private, &recipient_public, Some(options));
109    /// # Ok(())
110    /// # }
111    /// ```
112    pub fn seal_opt(
113        &self,
114        sender: &dyn Signer,
115        recipient: &dyn Encrypter,
116        options: Option<SigningOptions>,
117    ) -> Envelope {
118        self.sign_opt(sender, options).encrypt_to_recipient(recipient)
119    }
120
121    /// Unseals an envelope by decrypting it with the recipient's private key and then verifying
122    /// the signature using the sender's public key.
123    ///
124    /// This is a convenience method that combines decryption and signature verification in one step.
125    ///
126    /// # Arguments
127    ///
128    /// * `sender` - The public key used to verify the signature
129    /// * `recipient` - The private key used to decrypt the envelope
130    ///
131    /// # Returns
132    ///
133    /// A Result containing the unsealed envelope if successful, or an error if decryption
134    /// or signature verification fails
135    ///
136    /// # Example
137    ///
138    /// ```
139    /// # #[cfg(all(feature = "signature", feature = "recipient"))]
140    /// # fn main() -> Result<(), anyhow::Error> {
141    /// use bc_envelope::prelude::*;
142    /// use bc_components::{SignatureScheme, EncapsulationScheme};
143    ///
144    /// // Create an envelope
145    /// let envelope = Envelope::new("Confidential message");
146    ///
147    /// // Generate keys for sender and recipient using specific schemes
148    /// let (sender_private, sender_public) = SignatureScheme::Ed25519.keypair();
149    /// let (recipient_private, recipient_public) = EncapsulationScheme::X25519.keypair();
150    ///
151    /// // Seal the envelope
152    /// let sealed_envelope = envelope.seal(&sender_private, &recipient_public);
153    ///
154    /// // Unseal the envelope using the recipient's private key
155    /// let unsealed_envelope = sealed_envelope.unseal(&sender_public, &recipient_private)?;
156    ///
157    /// // Verify we got back the original message
158    /// let message: String = unsealed_envelope.extract_subject()?;
159    /// assert_eq!(message, "Confidential message");
160    /// # Ok(())
161    /// # }
162    /// ```
163    pub fn unseal(
164        &self,
165        sender: &dyn Verifier,
166        recipient: &dyn Decrypter,
167    ) -> Result<Envelope> {
168        self.decrypt_to_recipient(recipient)?.verify(sender)
169    }
170}
171
172#[cfg(all(test, feature = "signature", feature = "recipient"))]
173mod tests {
174    use super::*;
175    use bc_components::{
176        EncapsulationScheme,
177        SignatureScheme,
178        SigningOptions
179    };
180    use anyhow::Result;
181
182    #[test]
183    fn test_seal_and_unseal() -> Result<()> {
184        // Create a test envelope
185
186        let message = "Top secret message";
187        let original_envelope = Envelope::new(message);
188
189        // Generate keys for sender and recipient using established schemes
190        let (sender_private, sender_public) = SignatureScheme::Ed25519.keypair();
191        let (recipient_private, recipient_public) = EncapsulationScheme::X25519.keypair();
192
193        // Step 1: Seal the envelope
194        let sealed_envelope = original_envelope.seal(&sender_private, &recipient_public);
195
196        // Verify the envelope is encrypted
197        assert!(sealed_envelope.is_subject_encrypted());
198
199        // Step 2: Unseal the envelope
200        let unsealed_envelope = sealed_envelope.unseal(&sender_public, &recipient_private)?;
201
202        // Verify we got back the original message
203        let extracted_message: String = unsealed_envelope.extract_subject()?;
204        assert_eq!(extracted_message, message);
205
206        Ok(())
207    }
208
209    #[test]
210    #[cfg(all(feature = "signature", feature = "recipient"))]
211    fn test_seal_opt_with_options() -> Result<()> {
212        // Create a test envelope
213        let message = "Confidential data";
214        let original_envelope = Envelope::new(message);
215
216        // Generate keys for sender and recipient
217        let (sender_private, sender_public) = SignatureScheme::Ed25519.keypair();
218        let (recipient_private, recipient_public) = EncapsulationScheme::X25519.keypair();
219
220        // Create signing options
221        let options = SigningOptions::Ssh {
222            namespace: "test".to_string(),
223            hash_alg: ssh_key::HashAlg::Sha512,
224        };
225
226        // Seal the envelope with options
227        let sealed_envelope = original_envelope.seal_opt(
228            &sender_private,
229            &recipient_public,
230            Some(options)
231        );
232
233        // Verify the envelope is encrypted
234        assert!(sealed_envelope.is_subject_encrypted());
235
236        // Unseal the envelope
237        let unsealed_envelope = sealed_envelope.unseal(&sender_public, &recipient_private)?;
238
239        // Verify we got back the original message
240        let extracted_message: String = unsealed_envelope.extract_subject()?;
241        assert_eq!(extracted_message, message);
242
243        Ok(())
244    }
245}