From a2c16eda72461fc92ed097686dcfa08a5391ae91 Mon Sep 17 00:00:00 2001 From: harald Date: Sat, 19 Sep 2026 09:15:56 +0200 Subject: [PATCH] =?UTF-8?q?test(crypto):=20M-05=20=E2=80=94=20verify=20sch?= =?UTF-8?q?ema=20equality=20between=20standard=20and=20hidden=20vault=20co?= =?UTF-8?q?ntainers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/carrier_model_a_test.rs | 152 ++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/tests/carrier_model_a_test.rs b/tests/carrier_model_a_test.rs index 9eab424..060cb09 100644 --- a/tests/carrier_model_a_test.rs +++ b/tests/carrier_model_a_test.rs @@ -774,3 +774,155 @@ async fn test_v01_carrier_fs_copy_delegation_and_functionality() { let _ = std::fs::remove_file(&path); } +#[test] +fn test_m05_deniability_schema_equality_standard_vs_hidden() { + let path_std = temp_db_path("schema_std"); + let path_hidden = temp_db_path("schema_hidden"); + + let kdf_params = KdfParams { + memory_cost: MIN_MEMORY_COST_KIB, + time_cost: MIN_TIME_COST, + parallelism: 1, + }; + + let salt_std = generate_salt(); + let kek_std = derive_kek("StandardPass123!", &salt_std, &kdf_params).unwrap(); + let dek_std = generate_dek(); + let (wrapped_std, nonce_std, tag_std) = wrap_slot0_payload(&kek_std, &dek_std, 0).unwrap(); + + let db_std = Database::open(&path_std).expect("Open std db"); + db_std + .init_schema( + &salt_std, + &kdf_params, + &wrapped_std, + &nonce_std, + &tag_std, + ) + .expect("Init std schema"); + db_std.checkpoint().unwrap(); + + let salt_0 = generate_salt(); + let kek_0 = derive_kek("DecoyPass123!", &salt_0, &kdf_params).unwrap(); + let dek_0 = generate_dek(); + + let salt_1 = generate_salt(); + let kek_1 = derive_kek("HiddenPass123!", &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_hidden = Database::open(&path_hidden).expect("Open hidden db"); + db_hidden + .init_schema_with_carrier( + &salt_0, + &kdf_params, + &wrapped_0, + &nonce_0, + &tag_0, + Some(( + "carrier.dat", + 1024 * 1024, + &salt_1, + &kdf_params, + &wrapped_1, + &nonce_1, + &tag_1, + &dek_0, + &dek_1, + )), + ) + .expect("Init carrier schema"); + db_hidden.checkpoint().unwrap(); + + // Vergleiche Schemas via rusqlite + let conn_std = rusqlite::Connection::open(&path_std).unwrap(); + let conn_hidden = rusqlite::Connection::open(&path_hidden).unwrap(); + + let get_schema_elements = |conn: &rusqlite::Connection| -> Vec<(String, String, Option)> { + let mut stmt = conn + .prepare( + "SELECT type, name, sql FROM sqlite_master + WHERE type IN ('table', 'index') AND name NOT LIKE 'sqlite_%' + ORDER BY type, name", + ) + .unwrap(); + let rows = stmt + .query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, Option>(2)?, + )) + }) + .unwrap(); + rows.map(|r| r.unwrap()).collect() + }; + + let schema_std = get_schema_elements(&conn_std); + let schema_hidden = get_schema_elements(&conn_hidden); + + assert_eq!( + schema_std, schema_hidden, + "M-05: SQLite-Schema (Tabellen, Indizes, DDL) muss zwischen Standard- und Hidden-Vault identisch sein" + ); + + // Spalten- und Typinformationen vergleichen + for table in &["meta", "nodes", "chunks"] { + let get_table_info = |conn: &rusqlite::Connection| -> Vec<(i64, String, String, i64, Option, i64)> { + let mut stmt = conn.prepare(&format!("PRAGMA table_info({});", table)).unwrap(); + let rows = stmt + .query_map([], |row| { + Ok(( + row.get::<_, i64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, i64>(3)?, + row.get::<_, Option>(4)?, + row.get::<_, i64>(5)?, + )) + }) + .unwrap(); + rows.map(|r| r.unwrap()).collect() + }; + + let cols_std = get_table_info(&conn_std); + let cols_hidden = get_table_info(&conn_hidden); + assert_eq!( + cols_std, cols_hidden, + "M-05: Spalten und Typen für Tabelle '{}' müssen identisch sein", + table + ); + } + + // Pragmas vergleichen (page_size, auto_vacuum, secure_delete) + let get_pragma = |conn: &rusqlite::Connection, pragma: &str| -> i64 { + conn.query_row(&format!("PRAGMA {};", pragma), [], |r| r.get(0)) + .unwrap() + }; + + assert_eq!( + get_pragma(&conn_std, "page_size"), + get_pragma(&conn_hidden, "page_size") + ); + assert_eq!( + get_pragma(&conn_std, "auto_vacuum"), + get_pragma(&conn_hidden, "auto_vacuum") + ); + assert_eq!( + get_pragma(&conn_std, "secure_delete"), + get_pragma(&conn_hidden, "secure_delete") + ); + + drop(conn_std); + drop(conn_hidden); + drop(db_std); + drop(db_hidden); + + let _ = std::fs::remove_file(&path_std); + let _ = std::fs::remove_file(&path_hidden); +} +