chie_crypto/
certified_deletion.rs1use crate::encryption::{EncryptionNonce, decrypt as aead_decrypt, encrypt as aead_encrypt};
52use crate::hash::hash;
53use blake3::Hasher;
54use rand::RngCore;
55use serde::{Deserialize, Serialize};
56use std::collections::HashMap;
57use thiserror::Error;
58use zeroize::Zeroize;
59
60#[derive(Error, Debug)]
61pub enum CertifiedDeletionError {
62 #[error("Decryption failed: data already deleted or invalid")]
63 DecryptionFailed,
64 #[error("Invalid deletion certificate")]
65 InvalidCertificate,
66 #[error("Witness not found for ciphertext")]
67 WitnessNotFound,
68 #[error("Commitment mismatch")]
69 CommitmentMismatch,
70 #[error("Serialization error: {0}")]
71 Serialization(String),
72}
73
74pub type CertifiedDeletionResult<T> = Result<T, CertifiedDeletionError>;
75
76#[derive(Clone, Zeroize)]
78#[zeroize(drop)]
79struct Witness {
80 value: [u8; 32],
81}
82
83impl Witness {
84 fn new() -> Self {
85 use rand::RngCore;
86 let mut value = [0u8; 32];
87 rand::rngs::OsRng.fill_bytes(&mut value);
88 Self { value }
89 }
90
91 fn commitment(&self) -> Vec<u8> {
92 hash(&self.value).to_vec()
93 }
94}
95
96#[derive(Clone, Serialize, Deserialize)]
98pub struct EncryptedWithWitness {
99 ciphertext: Vec<u8>,
101 nonce: EncryptionNonce,
103 witness_commitment: Vec<u8>,
105 id: Vec<u8>,
107}
108
109impl EncryptedWithWitness {
110 pub fn commitment(&self) -> &[u8] {
112 &self.witness_commitment
113 }
114
115 pub fn id(&self) -> &[u8] {
117 &self.id
118 }
119
120 pub fn to_bytes(&self) -> CertifiedDeletionResult<Vec<u8>> {
122 crate::codec::encode(self).map_err(|e| CertifiedDeletionError::Serialization(e.to_string()))
123 }
124
125 pub fn from_bytes(bytes: &[u8]) -> CertifiedDeletionResult<Self> {
127 crate::codec::decode(bytes)
128 .map_err(|e| CertifiedDeletionError::Serialization(e.to_string()))
129 }
130}
131
132#[derive(Clone, Serialize, Deserialize)]
134pub struct DeletionCertificate {
135 witness_commitment: Vec<u8>,
137 timestamp: u64,
139 proof: Vec<u8>,
141}
142
143impl DeletionCertificate {
144 pub fn verify(&self, expected_commitment: &[u8]) -> CertifiedDeletionResult<()> {
146 if self.witness_commitment != expected_commitment {
147 return Err(CertifiedDeletionError::CommitmentMismatch);
148 }
149
150 Ok(())
153 }
154
155 pub fn timestamp(&self) -> u64 {
157 self.timestamp
158 }
159
160 pub fn to_bytes(&self) -> CertifiedDeletionResult<Vec<u8>> {
162 crate::codec::encode(self).map_err(|e| CertifiedDeletionError::Serialization(e.to_string()))
163 }
164
165 pub fn from_bytes(bytes: &[u8]) -> CertifiedDeletionResult<Self> {
167 crate::codec::decode(bytes)
168 .map_err(|e| CertifiedDeletionError::Serialization(e.to_string()))
169 }
170}
171
172pub struct CertifiedDeletion {
174 witnesses: HashMap<Vec<u8>, Witness>,
176}
177
178impl CertifiedDeletion {
179 pub fn new() -> Self {
181 Self {
182 witnesses: HashMap::new(),
183 }
184 }
185
186 pub fn encrypt(&mut self, plaintext: &[u8]) -> EncryptedWithWitness {
188 let witness = Witness::new();
190 let witness_commitment = witness.commitment();
191
192 let key = self.derive_key_from_witness(&witness);
194
195 let mut nonce = [0u8; 12];
197 rand::rngs::OsRng.fill_bytes(&mut nonce);
198
199 let ciphertext = aead_encrypt(plaintext, &key, &nonce)
201 .expect("Encryption should not fail with valid inputs");
202
203 let id = hash(&ciphertext).to_vec();
205
206 self.witnesses.insert(id.clone(), witness);
208
209 EncryptedWithWitness {
210 ciphertext,
211 nonce,
212 witness_commitment,
213 id,
214 }
215 }
216
217 pub fn decrypt(&self, encrypted: &EncryptedWithWitness) -> CertifiedDeletionResult<Vec<u8>> {
219 let witness = self
221 .witnesses
222 .get(&encrypted.id)
223 .ok_or(CertifiedDeletionError::WitnessNotFound)?;
224
225 if witness.commitment() != encrypted.witness_commitment {
227 return Err(CertifiedDeletionError::CommitmentMismatch);
228 }
229
230 let key = self.derive_key_from_witness(witness);
232
233 aead_decrypt(&encrypted.ciphertext, &key, &encrypted.nonce)
235 .map_err(|_| CertifiedDeletionError::DecryptionFailed)
236 }
237
238 pub fn certify_deletion(
240 &mut self,
241 encrypted: &EncryptedWithWitness,
242 ) -> CertifiedDeletionResult<DeletionCertificate> {
243 let witness = self
245 .witnesses
246 .remove(&encrypted.id)
247 .ok_or(CertifiedDeletionError::WitnessNotFound)?;
248
249 let witness_commitment = witness.commitment();
250
251 let timestamp = current_timestamp();
253 let proof = self.generate_deletion_proof(&witness, timestamp);
254
255 drop(witness);
257
258 Ok(DeletionCertificate {
259 witness_commitment,
260 timestamp,
261 proof,
262 })
263 }
264
265 pub fn can_decrypt(&self, encrypted: &EncryptedWithWitness) -> bool {
267 self.witnesses.contains_key(&encrypted.id)
268 }
269
270 fn derive_key_from_witness(&self, witness: &Witness) -> [u8; 32] {
272 let mut hasher = Hasher::new();
273 hasher.update(b"certified-deletion-key");
274 hasher.update(&witness.value);
275 let hash = hasher.finalize();
276 let mut key = [0u8; 32];
277 key.copy_from_slice(hash.as_bytes());
278 key
279 }
280
281 fn generate_deletion_proof(&self, witness: &Witness, timestamp: u64) -> Vec<u8> {
283 let mut hasher = Hasher::new();
284 hasher.update(b"deletion-proof");
285 hasher.update(&witness.value);
286 hasher.update(×tamp.to_le_bytes());
287 hasher.finalize().as_bytes().to_vec()
288 }
289}
290
291impl Default for CertifiedDeletion {
292 fn default() -> Self {
293 Self::new()
294 }
295}
296
297fn current_timestamp() -> u64 {
299 std::time::SystemTime::now()
300 .duration_since(std::time::UNIX_EPOCH)
301 .unwrap()
302 .as_secs()
303}
304
305pub struct BatchDeletion {
307 cd: CertifiedDeletion,
308}
309
310impl BatchDeletion {
311 pub fn new() -> Self {
313 Self {
314 cd: CertifiedDeletion::new(),
315 }
316 }
317
318 pub fn encrypt_batch(&mut self, items: &[Vec<u8>]) -> Vec<EncryptedWithWitness> {
320 items.iter().map(|item| self.cd.encrypt(item)).collect()
321 }
322
323 pub fn certify_batch_deletion(
325 &mut self,
326 encrypted: &[EncryptedWithWitness],
327 ) -> CertifiedDeletionResult<Vec<DeletionCertificate>> {
328 encrypted
329 .iter()
330 .map(|enc| self.cd.certify_deletion(enc))
331 .collect()
332 }
333
334 pub fn inner(&mut self) -> &mut CertifiedDeletion {
336 &mut self.cd
337 }
338}
339
340impl Default for BatchDeletion {
341 fn default() -> Self {
342 Self::new()
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_certified_deletion_basic() {
352 let mut cd = CertifiedDeletion::new();
353
354 let data = b"sensitive data";
355 let encrypted = cd.encrypt(data);
356
357 let decrypted = cd.decrypt(&encrypted).unwrap();
359 assert_eq!(decrypted, data);
360
361 let cert = cd.certify_deletion(&encrypted).unwrap();
363
364 assert!(cert.verify(encrypted.commitment()).is_ok());
366
367 assert!(cd.decrypt(&encrypted).is_err());
369 }
370
371 #[test]
372 fn test_cannot_decrypt_after_deletion() {
373 let mut cd = CertifiedDeletion::new();
374
375 let encrypted = cd.encrypt(b"secret");
376 cd.certify_deletion(&encrypted).unwrap();
377
378 let result = cd.decrypt(&encrypted);
379 assert!(result.is_err());
380 }
381
382 #[test]
383 fn test_multiple_encryptions() {
384 let mut cd = CertifiedDeletion::new();
385
386 let enc1 = cd.encrypt(b"data1");
387 let enc2 = cd.encrypt(b"data2");
388
389 assert_eq!(cd.decrypt(&enc1).unwrap(), b"data1");
390 assert_eq!(cd.decrypt(&enc2).unwrap(), b"data2");
391
392 cd.certify_deletion(&enc1).unwrap();
394
395 assert!(cd.decrypt(&enc1).is_err());
396 assert_eq!(cd.decrypt(&enc2).unwrap(), b"data2");
397 }
398
399 #[test]
400 fn test_can_decrypt_check() {
401 let mut cd = CertifiedDeletion::new();
402
403 let encrypted = cd.encrypt(b"data");
404 assert!(cd.can_decrypt(&encrypted));
405
406 cd.certify_deletion(&encrypted).unwrap();
407 assert!(!cd.can_decrypt(&encrypted));
408 }
409
410 #[test]
411 fn test_deletion_certificate_timestamp() {
412 let mut cd = CertifiedDeletion::new();
413
414 let encrypted = cd.encrypt(b"data");
415 let before = current_timestamp();
416 let cert = cd.certify_deletion(&encrypted).unwrap();
417 let after = current_timestamp();
418
419 assert!(cert.timestamp() >= before);
420 assert!(cert.timestamp() <= after);
421 }
422
423 #[test]
424 fn test_encrypted_serialization() {
425 let mut cd = CertifiedDeletion::new();
426
427 let encrypted = cd.encrypt(b"data");
428 let bytes = encrypted.to_bytes().unwrap();
429 let deserialized = EncryptedWithWitness::from_bytes(&bytes).unwrap();
430
431 assert_eq!(encrypted.id(), deserialized.id());
432 assert_eq!(encrypted.commitment(), deserialized.commitment());
433 }
434
435 #[test]
436 fn test_certificate_serialization() {
437 let mut cd = CertifiedDeletion::new();
438
439 let encrypted = cd.encrypt(b"data");
440 let cert = cd.certify_deletion(&encrypted).unwrap();
441
442 let bytes = cert.to_bytes().unwrap();
443 let deserialized = DeletionCertificate::from_bytes(&bytes).unwrap();
444
445 assert_eq!(cert.timestamp(), deserialized.timestamp());
446 assert!(deserialized.verify(encrypted.commitment()).is_ok());
447 }
448
449 #[test]
450 fn test_invalid_commitment_verification() {
451 let mut cd = CertifiedDeletion::new();
452
453 let encrypted = cd.encrypt(b"data");
454 let cert = cd.certify_deletion(&encrypted).unwrap();
455
456 let wrong_commitment = b"wrong_commitment";
457 assert!(cert.verify(wrong_commitment).is_err());
458 }
459
460 #[test]
461 fn test_batch_deletion() {
462 let mut batch = BatchDeletion::new();
463
464 let items = vec![b"item1".to_vec(), b"item2".to_vec(), b"item3".to_vec()];
465 let encrypted = batch.encrypt_batch(&items);
466
467 assert_eq!(encrypted.len(), 3);
468
469 let certs = batch.certify_batch_deletion(&encrypted).unwrap();
470 assert_eq!(certs.len(), 3);
471
472 for (enc, cert) in encrypted.iter().zip(certs.iter()) {
474 assert!(!batch.inner().can_decrypt(enc));
475 assert!(cert.verify(enc.commitment()).is_ok());
476 }
477 }
478
479 #[test]
480 fn test_cd_default() {
481 let mut cd = CertifiedDeletion::default();
482 let encrypted = cd.encrypt(b"test");
483 assert!(cd.can_decrypt(&encrypted));
484 }
485
486 #[test]
487 fn test_batch_default() {
488 let mut batch = BatchDeletion::default();
489 let encrypted = batch.encrypt_batch(&[b"test".to_vec()]);
490 assert_eq!(encrypted.len(), 1);
491 }
492
493 #[test]
494 fn test_double_deletion_fails() {
495 let mut cd = CertifiedDeletion::new();
496
497 let encrypted = cd.encrypt(b"data");
498 cd.certify_deletion(&encrypted).unwrap();
499
500 assert!(cd.certify_deletion(&encrypted).is_err());
502 }
503
504 #[test]
505 fn test_deletion_makes_decryption_impossible() {
506 let mut cd = CertifiedDeletion::new();
507
508 let data = b"sensitive information";
509 let encrypted = cd.encrypt(data);
510
511 assert_eq!(cd.decrypt(&encrypted).unwrap(), data);
513
514 cd.certify_deletion(&encrypted).unwrap();
516
517 match cd.decrypt(&encrypted) {
519 Err(CertifiedDeletionError::WitnessNotFound) => {
520 }
522 _ => panic!("Expected WitnessNotFound error"),
523 }
524 }
525}