cll2v0/app/cli.rs

142 lines
4.7 KiB
Rust

use std::env;
use clap::{Parser, Subcommand};
use ed25519_dalek::{Signature, SigningKey};
use sqlx::sqlite::SqlitePool;
use cll2v0::{db, keys};
/// cll2v0 is a playground for rust libraries.
/// This is signing service.
#[derive(Parser)]
#[command(arg_required_else_help(true), version, about)]
struct Args {
#[command(subcommand)]
cmd: Option<Command>,
}
#[derive(Subcommand)]
enum Command {
/// Add new ephemeral key to an existing persistent key
Add {
ephemeral_key: String,
persistent_key: String,
expires_at: i64,
},
/// Revoke an existing ephemeral key
Revoke { ephemeral_key: String },
/// Check persistent keys are available and display
Check,
/// Gen from seed some sensible ephemeral key args to be used with `add`
Gen { seed: u8 },
/// Sign a message
Sign {
signing_key: String,
message: String,
},
/// Verify a message
Verify {
verifying_key: String,
message: String,
signature: String,
},
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
let keys_path = env::var("CLL2V0_KEYS").expect("Expect `CLL2V0_KEYS` to be set");
let keychain = keys::mk_keychain(&keys_path);
let db_url = env::var("DATABASE_URL").expect("Expect `DATABASE_URL` to be set");
let pool = SqlitePool::connect(&db_url).await?;
for vkey_bytes in keychain.keys() {
let vkey = keys::from_bytes(vkey_bytes.into())?;
let _ = db::add_persistent_key(&pool, &vkey).await;
}
match args.cmd {
Some(Command::Add {
ephemeral_key,
persistent_key,
expires_at,
}) => {
println!("Adding new ephemeral key '{ephemeral_key}'");
let e = keys::from_hex(&ephemeral_key).expect("cannot parse key");
let p = keys::from_hex(&persistent_key).expect("cannot parse key");
let res = db::add_ephemeral_key(&pool, &e, &p, expires_at).await?;
println!("Added new ekey: {}", res);
}
Some(Command::Revoke { ephemeral_key }) => {
let e = keys::from_hex(&ephemeral_key).expect("cannot parse key");
let res = db::revoke_ephemeral_key(&pool, &e).await?;
println!("Revoked key: {}", res);
}
Some(Command::Check) => {
eprintln!("!Check skipped");
let res = db::list_persistent_keys(&pool).await?;
println!("Persistent keys:");
res.into_iter()
.for_each(|key| println!("{}", keys::to_hex(&key)))
}
Some(Command::Gen { seed }) => {
let ekey = SigningKey::from_bytes(&[
seed, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
]);
println!("Ephemeral signing key:");
println!("{}", keys::signing_key_to_hex(&ekey));
let res = db::list_persistent_keys(&pool).await?;
let idx = (seed as usize) % res.len();
let pkey = res.get(idx);
match pkey {
None => eprintln!("No persistent keys found"),
Some(pkey) => {
let now: usize = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis()
.try_into()
.unwrap();
let twenty_mins_from_now = 20usize * 60 * 1000 + now;
println!("Args");
println!(
"{} {} {}",
keys::to_hex(&ekey.verifying_key()),
keys::to_hex(pkey),
twenty_mins_from_now
)
}
}
}
Some(Command::Sign {
signing_key,
message,
}) => {
let mut skey = keys::signing_key_from_hex(&signing_key).unwrap();
let msg = hex::decode(message).unwrap();
let sig = keys::sign(&mut skey, msg);
println!("{}", hex::encode(sig.to_bytes()))
}
Some(Command::Verify {
verifying_key,
message,
signature,
}) => {
let vkey = keys::from_hex(&verifying_key).unwrap();
let msg = hex::decode(message).unwrap();
let sig = Signature::from_bytes(
&TryInto::<[u8; 64]>::try_into(hex::decode(signature).unwrap()).unwrap(),
);
let res = keys::verify(&vkey, &msg, &sig).unwrap();
println!("{:?}", res);
}
None => {
println!("See help");
}
}
Ok(())
}