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(&current_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}