use anyhow::Result;
use bc_components::URI;
use bc_xid::{XIDDocument, XIDGenesisMarkOptions, XIDInceptionKeyOptions};
use clap::Args;
use dcbor::Date;
use super::{
InputKey, KeyArgs, KeyArgsLike, OutputOptions, PrivateOptions,
ReadWritePasswordArgs, SigningArgs, XIDPrivilege,
generator_options::GeneratorOptions, update_key, xid_document_to_ur_string,
};
use crate::parse_ur_to_cbor;
#[derive(Debug, Args)]
#[group(skip)]
pub struct CommandArgs {
#[command(flatten)]
key_args: KeyArgs,
#[arg(long = "generator", default_value = "omit")]
generator_opts: GeneratorOptions,
#[arg(long)]
date: Option<String>,
#[arg(long)]
info: Option<String>,
#[arg(long)]
ur_tag: Option<u64>,
#[command(flatten)]
password_args: ReadWritePasswordArgs,
#[command(flatten)]
signing_args: SigningArgs,
}
impl KeyArgsLike for CommandArgs {
fn nickname(&self) -> &str { self.key_args.nickname() }
fn private_opts(&self) -> PrivateOptions { self.key_args.private_opts() }
fn endpoints(&self) -> &[URI] { self.key_args.endpoints() }
fn permissions(&self) -> &[XIDPrivilege] { self.key_args.permissions() }
fn keys(&self) -> Option<&str> { self.key_args.keys() }
}
impl crate::Exec for CommandArgs {
fn exec(&self) -> Result<String> {
let keys = self.read_key()?;
let shared_password = if self.generator_opts.is_encrypt()
|| self.private_opts().is_encrypt()
{
Some(
self.password_args
.write
.read_password("Encryption password:")?,
)
} else {
None
};
let genesis_mark_opts = match self.generator_opts {
GeneratorOptions::Omit => XIDGenesisMarkOptions::None,
GeneratorOptions::Include | GeneratorOptions::Encrypt => {
let date = if let Some(date_str) = &self.date {
Some(Date::from_string(date_str)?)
} else {
None
};
let info = if let Some(info_str) = &self.info {
Some(parse_ur_to_cbor(info_str, self.ur_tag)?)
} else {
None
};
let mut rng = bc_rand::SecureRandomNumberGenerator;
let random_seed =
provenance_mark::ProvenanceSeed::new_using(&mut rng);
XIDGenesisMarkOptions::Seed(random_seed, None, date, info)
}
GeneratorOptions::Elide => {
anyhow::bail!(
"Elide is not allowed for 'xid new'. Use 'omit' (the default) to create without a provenance mark, or 'include'/'encrypt' to create with one."
)
}
};
let mut xid_document = match &keys {
InputKey::PrivateBase(private_key_base) => XIDDocument::new(
XIDInceptionKeyOptions::PrivateKeyBase(
private_key_base.clone(),
),
genesis_mark_opts,
),
InputKey::Public(public_keys) => XIDDocument::new(
XIDInceptionKeyOptions::PublicKeys(public_keys.clone()),
genesis_mark_opts,
),
InputKey::PrivateKeys(private_keys) => {
let public_keys = private_keys.public_keys()?;
XIDDocument::new(
XIDInceptionKeyOptions::PublicAndPrivateKeys(
public_keys,
private_keys.clone(),
),
genesis_mark_opts,
)
}
InputKey::PrivateAndPublicKeys(private_keys, public_keys) => {
XIDDocument::new(
XIDInceptionKeyOptions::PublicAndPrivateKeys(
public_keys.clone(),
private_keys.clone(),
),
genesis_mark_opts,
)
}
};
let mut key = xid_document.keys().iter().next().unwrap().clone();
xid_document.take_key(&key);
update_key(
&mut key,
self.nickname(),
self.endpoints(),
self.permissions(),
);
xid_document.add_key(key)?;
let signing_options = self
.signing_args
.signing_options(Some(&self.password_args.read))?;
let output_opts =
OutputOptions::new(self.private_opts(), self.generator_opts);
xid_document_to_ur_string(
&xid_document,
&output_opts,
Some(&self.password_args.write),
shared_password,
signing_options,
)
}
}