fix(cli): resolve second factor via shared helper for recovery-qr + backup (key-file parity); wasm equivalence negative control

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01McMF5pUJbCMricmFEnf2sG
This commit is contained in:
adlee-was-taken
2026-06-26 00:11:50 -04:00
parent 23c8bbea65
commit 2203d817ec
5 changed files with 75 additions and 31 deletions

View File

@@ -103,10 +103,14 @@ pub(super) fn cmd_backup_export(
// Optional reference image.
let image_bytes = if include_image {
let path = match image {
Some(p) => p,
None => crate::session::get_image_path()?,
};
let pf = crate::session::read_params(&root)?;
if pf.second_factor == relicario_core::SecondFactor::Keyfile {
anyhow::bail!(
"--include-image is not applicable to a key-file vault (it has no reference image); \
back up your .relkey file directly"
);
}
let path = match image { Some(p) => p, None => crate::session::get_image_path()? };
Some(fs::read(&path)
.with_context(|| format!("failed to read reference image {}", path.display()))?)
} else {

View File

@@ -12,21 +12,23 @@ pub fn cmd_recovery_qr(cmd: RecoveryQrCmd) -> Result<()> {
}
fn cmd_recovery_qr_generate() -> Result<()> {
use relicario_core::{generate_recovery_qr, imgsecret};
use relicario_core::generate_recovery_qr;
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 root = crate::helpers::vault_dir()?;
let pf = crate::session::read_params(&root)?;
let secret = crate::session::resolve_second_factor_secret(&root, pf.second_factor)?;
let passphrase = Zeroizing::new(
rpassword::prompt_password("Enter vault passphrase: ")
.context("read passphrase")?
);
let passphrase = if let Some(p) = crate::test_passphrase_override() {
Zeroizing::new(p)
} else {
Zeroizing::new(
rpassword::prompt_password("Enter vault passphrase: ")
.context("read passphrase")?
)
};
let payload = generate_recovery_qr(passphrase.as_str(), &image_secret)
let payload = generate_recovery_qr(passphrase.as_str(), &secret)
.map_err(|e| anyhow::anyhow!("{e}"))?;
use qrcode::{EcLevel, QrCode, render::unicode};
@@ -64,6 +66,6 @@ fn cmd_recovery_qr_unwrap() -> Result<()> {
let secret = unwrap_recovery_qr(&bytes, passphrase.as_str())
.map_err(|e| anyhow::anyhow!("{e}"))?;
println!("image_secret: {}", hex::encode(secret.as_ref()));
println!("secret: {}", hex::encode(secret.as_ref()));
Ok(())
}