use anyhow::{Context as _, Result};
use clap::Args;
use yb_core::report::StorePresence;
use yb_core::Zeroizing;
use yb_core::{
auxiliaries::{
enable_pin_protected_management_key, generate_random_management_key, KeySwitch,
ProtectionMode,
},
errors::YbError,
Context, SecretOp,
};
use crate::cli::util::read_back_hint;
#[derive(Args, Debug, Default)]
pub struct RotateManagementKeyArgs {}
pub fn run(ctx: &Context, _args: &RotateManagementKeyArgs) -> Result<()> {
if let StorePresence::Present(store) = StorePresence::probe(&ctx.reader, ctx.piv.as_ref()) {
if store.kind.is_post_quantum() {
anyhow::bail!(
"this store uses your own management key; change it with ykman, then run `yb management-key set`"
);
}
}
let (old_key, old_key_in_printed, was_stored) =
preflight(ctx).context("nothing was changed on the YubiKey")?;
let (reader, piv) = (ctx.reader.as_str(), ctx.piv.as_ref());
let algo = piv.management_key_algorithm(reader)?;
let new_key = generate_random_management_key(algo);
enable_pin_protected_management_key(
reader,
piv,
&KeySwitch {
old_key: &old_key,
old_key_in_printed,
new_key: &new_key,
algo,
clearing_legacy_flag: ctx.protection == ProtectionMode::LegacyOrPukBlocked,
},
)?;
if !ctx.quiet {
if was_stored {
eprintln!("Management key rotated ({algo}).");
} else {
eprintln!(
"Management key replaced, and now kept on the YubiKey, unlocked by your \
PIN ({algo})."
);
}
eprintln!("{}", read_back_hint());
}
Ok(())
}
fn preflight(ctx: &Context) -> Result<(Zeroizing<String>, bool, bool)> {
ctx.ensure_supported_protection()?;
ctx.enforce_default_policy(SecretOp::RotateManagementKey)?;
let pin = ctx.require_pin()?.ok_or_else(|| {
YbError::new("a PIN is needed to change the management key")
.fix("set YB_PIN, use --pin-stdin, or run yb in a terminal")
})?;
ctx.piv.verify_pin(&ctx.reader, &pin)?;
let old_key = ctx.management_key_for_write()?;
let old_key_in_printed = ctx.management_key_in_printed()?;
let was_stored = ctx.management_key_protected()?;
Ok((old_key, old_key_in_printed, was_stored))
}