rust_bottle/bottle.rs
1use crate::ecdh::{ecdh_decrypt, ecdh_encrypt};
2use crate::errors::{BottleError, Result};
3use crate::signing::Sign;
4use rand::RngCore;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// A Bottle is a layered message container with encryption and signatures.
9///
10/// Bottles support multiple layers of encryption (each for a different recipient)
11/// and multiple signatures (from different signers). The encryption layers are
12/// applied sequentially, with the outermost layer being the last one added.
13///
14/// # Example
15///
16/// ```rust
17/// use rust_bottle::*;
18/// use rand::rngs::OsRng;
19///
20/// let message = b"Secret message";
21/// let mut bottle = Bottle::new(message.to_vec());
22///
23/// // Encrypt to multiple recipients (layered encryption)
24/// let rng = &mut OsRng;
25/// let bob_key = X25519Key::generate(rng);
26/// let charlie_key = X25519Key::generate(rng);
27///
28/// // First encryption (innermost)
29/// bottle.encrypt(rng, &bob_key.public_key_bytes()).unwrap();
30/// // Second encryption (outermost)
31/// bottle.encrypt(rng, &charlie_key.public_key_bytes()).unwrap();
32///
33/// // Sign the bottle
34/// let alice_key = Ed25519Key::generate(rng);
35/// let alice_pub = alice_key.public_key_bytes();
36/// bottle.sign(rng, &alice_key, &alice_pub).unwrap();
37///
38/// // Serialize for storage/transmission
39/// let serialized = bottle.to_bytes().unwrap();
40/// ```
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Bottle {
43 /// The message payload (may be encrypted if encryption layers exist)
44 message: Vec<u8>,
45 /// Encryption layers (outermost first, innermost last)
46 encryptions: Vec<EncryptionLayer>,
47 /// Signature layers (all signers)
48 signatures: Vec<SignatureLayer>,
49 /// Application-specific metadata (key-value pairs)
50 metadata: HashMap<String, String>,
51}
52
53/// An encryption layer in a bottle.
54///
55/// Each encryption layer represents one level of encryption, typically for
56/// a different recipient. Layers are applied sequentially, with the
57/// outermost layer being the last one added.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59struct EncryptionLayer {
60 /// Encrypted data (ciphertext)
61 ciphertext: Vec<u8>,
62 /// Public key fingerprint (SHA-256 hash of recipient's public key)
63 key_fingerprint: Vec<u8>,
64 /// Algorithm identifier (e.g., "ECDH-AES256-GCM")
65 algorithm: String,
66}
67
68/// A signature layer in a bottle.
69///
70/// Multiple signatures can be applied to a bottle, each from a different
71/// signer. All signatures are verified independently.
72#[derive(Debug, Clone, Serialize, Deserialize)]
73struct SignatureLayer {
74 /// Signature bytes
75 signature: Vec<u8>,
76 /// Public key fingerprint (SHA-256 hash of signer's public key)
77 key_fingerprint: Vec<u8>,
78 /// Algorithm identifier (e.g., "ECDSA-SHA256", "Ed25519")
79 algorithm: String,
80}
81
82/// Information about a bottle without decrypting it.
83///
84/// This structure provides metadata about a bottle's encryption and signature
85/// status without requiring decryption keys.
86///
87/// # Example
88///
89/// ```rust
90/// use rust_bottle::*;
91///
92/// let bottle = Bottle::new(b"Message".to_vec());
93/// let opener = Opener::new();
94/// let info = opener.open_info(&bottle).unwrap();
95///
96/// assert!(!info.is_encrypted);
97/// assert!(!info.is_signed);
98/// ```
99#[derive(Debug, Clone)]
100pub struct BottleInfo {
101 /// Whether the bottle has any encryption layers
102 pub is_encrypted: bool,
103 /// Whether the bottle has any signature layers
104 pub is_signed: bool,
105 /// Public key fingerprints of all signers
106 pub signers: Vec<Vec<u8>>,
107 /// Public key fingerprints of all recipients (if encrypted)
108 pub recipients: Vec<Vec<u8>>,
109}
110
111impl Bottle {
112 /// Create a new bottle with a message.
113 ///
114 /// The message is initially unencrypted and unsigned. Encryption and
115 /// signatures can be added using the `encrypt` and `sign` methods.
116 ///
117 /// # Arguments
118 ///
119 /// * `message` - The message payload to store in the bottle
120 ///
121 /// # Example
122 ///
123 /// ```rust
124 /// use rust_bottle::Bottle;
125 ///
126 /// let bottle = Bottle::new(b"Hello, world!".to_vec());
127 /// assert!(!bottle.is_encrypted());
128 /// assert!(!bottle.is_signed());
129 /// ```
130 pub fn new(message: Vec<u8>) -> Self {
131 Self {
132 message,
133 encryptions: Vec::new(),
134 signatures: Vec::new(),
135 metadata: HashMap::new(),
136 }
137 }
138
139 /// Get the message payload.
140 ///
141 /// If the bottle is encrypted, this returns the encrypted ciphertext
142 /// (outermost layer). Use `Opener::open` to decrypt.
143 ///
144 /// # Returns
145 ///
146 /// A reference to the message bytes (encrypted or plaintext)
147 pub fn message(&self) -> &[u8] {
148 &self.message
149 }
150
151 /// Check if the bottle has any encryption layers.
152 ///
153 /// # Returns
154 ///
155 /// `true` if the bottle has one or more encryption layers, `false` otherwise
156 pub fn is_encrypted(&self) -> bool {
157 !self.encryptions.is_empty()
158 }
159
160 /// Check if the bottle has any signature layers.
161 ///
162 /// # Returns
163 ///
164 /// `true` if the bottle has one or more signatures, `false` otherwise
165 pub fn is_signed(&self) -> bool {
166 !self.signatures.is_empty()
167 }
168
169 /// Get the number of encryption layers.
170 ///
171 /// Each call to `encrypt` adds a new encryption layer. Layers are
172 /// applied sequentially, with the last added layer being the outermost.
173 ///
174 /// # Returns
175 ///
176 /// The number of encryption layers (0 if unencrypted)
177 pub fn encryption_count(&self) -> usize {
178 self.encryptions.len()
179 }
180
181 /// Encrypt the bottle to a public key.
182 ///
183 /// This adds a new encryption layer. If the bottle is already encrypted,
184 /// the existing ciphertext is encrypted again (layered encryption).
185 /// Each layer can target a different recipient.
186 ///
187 /// # Arguments
188 ///
189 /// * `rng` - A cryptographically secure random number generator
190 /// * `public_key` - The recipient's public key (X25519 or P-256 format)
191 ///
192 /// # Returns
193 ///
194 /// * `Ok(())` if encryption succeeds
195 /// * `Err(BottleError::Encryption)` if encryption fails
196 /// * `Err(BottleError::InvalidKeyType)` if the key format is invalid
197 ///
198 /// # Example
199 ///
200 /// ```rust
201 /// use rust_bottle::*;
202 /// use rand::rngs::OsRng;
203 ///
204 /// let mut bottle = Bottle::new(b"Secret".to_vec());
205 /// let rng = &mut OsRng;
206 /// let key = X25519Key::generate(rng);
207 ///
208 /// bottle.encrypt(rng, &key.public_key_bytes()).unwrap();
209 /// assert!(bottle.is_encrypted());
210 /// ```
211 pub fn encrypt<R: RngCore + rand::CryptoRng>(
212 &mut self,
213 rng: &mut R,
214 public_key: &[u8],
215 ) -> Result<()> {
216 // Determine what to encrypt
217 let data_to_encrypt = if self.encryptions.is_empty() {
218 // First encryption: encrypt the message directly
219 self.message.clone()
220 } else {
221 // Additional encryption: encrypt the current message (which is already encrypted)
222 self.message.clone()
223 };
224
225 // Encrypt using ECDH
226 let ciphertext = ecdh_encrypt(rng, &data_to_encrypt, public_key)?;
227
228 // Create encryption layer
229 let fingerprint = crate::hash::sha256(public_key);
230 let layer = EncryptionLayer {
231 ciphertext: ciphertext.clone(),
232 key_fingerprint: fingerprint,
233 algorithm: "ECDH-AES256-GCM".to_string(),
234 };
235
236 // Replace message with the new ciphertext
237 self.message = ciphertext;
238
239 // Add the layer
240 self.encryptions.push(layer);
241 Ok(())
242 }
243
244 /// Sign the bottle with a private key.
245 ///
246 /// This adds a new signature layer. Multiple signers can sign the same
247 /// bottle by calling this method multiple times. The signature covers
248 /// the message and all encryption layers.
249 ///
250 /// # Arguments
251 ///
252 /// * `rng` - A random number generator (may be used for non-deterministic signing)
253 /// * `signer` - A signer implementing the `Sign` trait (e.g., `Ed25519Key`, `EcdsaP256Key`)
254 /// * `public_key` - The signer's public key (used for fingerprinting)
255 ///
256 /// # Returns
257 ///
258 /// * `Ok(())` if signing succeeds
259 /// * `Err(BottleError::VerifyFailed)` if signing fails
260 ///
261 /// # Example
262 ///
263 /// ```rust
264 /// use rust_bottle::*;
265 /// use rand::rngs::OsRng;
266 ///
267 /// let mut bottle = Bottle::new(b"Message".to_vec());
268 /// let rng = &mut OsRng;
269 /// let key = Ed25519Key::generate(rng);
270 /// let pub_key = key.public_key_bytes();
271 ///
272 /// bottle.sign(rng, &key, &pub_key).unwrap();
273 /// assert!(bottle.is_signed());
274 /// ```
275 pub fn sign<R: RngCore>(
276 &mut self,
277 rng: &mut R,
278 signer: &dyn Sign,
279 public_key: &[u8],
280 ) -> Result<()> {
281 // Create data to sign (message + all encryptions)
282 let data_to_sign = self.create_signing_data()?;
283
284 // Sign the data
285 let signature = signer.sign(rng, &data_to_sign)?;
286
287 // Create signature layer
288 // Use the public key to create the fingerprint
289 let fingerprint = crate::hash::sha256(public_key);
290 let layer = SignatureLayer {
291 signature,
292 key_fingerprint: fingerprint,
293 algorithm: "ECDSA-SHA256".to_string(), // Will be determined from signer type
294 };
295
296 self.signatures.push(layer);
297 Ok(())
298 }
299
300 /// Set metadata key-value pair.
301 ///
302 /// Metadata is application-specific data stored with the bottle.
303 /// It is not encrypted or signed, so it should not contain sensitive
304 /// information.
305 ///
306 /// # Arguments
307 ///
308 /// * `key` - Metadata key
309 /// * `value` - Metadata value
310 ///
311 /// # Example
312 ///
313 /// ```rust
314 /// use rust_bottle::Bottle;
315 ///
316 /// let mut bottle = Bottle::new(b"Message".to_vec());
317 /// bottle.set_metadata("sender", "alice@example.com");
318 /// bottle.set_metadata("timestamp", "2024-01-01T00:00:00Z");
319 /// ```
320 pub fn set_metadata(&mut self, key: &str, value: &str) {
321 self.metadata.insert(key.to_string(), value.to_string());
322 }
323
324 /// Get metadata value by key.
325 ///
326 /// # Arguments
327 ///
328 /// * `key` - Metadata key to look up
329 ///
330 /// # Returns
331 ///
332 /// * `Some(&str)` if the key exists
333 /// * `None` if the key is not found
334 ///
335 /// # Example
336 ///
337 /// ```rust
338 /// use rust_bottle::Bottle;
339 ///
340 /// let mut bottle = Bottle::new(b"Message".to_vec());
341 /// bottle.set_metadata("sender", "alice");
342 /// assert_eq!(bottle.metadata("sender"), Some("alice"));
343 /// ```
344 pub fn metadata(&self, key: &str) -> Option<&str> {
345 self.metadata.get(key).map(|s| s.as_str())
346 }
347
348 /// Create data to sign (message + encryption layers).
349 ///
350 /// The signature covers both the message and all encryption layers
351 /// to ensure integrity of the entire bottle structure.
352 ///
353 /// # Returns
354 ///
355 /// Concatenated bytes of message and all encryption ciphertexts
356 fn create_signing_data(&self) -> Result<Vec<u8>> {
357 let mut data = self.message.clone();
358 for enc in &self.encryptions {
359 data.extend_from_slice(&enc.ciphertext);
360 }
361 Ok(data)
362 }
363
364 /// Serialize bottle to bytes using bincode.
365 ///
366 /// The serialized format is binary and efficient. It includes all
367 /// encryption layers, signatures, and metadata.
368 ///
369 /// # Returns
370 ///
371 /// * `Ok(Vec<u8>)` - Serialized bottle bytes
372 /// * `Err(BottleError::Serialization)` - If serialization fails
373 ///
374 /// # Example
375 ///
376 /// ```rust
377 /// use rust_bottle::Bottle;
378 ///
379 /// let bottle = Bottle::new(b"Message".to_vec());
380 /// let bytes = bottle.to_bytes().unwrap();
381 /// let restored = Bottle::from_bytes(&bytes).unwrap();
382 /// ```
383 pub fn to_bytes(&self) -> Result<Vec<u8>> {
384 bincode::serialize(self)
385 .map_err(|e| BottleError::Serialization(format!("Failed to serialize bottle: {}", e)))
386 }
387
388 /// Deserialize bottle from bytes.
389 ///
390 /// # Arguments
391 ///
392 /// * `data` - Serialized bottle bytes (from `to_bytes`)
393 ///
394 /// # Returns
395 ///
396 /// * `Ok(Bottle)` - Deserialized bottle
397 /// * `Err(BottleError::Deserialization)` - If deserialization fails
398 ///
399 /// # Example
400 ///
401 /// ```rust
402 /// use rust_bottle::Bottle;
403 ///
404 /// let bottle = Bottle::new(b"Message".to_vec());
405 /// let bytes = bottle.to_bytes().unwrap();
406 /// let restored = Bottle::from_bytes(&bytes).unwrap();
407 /// assert_eq!(bottle.message(), restored.message());
408 /// ```
409 pub fn from_bytes(data: &[u8]) -> Result<Self> {
410 bincode::deserialize(data).map_err(|e| {
411 BottleError::Deserialization(format!("Failed to deserialize bottle: {}", e))
412 })
413 }
414}
415
416/// Opener for bottles.
417///
418/// The Opener provides methods to decrypt and inspect bottles. It can
419/// decrypt bottles with multiple encryption layers, working from the
420/// outermost layer inward.
421///
422/// # Example
423///
424/// ```rust
425/// use rust_bottle::*;
426/// use rand::rngs::OsRng;
427///
428/// let mut bottle = Bottle::new(b"Secret".to_vec());
429/// let rng = &mut OsRng;
430/// let key = X25519Key::generate(rng);
431/// bottle.encrypt(rng, &key.public_key_bytes()).unwrap();
432///
433/// let opener = Opener::new();
434/// let decrypted = opener.open(&bottle, Some(&key.private_key_bytes())).unwrap();
435/// ```
436pub struct Opener {
437 // Optional keychain for automatic key lookup
438 // keychain: Option<Keychain>,
439}
440
441impl Opener {
442 /// Create a new opener.
443 ///
444 /// # Returns
445 ///
446 /// A new `Opener` instance
447 pub fn new() -> Self {
448 Self {}
449 }
450
451 /// Open a bottle, decrypting if needed.
452 ///
453 /// This method decrypts all encryption layers sequentially, starting
454 /// from the outermost layer and working inward. Each layer requires
455 /// the appropriate private key.
456 ///
457 /// # Arguments
458 ///
459 /// * `bottle` - The bottle to open
460 /// * `private_key` - Optional private key for decryption. Required if the bottle is encrypted.
461 ///
462 /// # Returns
463 ///
464 /// * `Ok(Vec<u8>)` - The decrypted message
465 /// * `Err(BottleError::NoAppropriateKey)` - If encryption exists but no key provided
466 /// * `Err(BottleError::Decryption)` - If decryption fails
467 /// * `Err(BottleError::InvalidKeyType)` - If the key format is invalid
468 ///
469 /// # Note
470 ///
471 /// For layered encryption, the same key is used for all layers in the
472 /// current implementation. Future versions may support different keys
473 /// per layer.
474 ///
475 /// # Example
476 ///
477 /// ```rust
478 /// use rust_bottle::*;
479 /// use rand::rngs::OsRng;
480 ///
481 /// let message = b"Hello, world!";
482 /// let mut bottle = Bottle::new(message.to_vec());
483 ///
484 /// let rng = &mut OsRng;
485 /// let key = X25519Key::generate(rng);
486 /// bottle.encrypt(rng, &key.public_key_bytes()).unwrap();
487 ///
488 /// let opener = Opener::new();
489 /// let decrypted = opener.open(&bottle, Some(&key.private_key_bytes())).unwrap();
490 /// assert_eq!(decrypted, message);
491 /// ```
492 pub fn open(&self, bottle: &Bottle, private_key: Option<&[u8]>) -> Result<Vec<u8>> {
493 if bottle.encryptions.is_empty() {
494 // No encryption, return message directly
495 return Ok(bottle.message.clone());
496 }
497
498 let key = private_key.ok_or(BottleError::NoAppropriateKey)?;
499
500 // Decrypt layers from outermost to innermost
501 // The message contains the outermost ciphertext
502 let mut current_data = bottle.message.clone();
503
504 for _layer in bottle.encryptions.iter().rev() {
505 // Decrypt this layer
506 current_data = ecdh_decrypt(¤t_data, key)?;
507 }
508
509 // After decrypting all layers, we have the original message
510 Ok(current_data)
511 }
512
513 /// Get information about a bottle without decrypting it.
514 ///
515 /// This method provides metadata about encryption and signature status
516 /// without requiring decryption keys. Useful for inspecting bottles
517 /// before attempting to decrypt them.
518 ///
519 /// # Arguments
520 ///
521 /// * `bottle` - The bottle to inspect
522 ///
523 /// # Returns
524 ///
525 /// * `Ok(BottleInfo)` - Information about the bottle
526 ///
527 /// # Example
528 ///
529 /// ```rust
530 /// use rust_bottle::*;
531 /// use rand::rngs::OsRng;
532 ///
533 /// let mut bottle = Bottle::new(b"Message".to_vec());
534 /// let rng = &mut OsRng;
535 /// let key = Ed25519Key::generate(rng);
536 /// let pub_key = key.public_key_bytes();
537 /// bottle.sign(rng, &key, &pub_key).unwrap();
538 ///
539 /// let opener = Opener::new();
540 /// let info = opener.open_info(&bottle).unwrap();
541 /// assert!(info.is_signed);
542 /// assert!(info.is_signed_by(&pub_key));
543 /// ```
544 pub fn open_info(&self, bottle: &Bottle) -> Result<BottleInfo> {
545 Ok(BottleInfo {
546 is_encrypted: bottle.is_encrypted(),
547 is_signed: bottle.is_signed(),
548 signers: bottle
549 .signatures
550 .iter()
551 .map(|s| s.key_fingerprint.clone())
552 .collect(),
553 recipients: bottle
554 .encryptions
555 .iter()
556 .map(|e| e.key_fingerprint.clone())
557 .collect(),
558 })
559 }
560}
561
562impl Default for Opener {
563 fn default() -> Self {
564 Self::new()
565 }
566}
567
568impl BottleInfo {
569 /// Check if the bottle is signed by a specific public key.
570 ///
571 /// This method compares the public key's fingerprint against the list
572 /// of signer fingerprints in the bottle.
573 ///
574 /// # Arguments
575 ///
576 /// * `public_key` - The public key to check (any format)
577 ///
578 /// # Returns
579 ///
580 /// * `true` if the key's fingerprint matches a signer
581 /// * `false` otherwise
582 ///
583 /// # Example
584 ///
585 /// ```rust
586 /// use rust_bottle::*;
587 /// use rand::rngs::OsRng;
588 ///
589 /// let mut bottle = Bottle::new(b"Message".to_vec());
590 /// let rng = &mut OsRng;
591 /// let key = Ed25519Key::generate(rng);
592 /// let pub_key = key.public_key_bytes();
593 /// bottle.sign(rng, &key, &pub_key).unwrap();
594 ///
595 /// let opener = Opener::new();
596 /// let info = opener.open_info(&bottle).unwrap();
597 /// assert!(info.is_signed_by(&pub_key));
598 /// ```
599 pub fn is_signed_by(&self, public_key: &[u8]) -> bool {
600 let fingerprint = crate::hash::sha256(public_key);
601 self.signers.contains(&fingerprint)
602 }
603}