refactor(cli): move cmd_recovery_qr family into commands/recovery_qr.rs
This commit is contained in:
@@ -14,6 +14,7 @@ pub mod import;
|
||||
pub mod init;
|
||||
pub mod list;
|
||||
pub mod rate;
|
||||
pub mod recovery_qr;
|
||||
pub mod status;
|
||||
pub mod sync;
|
||||
pub mod trash;
|
||||
|
||||
69
crates/relicario-cli/src/commands/recovery_qr.rs
Normal file
69
crates/relicario-cli/src/commands/recovery_qr.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
//! `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::<unicode::Dense1x2>()
|
||||
.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(())
|
||||
}
|
||||
@@ -457,7 +457,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
Commands::Rate { passphrase } => commands::rate::cmd_rate(passphrase),
|
||||
Commands::Device { action } => cmd_device(action),
|
||||
Commands::RecoveryQr { cmd } => cmd_recovery_qr(cmd),
|
||||
Commands::RecoveryQr { cmd } => commands::recovery_qr::cmd_recovery_qr(cmd),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1322,66 +1322,3 @@ fn cmd_device(action: DeviceAction) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
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::<unicode::Dense1x2>()
|
||||
.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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user