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