grafos_securestore/
crypto.rs1use alloc::string::String;
8use alloc::vec::Vec;
9
10#[derive(Debug, PartialEq)]
12pub struct CryptoError(pub String);
13
14pub trait CryptoBackend {
20 fn encrypt(
22 &self,
23 key: &[u8],
24 nonce: &[u8],
25 aad: &[u8],
26 plaintext: &[u8],
27 ) -> Result<Vec<u8>, CryptoError>;
28
29 fn decrypt(
31 &self,
32 key: &[u8],
33 nonce: &[u8],
34 aad: &[u8],
35 ciphertext: &[u8],
36 ) -> Result<Vec<u8>, CryptoError>;
37
38 fn generate_key(&self) -> Vec<u8>;
40
41 fn generate_nonce(&self) -> Vec<u8>;
43}
44
45pub struct MockCryptoBackend {
50 key_counter: core::cell::Cell<u64>,
51 nonce_counter: core::cell::Cell<u64>,
52}
53
54impl MockCryptoBackend {
55 pub fn new() -> Self {
56 Self {
57 key_counter: core::cell::Cell::new(1),
58 nonce_counter: core::cell::Cell::new(1),
59 }
60 }
61}
62
63impl Default for MockCryptoBackend {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl CryptoBackend for MockCryptoBackend {
70 fn encrypt(
71 &self,
72 key: &[u8],
73 _nonce: &[u8],
74 _aad: &[u8],
75 plaintext: &[u8],
76 ) -> Result<Vec<u8>, CryptoError> {
77 if key.is_empty() {
78 return Err(CryptoError("empty key".into()));
79 }
80 let ciphertext: Vec<u8> = plaintext
81 .iter()
82 .enumerate()
83 .map(|(i, &b)| b ^ key[i % key.len()])
84 .collect();
85 Ok(ciphertext)
86 }
87
88 fn decrypt(
89 &self,
90 key: &[u8],
91 _nonce: &[u8],
92 _aad: &[u8],
93 ciphertext: &[u8],
94 ) -> Result<Vec<u8>, CryptoError> {
95 if key.is_empty() {
96 return Err(CryptoError("empty key".into()));
97 }
98 let plaintext: Vec<u8> = ciphertext
100 .iter()
101 .enumerate()
102 .map(|(i, &b)| b ^ key[i % key.len()])
103 .collect();
104 Ok(plaintext)
105 }
106
107 fn generate_key(&self) -> Vec<u8> {
108 let counter = self.key_counter.get();
109 self.key_counter.set(counter + 1);
110 let mut key = Vec::with_capacity(32);
111 for i in 0..32u8 {
112 key.push(i.wrapping_add(counter as u8));
113 }
114 key
115 }
116
117 fn generate_nonce(&self) -> Vec<u8> {
118 let counter = self.nonce_counter.get();
119 self.nonce_counter.set(counter + 1);
120 let mut nonce = Vec::with_capacity(12);
121 for i in 0..12u8 {
122 nonce.push(i.wrapping_add(counter as u8));
123 }
124 nonce
125 }
126}
127
128#[cfg(feature = "crypto-aes-gcm")]
133mod aes_gcm_backend {
134 use super::{CryptoBackend, CryptoError};
135 use aes_gcm::aead::{Aead, KeyInit, Payload};
136 use aes_gcm::{Aes256Gcm, Nonce};
137 use alloc::string::ToString;
138 use alloc::vec::Vec;
139
140 pub struct AesGcmBackend;
145
146 impl AesGcmBackend {
147 pub fn new() -> Self {
148 Self
149 }
150 }
151
152 impl Default for AesGcmBackend {
153 fn default() -> Self {
154 Self::new()
155 }
156 }
157
158 impl CryptoBackend for AesGcmBackend {
159 fn encrypt(
160 &self,
161 key: &[u8],
162 nonce: &[u8],
163 aad: &[u8],
164 plaintext: &[u8],
165 ) -> Result<Vec<u8>, CryptoError> {
166 if key.len() != 32 {
167 return Err(CryptoError("AES-256-GCM requires a 32-byte key".into()));
168 }
169 if nonce.len() != 12 {
170 return Err(CryptoError("AES-256-GCM requires a 12-byte nonce".into()));
171 }
172 let cipher = Aes256Gcm::new_from_slice(key).map_err(|e| CryptoError(e.to_string()))?;
173 let nonce = Nonce::from_slice(nonce);
174 let payload = Payload {
175 msg: plaintext,
176 aad,
177 };
178 cipher
179 .encrypt(nonce, payload)
180 .map_err(|e| CryptoError(e.to_string()))
181 }
182
183 fn decrypt(
184 &self,
185 key: &[u8],
186 nonce: &[u8],
187 aad: &[u8],
188 ciphertext: &[u8],
189 ) -> Result<Vec<u8>, CryptoError> {
190 if key.len() != 32 {
191 return Err(CryptoError("AES-256-GCM requires a 32-byte key".into()));
192 }
193 if nonce.len() != 12 {
194 return Err(CryptoError("AES-256-GCM requires a 12-byte nonce".into()));
195 }
196 let cipher = Aes256Gcm::new_from_slice(key).map_err(|e| CryptoError(e.to_string()))?;
197 let nonce = Nonce::from_slice(nonce);
198 let payload = Payload {
199 msg: ciphertext,
200 aad,
201 };
202 cipher
203 .decrypt(nonce, payload)
204 .map_err(|e| CryptoError(e.to_string()))
205 }
206
207 fn generate_key(&self) -> Vec<u8> {
208 let mut key = alloc::vec![0u8; 32];
209 getrandom::getrandom(&mut key).expect("getrandom failed for key generation");
210 key
211 }
212
213 fn generate_nonce(&self) -> Vec<u8> {
214 let mut nonce = alloc::vec![0u8; 12];
215 getrandom::getrandom(&mut nonce).expect("getrandom failed for nonce generation");
216 nonce
217 }
218 }
219}
220
221#[cfg(feature = "crypto-aes-gcm")]
222pub use aes_gcm_backend::AesGcmBackend;
223
224#[cfg(feature = "crypto-chacha20poly1305")]
229mod chacha_backend {
230 use super::{CryptoBackend, CryptoError};
231 use alloc::string::ToString;
232 use alloc::vec::Vec;
233 use chacha20poly1305::aead::{Aead, KeyInit, Payload};
234 use chacha20poly1305::{ChaCha20Poly1305, Nonce};
235
236 pub struct ChaChaBackend;
241
242 impl ChaChaBackend {
243 pub fn new() -> Self {
244 Self
245 }
246 }
247
248 impl Default for ChaChaBackend {
249 fn default() -> Self {
250 Self::new()
251 }
252 }
253
254 impl CryptoBackend for ChaChaBackend {
255 fn encrypt(
256 &self,
257 key: &[u8],
258 nonce: &[u8],
259 aad: &[u8],
260 plaintext: &[u8],
261 ) -> Result<Vec<u8>, CryptoError> {
262 if key.len() != 32 {
263 return Err(CryptoError(
264 "ChaCha20-Poly1305 requires a 32-byte key".into(),
265 ));
266 }
267 if nonce.len() != 12 {
268 return Err(CryptoError(
269 "ChaCha20-Poly1305 requires a 12-byte nonce".into(),
270 ));
271 }
272 let cipher =
273 ChaCha20Poly1305::new_from_slice(key).map_err(|e| CryptoError(e.to_string()))?;
274 let nonce = Nonce::from_slice(nonce);
275 let payload = Payload {
276 msg: plaintext,
277 aad,
278 };
279 cipher
280 .encrypt(nonce, payload)
281 .map_err(|e| CryptoError(e.to_string()))
282 }
283
284 fn decrypt(
285 &self,
286 key: &[u8],
287 nonce: &[u8],
288 aad: &[u8],
289 ciphertext: &[u8],
290 ) -> Result<Vec<u8>, CryptoError> {
291 if key.len() != 32 {
292 return Err(CryptoError(
293 "ChaCha20-Poly1305 requires a 32-byte key".into(),
294 ));
295 }
296 if nonce.len() != 12 {
297 return Err(CryptoError(
298 "ChaCha20-Poly1305 requires a 12-byte nonce".into(),
299 ));
300 }
301 let cipher =
302 ChaCha20Poly1305::new_from_slice(key).map_err(|e| CryptoError(e.to_string()))?;
303 let nonce = Nonce::from_slice(nonce);
304 let payload = Payload {
305 msg: ciphertext,
306 aad,
307 };
308 cipher
309 .decrypt(nonce, payload)
310 .map_err(|e| CryptoError(e.to_string()))
311 }
312
313 fn generate_key(&self) -> Vec<u8> {
314 let mut key = alloc::vec![0u8; 32];
315 getrandom::getrandom(&mut key).expect("getrandom failed for key generation");
316 key
317 }
318
319 fn generate_nonce(&self) -> Vec<u8> {
320 let mut nonce = alloc::vec![0u8; 12];
321 getrandom::getrandom(&mut nonce).expect("getrandom failed for nonce generation");
322 nonce
323 }
324 }
325}
326
327#[cfg(feature = "crypto-chacha20poly1305")]
328pub use chacha_backend::ChaChaBackend;