use anyhow::Result;
use bc_envelope::prelude::*;
use clap::{Args, ValueEnum};
use dcbor::prelude::Date;
#[derive(Debug, Clone, Copy, ValueEnum)]
#[value(rename_all = "kebab-case")]
enum SigningSchemeArg {
Schnorr,
Ecdsa,
Ed25519,
SshEd25519,
SshDsa,
SshEcdsaP256,
SshEcdsaP384,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
#[value(rename_all = "kebab-case")]
enum EncapsulationSchemeArg {
X25519,
}
#[derive(Debug, Args)]
#[group(skip)]
pub struct CommandArgs {
#[arg(name = "INPUT")]
input: Option<String>,
#[arg(long, default_value = "schnorr")]
signing: SigningSchemeArg,
#[arg(long, default_value = "x25519")]
encryption: EncapsulationSchemeArg,
}
impl crate::Exec for CommandArgs {
fn exec(&self) -> Result<String> {
let private_key_base = if let Some(input_ur) = &self.input {
parse_input(input_ur)?
} else {
bc_components::PrivateKeyBase::new()
};
let private_keys = match self.signing {
SigningSchemeArg::Schnorr => {
private_key_base.schnorr_private_keys()
}
SigningSchemeArg::Ecdsa => private_key_base.ecdsa_private_keys(),
SigningSchemeArg::Ed25519 => bc_components::PrivateKeys::with_keys(
private_key_base.ed25519_signing_private_key(),
bc_components::EncapsulationPrivateKey::X25519(
private_key_base.x25519_private_key(),
),
),
SigningSchemeArg::SshEd25519 => private_key_base
.ssh_private_keys(ssh_key::Algorithm::Ed25519, "")?,
SigningSchemeArg::SshDsa => private_key_base
.ssh_private_keys(ssh_key::Algorithm::Dsa, "")?,
SigningSchemeArg::SshEcdsaP256 => private_key_base
.ssh_private_keys(
ssh_key::Algorithm::Ecdsa {
curve: ssh_key::EcdsaCurve::NistP256,
},
"",
)?,
SigningSchemeArg::SshEcdsaP384 => private_key_base
.ssh_private_keys(
ssh_key::Algorithm::Ecdsa {
curve: ssh_key::EcdsaCurve::NistP384,
},
"",
)?,
};
Ok(private_keys.ur_string())
}
}
fn parse_input(input: &str) -> Result<bc_components::PrivateKeyBase> {
if let Ok(private_key_base) =
bc_components::PrivateKeyBase::from_ur_string(input)
{
return Ok(private_key_base);
}
if let Ok(seed) = bc_components::Seed::from_ur_string(input) {
return Ok(bc_components::PrivateKeyBase::new_with_provider(seed));
}
seed_from_envelope(input)
.map(bc_components::PrivateKeyBase::new_with_provider)
}
fn seed_from_envelope(input: &str) -> Result<bc_components::Seed> {
let envelope = Envelope::from_ur_string(input)?;
envelope.check_type_value(&known_values::SEED_TYPE)?;
let data = envelope
.subject()
.try_leaf()?
.try_into_byte_string()?
.to_vec();
let name = envelope
.extract_optional_object_for_predicate::<String>(known_values::NAME)?
.unwrap_or_default();
let note = envelope
.extract_optional_object_for_predicate::<String>(known_values::NOTE)?
.unwrap_or_default();
let creation_date = envelope
.extract_optional_object_for_predicate::<Date>(known_values::DATE)?;
bc_components::Seed::new_opt(
data,
optional_non_empty(name),
optional_non_empty(note),
creation_date,
)
.map_err(Into::into)
}
fn optional_non_empty(value: String) -> Option<String> {
if value.is_empty() { None } else { Some(value) }
}