otplus_core/cipher/
mod.rs1pub 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#[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
62impl Drop for Cipher {
66 fn drop(&mut self) {
67 self.dek.as_mut_slice().zeroize();
68 }
69}
70
71impl 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}