dtool 0.17.0

A command-line tool collection to assist development
use crate::modules::base::Hex;
use crate::modules::{base, Command, Module};
use clap::{Arg, ArgMatches, SubCommand};

mod ed25519;

pub enum AltSecretKey {
	MiniSecretKey(Vec<u8>),
}

pub fn module<'a, 'b>() -> Module<'a, 'b> {
	Module {
		desc: "EdDSA (Ed25519)".to_string(),
		commands: commands(),
		get_cases: cases::cases,
	}
}

pub fn commands<'a, 'b>() -> Vec<Command<'a, 'b>> {
	vec![
		Command {
			app: SubCommand::with_name("ed_gk")
				.about("EdDSA generate key pair (Mini secret key, Public key)"),
			f: ed_gk,
		},
		Command {
			app: SubCommand::with_name("ed_sign")
				.about("EdDSA sign")
				.arg(
					Arg::with_name("INPUT")
						.help("Message (Hex)")
						.required(false)
						.index(1),
				)
				.arg(
					Arg::with_name("MINI_SECRET_KEY")
						.long("mini-secret-key")
						.short("m")
						.help("Mini secret key (Mini private key, Hex)")
						.takes_value(true)
						.required(false),
				),
			f: ed_sign,
		},
		Command {
			app: SubCommand::with_name("ed_verify")
				.about("EdDSA verify")
				.arg(
					Arg::with_name("INPUT")
						.help("Message (Hex)")
						.required(false)
						.index(1),
				)
				.arg(
					Arg::with_name("PUBLIC_KEY")
						.long("public-key")
						.short("p")
						.help("Public key (Hex)")
						.takes_value(true)
						.required(true),
				)
				.arg(
					Arg::with_name("SIGNATURE")
						.long("sig")
						.short("S")
						.help("Signature (Hex)")
						.takes_value(true)
						.required(true),
				),
			f: ed_verify,
		},
		Command {
			app: SubCommand::with_name("ed_pk")
				.about("EdDSA calculate public key")
				.arg(
					Arg::with_name("MINI_SECRET_KEY")
						.long("mini-secret-key")
						.short("m")
						.help("Mini secret key (Mini private key, Hex)")
						.takes_value(true)
						.required(false),
				),
			f: ed_pk,
		},
	]
}

fn ed_gk(_matches: &ArgMatches) -> Result<Vec<String>, String> {
	let (private_key, public_key) = ed25519::ed_gk_ed25519()?;

	let (private_key, public_key): (String, String) =
		(Hex::from(private_key).into(), Hex::from(public_key).into());

	let result = format!("({}, {})", private_key, public_key);

	Ok(vec![result])
}

fn ed_sign(matches: &ArgMatches) -> Result<Vec<String>, String> {
	let secret_key = get_alt_secret_key(matches)?;

	let input = base::input_string(matches)?;
	let input: Vec<u8> = input.parse::<Hex>().map_err(|_| "Invalid input")?.into();

	let sig = ed25519::ed_sign_ed25519(secret_key, input)?;

	let result = Hex::from(sig).into();

	Ok(vec![result])
}

fn ed_verify(matches: &ArgMatches) -> Result<Vec<String>, String> {
	let public_key = matches.value_of("PUBLIC_KEY").ok_or("Invalid public key")?;
	let public_key: Vec<u8> = public_key
		.parse::<Hex>()
		.map_err(|_| "Invalid secret key")?
		.into();

	let sig = matches.value_of("SIGNATURE").ok_or("Invalid signature")?;
	let sig: Vec<u8> = sig.parse::<Hex>().map_err(|_| "Invalid signature")?.into();

	let input = base::input_string(matches)?;
	let input: Vec<u8> = input.parse::<Hex>().map_err(|_| "Invalid input")?.into();

	ed25519::ed_verify_ed25519(public_key, sig, input)?;

	let result = "true".to_string();

	Ok(vec![result])
}

fn ed_pk(matches: &ArgMatches) -> Result<Vec<String>, String> {
	let secret_key = get_alt_secret_key(matches)?;

	let public_key = ed25519::ed_pk_ed25519(secret_key)?;

	let result = Hex::from(public_key).into();

	Ok(vec![result])
}

fn get_alt_secret_key(matches: &ArgMatches) -> Result<AltSecretKey, String> {
	if matches.is_present("MINI_SECRET_KEY") {
		let secret_key = matches
			.value_of("MINI_SECRET_KEY")
			.ok_or("Invalid mini secret key")?;
		let secret_key: Vec<u8> = secret_key
			.parse::<Hex>()
			.map_err(|_| "Invalid mini secret key")?
			.into();
		Ok(AltSecretKey::MiniSecretKey(secret_key))
	} else {
		Err("Mini secret key should be provided".to_string())
	}
}

mod cases {
	use super::ed25519;
	use crate::modules::Case;
	use linked_hash_map::LinkedHashMap;
	use std::iter::empty;

	pub fn cases() -> LinkedHashMap<&'static str, Vec<Case>> {
		empty()
			.chain(ed25519::cases())
			.fold(LinkedHashMap::new(), |mut map, (name, mut cases)| {
				let list = map.entry(name).or_insert(vec![]);
				list.append(&mut cases);
				map
			})
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::modules::base::test::test_module;

	#[test]
	fn test_cases() {
		test_module(module());
	}
}