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