//! `relicario recovery-qr {generate,unwrap}` — last-resort vault-key escape hatch. use anyhow::{Context, Result}; use crate::RecoveryQrCmd; pub fn cmd_recovery_qr(cmd: RecoveryQrCmd) -> Result<()> { match cmd { RecoveryQrCmd::Generate => cmd_recovery_qr_generate(), RecoveryQrCmd::Unwrap => cmd_recovery_qr_unwrap(), } } fn cmd_recovery_qr_generate() -> Result<()> { use relicario_core::{generate_recovery_qr, imgsecret}; use zeroize::Zeroizing; let image_path = crate::session::get_image_path()?; let image_bytes = std::fs::read(&image_path) .with_context(|| format!("read reference image {}", image_path.display()))?; let image_secret = imgsecret::extract(&image_bytes) .context("extract image secret")?; let passphrase = Zeroizing::new( rpassword::prompt_password("Enter vault passphrase: ") .context("read passphrase")? ); let payload = generate_recovery_qr(passphrase.as_str(), &image_secret) .map_err(|e| anyhow::anyhow!("{e}"))?; use qrcode::{EcLevel, QrCode, render::unicode}; let code = QrCode::with_error_correction_level(payload.as_bytes(), EcLevel::M) .expect("valid payload"); let image = code .render::() .dark_color(unicode::Dense1x2::Dark) .light_color(unicode::Dense1x2::Light) .build(); println!("{image}"); println!("Recovery QR generated. Print or photograph this code and store it securely."); println!("The QR has NOT been saved to disk."); Ok(()) } fn cmd_recovery_qr_unwrap() -> Result<()> { use relicario_core::unwrap_recovery_qr; use std::io::BufRead; use zeroize::Zeroizing; println!("Paste the base64 recovery QR payload and press Enter:"); let stdin = std::io::stdin(); let payload_b64 = stdin.lock().lines().next() .context("no input")??; let payload_b64 = payload_b64.trim().to_owned(); let bytes = data_encoding::BASE64.decode(payload_b64.as_bytes()) .map_err(|e| anyhow::anyhow!("base64 decode: {e}"))?; let passphrase = Zeroizing::new( rpassword::prompt_password("Enter passphrase: ") .context("read passphrase")? ); let secret = unwrap_recovery_qr(&bytes, passphrase.as_str()) .map_err(|e| anyhow::anyhow!("{e}"))?; println!("image_secret: {}", hex::encode(secret.as_ref())); Ok(()) }