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}