use crate::{input, options::output};
use camino::{Utf8Path, Utf8PathBuf};
use clap::{Args, Parser, ValueEnum};
use rand_chacha::ChaCha20Rng;
use rand_core::{CryptoRngCore, SeedableRng};
use rand_hc::Hc128Rng;
use rand_seeder::Seeder;
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
#[error(transparent)]
#[remain::sorted]
pub(crate) enum Error {
EllipticCurve(#[from] elliptic_curve::Error),
Input(#[from] input::Error),
Io(#[from] std::io::Error),
}
#[derive(Debug, Clone, clap::Parser)]
pub(crate) struct Command {
#[clap(flatten)]
input: input::InputOrOptions<Options>,
#[command(flatten)]
output_file: output::OutputFile,
}
impl crate::Output for Options {}
#[derive(Debug, Clone, clap::Parser, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
#[command(about = "generates private keys")]
#[group(id = "key")]
#[remain::sorted]
#[rustfmt::skip]
#[schemars(rename = "KeyGenerationOptions")]
pub(crate) struct Options {
#[arg(
env = "KEY_CURVE",
long = "key-curve",
id = "key.curve",
value_name = "CURVE",
default_value = "k256"
)]
curve: Curve,
#[arg(
env = "OUTPUT_FORMAT",
long = "output-format",
id = "output.format",
value_name = "FORMAT",
default_value = "jwk",
help_heading = "Output Options",
)]
format: Format,
#[command(flatten)]
rng: crate::options::rng::RngOptions,
}
impl Command {
pub async fn run(self, valkey: &crate::integrations::fred::ValkeyOptions) -> Result<Box<dyn crate::Output>, Error> {
let Self {
input,
output_file: output::OutputFile { path },
} = self;
let Options { curve, format, rng, .. } = input.consume()?;
let mut rng = rng.to_crypto_rng();
let key = SecretKey::new(&mut rng, &curve);
let contents = format.encode(&key);
match path {
Some(path) => fs_err::write(path, contents)?,
None => println!("{contents}"),
}
Ok(Box::new(()))
}
}
#[derive(Debug, Clone, Copy, Default, clap::ValueEnum, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
#[clap(rename_all = "lowercase")]
#[remain::sorted]
pub enum Curve {
#[default]
K256,
P256,
}
#[derive(Debug, Clone, Copy, Default, clap::ValueEnum, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
#[clap(rename_all = "lowercase")]
#[remain::sorted]
pub enum Format {
#[default]
Jwk,
}
impl Format {
fn encode(&self, key: &SecretKey) -> String {
match self {
Self::Jwk => match key {
SecretKey::K256(key) => key.to_jwk_string().to_string(),
SecretKey::P256(key) => key.to_jwk_string().to_string(),
},
}
}
}
#[derive(Debug, Clone)]
#[remain::sorted]
pub enum SecretKey {
K256(k256::SecretKey),
P256(p256::SecretKey),
}
impl SecretKey {
fn new(rng: &mut impl CryptoRngCore, curve: &Curve) -> Self {
match curve {
Curve::K256 => Self::K256(k256::SecretKey::random(rng)),
Curve::P256 => Self::P256(p256::SecretKey::random(rng)),
}
}
}