Skip to main content

otplus_core/cipher/
mod.rs

1pub mod root_key;
2pub use root_key::RootKey;
3pub use root_key::RootKeyMetadata;
4pub mod derived_key;
5pub use derived_key::DerivedKey;
6
7use chacha20poly1305::{aead::{Aead, OsRng}, AeadCore, ChaCha20Poly1305, KeyInit, Nonce, Key};
8use serde::{ser::SerializeStruct, Deserialize, Serialize};
9use zeroize::Zeroize;
10use crate::helpers::Helpers;
11
12/**
13 * Encryption is a struct that contains a dek and a nonce.
14 * The dek is a 32 byte key used to encrypt and decrypt data.
15 * The nonce is a 12 byte nonce used to encrypt and decrypt data.
16 */
17
18#[derive(Debug)]
19pub struct Cipher {
20    dek: Key,
21    nonce: Nonce,
22}
23
24impl Serialize for Cipher {
25    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26        where
27            S: serde::Serializer {
28        let mut state = serializer.serialize_struct("Cipher", 2)?;
29        state.serialize_field("dek", &Helpers::base64_encode(&self.dek.as_slice()))?;
30        state.serialize_field("nonce", &Helpers::base64_encode(&self.nonce.as_slice()))?;
31        state.end()
32    }
33}
34
35impl<'de> Deserialize<'de> for Cipher {
36    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37        where
38            D: serde::Deserializer<'de>,
39        {
40            #[derive(serde::Deserialize)]
41            struct CipherHelper {
42                dek: String,
43                nonce: String,
44            }
45
46            let helper = CipherHelper::deserialize(deserializer)?;
47            let nonce_array = Nonce::from_slice(&Helpers::base64_decode(&helper.nonce).unwrap()).clone();
48
49            if nonce_array.len() != 12 {
50                return Err(serde::de::Error::custom("Invalid nonce length"));
51            }
52
53            let dek_array = Key::from_slice(&Helpers::base64_decode(&helper.dek).unwrap()).clone();
54
55            Ok(Cipher {
56                dek: dek_array,
57                nonce: nonce_array,
58            })
59        }
60}
61
62/**
63 * The dek should be zeroized on drop (clear from memory)
64*/
65impl Drop for Cipher {
66    fn drop(&mut self) {
67        self.dek.as_mut_slice().zeroize();
68    }
69}
70
71/**
72 * If user use the default constructor, the dek and nonce should be generated randomly
73 */
74
75impl Default for Cipher {
76    fn default() -> Self {
77        let mut os_rng = OsRng::default();
78
79        Self {
80            dek: ChaCha20Poly1305::generate_key(&mut os_rng),
81            nonce: ChaCha20Poly1305::generate_nonce(&mut os_rng)
82        }
83    }
84}
85
86impl Cipher {
87    pub fn new(dek: Key, nonce: Nonce) -> Self {
88        Self { dek, nonce }
89    }
90
91    pub fn encrypt(&self, plaintext: &[u8]) -> Vec<u8> {
92        let cipher = ChaCha20Poly1305::new(&Key::from_slice(&self.dek));
93        cipher.encrypt(&self.nonce, plaintext).unwrap()
94    }
95
96    pub fn decrypt(&self, ciphertext: &[u8]) -> Vec<u8> {
97        let cipher = ChaCha20Poly1305::new(&Key::from_slice(&self.dek));
98        cipher.decrypt(&self.nonce, ciphertext).unwrap()
99    }
100}
101
102#[cfg(test)]
103mod cipher_tests {
104    use super::*;
105
106    #[test]
107    fn test_cipher_serialize_deserialize() {
108        let cipher = Cipher::default();
109        let serialized = serde_json::to_string(&cipher).unwrap();
110
111        println!("Serialized: {}", serialized);
112        let deserialized: Cipher = serde_json::from_str(&serialized).unwrap();
113        println!("Deserialized: {:?}", deserialized);
114
115        assert_eq!(cipher.dek.as_slice(), deserialized.dek.as_slice());
116        assert_eq!(cipher.nonce.as_slice(), deserialized.nonce.as_slice());
117        assert_eq!(cipher.nonce.len(), deserialized.nonce.len());
118    }
119
120    #[test]
121    fn test_cipher_encrypt_decrypt() {
122        let dek = ChaCha20Poly1305::generate_key(&mut OsRng::default()).as_slice().to_vec();
123        let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng::default());
124        let cipher = Cipher::new(Key::from_slice(&dek).clone(), nonce);
125        let plaintext = b"hello world";
126        let ciphertext = cipher.encrypt(plaintext);
127        let decrypted = cipher.decrypt(&ciphertext);
128
129        assert_eq!(plaintext, decrypted.as_slice());
130    }
131}