1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
use serde::{de::DeserializeOwned, Deserialize, Serialize};

use crate::{
    crypto::{Ciphertext, Decrypter, DecrypterExt, Encrypter, EncrypterExt, SymKey, SymKeyKind},
    Result,
};

#[derive(Serialize, Deserialize)]
/// A struct which contains the cipher text for a message along with the ciphertext of the key that
/// was used to encrypt the message. This is useful for encrypting messages that are too long to be
/// encrypted using an asymmetric encryption scheme.
pub struct Envelope<T> {
    /// The ciphertext of the key used to encrypt the payload.
    key: Ciphertext<SymKey>,
    /// The ciphertext of the message encrypted using a symmetric cipher.
    payload: Ciphertext<T>,
}

impl<T: Serialize + DeserializeOwned> Envelope<T> {
    /// Creates a new [Envelope] containing `plaintext` whose symmetric key is encrypted using
    /// `encrypter`.
    pub fn new<E: Encrypter>(plaintext: &T, encrypter: E) -> Result<Envelope<T>> {
        let symkey = SymKey::generate(SymKeyKind::default())?;
        let payload = symkey.ser_encrypt(plaintext)?;
        let key = encrypter.ser_encrypt(&symkey)?;
        Ok(Envelope { key, payload })
    }

    /// Uses `decrypter` to decrypt the key in this [Envelope] and uses this key to decrypt and
    /// return the original message.
    pub fn open<D: Decrypter>(&self, decrypter: D) -> Result<T> {
        let symkey = decrypter.ser_decrypt(&self.key)?;
        symkey.ser_decrypt(&self.payload)
    }
}

#[cfg(test)]
mod tests {
    use crate::crypto::{envelope::Envelope, ConcreteCreds};

    #[test]
    /// Tests that a message can be extracted from an [Envelope] when the proper credentials are
    /// used.
    fn encrypt_decrypt() {
        let creds = ConcreteCreds::generate().unwrap();
        let expected = "We attack at the crack of noon.".to_string();

        let envelope = Envelope::new(&expected, &creds).unwrap();
        let actual = envelope.open(&creds).unwrap();

        assert_eq!(expected, actual);
    }
}