use std::io::{stdout, BufWriter};
use anyhow::Result;
use clap::{Args, Subcommand};
use didkit::{get_verification_method, ssi::ssh, Error, Source, DID_METHODS, JWK};
use sshkeys::PublicKey;
#[derive(Subcommand)]
pub enum KeyCmd {
#[clap(subcommand)]
Generate(KeyGenerateCmd),
#[clap(subcommand)]
To(Box<KeyToCmd>),
#[clap(subcommand)]
From(Box<KeyFromCmd>),
}
#[derive(Subcommand)]
pub enum KeyToCmd {
Did(KeyToDIDArgs),
VerificationMethod(KeyToVMArgs),
}
#[derive(Subcommand)]
pub enum KeyFromCmd {
Ssh(KeyFromSSHArgs),
}
#[derive(Subcommand)]
pub enum KeyGenerateCmd {
Ed25519,
Secp256k1,
Secp256r1,
}
#[derive(Args)]
pub struct KeyToDIDArgs {
#[clap(default_value = "key")]
pub method_pattern: String,
#[clap(flatten)]
pub key: crate::KeyArg,
}
#[derive(Args)]
pub struct KeyToVMArgs {
pub method_pattern: Option<String>,
#[clap(flatten)]
pub key: crate::KeyArg,
}
#[derive(Args)]
pub struct KeyFromSSHArgs {
ssh_pk: String,
}
pub async fn cli(cmd: KeyCmd) -> Result<()> {
match cmd {
KeyCmd::Generate(cmd_generate) => generate(cmd_generate).await?,
KeyCmd::To(cmd_to) => to(*cmd_to).await?,
KeyCmd::From(cmd_from) => from(*cmd_from).await?,
};
Ok(())
}
pub async fn to(cmd: KeyToCmd) -> Result<()> {
match cmd {
KeyToCmd::Did(cmd_did) => to_did(cmd_did).await?,
KeyToCmd::VerificationMethod(cmd_vm) => to_vm(cmd_vm).await?,
};
Ok(())
}
pub async fn from(cmd: KeyFromCmd) -> Result<()> {
match cmd {
KeyFromCmd::Ssh(cmd_ssh) => from_ssh(cmd_ssh).await?,
};
Ok(())
}
pub async fn generate(cmd: KeyGenerateCmd) -> Result<()> {
let jwk = match cmd {
KeyGenerateCmd::Ed25519 => JWK::generate_ed25519().unwrap(),
KeyGenerateCmd::Secp256k1 => JWK::generate_secp256k1().unwrap(),
KeyGenerateCmd::Secp256r1 => JWK::generate_p256().unwrap(),
};
let jwk_str = serde_json::to_string(&jwk).unwrap();
println!("{jwk_str}");
Ok(())
}
pub async fn to_did(args: KeyToDIDArgs) -> Result<()> {
let jwk = args.key.get_jwk();
let did = DID_METHODS
.generate(&Source::KeyAndPattern(&jwk, &args.method_pattern))
.ok_or(Error::UnableToGenerateDID)
.unwrap();
println!("{did}");
Ok(())
}
pub async fn to_vm(args: KeyToVMArgs) -> Result<()> {
let method_pattern = match args.method_pattern {
Some(pattern) => pattern,
None => {
eprintln!(
"didkit: key-to-verification-method should be used with method pattern option"
);
"key".to_string()
}
};
let jwk = args.key.get_jwk();
let did = DID_METHODS
.generate(&Source::KeyAndPattern(&jwk, &method_pattern))
.ok_or(Error::UnableToGenerateDID)
.unwrap();
let did_resolver = DID_METHODS.to_resolver();
let vm = get_verification_method(&did, did_resolver)
.await
.ok_or(Error::UnableToGetVerificationMethod)
.unwrap();
println!("{vm}");
Ok(())
}
pub async fn from_ssh(args: KeyFromSSHArgs) -> Result<()> {
let ssh_pk = PublicKey::from_string(&args.ssh_pk).unwrap();
let jwk = ssh::ssh_pkk_to_jwk(&ssh_pk.kind).unwrap();
let stdout_writer = BufWriter::new(stdout());
serde_json::to_writer_pretty(stdout_writer, &jwk).unwrap();
Ok(())
}