From 482432758cfe22dc2721b5afd66731a590003808 Mon Sep 17 00:00:00 2001 From: oxalica Date: Mon, 26 Aug 2024 23:45:56 -0400 Subject: [PATCH] Allow using userkey literal in ctl commands --- Cargo.lock | 1 + blahctl/Cargo.toml | 1 + blahctl/src/main.rs | 25 ++++++++++++++++++++----- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a21093..77acfea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,6 +249,7 @@ dependencies = [ "blah", "clap", "ed25519-dalek", + "hex", "rand", "reqwest", "rusqlite", diff --git a/blahctl/Cargo.toml b/blahctl/Cargo.toml index 9709c1d..ec58ab3 100644 --- a/blahctl/Cargo.toml +++ b/blahctl/Cargo.toml @@ -9,6 +9,7 @@ bitflags = "2.6.0" blah.path = ".." clap = { version = "4.5.16", features = ["derive"] } ed25519-dalek = { version = "2.1.1", features = ["pkcs8", "pem", "rand_core"] } +hex = "0.4" rand = "0.8.5" reqwest = { version = "0.12.7", features = ["json"] } rusqlite = "0.32.1" diff --git a/blahctl/src/main.rs b/blahctl/src/main.rs index 2233fda..d1918da 100644 --- a/blahctl/src/main.rs +++ b/blahctl/src/main.rs @@ -7,7 +7,7 @@ use bitflags::Flags; use blah::types::{ChatPayload, CreateRoomPayload, ServerPermission, UserKey, WithSig}; use ed25519_dalek::pkcs8::spki::der::pem::LineEnding; use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey}; -use ed25519_dalek::{SigningKey, VerifyingKey}; +use ed25519_dalek::{SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH}; use rand::rngs::OsRng; use reqwest::Url; use rusqlite::{named_params, Connection}; @@ -65,7 +65,7 @@ enum DbCommand { fn flag_parser(s: &str) -> clap::error::Result { bitflags::parser::from_str_strict(s) - .map_err(|_| clap::error::Error::new(clap::error::ErrorKind::InvalidValue)) + .map_err(|_| clap::Error::new(clap::error::ErrorKind::InvalidValue)) } #[derive(Debug, clap::Subcommand)] @@ -95,18 +95,33 @@ enum ApiCommand { #[derive(Debug, clap::Args)] #[clap(group = clap::ArgGroup::new("user").required(true).multiple(false))] struct User { - /// The path to a user public key. + /// Hex-encoded public key. + #[arg(long, group = "user", value_parser = userkey_parser)] + key: Option, + + /// Path to a user public key. #[arg(long, short = 'f', group = "user")] public_key_file: Option, - /// The user domain where `/.well-known/blah/key` is hosted. + /// User's URL where `/.well-known/blah/key` is hosted. #[arg(long, group = "user")] url: Option, } +fn userkey_parser(s: &str) -> clap::error::Result { + (|| { + let mut buf = [0u8; PUBLIC_KEY_LENGTH]; + hex::decode_to_slice(s, &mut buf).ok()?; + VerifyingKey::from_bytes(&buf).ok() + })() + .ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidValue)) +} + impl User { async fn fetch_key(&self) -> Result { - let rawkey = if let Some(path) = &self.public_key_file { + let rawkey = if let Some(key) = &self.key { + return Ok(UserKey(key.to_bytes())); + } else if let Some(path) = &self.public_key_file { fs::read_to_string(path).context("failed to read key file")? } else if let Some(url) = &self.url { let url = url.join(KEY_URL_SUBPATH)?;