feat(recovery): implement header backup/restore, BIP-39 recovery key, and integrity verification

This commit is contained in:
2026-09-08 09:49:08 +02:00
parent 2d14c64c3e
commit 1c8a860184
11 changed files with 1451 additions and 57 deletions
+33 -14
View File
@@ -12,11 +12,18 @@ use tokio::net::TcpListener;
use tokio::sync::watch;
use tracing::{debug, warn};
use crate::crypto::{derive_kek, unwrap_dek};
use crate::crypto::{derive_kek, mnemonic_to_dek, unwrap_dek};
use crate::storage::Database;
use crate::ui;
use crate::vfs::SanctumFs;
/// Authentifizierungsmethode für das Einbinden eines Containers: Entweder Master-Passwort oder 24-Wort Notfallschlüssel.
#[derive(Debug, Clone)]
pub enum ContainerAuth {
Password(String),
RecoveryKey(String),
}
/// Hilfsfunktion zur Formatierung des Laufwerksbuchstabens (z. B. 'S' -> "S:")
pub fn format_drive(drive_letter: char) -> String {
format!("{}:", drive_letter.to_ascii_uppercase())
@@ -73,7 +80,7 @@ pub async fn mount_container(
container_path: &Path,
drive_letter: char,
requested_port: Option<u16>,
password: &str,
auth: ContainerAuth,
) -> Result<()> {
let drive_str = format_drive(drive_letter);
@@ -93,21 +100,33 @@ pub async fn mount_container(
.read_meta()
.context("Konnte Container-Header nicht lesen")?;
ui::step(2, 4, "🔑", "Leite KEK via Argon2id ab...");
let kek = derive_kek(password, &meta.kdf_salt, &meta.kdf_params)
.context("Schlüsselableitung fehlgeschlagen")?;
let (dek, version) = match auth {
ContainerAuth::Password(ref password) => {
ui::step(2, 4, "🔑", "Leite KEK via Argon2id ab...");
let kek = derive_kek(password, &meta.kdf_salt, &meta.kdf_params)
.context("Schlüsselableitung fehlgeschlagen")?;
ui::step(3, 4, "🔓", "Entschlüssele DEK via AES-256-GCM...");
let dek = unwrap_dek(
&kek,
&meta.wrapped_dek,
&meta.header_nonce,
&meta.header_tag,
)
.context("Ungültiges Master-Passwort oder Container beschädigt")?;
ui::step(3, 4, "🔓", "Entschlüssele DEK via AES-256-GCM...");
let dek = unwrap_dek(
&kek,
&meta.wrapped_dek,
&meta.header_nonce,
&meta.header_tag,
)
.context("Ungültiges Master-Passwort oder Container beschädigt")?;
(dek, meta.version)
}
ContainerAuth::RecoveryKey(ref phrase) => {
ui::step(2, 4, "🔑", "Dekodiere DEK aus 24-Wort Notfallschlüssel...");
let dek = mnemonic_to_dek(phrase)
.context("Ungültiger 24-Wort Notfallschlüssel")?;
ui::step(3, 4, "🔓", "Notfallschlüssel erfolgreich verifiziert!");
(dek, meta.version)
}
};
// WebDAV Filesystem und Handler konfigurieren
let fs = SanctumFs::new(db.clone(), dek, meta.version);
let fs = SanctumFs::new(db.clone(), dek, version);
let dav_server = DavHandler::builder()
.filesystem(Box::new(fs))
.locksystem(FakeLs::new())