fix(crypto): K-03 — emergency card persistence warning and rekey command

This commit is contained in:
2026-09-19 00:58:39 +02:00
parent 36a4094336
commit bdddd812ac
8 changed files with 560 additions and 9 deletions
+90
View File
@@ -134,6 +134,13 @@ enum Commands {
recovery_key: Option<String>,
},
/// Erneuert den internen Verschlüsselungsschlüssel (DEK), verschlüsselt alle Chunks um und generiert ein neues Notfallblatt (K-03)
Rekey {
/// Pfad zur .sanctum Containerdatei
#[arg(short, long)]
path: PathBuf,
},
/// Erstellt ein konsistentes Online-Backup (Hot-Backup) des laufenden Containers
Backup {
/// Pfad zur Quelldatei (.sanctum Containerdatei)
@@ -879,6 +886,86 @@ fn handle_passwd(container_path: &Path, recovery_key: Option<&str>) -> Result<()
println!(" • KDF: Argon2id mit frischem Salt");
println!(" • Status: DEK sicher neu verpackt (alle Chunks intakt)");
println!();
println!(" [!] HINWEIS: Das 24-Wörter-Notfallblatt bleibt weiterhin gültig!");
println!(" Um den Container vollständig mit neuem Master-Schlüssel zu sichern,");
println!(" nutzen Sie `sanctum rekey`.");
println!();
Ok(())
}
fn handle_rekey(container_path: &Path) -> Result<()> {
if !container_path.exists() {
bail!(
"Containerdatei '{}' existiert nicht.",
container_path.display()
);
}
println!("┌─────────────────────────────────────────────────────────────┐");
println!("│ Sanctum — Master-Schlüssel erneuern (Rekeying) │");
println!("└─────────────────────────────────────────────────────────────┘");
println!(" Container: {}", container_path.display());
println!();
println!(" [!] WICHTIGER SICHERHEITSHINWEIS (K-03):");
println!(" Beim Rekeying wird der interne Daten-Verschlüsselungsschlüssel (DEK)");
println!(" neu generiert und alle Datenblöcke werden umverschlüsselt.");
println!(" Das bisherige 24-Wörter-Notfallblatt wird damit UNWIDERRUFLICH UNGÜLTIG.");
println!();
let password = Zeroizing::new(
rpassword::prompt_password("Master-Passwort des Containers eingeben: ")
.context("Fehler beim Einlesen des Master-Passworts")?,
);
if password.trim().is_empty() {
bail!("Das Master-Passwort darf nicht leer sein.");
}
println!();
ui::step(
1,
3,
"🔄",
"Entschlüssele Datenblöcke & verschlüssele mit neuem DEK...",
);
let new_phrase = sanctum::recovery::rekey_container(container_path, &password)
.context("Fehler beim Ausführen des Rekeyings")?;
ui::step(
2,
3,
"🔒",
"Aktualisiere Container-Header & Metadaten-Authentifizierung...",
);
ui::step(3, 3, "🔑", "Generiere neues 24-Wort BIP-39 Notfallblatt...");
println!();
println!("┌─────────────────────────────────────────────────────────────┐");
println!("│ ✔ Container erfolgreich rekeyed! │");
println!("└─────────────────────────────────────────────────────────────┘");
println!();
println!(" • Container: {}", container_path.display());
println!(" • Status: Alle Chunks wurden unter einem frischen DEK neu verschlüsselt.");
println!(" • Alt-Schlüssel:Bisheriges Notfallblatt ist ab sofort ungültig und entwertet.");
println!();
println!("┌─────────────────────────────────────────────────────────────┐");
println!("│ NEUES 24-WORT BIP-39 NOTFALLBLATT (NOTFALL-WIEDERHERSTELLUNG)│");
println!("└─────────────────────────────────────────────────────────────┘");
println!();
let words: Vec<&str> = new_phrase.split_whitespace().collect();
for (i, chunk) in words.chunks(4).enumerate() {
let line = chunk
.iter()
.enumerate()
.map(|(j, w)| format!("{:2}. {:<12}", i * 4 + j + 1, w))
.collect::<Vec<_>>()
.join(" ");
println!(" {}", line);
}
println!();
println!(" [!] WICHTIG: Drucken Sie diese Wörter aus oder notieren Sie sie sicher.");
println!(" Das alte Notfallblatt kann nicht mehr zur Wiederherstellung verwendet werden.");
println!();
Ok(())
}
@@ -1573,6 +1660,9 @@ async fn run() -> Result<()> {
Commands::UpgradeFormat { path } => {
handle_upgrade_format(&path)?;
}
Commands::Rekey { path } => {
handle_rekey(&path)?;
}
Commands::Sync {
path,
source,