parent
7b00455385
commit
6b0f1c72be
@ -1,371 +0,0 @@
|
|||||||
//! Arg parsing
|
|
||||||
#![allow(unused_macros)]
|
|
||||||
use super::*;
|
|
||||||
use tokio::{
|
|
||||||
sync::{
|
|
||||||
mpsc,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
iter,
|
|
||||||
};
|
|
||||||
use eyre::eyre;
|
|
||||||
use config::op::{self, Password};
|
|
||||||
use error::ParseErrorKind;
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
|
||||||
enum IsSigning
|
|
||||||
{
|
|
||||||
No,
|
|
||||||
Yes,
|
|
||||||
Only,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for IsSigning
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self::No
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
|
||||||
enum KeyKind
|
|
||||||
{
|
|
||||||
Autodetect(IsSigning),
|
|
||||||
RsaPublic(IsSigning),
|
|
||||||
RsaPrivate(IsSigning), // is signing key?
|
|
||||||
Aes,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
|
||||||
enum TranslationMode {
|
|
||||||
Autodetect,
|
|
||||||
Encrypt,
|
|
||||||
Decrypt,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TranslationMode
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self::Autodetect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
|
||||||
enum Atom
|
|
||||||
{
|
|
||||||
Key(KeyKind, String, Password),
|
|
||||||
NonSpecific,
|
|
||||||
FileAuto(String),
|
|
||||||
FileSpecific(String, String, TranslationMode),
|
|
||||||
SetMode(config::op::Mode),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Atom
|
|
||||||
{
|
|
||||||
pub fn hash_into(&self, map: &mut smallmap::Map<u64, ()>) -> bool
|
|
||||||
{
|
|
||||||
use std::hash::{Hash,Hasher,};
|
|
||||||
let hasher = std::collections::hash_map::DefaultHasher::new();
|
|
||||||
self.hash(&mut hasher);
|
|
||||||
let vl = hasher.finish();
|
|
||||||
if map.contains_key(&vl) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
map.insert(hasher.finish(),());
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument]
|
|
||||||
pub async fn parse<I: IntoIterator<Item=String> + std::fmt::Debug>(args: I) -> Result<config::Operation, eyre::Report>
|
|
||||||
{
|
|
||||||
let (mut tx, mut rx) = mpsc::channel(1);
|
|
||||||
|
|
||||||
macro_rules! unwrap_handler {
|
|
||||||
($handler:expr) => ($handler.await
|
|
||||||
.wrap_err_with(|| eyre!("Atom parser panicked")
|
|
||||||
.with_warning(|| "This usually indicates a bug in the program, not invalid input.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
|
||||||
let handler = if let Some(arg) = args.next()
|
|
||||||
{
|
|
||||||
match arg.to_lowercase().trim() {
|
|
||||||
"--generate" => todo!(),
|
|
||||||
"--help" => todo!(),
|
|
||||||
"--stat" => todo!(),
|
|
||||||
_ => {
|
|
||||||
let handler = tokio::spawn(async move {
|
|
||||||
let mut output = op::Normal::default();
|
|
||||||
let mut had = smallmap::Map::new();
|
|
||||||
while let Some(atom) = rx.recv().await {
|
|
||||||
debug!("[Normal] recv atom: {:?}", atom);
|
|
||||||
// Construct `Normal` with atoms
|
|
||||||
|
|
||||||
if Atom::hash_into(&atom, &mut had) {
|
|
||||||
return Err(error::ConstructError::AlreadyExists(format!("TODO: {:?}", atom)));
|
|
||||||
}
|
|
||||||
|
|
||||||
match atom {
|
|
||||||
Atom::NonSpecific => output.in_place = true,
|
|
||||||
Atom::SetMode(mode) => output.mode = Some(mode),
|
|
||||||
Atom::FileAuto(file) => output.files.push((file, None, None)),
|
|
||||||
Atom::FileSpecific(finput, foutput, mode) => {
|
|
||||||
let mode = match mode {
|
|
||||||
TranslationMode::Autodetect => resolve::find_file_mode(&finput).await?,
|
|
||||||
TranslationMode::Encrypt => op::Mode::Encrypt,
|
|
||||||
TranslationMode::Decrypt => op::Mode::Decrypt,
|
|
||||||
};
|
|
||||||
output.files.push((finput, Some(foutput), Some(mode)));
|
|
||||||
},
|
|
||||||
Atom::Key(kind, string, password) => {
|
|
||||||
let (kind, sign) = match kind {
|
|
||||||
KeyKind::Autodetect(s) => (resolve::find_key_mode(&string).await?, s),
|
|
||||||
KeyKind::RsaPrivate(s) => (config::KeyKind::RsaPrivate, s),
|
|
||||||
KeyKind::RsaPublic(s) => (config::KeyKind::RsaPublic, s),
|
|
||||||
KeyKind::Aes => (config::KeyKind::Aes, Default::default()),
|
|
||||||
};
|
|
||||||
if sign != IsSigning::No && kind == config::KeyKind::Aes {
|
|
||||||
warn!("Key was detected to not be RSA, option to sign is set and ignored.");
|
|
||||||
} else {
|
|
||||||
output.sign.push((string.clone(), password.clone()));
|
|
||||||
}
|
|
||||||
match kind {
|
|
||||||
config::KeyKind::Aes => output.aes.push((string, password)),
|
|
||||||
config::KeyKind::RsaPublic => output.rsa.push((string, password)),
|
|
||||||
config::KeyKind::RsaPrivate => output.rsa.push((string, password))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(config::Operation::Normal(output))
|
|
||||||
}.instrument(tracing::info_span!("handler")));
|
|
||||||
match parse_normal(std::iter::once(arg)
|
|
||||||
.chain(args.map(Into::into)), &mut tx).await
|
|
||||||
{
|
|
||||||
// The `handler` has dropped `rx` and returned an error, let unwrap_handler down there handle this.
|
|
||||||
Err(error::Error::AtomInternal) => Ok(()),
|
|
||||||
other => other,
|
|
||||||
}
|
|
||||||
.with_note(|| "In parsing of normal operation spec")?;
|
|
||||||
|
|
||||||
|
|
||||||
handler
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(eyre!("No arguments specified"));
|
|
||||||
};
|
|
||||||
|
|
||||||
match unwrap_handler!(handler)? {
|
|
||||||
Err(error) => {
|
|
||||||
return Err(error)
|
|
||||||
.wrap_err_with(|| eyre!("Failed to construct operation from arguments"))
|
|
||||||
.with_note(|| "In constructing of operation from spec");
|
|
||||||
},
|
|
||||||
Ok(operation) => {
|
|
||||||
//TODO: Validate operation here?
|
|
||||||
Ok(operation)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn parse_normal<I: IntoIterator<Item=String>>(args: I, atoms: &mut mpsc::Sender<Atom>) -> Result<(), error::Error>
|
|
||||||
{
|
|
||||||
let mut args = args.into_iter();
|
|
||||||
let mut reading=true;
|
|
||||||
|
|
||||||
let mut mode_set=false;
|
|
||||||
while let Some(arg) = args.next()
|
|
||||||
{
|
|
||||||
|
|
||||||
macro_rules! take_one {
|
|
||||||
($msg:literal $($tt:tt)*) => {
|
|
||||||
match args.next() {
|
|
||||||
Some(v) => v,
|
|
||||||
None => return Err((arg, ParseErrorKind::ExpectedArg(format!($msg $($tt)*))).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if reading && arg.starts_with("-") {
|
|
||||||
let mut opt = arg.chars().skip(1);
|
|
||||||
|
|
||||||
macro_rules! check_mode_set
|
|
||||||
{
|
|
||||||
() => {
|
|
||||||
if mode_set {
|
|
||||||
return Err((arg, error::ParseErrorKind::ModeSet).into());
|
|
||||||
} else {
|
|
||||||
mode_set=true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match opt.next() {
|
|
||||||
Some('a') => check_mode_set!(),
|
|
||||||
Some('e') => {
|
|
||||||
check_mode_set!();
|
|
||||||
atoms.send(Atom::SetMode(config::op::Mode::Encrypt)).await?;
|
|
||||||
},
|
|
||||||
Some('d') => {
|
|
||||||
check_mode_set!();
|
|
||||||
atoms.send(Atom::SetMode(config::op::Mode::Decrypt)).await?;
|
|
||||||
},
|
|
||||||
Some('i') => match opt.next() {
|
|
||||||
None => atoms.send(Atom::NonSpecific).await?,
|
|
||||||
Some(r) => return Err((ParseErrorKind::BadTail('i', iter::once(r).chain(opt).collect()), arg).swap().into()),
|
|
||||||
},
|
|
||||||
Some('t') => {
|
|
||||||
let input = take_one!("t: Expected input file");
|
|
||||||
let output = take_one!("t: Expected output file");
|
|
||||||
let mut trans = TranslationMode::default();
|
|
||||||
|
|
||||||
let mut had = smallmap::Map::new();
|
|
||||||
while let Some(opt) = opt.next() {
|
|
||||||
if (had.contains_key(&opt), had.insert(opt, ())).0 {
|
|
||||||
// arg has been repeated, not allowed
|
|
||||||
return Err((arg, error::ParseErrorKind::NotUnique(opt)).into());
|
|
||||||
}
|
|
||||||
macro_rules! check_combine {
|
|
||||||
($($chars:expr),+) => {
|
|
||||||
$(if had.contains_key(&$chars) {
|
|
||||||
return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars))));
|
|
||||||
})*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match opt {
|
|
||||||
'e' => trans= TranslationMode::Encrypt,
|
|
||||||
'd' => trans= TranslationMode::Decrypt,
|
|
||||||
_ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('t', opt)).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
atoms.send(Atom::FileSpecific(input, output, trans)).await?;
|
|
||||||
},
|
|
||||||
Some('k') => {
|
|
||||||
let key_file = take_one!("k: Expected a key file");
|
|
||||||
enum KeySpec {
|
|
||||||
Autodetect,
|
|
||||||
RsaPrivate,
|
|
||||||
RsaPublic,
|
|
||||||
Aes,
|
|
||||||
}
|
|
||||||
let mut kind = KeySpec::Autodetect;
|
|
||||||
let mut password = Password::default();
|
|
||||||
let mut signing = IsSigning::default();
|
|
||||||
|
|
||||||
let mut had = smallmap::Map::new();
|
|
||||||
while let Some(opt) = opt.next() {
|
|
||||||
if (had.contains_key(&opt), had.insert(opt, ())).0 {
|
|
||||||
// arg has been repeated, not allowed
|
|
||||||
return Err((arg, error::ParseErrorKind::NotUnique(opt)).into());
|
|
||||||
}
|
|
||||||
macro_rules! check_combine {
|
|
||||||
($($chars:expr),+) => {
|
|
||||||
$(if had.contains_key(&$chars) {
|
|
||||||
return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars))));
|
|
||||||
})*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match opt {
|
|
||||||
'R' => {
|
|
||||||
check_combine!['r','a'];
|
|
||||||
kind = KeySpec::RsaPrivate;
|
|
||||||
},
|
|
||||||
'r' => {
|
|
||||||
check_combine!['R','a'];
|
|
||||||
kind = KeySpec::RsaPublic;
|
|
||||||
},
|
|
||||||
'a' => {
|
|
||||||
check_combine!['r','R'];
|
|
||||||
kind = KeySpec::Aes;
|
|
||||||
},
|
|
||||||
'P' => {
|
|
||||||
check_combine!['p'];
|
|
||||||
password= Password::Yes;
|
|
||||||
},
|
|
||||||
'p' => {
|
|
||||||
check_combine!['P'];
|
|
||||||
password = Password::Specific(take_one!("P: Expected a password"));
|
|
||||||
},
|
|
||||||
'S' => {
|
|
||||||
check_combine!['s', 'a'];
|
|
||||||
signing = IsSigning::Only;
|
|
||||||
},
|
|
||||||
's' => {
|
|
||||||
check_combine!['S', 'a'];
|
|
||||||
signing = IsSigning::Yes;
|
|
||||||
},
|
|
||||||
_ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('k', opt)).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let kind = match kind {
|
|
||||||
KeySpec::Autodetect => KeyKind::Autodetect(signing),
|
|
||||||
KeySpec::RsaPublic => KeyKind::RsaPublic(signing),
|
|
||||||
KeySpec::RsaPrivate => KeyKind::RsaPrivate(signing),
|
|
||||||
KeySpec::Aes => KeyKind::Aes,
|
|
||||||
};
|
|
||||||
atoms.send(Atom::Key(kind, key_file, password)).await?;
|
|
||||||
},
|
|
||||||
Some(unknown) => return Err((arg, error::ParseErrorKind::UnknownOption(unknown)).into()),
|
|
||||||
None => reading=false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reading=false;
|
|
||||||
atoms.send(Atom::FileAuto(arg)).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
todo!()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[instrument]
|
|
||||||
pub fn parse<T: Into<String>, I: IntoIterator<Item = T> + std::fmt::Debug>(args: I) -> Result<Spec, eyre::Report>
|
|
||||||
{
|
|
||||||
let mut args = args.into_iter();
|
|
||||||
if let Some(arg) = args.next()
|
|
||||||
{
|
|
||||||
let arg =arg.into();
|
|
||||||
match arg.to_lowercase().trim() {
|
|
||||||
"--generate" => Ok(Spec::Concrete(config::Operation::GenerateKey(parse_keygen(args.map(Into::into))
|
|
||||||
.with_note(|| "Mode was `--generate`")?))),
|
|
||||||
"--help" => Ok(Spec::Concrete(config::Operation::Help)),
|
|
||||||
"--stat" => Ok(Spec::Concrete(config::Operation::KeyInfo(parse_stat(args.map(Into::into))
|
|
||||||
.with_note(|| "Mode was `--stat")?))),
|
|
||||||
_ => Ok(parse_normal(std::iter::once(arg)
|
|
||||||
.chain(args.map(Into::into)))
|
|
||||||
.with_note(|| "Mode was crypt")?.into()),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(eyre!("No arguments specified"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_normal<I: IntoIterator<Item = String>>(args: I) -> Result<OperationSpec, eyre::Report>
|
|
||||||
{
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn parse_keygen<I: IntoIterator<Item = String>>(args: I) -> Result<config::op::GenerateKey, eyre::Report>
|
|
||||||
{
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn parse_stat<I: IntoIterator<Item = String>>(args: I) -> Result<Vec<(String, config::op::Password)>, eyre::Report>
|
|
||||||
{
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
*/
|
|
@ -0,0 +1,166 @@
|
|||||||
|
//! Parser for generate mode
|
||||||
|
use super::*;
|
||||||
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
pub(super) async fn handle(mut rx: Receiver<Atom>) -> Result<config::Operation, error::ConstructError>
|
||||||
|
{
|
||||||
|
let mut had = smallmap::Map::new();
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Output
|
||||||
|
{
|
||||||
|
kind: Option<GenerateKeyKind>,
|
||||||
|
input: Option<String>,
|
||||||
|
password: Password,
|
||||||
|
input_password: Password,
|
||||||
|
format: KeyFormat,
|
||||||
|
output: Option<String>,
|
||||||
|
output_public: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
|
fn _invalidate<D: std::fmt::Display>(msg: D) -> !
|
||||||
|
{
|
||||||
|
panic!("{}. This should've been handled by `parse()`", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! invalidate {
|
||||||
|
|
||||||
|
($msg:literal) => {
|
||||||
|
_invalidate($msg);
|
||||||
|
};
|
||||||
|
($msg:literal $($tt:tt)*) => {
|
||||||
|
_invalidate(format!($msg $($tt)*));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut stage = Output{
|
||||||
|
kind: Some(match rx.recv().await.unwrap() {
|
||||||
|
Atom::Generate(GenerateAtom::Type(ty)) => ty,
|
||||||
|
_ => invalidate!("First atom is not type."),
|
||||||
|
}),
|
||||||
|
..Output::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some(Atom::Generate(atom)) = rx.recv().await
|
||||||
|
{
|
||||||
|
if GenerateAtom::hash_into(&atom, &mut had) {
|
||||||
|
return Err(error::ConstructError::AlreadyExists(format!("TODO: {:?}", atom)));
|
||||||
|
}
|
||||||
|
match atom {
|
||||||
|
GenerateAtom::Type(_) => invalidate!("Type cannot be specified twice."),
|
||||||
|
GenerateAtom::Input(input) => stage.input = Some(input),
|
||||||
|
GenerateAtom::Password(passwd) => stage.password = passwd,
|
||||||
|
GenerateAtom::Format(format) => stage.format =format,
|
||||||
|
GenerateAtom::Output(output) => stage.output = Some(output),
|
||||||
|
GenerateAtom::PublicOutput(output) => stage.output_public = Some(output),
|
||||||
|
GenerateAtom::InputPassword(passwd) => stage.input_password = passwd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_pw = stage.input_password;
|
||||||
|
let output = if let Some(GenerateKeyKind::Rsa) = stage.kind {
|
||||||
|
//RSA
|
||||||
|
config::op::GenerateKey::Rsa(config::op::Rsa {
|
||||||
|
input: stage.input.map(|inp| (inp, input_pw)),
|
||||||
|
output_priv: stage.output.ok_or_else(|| error::ConstructError::ExpectedNotPresent("output"))?,
|
||||||
|
output_pub: stage.output_public,
|
||||||
|
password: stage.password,
|
||||||
|
description: Default::default(), /* TODO */
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
//AES
|
||||||
|
config::op::GenerateKey::Aes(config::op::Aes {
|
||||||
|
input: stage.input.map(|inp| (inp, input_pw)),
|
||||||
|
output: stage.output.ok_or_else(|| error::ConstructError::ExpectedNotPresent("output"))?,
|
||||||
|
password: stage.password,
|
||||||
|
description: Default::default(), /* TODO */
|
||||||
|
})
|
||||||
|
};
|
||||||
|
Ok(config::Operation::GenerateKey(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split<'a>(input: &'a str) -> (&'a str, Option<&'a str>)
|
||||||
|
{
|
||||||
|
if let Some(index) = input.find('=') {
|
||||||
|
(&input[..index], Some(&input[(index+1)..]))
|
||||||
|
} else {
|
||||||
|
(input, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) async fn parse<I: IntoIterator<Item=String>>(args: I, atoms: &mut mpsc::Sender<Atom>) -> Result<(), error::Error>
|
||||||
|
{
|
||||||
|
let mut args = args.into_iter();
|
||||||
|
let mut reading = true;
|
||||||
|
let mut sent_output=0usize;
|
||||||
|
|
||||||
|
macro_rules! send {
|
||||||
|
($atom:expr) => {
|
||||||
|
atoms.send(Atom::Generate($atom))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (mode, _) = {
|
||||||
|
let arg = args.next();
|
||||||
|
match arg.as_ref().map(|x| x.trim().to_uppercase()).as_deref() {
|
||||||
|
Some("RSA") => (GenerateKeyKind::Rsa, send!(GenerateAtom::Type(GenerateKeyKind::Rsa)).await?),
|
||||||
|
Some("AES") => (GenerateKeyKind::Aes, send!(GenerateAtom::Type(GenerateKeyKind::Aes)).await?),
|
||||||
|
_ => return Err(error::Error::ExpectedKind(arg)),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
while let Some(arg) = args.next()
|
||||||
|
{
|
||||||
|
macro_rules! take_one {
|
||||||
|
($msg:literal $($tt:tt)*) => {
|
||||||
|
match args.next() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return Err((arg, error::ParseErrorKind::ExpectedArg(format!($msg $($tt)*))).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reading && arg.starts_with("--") {
|
||||||
|
let (key, value) = split(&arg);
|
||||||
|
match &key.to_lowercase().trim()[2..] {
|
||||||
|
"input" => send!(GenerateAtom::Input(take_one!("--input: Expected filename"))).await?,
|
||||||
|
"password" if value.is_some() => {
|
||||||
|
let value = value.unwrap();
|
||||||
|
send!(GenerateAtom::Password(Password::Specific(value.to_owned()))).await?;
|
||||||
|
},
|
||||||
|
"password" => send!(GenerateAtom::Password(Password::Yes)).await?,
|
||||||
|
"format" if mode.is_rsa() =>
|
||||||
|
send!(GenerateAtom::Format(match take_one!("--format: Expected output format").to_uppercase().trim() {
|
||||||
|
"PEM" => KeyFormat::PEM,
|
||||||
|
"BIN" => KeyFormat::Bin,
|
||||||
|
"TEXT" => KeyFormat::Text,
|
||||||
|
__ => return Err((arg, error::ParseErrorKind::UnknownKeyFormat("rsa")).into()),
|
||||||
|
})).await?,
|
||||||
|
"format" =>
|
||||||
|
send!(GenerateAtom::Format(match take_one!("--format: Expected output format").to_uppercase().trim() {
|
||||||
|
"BIN" => KeyFormat::Bin,
|
||||||
|
"TEXT" => KeyFormat::Text,
|
||||||
|
__ => return Err((arg, error::ParseErrorKind::UnknownKeyFormat("aes")).into()),
|
||||||
|
})).await?,
|
||||||
|
_ => return Err((arg, error::ParseErrorKind::UnexpectedArg(None)).into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reading=false;
|
||||||
|
match sent_output
|
||||||
|
{
|
||||||
|
0 => send!(GenerateAtom::Output(arg)).await?,
|
||||||
|
1 if mode.is_rsa() => send!(GenerateAtom::PublicOutput(arg)).await?,
|
||||||
|
_ => return Err((arg, error::ParseErrorKind::UnexpectedArg(None)).into()),
|
||||||
|
}
|
||||||
|
sent_output +=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,257 @@
|
|||||||
|
//! Arg parsing
|
||||||
|
#![allow(unused_macros)]
|
||||||
|
use super::*;
|
||||||
|
use tokio::{
|
||||||
|
sync::{
|
||||||
|
mpsc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
iter,
|
||||||
|
};
|
||||||
|
use eyre::eyre;
|
||||||
|
use config::op::{self, Password};
|
||||||
|
use error::ParseErrorKind;
|
||||||
|
|
||||||
|
mod normal;
|
||||||
|
mod generate;
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum IsSigning
|
||||||
|
{
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
Only,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IsSigning
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self
|
||||||
|
{
|
||||||
|
Self::No
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum KeyKind
|
||||||
|
{
|
||||||
|
Autodetect(IsSigning),
|
||||||
|
RsaPublic(IsSigning),
|
||||||
|
RsaPrivate(IsSigning), // is signing key?
|
||||||
|
Aes,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum TranslationMode {
|
||||||
|
Autodetect,
|
||||||
|
Encrypt,
|
||||||
|
Decrypt,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TranslationMode
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self
|
||||||
|
{
|
||||||
|
Self::Autodetect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum KeyFormat
|
||||||
|
{
|
||||||
|
Bin,
|
||||||
|
Text,
|
||||||
|
PEM,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KeyFormat
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self
|
||||||
|
{
|
||||||
|
Self::Bin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum GenerateKeyKind
|
||||||
|
{
|
||||||
|
Rsa,
|
||||||
|
Aes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenerateKeyKind
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_rsa(&self) -> bool
|
||||||
|
{
|
||||||
|
if let Self::Rsa = self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum GenerateAtom
|
||||||
|
{
|
||||||
|
Type(GenerateKeyKind),
|
||||||
|
Input(String),
|
||||||
|
InputPassword(Password),
|
||||||
|
Password(Password),
|
||||||
|
Format(KeyFormat),
|
||||||
|
Output(String),
|
||||||
|
PublicOutput(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenerateAtom
|
||||||
|
{
|
||||||
|
pub fn hash_into(&self, map: &mut smallmap::Map<u64, ()>) -> bool
|
||||||
|
{
|
||||||
|
use std::hash::{Hash,Hasher,};
|
||||||
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
let vl = hasher.finish();
|
||||||
|
if map.contains_key(&vl) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
map.insert(hasher.finish(),());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum Atom
|
||||||
|
{
|
||||||
|
Key(KeyKind, String, Password),
|
||||||
|
NonSpecific,
|
||||||
|
FileAuto(String),
|
||||||
|
FileSpecific(String, String, TranslationMode),
|
||||||
|
SetMode(config::op::Mode),
|
||||||
|
|
||||||
|
Generate(GenerateAtom),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom
|
||||||
|
{
|
||||||
|
pub fn hash_into(&self, map: &mut smallmap::Map<u64, ()>) -> bool
|
||||||
|
{
|
||||||
|
use std::hash::{Hash,Hasher,};
|
||||||
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
let vl = hasher.finish();
|
||||||
|
if map.contains_key(&vl) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
map.insert(hasher.finish(),());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
pub async fn parse<I: IntoIterator<Item=String> + std::fmt::Debug>(args: I) -> Result<config::Operation, eyre::Report>
|
||||||
|
{
|
||||||
|
let (mut tx, rx) = mpsc::channel(1);
|
||||||
|
|
||||||
|
macro_rules! unwrap_handler {
|
||||||
|
($handler:expr) => ($handler.await
|
||||||
|
.wrap_err_with(|| eyre!("Atom parser panicked")
|
||||||
|
.with_warning(|| "This usually indicates a bug in the program, not invalid input.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args = args.into_iter();
|
||||||
|
let handler = if let Some(arg) = args.next()
|
||||||
|
{
|
||||||
|
match arg.to_lowercase().trim() {
|
||||||
|
"--generate" => {
|
||||||
|
let handler = tokio::spawn(generate::handle(rx));
|
||||||
|
match generate::parse(std::iter::once(arg)
|
||||||
|
.chain(args.map(Into::into)), &mut tx).await
|
||||||
|
{
|
||||||
|
// The `handler` has dropped `rx` and returned an error, let unwrap_handler down there handle this.
|
||||||
|
Err(error::Error::AtomInternal) => Ok(()),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
.with_note(|| "In parsing of generate operation spec")?;
|
||||||
|
|
||||||
|
handler
|
||||||
|
},
|
||||||
|
"--help" => return Ok(config::Operation::Help),
|
||||||
|
"--stat" => todo!(),
|
||||||
|
_ => {
|
||||||
|
let handler = tokio::spawn(normal::handle(rx));
|
||||||
|
match normal::parse(std::iter::once(arg)
|
||||||
|
.chain(args.map(Into::into)), &mut tx).await
|
||||||
|
{
|
||||||
|
// The `handler` has dropped `rx` and returned an error, let unwrap_handler down there handle this.
|
||||||
|
Err(error::Error::AtomInternal) => Ok(()),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
.with_note(|| "In parsing of normal operation spec")?;
|
||||||
|
|
||||||
|
handler
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(eyre!("No arguments specified"));
|
||||||
|
};
|
||||||
|
|
||||||
|
match unwrap_handler!(handler)? {
|
||||||
|
Err(error) => {
|
||||||
|
return Err(error)
|
||||||
|
.wrap_err_with(|| eyre!("Failed to construct operation from arguments"))
|
||||||
|
.with_note(|| "In constructing of operation from spec");
|
||||||
|
},
|
||||||
|
Ok(operation) => {
|
||||||
|
//TODO: Validate operation here?
|
||||||
|
Ok(operation)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[instrument]
|
||||||
|
pub fn parse<T: Into<String>, I: IntoIterator<Item = T> + std::fmt::Debug>(args: I) -> Result<Spec, eyre::Report>
|
||||||
|
{
|
||||||
|
let mut args = args.into_iter();
|
||||||
|
if let Some(arg) = args.next()
|
||||||
|
{
|
||||||
|
let arg =arg.into();
|
||||||
|
match arg.to_lowercase().trim() {
|
||||||
|
"--generate" => Ok(Spec::Concrete(config::Operation::GenerateKey(parse_keygen(args.map(Into::into))
|
||||||
|
.with_note(|| "Mode was `--generate`")?))),
|
||||||
|
"--help" => Ok(Spec::Concrete(config::Operation::Help)),
|
||||||
|
"--stat" => Ok(Spec::Concrete(config::Operation::KeyInfo(parse_stat(args.map(Into::into))
|
||||||
|
.with_note(|| "Mode was `--stat")?))),
|
||||||
|
_ => Ok(parse_normal(std::iter::once(arg)
|
||||||
|
.chain(args.map(Into::into)))
|
||||||
|
.with_note(|| "Mode was crypt")?.into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(eyre!("No arguments specified"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_normal<I: IntoIterator<Item = String>>(args: I) -> Result<OperationSpec, eyre::Report>
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn parse_keygen<I: IntoIterator<Item = String>>(args: I) -> Result<config::op::GenerateKey, eyre::Report>
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn parse_stat<I: IntoIterator<Item = String>>(args: I) -> Result<Vec<(String, config::op::Password)>, eyre::Report>
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
*/
|
@ -0,0 +1,203 @@
|
|||||||
|
//! Parser for normal mode
|
||||||
|
use super::*;
|
||||||
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
pub(super) async fn handle(mut rx: Receiver<Atom>) -> Result<config::Operation, error::ConstructError>
|
||||||
|
{
|
||||||
|
let mut output = op::Normal::default();
|
||||||
|
let mut had = smallmap::Map::new();
|
||||||
|
while let Some(atom) = rx.recv().await {
|
||||||
|
debug!("[Normal] recv atom: {:?}", atom);
|
||||||
|
// Construct `Normal` with atoms
|
||||||
|
|
||||||
|
if Atom::hash_into(&atom, &mut had) {
|
||||||
|
return Err(error::ConstructError::AlreadyExists(format!("TODO: {:?}", atom)));
|
||||||
|
}
|
||||||
|
|
||||||
|
match atom {
|
||||||
|
Atom::NonSpecific => output.in_place = true,
|
||||||
|
Atom::SetMode(mode) => output.mode = Some(mode),
|
||||||
|
Atom::FileAuto(file) => output.files.push((file, None, None)),
|
||||||
|
Atom::FileSpecific(finput, foutput, mode) => {
|
||||||
|
let mode = match mode {
|
||||||
|
TranslationMode::Autodetect => resolve::find_file_mode(&finput).await?,
|
||||||
|
TranslationMode::Encrypt => op::Mode::Encrypt,
|
||||||
|
TranslationMode::Decrypt => op::Mode::Decrypt,
|
||||||
|
};
|
||||||
|
output.files.push((finput, Some(foutput), Some(mode)));
|
||||||
|
},
|
||||||
|
Atom::Key(kind, string, password) => {
|
||||||
|
let (kind, sign) = match kind {
|
||||||
|
KeyKind::Autodetect(s) => (resolve::find_key_mode(&string).await?, s),
|
||||||
|
KeyKind::RsaPrivate(s) => (config::KeyKind::RsaPrivate, s),
|
||||||
|
KeyKind::RsaPublic(s) => (config::KeyKind::RsaPublic, s),
|
||||||
|
KeyKind::Aes => (config::KeyKind::Aes, Default::default()),
|
||||||
|
};
|
||||||
|
if sign != IsSigning::No && kind == config::KeyKind::Aes {
|
||||||
|
warn!("Key was detected to not be RSA, option to sign is set and ignored.");
|
||||||
|
} else {
|
||||||
|
output.sign.push((string.clone(), password.clone()));
|
||||||
|
}
|
||||||
|
match kind {
|
||||||
|
config::KeyKind::Aes => output.aes.push((string, password)),
|
||||||
|
config::KeyKind::RsaPublic => output.rsa.push((string, password)),
|
||||||
|
config::KeyKind::RsaPrivate => output.rsa.push((string, password))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Atom::Generate(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(config::Operation::Normal(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn parse<I: IntoIterator<Item=String>>(args: I, atoms: &mut mpsc::Sender<Atom>) -> Result<(), error::Error>
|
||||||
|
{
|
||||||
|
let mut args = args.into_iter();
|
||||||
|
let mut reading=true;
|
||||||
|
|
||||||
|
let mut mode_set=false;
|
||||||
|
while let Some(arg) = args.next()
|
||||||
|
{
|
||||||
|
|
||||||
|
macro_rules! take_one {
|
||||||
|
($msg:literal $($tt:tt)*) => {
|
||||||
|
match args.next() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return Err((arg, ParseErrorKind::ExpectedArg(format!($msg $($tt)*))).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reading && arg.starts_with("-") {
|
||||||
|
let mut opt = arg.chars().skip(1);
|
||||||
|
|
||||||
|
macro_rules! check_mode_set
|
||||||
|
{
|
||||||
|
() => {
|
||||||
|
if mode_set {
|
||||||
|
return Err((arg, error::ParseErrorKind::ModeSet).into());
|
||||||
|
} else {
|
||||||
|
mode_set=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match opt.next() {
|
||||||
|
Some('a') => check_mode_set!(),
|
||||||
|
Some('e') => {
|
||||||
|
check_mode_set!();
|
||||||
|
atoms.send(Atom::SetMode(config::op::Mode::Encrypt)).await?;
|
||||||
|
},
|
||||||
|
Some('d') => {
|
||||||
|
check_mode_set!();
|
||||||
|
atoms.send(Atom::SetMode(config::op::Mode::Decrypt)).await?;
|
||||||
|
},
|
||||||
|
Some('i') => match opt.next() {
|
||||||
|
None => atoms.send(Atom::NonSpecific).await?,
|
||||||
|
Some(r) => return Err((ParseErrorKind::BadTail('i', iter::once(r).chain(opt).collect()), arg).swap().into()),
|
||||||
|
},
|
||||||
|
Some('t') => {
|
||||||
|
let input = take_one!("t: Expected input file");
|
||||||
|
let output = take_one!("t: Expected output file");
|
||||||
|
let mut trans = TranslationMode::default();
|
||||||
|
|
||||||
|
let mut had = smallmap::Map::new();
|
||||||
|
while let Some(opt) = opt.next() {
|
||||||
|
if (had.contains_key(&opt), had.insert(opt, ())).0 {
|
||||||
|
// arg has been repeated, not allowed
|
||||||
|
return Err((arg, error::ParseErrorKind::NotUnique(opt)).into());
|
||||||
|
}
|
||||||
|
macro_rules! check_combine {
|
||||||
|
($($chars:expr),+) => {
|
||||||
|
$(if had.contains_key(&$chars) {
|
||||||
|
return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars))));
|
||||||
|
})*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
match opt {
|
||||||
|
'e' => trans= TranslationMode::Encrypt,
|
||||||
|
'd' => trans= TranslationMode::Decrypt,
|
||||||
|
_ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('t', opt)).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
atoms.send(Atom::FileSpecific(input, output, trans)).await?;
|
||||||
|
},
|
||||||
|
Some('k') => {
|
||||||
|
let key_file = take_one!("k: Expected a key file");
|
||||||
|
enum KeySpec {
|
||||||
|
Autodetect,
|
||||||
|
RsaPrivate,
|
||||||
|
RsaPublic,
|
||||||
|
Aes,
|
||||||
|
}
|
||||||
|
let mut kind = KeySpec::Autodetect;
|
||||||
|
let mut password = Password::default();
|
||||||
|
let mut signing = IsSigning::default();
|
||||||
|
|
||||||
|
let mut had = smallmap::Map::new();
|
||||||
|
while let Some(opt) = opt.next() {
|
||||||
|
if (had.contains_key(&opt), had.insert(opt, ())).0 {
|
||||||
|
// arg has been repeated, not allowed
|
||||||
|
return Err((arg, error::ParseErrorKind::NotUnique(opt)).into());
|
||||||
|
}
|
||||||
|
macro_rules! check_combine {
|
||||||
|
($($chars:expr),+) => {
|
||||||
|
$(if had.contains_key(&$chars) {
|
||||||
|
return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars))));
|
||||||
|
})*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
match opt {
|
||||||
|
'R' => {
|
||||||
|
check_combine!['r','a'];
|
||||||
|
kind = KeySpec::RsaPrivate;
|
||||||
|
},
|
||||||
|
'r' => {
|
||||||
|
check_combine!['R','a'];
|
||||||
|
kind = KeySpec::RsaPublic;
|
||||||
|
},
|
||||||
|
'a' => {
|
||||||
|
check_combine!['r','R'];
|
||||||
|
kind = KeySpec::Aes;
|
||||||
|
},
|
||||||
|
'P' => {
|
||||||
|
check_combine!['p'];
|
||||||
|
password= Password::Yes;
|
||||||
|
},
|
||||||
|
'p' => {
|
||||||
|
check_combine!['P'];
|
||||||
|
password = Password::Specific(take_one!("P: Expected a password"));
|
||||||
|
},
|
||||||
|
'S' => {
|
||||||
|
check_combine!['s', 'a'];
|
||||||
|
signing = IsSigning::Only;
|
||||||
|
},
|
||||||
|
's' => {
|
||||||
|
check_combine!['S', 'a'];
|
||||||
|
signing = IsSigning::Yes;
|
||||||
|
},
|
||||||
|
_ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('k', opt)).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind = match kind {
|
||||||
|
KeySpec::Autodetect => KeyKind::Autodetect(signing),
|
||||||
|
KeySpec::RsaPublic => KeyKind::RsaPublic(signing),
|
||||||
|
KeySpec::RsaPrivate => KeyKind::RsaPrivate(signing),
|
||||||
|
KeySpec::Aes => KeyKind::Aes,
|
||||||
|
};
|
||||||
|
atoms.send(Atom::Key(kind, key_file, password)).await?;
|
||||||
|
},
|
||||||
|
Some(unknown) => return Err((arg, error::ParseErrorKind::UnknownOption(unknown)).into()),
|
||||||
|
None => reading=false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reading=false;
|
||||||
|
atoms.send(Atom::FileAuto(arg)).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in new issue