feat(carrier): implement carrier format V2 paged manifest and secondary index

This commit is contained in:
2026-09-20 00:15:11 +02:00
parent 02f731f57d
commit 167e4b65fc
6 changed files with 1241 additions and 137 deletions
+66 -12
View File
@@ -713,8 +713,8 @@ impl Database {
)?;
let c_id = conn.last_insert_rowid();
// Berechne Blockanzahl (min. 2 Blöcke: Block 0 für Manifest, Block 1+ für Nutzdaten)
let total_blocks = c_size.div_ceil(CHUNK_SIZE as u64).max(2) as u32;
// Berechne Blockanzahl (min. 3 Blöcke: Block 0/1 für Superblock, Block 2 für Inode-Seite)
let total_blocks = c_size.div_ceil(CHUNK_SIZE as u64).max(3) as u32;
// Slot 1 (Hidden Vault) einfügen
let params_json_1 = serde_json::to_string(h_params)?;
@@ -732,13 +732,67 @@ impl Database {
],
)?;
// Initialisiere CarrierManifest für redundante Blöcke 0 und 1 (C-02)
// Initialisiere Carrier-Format V2 (Superblock auf Block 0/1, Inode-Seite auf Block 2)
let manifest = crate::carrier::CarrierManifest::new(total_blocks);
let manifest_bytes = serde_json::to_vec(&manifest)?;
// 1. Block 0 schreiben (Primärkopie, Gen 0)
// 1. Block 2 schreiben: Erste Inode-Tabellenseite mit Wurzelverzeichnis '/'
let root_page = crate::carrier::CarrierInodePage {
magic: *crate::carrier::CARRIER_PAGE_MAGIC,
entries: manifest.inodes.values().cloned().collect(),
};
let root_page_bytes = serde_json::to_vec(&root_page)?;
let (inner_ct_2, inner_nonce_2, inner_tag_2) =
crate::crypto::encrypt_chunk(dek_1, c_id, 2, &root_page_bytes, FORMAT_VERSION, 0)?;
let inner_ct_len_2 = inner_ct_2.len() as u32;
let mut outer_plaintext_2 = vec![0u8; CHUNK_SIZE];
OsRng.fill_bytes(&mut outer_plaintext_2);
outer_plaintext_2[0..12].copy_from_slice(&inner_nonce_2);
outer_plaintext_2[12..28].copy_from_slice(&inner_tag_2);
outer_plaintext_2[28..32].copy_from_slice(&inner_ct_len_2.to_le_bytes());
let ct_end_2 = 32 + inner_ct_2.len();
if ct_end_2 > CHUNK_SIZE {
bail!("Inode-Page-Payload zu groß für Block 2");
}
outer_plaintext_2[32..ct_end_2].copy_from_slice(&inner_ct_2);
let (outer_ct_2, outer_nonce_2, outer_tag_2) = crate::crypto::encrypt_chunk(
dek_0,
c_id,
2,
&outer_plaintext_2,
FORMAT_VERSION,
0,
)?;
conn.execute(
"INSERT INTO chunks (node_id, chunk_index, generation, nonce, tag, ciphertext)
VALUES (?1, 2, 0, ?2, ?3, ?4)",
params![
c_id,
outer_nonce_2.as_slice(),
outer_tag_2.as_slice(),
outer_ct_2
],
)?;
// 2. Superblock (Block 0 & 1, redundante Zweitkopie C-02, Format V2)
let superblock = crate::carrier::CarrierSuperblock {
magic: *crate::carrier::CARRIER_MAGIC,
version: crate::carrier::CARRIER_VERSION_V2,
manifest_generation: 0,
total_blocks,
free_blocks: manifest.free_blocks.clone(),
next_inode_id: manifest.next_inode_id,
page_block_indices: vec![2],
};
let sb_bytes = serde_json::to_vec(&superblock)?;
// Block 0 schreiben (Primärkopie)
let (inner_ct_0, inner_nonce_0, inner_tag_0) =
crate::crypto::encrypt_chunk(dek_1, c_id, 0, &manifest_bytes, FORMAT_VERSION, 0)?;
crate::crypto::encrypt_chunk(dek_1, c_id, 0, &sb_bytes, FORMAT_VERSION, 0)?;
let inner_ct_len_0 = inner_ct_0.len() as u32;
let mut outer_plaintext_0 = vec![0u8; CHUNK_SIZE];
@@ -749,7 +803,7 @@ impl Database {
outer_plaintext_0[28..32].copy_from_slice(&inner_ct_len_0.to_le_bytes());
let ct_end_0 = 32 + inner_ct_0.len();
if ct_end_0 > CHUNK_SIZE {
bail!("Manifest-Payload zu groß für Block 0");
bail!("Superblock-Payload zu groß für Block 0");
}
outer_plaintext_0[32..ct_end_0].copy_from_slice(&inner_ct_0);
@@ -773,9 +827,9 @@ impl Database {
],
)?;
// 2. Block 1 schreiben (Redundante Zweitkopie, Gen 0, C-02)
// Block 1 schreiben (Redundante Zweitkopie, C-02)
let (inner_ct_1, inner_nonce_1, inner_tag_1) =
crate::crypto::encrypt_chunk(dek_1, c_id, 1, &manifest_bytes, FORMAT_VERSION, 0)?;
crate::crypto::encrypt_chunk(dek_1, c_id, 1, &sb_bytes, FORMAT_VERSION, 0)?;
let inner_ct_len_1 = inner_ct_1.len() as u32;
let mut outer_plaintext_1 = vec![0u8; CHUNK_SIZE];
@@ -786,7 +840,7 @@ impl Database {
outer_plaintext_1[28..32].copy_from_slice(&inner_ct_len_1.to_le_bytes());
let ct_end_1 = 32 + inner_ct_1.len();
if ct_end_1 > CHUNK_SIZE {
bail!("Manifest-Payload zu groß für Block 1");
bail!("Superblock-Payload zu groß für Block 1");
}
outer_plaintext_1[32..ct_end_1].copy_from_slice(&inner_ct_1);
@@ -810,7 +864,7 @@ impl Database {
],
)?;
// Blöcke 2..total_blocks-1 mit DEK_0 vorallokieren (C-02)
// 3. Blöcke 3..total_blocks-1 mit DEK_0 vorallokieren
let mut chunk_stmt = conn.prepare(
"INSERT INTO chunks (node_id, chunk_index, generation, nonce, tag, ciphertext)
VALUES (?1, ?2, 0, ?3, ?4, ?5)",
@@ -820,7 +874,7 @@ impl Database {
OsRng.fill_bytes(&mut dummy_noise);
conn.execute_batch("BEGIN TRANSACTION;")?;
for b in 2..total_blocks {
for b in 3..total_blocks {
let (ct, nonce, tag) =
crate::crypto::encrypt_chunk(dek_0, c_id, b, &dummy_noise, FORMAT_VERSION, 0)?;
chunk_stmt.execute(params![c_id, b, nonce.as_slice(), tag.as_slice(), ct,])?;