use archon_core::crypto::{verify, verify_in_domain};
use archon_core::hexbytes::{pubkey_from_hex, signature_from_hex};
use archon_core::keytext::decode_key;
use crate::io::{read_bytes, wants_help};
const USAGE: &str =
"usage: archon verify --pubkey <ed25519:...|hex> --sig <hex> [--domain <d>] [--in <file>]\n \
verifies the signature over the input bytes (stdin, or --in <file>); prints valid (exit 0) \
or invalid (exit 1). --domain checks a domain-separated signature.";
pub fn run(args: &[String]) -> Result<(), String> {
if wants_help(args) {
println!("{USAGE}");
return Ok(());
}
let mut pubkey: Option<Vec<u8>> = None;
let mut sig: Option<[u8; 64]> = None;
let mut domain: Option<String> = None;
let mut in_path: Option<String> = None;
let mut i = 0;
while i < args.len() {
let flag = &args[i];
let value = args
.get(i + 1)
.filter(|v| !v.is_empty() || flag == "--domain")
.ok_or_else(|| format!("flag {flag:?} needs a value\n{USAGE}"))?;
match flag.as_str() {
"--pubkey" => pubkey = Some(parse_pubkey(value)?),
"--sig" => sig = Some(signature_from_hex(value).map_err(|e| format!("--sig: {e}"))?),
"--domain" => domain = Some(value.clone()),
"--in" => in_path = Some(value.clone()),
other => return Err(format!("unknown flag {other:?}\n{USAGE}")),
}
i += 2;
}
let (Some(pubkey), Some(sig)) = (pubkey, sig) else {
return Err(format!("--pubkey and --sig are required\n{USAGE}"));
};
let message = read_bytes(in_path.as_deref())?;
let ok = match domain.as_deref() {
Some(d) => verify_in_domain(&pubkey, d, &message, &sig),
None => verify(&pubkey, &message, &sig),
};
if ok {
println!("valid");
Ok(())
} else {
println!("invalid");
std::process::exit(1);
}
}
fn parse_pubkey(value: &str) -> Result<Vec<u8>, String> {
if value.starts_with("ed25519:") {
decode_key(value).map_err(|e| format!("--pubkey: {e}"))
} else {
pubkey_from_hex(value)
.map(|k| k.to_vec())
.map_err(|e| format!("--pubkey: {e}"))
}
}