use anyhow::{bail, Result};
use clap::{Args, Subcommand};
use yb_core::Context;
#[derive(Args, Debug)]
pub struct ManagementKeyArgs {
#[command(subcommand)]
pub command: ManagementKeyCommand,
}
#[derive(Subcommand, Debug)]
pub enum ManagementKeyCommand {
Set,
}
pub fn run(ctx: &Context, args: &ManagementKeyArgs) -> Result<()> {
match args.command {
ManagementKeyCommand::Set => set(ctx),
}
}
#[cfg(windows)]
fn set(_: &Context) -> Result<()> {
bail!("post-quantum stores are not supported on Windows")
}
#[cfg(not(windows))]
fn set(ctx: &Context) -> Result<()> {
use anyhow::Context as _;
use yb_core::auxiliaries::DEFAULT_MANAGEMENT_KEY;
use yb_core::errors::YbError;
use yb_core::orchestrator::post_quantum::rewrap_management_key;
use yb_core::store::Store;
use yb_core::Zeroizing;
let store = Store::from_device(&ctx.reader, ctx.piv.as_ref())?;
let Some(descriptor) = store.descriptor() else {
bail!(
"`yb management-key set` is for post-quantum stores; legacy stores keep their key \
in PRINTED (`yb rotate-management-key`)"
);
};
let key = match ctx.management_key.clone() {
Some(key) => key,
None if atty::is(atty::Stream::Stderr) => Zeroizing::new(
rpassword::prompt_password("New management key (hex): ")?
.trim()
.to_owned(),
),
None => {
return Err(YbError::new("the new management key is needed")
.fix("set YB_MANAGEMENT_KEY, or run yb in a terminal")
.into())
}
};
if key.eq_ignore_ascii_case(DEFAULT_MANAGEMENT_KEY) {
return Err(YbError::new("the management key is the factory default")
.why("a post-quantum store uses the YubiKey's own management key")
.fix("change it first, e.g. `ykman piv access change-management-key`")
.into());
}
if ctx
.piv
.authenticate_management_key(&ctx.reader, &key)
.is_err()
{
bail!("the YubiKey rejects this management key; nothing was changed");
}
if !ctx.quiet {
eprintln!(
"Note: if you did not change the management key yourself, the PIV application may \
have been reset, and the store tampered with: check before going on."
);
}
let algo = ctx.piv.management_key_algorithm(&ctx.reader)?;
let session = ctx.post_quantum_session()?;
rewrap_management_key(
ctx.piv.as_ref(),
&ctx.reader,
descriptor,
&session.device,
session.pin(),
&key,
algo,
&*ctx.on_touch,
)
.context("nothing was changed")?;
if !ctx.quiet {
eprintln!(
"The store now uses the new management key ({}).",
algo.name()
);
}
Ok(())
}