1use crate::error::{Result, SdkError};
7use amaters_core::{CipherBlob, Key};
8use std::path::Path;
9
10#[derive(Clone)]
15pub struct FheKeys {
16 #[cfg(feature = "fhe")]
17 client_key: tfhe::ClientKey,
18 #[cfg(feature = "fhe")]
19 server_key: tfhe::ServerKey,
20 #[cfg(not(feature = "fhe"))]
21 _placeholder: (),
22}
23
24impl FheKeys {
25 pub fn generate() -> Result<Self> {
30 #[cfg(feature = "fhe")]
31 {
32 let config = tfhe::ConfigBuilder::default().build();
33 let (client_key, server_key) = tfhe::generate_keys(config);
34 Ok(Self {
35 client_key,
36 server_key,
37 })
38 }
39 #[cfg(not(feature = "fhe"))]
40 {
41 Ok(Self { _placeholder: () })
42 }
43 }
44
45 #[cfg(feature = "fhe")]
47 pub fn client_key(&self) -> &tfhe::ClientKey {
48 &self.client_key
49 }
50
51 #[cfg(feature = "fhe")]
53 pub fn server_key(&self) -> &tfhe::ServerKey {
54 &self.server_key
55 }
56
57 #[cfg(feature = "fhe")]
62 pub fn set_as_global_server_key(&self) {
63 tfhe::set_server_key(self.server_key.clone());
64 }
65
66 pub fn load_from_file(path: impl AsRef<Path>) -> Result<Self> {
71 #[cfg(feature = "fhe")]
72 {
73 let bytes = std::fs::read(path.as_ref())
74 .map_err(|e| SdkError::Fhe(format!("failed to read key file: {}", e)))?;
75 #[cfg(feature = "serialization")]
76 {
77 let (client_key, server_key): (tfhe::ClientKey, tfhe::ServerKey) =
78 oxicode::serde::decode_serde(&bytes)
79 .map_err(|e| SdkError::Fhe(format!("failed to deserialize keys: {}", e)))?;
80 Ok(Self {
81 client_key,
82 server_key,
83 })
84 }
85 #[cfg(not(feature = "serialization"))]
86 {
87 let _ = bytes;
88 Err(SdkError::Fhe(
89 "serialization feature required for key file loading".to_string(),
90 ))
91 }
92 }
93 #[cfg(not(feature = "fhe"))]
94 {
95 let _ = path;
96 Ok(Self { _placeholder: () })
97 }
98 }
99
100 pub fn save_to_file(&self, path: impl AsRef<Path>) -> Result<()> {
105 #[cfg(feature = "fhe")]
106 {
107 #[cfg(feature = "serialization")]
108 {
109 let bytes = oxicode::serde::encode_serde(&(&self.client_key, &self.server_key))
110 .map_err(|e| SdkError::Fhe(format!("failed to serialize keys: {}", e)))?;
111 std::fs::write(path.as_ref(), &bytes)
112 .map_err(|e| SdkError::Fhe(format!("failed to write key file: {}", e)))?;
113 Ok(())
114 }
115 #[cfg(not(feature = "serialization"))]
116 {
117 let _ = path;
118 Err(SdkError::Fhe(
119 "serialization feature required for key file saving".to_string(),
120 ))
121 }
122 }
123 #[cfg(not(feature = "fhe"))]
124 {
125 let _ = path;
126 Ok(())
127 }
128 }
129
130 #[cfg(feature = "serialization")]
132 pub fn to_bytes(&self) -> Result<Vec<u8>> {
133 #[cfg(feature = "fhe")]
134 {
135 oxicode::serde::encode_serde(&(&self.client_key, &self.server_key)).map_err(|e| {
136 SdkError::Serialization(format!("failed to serialize FHE keys: {}", e))
137 })
138 }
139 #[cfg(not(feature = "fhe"))]
140 {
141 Ok(Vec::new())
142 }
143 }
144
145 #[cfg(feature = "serialization")]
147 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
148 #[cfg(feature = "fhe")]
149 {
150 let (client_key, server_key): (tfhe::ClientKey, tfhe::ServerKey) =
151 oxicode::serde::decode_serde(bytes).map_err(|e| {
152 SdkError::Serialization(format!("failed to deserialize FHE keys: {}", e))
153 })?;
154 Ok(Self {
155 client_key,
156 server_key,
157 })
158 }
159 #[cfg(not(feature = "fhe"))]
160 {
161 let _ = bytes;
162 Ok(Self { _placeholder: () })
163 }
164 }
165}
166
167pub struct FheEncryptor {
169 keys: FheKeys,
170}
171
172impl FheEncryptor {
173 pub fn new() -> Result<Self> {
175 Ok(Self {
176 keys: FheKeys::generate()?,
177 })
178 }
179
180 pub fn with_keys(keys: FheKeys) -> Self {
182 Self { keys }
183 }
184
185 pub fn keys(&self) -> &FheKeys {
187 &self.keys
188 }
189
190 pub fn encrypt(&self, plaintext: &[u8]) -> Result<CipherBlob> {
196 #[cfg(feature = "fhe")]
197 {
198 use tfhe::prelude::FheTryEncrypt;
199
200 let mut encrypted_parts: Vec<Vec<u8>> = Vec::with_capacity(plaintext.len());
202 for &byte in plaintext {
203 let encrypted: tfhe::FheUint8 =
204 tfhe::FheUint8::try_encrypt(byte, self.keys.client_key())
205 .map_err(|e| SdkError::Fhe(format!("failed to encrypt byte: {}", e)))?;
206 #[cfg(feature = "serialization")]
208 {
209 let serialized = oxicode::serde::encode_serde(&encrypted).map_err(|e| {
210 SdkError::Fhe(format!("failed to serialize encrypted byte: {}", e))
211 })?;
212 encrypted_parts.push(serialized);
213 }
214 #[cfg(not(feature = "serialization"))]
215 {
216 let _ = encrypted;
217 return Err(SdkError::Fhe(
218 "serialization feature required for FHE encryption".to_string(),
219 ));
220 }
221 }
222 let count = plaintext.len() as u64;
224 let total_size = 8 + encrypted_parts.iter().map(|p| 8 + p.len()).sum::<usize>();
225 let mut blob_data = Vec::with_capacity(total_size);
226 blob_data.extend_from_slice(&count.to_le_bytes());
227 for part in &encrypted_parts {
228 let len = part.len() as u64;
229 blob_data.extend_from_slice(&len.to_le_bytes());
230 blob_data.extend_from_slice(part);
231 }
232 Ok(CipherBlob::new(blob_data))
233 }
234 #[cfg(not(feature = "fhe"))]
235 {
236 Ok(CipherBlob::new(plaintext.to_vec()))
239 }
240 }
241
242 pub fn decrypt(&self, ciphertext: &CipherBlob) -> Result<Vec<u8>> {
247 #[cfg(feature = "fhe")]
248 {
249 use tfhe::prelude::FheDecrypt;
250
251 let data = ciphertext.to_vec();
252 if data.len() < 8 {
253 return Err(SdkError::Fhe("ciphertext too short".to_string()));
254 }
255 let count = u64::from_le_bytes(
256 data[..8]
257 .try_into()
258 .map_err(|_| SdkError::Fhe("invalid ciphertext header".to_string()))?,
259 ) as usize;
260
261 let mut offset = 8usize;
262 let mut plaintext = Vec::with_capacity(count);
263
264 for _ in 0..count {
265 if offset + 8 > data.len() {
266 return Err(SdkError::Fhe(
267 "ciphertext truncated: missing length field".to_string(),
268 ));
269 }
270 let part_len = u64::from_le_bytes(
271 data[offset..offset + 8]
272 .try_into()
273 .map_err(|_| SdkError::Fhe("invalid ciphertext part length".to_string()))?,
274 ) as usize;
275 offset += 8;
276
277 if offset + part_len > data.len() {
278 return Err(SdkError::Fhe(
279 "ciphertext truncated: insufficient data".to_string(),
280 ));
281 }
282
283 #[cfg(feature = "serialization")]
284 {
285 let encrypted: tfhe::FheUint8 = oxicode::serde::decode_serde(
286 &data[offset..offset + part_len],
287 )
288 .map_err(|e| {
289 SdkError::Fhe(format!("failed to deserialize encrypted byte: {}", e))
290 })?;
291 let byte: u8 = encrypted.decrypt(self.keys.client_key());
292 plaintext.push(byte);
293 }
294 #[cfg(not(feature = "serialization"))]
295 {
296 return Err(SdkError::Fhe(
297 "serialization feature required for FHE decryption".to_string(),
298 ));
299 }
300
301 offset += part_len;
302 }
303
304 Ok(plaintext)
305 }
306 #[cfg(not(feature = "fhe"))]
307 {
308 Ok(ciphertext.to_vec())
311 }
312 }
313
314 pub fn encrypt_key(&self, key: &Key) -> Result<CipherBlob> {
316 #[cfg(feature = "fhe")]
317 {
318 self.encrypt(key.as_bytes())
319 }
320 #[cfg(not(feature = "fhe"))]
321 {
322 Ok(CipherBlob::new(key.to_vec()))
324 }
325 }
326
327 pub fn encrypt_batch(&self, plaintexts: &[&[u8]]) -> Result<Vec<CipherBlob>> {
329 plaintexts.iter().map(|p| self.encrypt(p)).collect()
330 }
331}
332
333impl Default for FheEncryptor {
334 fn default() -> Self {
335 Self::new().expect("failed to create default encryptor")
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn test_fhe_keys_generate_no_fhe() {
345 #[cfg(not(feature = "fhe"))]
346 {
347 let keys = FheKeys::generate().expect("generate keys should succeed");
348 let dir = std::env::temp_dir();
350 let path = dir.join("test_fhe_keys_generate");
351 keys.save_to_file(&path)
352 .expect("save_to_file should succeed in stub mode");
353 }
354 }
355
356 #[test]
357 fn test_encrypt_decrypt_roundtrip_no_fhe() {
358 #[cfg(not(feature = "fhe"))]
359 {
360 let encryptor = FheEncryptor::new().expect("create encryptor");
361 let plaintext = b"hello world roundtrip test";
362 let ciphertext = encryptor.encrypt(plaintext).expect("encrypt");
363 let decrypted = encryptor.decrypt(&ciphertext).expect("decrypt");
364
365 assert_eq!(decrypted, plaintext);
367 }
368 }
369
370 #[test]
371 fn test_file_save_load_roundtrip_no_fhe() {
372 #[cfg(not(feature = "fhe"))]
373 {
374 let dir = std::env::temp_dir();
375 let path = dir.join("test_fhe_keys_save_load");
376
377 let keys = FheKeys::generate().expect("generate keys");
378 keys.save_to_file(&path).expect("save keys");
379
380 let _loaded = FheKeys::load_from_file(&path).expect("load keys");
381 }
383 }
384
385 #[cfg(feature = "serialization")]
386 #[test]
387 fn test_serialization_roundtrip_no_fhe() {
388 #[cfg(not(feature = "fhe"))]
389 {
390 let keys = FheKeys::generate().expect("generate keys");
391 let bytes = keys.to_bytes().expect("serialize keys");
392 let _restored = FheKeys::from_bytes(&bytes).expect("deserialize keys");
393 }
395 }
396
397 #[test]
398 fn test_batch_encrypt_no_fhe() {
399 #[cfg(not(feature = "fhe"))]
400 {
401 let encryptor = FheEncryptor::new().expect("create encryptor");
402 let data: Vec<&[u8]> = vec![b"one", b"two", b"three"];
403
404 let encrypted = encryptor.encrypt_batch(&data).expect("batch encrypt");
405 assert_eq!(encrypted.len(), 3);
406
407 for (i, ct) in encrypted.iter().enumerate() {
409 let decrypted = encryptor.decrypt(ct).expect("decrypt");
410 assert_eq!(decrypted, data[i]);
411 }
412 }
413 }
414
415 #[test]
416 fn test_encrypt_key_no_fhe() {
417 #[cfg(not(feature = "fhe"))]
418 {
419 let encryptor = FheEncryptor::new().expect("create encryptor");
420 let key = Key::new(b"test-key-data".to_vec());
421 let cipher = encryptor.encrypt_key(&key).expect("encrypt key");
422 let decrypted = encryptor.decrypt(&cipher).expect("decrypt");
423 assert_eq!(decrypted, key.as_bytes());
424 }
425 }
426
427 #[test]
428 fn test_encryptor_with_keys() {
429 #[cfg(not(feature = "fhe"))]
430 {
431 let keys = FheKeys::generate().expect("generate keys");
432 let encryptor = FheEncryptor::with_keys(keys);
433 let _keys_ref = encryptor.keys();
434 let plaintext = b"test with_keys";
435 let ciphertext = encryptor.encrypt(plaintext).expect("encrypt");
436 let decrypted = encryptor.decrypt(&ciphertext).expect("decrypt");
437 assert_eq!(decrypted, plaintext);
438 }
439 }
440
441 #[test]
442 fn test_empty_plaintext_no_fhe() {
443 #[cfg(not(feature = "fhe"))]
444 {
445 let encryptor = FheEncryptor::new().expect("create encryptor");
446 let plaintext = b"";
447 let ciphertext = encryptor.encrypt(plaintext).expect("encrypt empty");
448 let decrypted = encryptor.decrypt(&ciphertext).expect("decrypt empty");
449 assert_eq!(decrypted, plaintext);
450 }
451 }
452}