114 lines
3.9 KiB
Rust
114 lines
3.9 KiB
Rust
use std::collections::HashSet;
|
|
use std::path::PathBuf;
|
|
use rand::rngs::OsRng;
|
|
use rand::RngCore;
|
|
|
|
use sanctum::crypto::{
|
|
derive_kek, generate_dek, generate_salt, wrap_slot0_payload,
|
|
wrap_slot1_payload, KdfParams, MIN_MEMORY_COST_KIB, MIN_TIME_COST,
|
|
};
|
|
use sanctum::storage::Database;
|
|
use sanctum::sync::{delete_orphans_in_vault, SyncStats};
|
|
|
|
fn temp_db_path(prefix: &str) -> PathBuf {
|
|
let mut path = std::env::temp_dir();
|
|
let id: u64 = OsRng.next_u64();
|
|
path.push(format!("sanctum_test_{}_{}.sanctum", prefix, id));
|
|
path
|
|
}
|
|
|
|
#[test]
|
|
fn test_carrier_protection_in_storage_and_sync() {
|
|
let path = temp_db_path("carrier_sec");
|
|
let carrier_name = "secret_carrier.iso";
|
|
let carrier_size_bytes = 2 * 1024 * 1024; // 2 MB
|
|
|
|
let pass_decoy = "DecoyPassword2026!";
|
|
let pass_hidden = "SuperSecretHiddenPassword2026!";
|
|
|
|
let kdf_params = KdfParams {
|
|
memory_cost: MIN_MEMORY_COST_KIB,
|
|
time_cost: MIN_TIME_COST,
|
|
parallelism: 1,
|
|
};
|
|
|
|
let salt_0 = generate_salt();
|
|
let kek_0 = derive_kek(pass_decoy, &salt_0, &kdf_params).unwrap();
|
|
let dek_0 = generate_dek();
|
|
|
|
let salt_1 = generate_salt();
|
|
let kek_1 = derive_kek(pass_hidden, &salt_1, &kdf_params).unwrap();
|
|
let dek_1 = generate_dek();
|
|
|
|
let carrier_node_id = 3i64;
|
|
let (wrapped_0, nonce_0, tag_0) =
|
|
wrap_slot0_payload(&kek_0, &dek_0, carrier_node_id).unwrap();
|
|
let (wrapped_1, nonce_1, tag_1) =
|
|
wrap_slot1_payload(&kek_1, &dek_1, &dek_0, carrier_node_id).unwrap();
|
|
|
|
let db = Database::open(&path).expect("Open database");
|
|
|
|
let created_cid = db
|
|
.init_schema_with_carrier(
|
|
&salt_0,
|
|
&kdf_params,
|
|
&wrapped_0,
|
|
&nonce_0,
|
|
&tag_0,
|
|
Some((
|
|
carrier_name,
|
|
carrier_size_bytes,
|
|
&salt_1,
|
|
&kdf_params,
|
|
&wrapped_1,
|
|
&nonce_1,
|
|
&tag_1,
|
|
&dek_0,
|
|
&dek_1,
|
|
)),
|
|
)
|
|
.expect("Init carrier schema");
|
|
|
|
db.checkpoint().unwrap();
|
|
assert_eq!(created_cid, Some(carrier_node_id));
|
|
|
|
// 1. Direkter Löschversuch der Trägerdatei muss fail-closed abgewehrt werden
|
|
let del_res = db.delete_node(carrier_node_id);
|
|
assert!(del_res.is_err(), "Löschen der Trägerdatei muss fehlschlagen");
|
|
assert!(del_res.unwrap_err().to_string().contains("Carrier-Schutz"));
|
|
|
|
// 2. Umbenennung der Trägerdatei muss fail-closed abgewehrt werden
|
|
let rename_res = db.rename_node_in_vault(carrier_node_id, 1, "renamed.iso", 0, &dek_0);
|
|
assert!(rename_res.is_err(), "Umbenennen der Trägerdatei muss fehlschlagen");
|
|
assert!(rename_res.unwrap_err().to_string().contains("Carrier-Schutz"));
|
|
|
|
// 3. Truncate der Trägerdatei muss fail-closed abgewehrt werden
|
|
let trunc_res = db.truncate_chunks_after(carrier_node_id, 0);
|
|
assert!(trunc_res.is_err(), "Truncate der Trägerdatei muss fehlschlagen");
|
|
assert!(trunc_res.unwrap_err().to_string().contains("Carrier-Schutz"));
|
|
|
|
// 4. delete_orphans_in_vault mit leerem lokalem Pfad-Set:
|
|
// Der Carrier darf unter keinen Umständen gelöscht werden!
|
|
let local_paths = HashSet::new();
|
|
let mut stats = SyncStats::default();
|
|
let orphan_res = delete_orphans_in_vault(
|
|
&db,
|
|
0, // Decoy Vault ID
|
|
&dek_0,
|
|
1, // Root-Knoten
|
|
"",
|
|
&local_paths,
|
|
false, // kein dry_run
|
|
true, // quiet
|
|
&mut stats,
|
|
);
|
|
assert!(orphan_res.is_ok(), "delete_orphans_in_vault muss ohne Fehler durchlaufen");
|
|
assert_eq!(stats.files_deleted, 0, "Carrier-Datei darf nicht gelöscht worden sein");
|
|
|
|
// Sicherstellen, dass Carrier nach wie vor in der DB existiert
|
|
let carrier_rec = db.get_node_by_id(carrier_node_id).unwrap();
|
|
assert!(carrier_rec.is_some(), "Carrier-Knoten muss nach Sync unversehrt vorhanden sein");
|
|
|
|
let _ = std::fs::remove_file(&path);
|
|
}
|