You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
433 lines
12 KiB
433 lines
12 KiB
use super::*;
|
|
use std::{
|
|
convert::{
|
|
TryFrom,TryInto,
|
|
},
|
|
};
|
|
|
|
mod builder {
|
|
use super::*;
|
|
|
|
#[derive(PartialEq,Eq,Debug)]
|
|
pub enum OperationMode
|
|
{
|
|
Normal,
|
|
KeyGen(GenerateKey),
|
|
Help,
|
|
}
|
|
|
|
#[derive(PartialEq,Eq,Debug)]
|
|
pub struct KeyGenOps
|
|
{
|
|
pub format: KeyOutputFormat,
|
|
pub cycle: Option<String>,
|
|
pub use_password: bool,
|
|
pub password: Option<String>,
|
|
pub output: Option<String>,
|
|
pub private_only: bool,
|
|
pub output_public: Option<String>,
|
|
pub public_only: bool,
|
|
}
|
|
|
|
impl Default for KeyGenOps
|
|
{
|
|
fn default() -> Self {
|
|
Self {
|
|
format: KeyOutputFormat::Binary,
|
|
cycle: None,
|
|
use_password: false,
|
|
password: None,
|
|
output: None,
|
|
private_only: false,
|
|
output_public: None,
|
|
public_only: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq,Eq,Debug)]
|
|
pub struct NormalOps
|
|
{
|
|
pub mode: super::super::OperationMode,
|
|
pub rsa: Vec<String>,
|
|
pub sign: Vec<String>,
|
|
pub auto_sign: bool,
|
|
pub aes: Vec<String>,
|
|
|
|
pub translate: Vec<(String, Option<String>)>,
|
|
pub in_place: bool,
|
|
pub sync: bool,
|
|
}
|
|
|
|
impl Default for NormalOps
|
|
{
|
|
fn default() -> Self
|
|
{
|
|
Self {
|
|
mode: super::super::OperationMode::Autodetect,
|
|
rsa: Vec::new(),
|
|
sign: Vec::new(),
|
|
auto_sign: false,
|
|
aes: Vec::new(),
|
|
translate: Vec::new(),
|
|
in_place: false,
|
|
sync: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq,Eq,Debug)]
|
|
pub struct Operation
|
|
{
|
|
pub mode: OperationMode,
|
|
|
|
pub verbose: bool,
|
|
pub keygen: KeyGenOps,
|
|
pub normal: NormalOps,
|
|
}
|
|
|
|
impl Default for Operation
|
|
{
|
|
fn default() -> Self
|
|
{
|
|
Self {
|
|
mode: OperationMode::Help,
|
|
keygen: KeyGenOps::default(),
|
|
normal: Default::default(),
|
|
verbose: false,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub 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 async fn parse<I,T>(input: &mut I) -> Result<Operation, Error>
|
|
where I: Iterator<Item=T>,
|
|
T: AsRef<str>
|
|
{
|
|
let mut i=0;
|
|
|
|
let mut op = builder::Operation::default();
|
|
let mut reading = true;
|
|
let mut rest = Vec::new();
|
|
let mut cfg_files = Vec::new();
|
|
|
|
macro_rules! take_one
|
|
{
|
|
($message:expr) => {(i+=1, input.next().ok_or_else(|| $message)?).1.as_ref()}
|
|
}
|
|
|
|
while let Some(arg) = input.next()
|
|
{
|
|
let arg = arg.as_ref();
|
|
'next: {
|
|
if i == 0 {
|
|
// First arg
|
|
match arg {
|
|
"--help" => return Ok(Operation::Help),
|
|
"--keygen" => {
|
|
op.keygen = builder::KeyGenOps::default();
|
|
|
|
op.mode = builder::OperationMode::KeyGen(match take_one!("Expected a key type").to_uppercase().as_str() {
|
|
"AES" => GenerateKey::Aes,
|
|
"RSA" => GenerateKey::Rsa,
|
|
d => return Err(format!("Unknown key type `{}`. Expected `RSA` or `AES`.",d))?,
|
|
});
|
|
break 'next;
|
|
},
|
|
_ => {
|
|
op.mode = builder::OperationMode::Normal;
|
|
},
|
|
}
|
|
}
|
|
|
|
match op.mode {
|
|
builder::OperationMode::KeyGen(GenerateKey::Rsa) => {
|
|
let (name, value) = split(arg);
|
|
|
|
'cont: {
|
|
match name.to_lowercase().as_str() {
|
|
"-c" => {
|
|
cfg_files.push(take_one!("Expected a filename").to_owned());
|
|
},
|
|
"--verbose" if value.is_some() => op.verbose = value.unwrap().to_lowercase().trim() == "true",
|
|
"--verbose" => op.verbose = true,
|
|
"--cycle" if value.is_some() => op.keygen.cycle = Some(value.unwrap().to_owned()),
|
|
"--cycle" => op.keygen.cycle = Some(take_one!("Expected a filename").to_owned()),
|
|
"--format" if value.is_some() => op.keygen.format = value.unwrap().parse()?,
|
|
"--format" => op.keygen.format = take_one!("Expected an output format").parse()?,
|
|
"--password" if value.is_some() => {
|
|
op.keygen.use_password = true;
|
|
op.keygen.password = Some(value.unwrap().to_owned());
|
|
},
|
|
"--password" => op.keygen.use_password = true,
|
|
"--private-only" => op.keygen.private_only = true,
|
|
"--public-only" => op.keygen.public_only = true,
|
|
_ => break 'cont,
|
|
}
|
|
break 'next;
|
|
};
|
|
},
|
|
builder::OperationMode::KeyGen(GenerateKey::Aes) => {
|
|
let (name, value) = split(arg);
|
|
'cont: {
|
|
match name.to_lowercase().as_str() {
|
|
"--verbose" if value.is_some() => op.verbose = value.unwrap().to_lowercase().trim() == "true",
|
|
"--verbose" => op.verbose = true,
|
|
"--cycle" if value.is_some() => op.keygen.cycle = Some(value.unwrap().to_owned()),
|
|
"--cycle" => op.keygen.cycle = Some(take_one!("Expected a filename").to_owned()),
|
|
"--format" if value.is_some() => op.keygen.format = value.unwrap().parse()?,
|
|
"--format" => op.keygen.format = take_one!("Expected an output format").parse()?,
|
|
"--password" if value.is_some() => {
|
|
op.keygen.use_password = true;
|
|
op.keygen.password = Some(value.unwrap().to_owned());
|
|
},
|
|
"--password" => op.keygen.use_password = true,
|
|
_ => break 'cont,
|
|
}
|
|
break 'next;
|
|
}
|
|
},
|
|
builder::OperationMode::Normal => {
|
|
let (name, value) = split(arg);
|
|
if reading {
|
|
'cont: {
|
|
match name.to_lowercase().as_str() {
|
|
"--rsa" if value.is_some() => op.normal.rsa.extend(value.unwrap().split(',').map(|x| x.to_owned())),
|
|
"--rsa" => op.normal.rsa.push(take_one!("Expected an RSA key filename").to_owned()),
|
|
"--sign" if value.is_some() => op.normal.sign.extend(value.unwrap().split(',').map(|x| x.to_owned())),
|
|
"--sign" => op.normal.sign.push(take_one!("Expected an RSA signing key filename").to_owned()),
|
|
"--auto-sign" => op.normal.auto_sign = true,
|
|
"--aes" if value.is_some() => op.normal.aes.extend(value.unwrap().split(',').map(|x| x.to_owned())),
|
|
"--aes" => op.normal.aes.push(take_one!("Expected an AES key filename").to_owned()),
|
|
"--translate" if value.is_some() => {
|
|
let mut value = value.unwrap().split(',');
|
|
let input = value.next().ok_or("Invalid --translate singleton")?.to_owned();
|
|
let output = value.next().ok_or("Invalid --translate singleton (expected an output)")?.to_owned();
|
|
op.normal.translate.push((input, Some(output)));
|
|
},
|
|
"--translate" => {
|
|
let input = take_one!("No --translate input").to_owned();
|
|
let output = take_one!("No --translate output").to_owned();
|
|
op.normal.translate.push((input.to_owned(), Some(output.to_owned())));
|
|
},
|
|
"--in-place" => op.normal.in_place = true,
|
|
"--verbose" => op.verbose = true,
|
|
"--sync" => op.normal.sync = true,
|
|
|
|
"-e" => {
|
|
op.normal.mode = super::OperationMode::Encrypt;
|
|
},
|
|
"-d" => {
|
|
op.normal.mode = super::OperationMode::Decrypt;
|
|
},
|
|
"-v" => {
|
|
op.normal.mode = super::OperationMode::Verify;
|
|
},
|
|
|
|
"--" => reading = false,
|
|
_ => {
|
|
reading = false;
|
|
break 'cont;
|
|
},
|
|
}
|
|
break 'next;
|
|
};
|
|
}
|
|
},
|
|
_ => return Err("Try `--help`")?,
|
|
}
|
|
rest.push(arg.to_owned());
|
|
};
|
|
i+=1;
|
|
|
|
}
|
|
|
|
// Add `rest`
|
|
match op.mode {
|
|
builder::OperationMode::Normal => {
|
|
if rest.len() < 1 {
|
|
return Err("Expected at least one input file")?;
|
|
}
|
|
op.normal.translate.extend(rest.into_iter().map(|x| (x, None)));
|
|
},
|
|
builder::OperationMode::KeyGen(GenerateKey::Aes) => {
|
|
if rest.len() > 1 {
|
|
return Err("Expected only one output file")?;
|
|
} else if rest.len() < 1 && op.keygen.cycle.is_none() {
|
|
return Err("Expected one output file or `--cycle`")?;
|
|
} else if rest.len()>0 {
|
|
op.keygen.output = Some(rest.into_iter().next().unwrap());
|
|
}
|
|
},
|
|
builder::OperationMode::KeyGen(GenerateKey::Rsa) => {
|
|
if rest.len() > 2 {
|
|
return Err("Expected one or 2 output files")?;
|
|
} else if rest.len() < 1 && op.keygen.cycle.is_none() {
|
|
return Err("Expected at least one output or `--cycle`")?;
|
|
} else if rest.len() == 2 {
|
|
let mut iter = rest.into_iter();
|
|
|
|
if op.keygen.public_only {
|
|
return Err("Expected one public output")?;
|
|
} else {
|
|
op.keygen.output = Some(iter.next().unwrap());
|
|
op.keygen.output_public = Some(iter.next().unwrap());
|
|
}
|
|
} else {
|
|
let mut iter = rest.into_iter();
|
|
if op.keygen.public_only {
|
|
op.keygen.output_public = Some(iter.next().ok_or("`--public-only` requires one output.")?);
|
|
} else {
|
|
op.keygen.output = iter.next();
|
|
}
|
|
}
|
|
},
|
|
_ => return Err("No params specified. Try `--help`.")?,
|
|
};
|
|
|
|
// Ensure valid
|
|
let mut op = op.try_into()?;
|
|
|
|
// Add spec
|
|
for spec in cfg_files.into_iter()
|
|
{
|
|
spec::Spec::re //TODO: Fuck this whole module. Just rewrite all of this crap shit FUCK
|
|
}
|
|
|
|
Ok(op)
|
|
}
|
|
|
|
|
|
impl TryFrom<builder::Operation> for Operation
|
|
{
|
|
type Error = Error;
|
|
|
|
fn try_from(op: builder::Operation) -> Result<Self, Self::Error>
|
|
{
|
|
match op.mode {
|
|
builder::OperationMode::Normal => {
|
|
|
|
if op.keygen != builder::KeyGenOps::default() {
|
|
return Err("Invalid mode for params.")?;
|
|
}
|
|
|
|
if op.normal.rsa.len() < 1 && op.normal.auto_sign {
|
|
warn!("`--auto-sign` here does nothing.");
|
|
}
|
|
if op.normal.aes.len() < 1 && op.normal.rsa.len() < 1 {
|
|
return Err("No keys specified.")?;
|
|
}
|
|
if op.normal.translate.len() < 1 {
|
|
return Err("No files specified.")?;
|
|
}
|
|
#[cfg(not(feature="threads"))]
|
|
if op.normal.sync {
|
|
warn!("`--sync` here does nothing, binary not compiled with `threads` feature.");
|
|
}
|
|
|
|
// Number of inputs without specified outputs
|
|
let autodetect_inputs = op.normal.translate.iter().filter(|x| x.1.is_none()).count();
|
|
if op.normal.in_place && autodetect_inputs < 1 {
|
|
warn!("`--in-place` here does nothing, no autodetected outputs.");
|
|
}
|
|
|
|
Ok(Operation::Normal{
|
|
mode: op.normal.mode,
|
|
rsa: op.normal.rsa.into_iter().map(|x| x.into()).collect(),
|
|
sign: op.normal.sign.into_iter().map(|x| x.into()).collect(),
|
|
auto_sign: op.normal.auto_sign,
|
|
aes: op.normal.aes.into_iter().map(|x| x.into()).collect(),
|
|
translate: op.normal.translate.into_iter().map(|x| (x.0.into(), x.1.and_then(|y| Some(y.into())))).collect(),
|
|
in_place: op.normal.in_place,
|
|
verbose: op.verbose,
|
|
#[cfg(feature="threads")]
|
|
sync: op.normal.sync,
|
|
})
|
|
},
|
|
builder::OperationMode::KeyGen(GenerateKey::Rsa) => {
|
|
if op.normal != builder::NormalOps::default() {
|
|
return Err("Invalid mode for params.")?;
|
|
}
|
|
|
|
if op.keygen.public_only && op.keygen.private_only {
|
|
return Err("`--private-only` and `--public-only` will generate nothing.")?;
|
|
}
|
|
if op.keygen.public_only && op.keygen.cycle.is_none() {
|
|
warn!("`--public-only` is only useful with `--cycle`.");
|
|
}
|
|
if op.keygen.output.is_none()
|
|
&& op.keygen.output_public.is_none()
|
|
&& op.keygen.cycle.is_none() {
|
|
return Err("No output found.")?;
|
|
}
|
|
if op.keygen.public_only && op.keygen.cycle.is_none() && op.keygen.output_public.is_none()
|
|
{
|
|
return Err("`--public-only` requires one output or `--cycle`.")?;
|
|
}
|
|
if op.keygen.private_only && op.keygen.output_public.is_some()
|
|
{
|
|
warn!("Unused public key output.");
|
|
}
|
|
|
|
Ok(Operation::KeyGen{
|
|
key_type: GenerateKey::Rsa,
|
|
format: op.keygen.format,
|
|
cycle: op.keygen.cycle.and_then(|x| Some(x.into())),
|
|
use_password: op.keygen.use_password,
|
|
output: op.keygen.output.and_then(|x| Some(x.into())),
|
|
private_only: op.keygen.private_only,
|
|
public_only: op.keygen.public_only,
|
|
output_public: op.keygen.output_public.and_then(|x| Some(x.into())),
|
|
password: op.keygen.password,
|
|
verbose: op.verbose,
|
|
})
|
|
},
|
|
builder::OperationMode::KeyGen(GenerateKey::Aes) => {
|
|
if op.normal != builder::NormalOps::default() {
|
|
return Err("Invalid mode for params.")?;
|
|
}
|
|
|
|
if op.keygen.private_only ||
|
|
op.keygen.public_only ||
|
|
op.keygen.output_public.is_some()
|
|
{
|
|
return Err("Invalid params for mode.")?;
|
|
}
|
|
if !op.keygen.format.is_aes_okay()
|
|
{
|
|
return Err("This output format is not valid for AES key.")?;
|
|
}
|
|
|
|
|
|
if op.keygen.cycle.is_none() && op.keygen.output.is_none() {
|
|
return Err("No output set.")?;
|
|
}
|
|
|
|
Ok(Operation::KeyGen{
|
|
key_type: GenerateKey::Aes,
|
|
format: op.keygen.format,
|
|
cycle: op.keygen.cycle.and_then(|x| Some(x.into())),
|
|
use_password: op.keygen.use_password,
|
|
output: op.keygen.output.and_then(|x| Some(x.into())),
|
|
password: op.keygen.password,
|
|
verbose: op.verbose,
|
|
|
|
private_only: false,
|
|
public_only: false,
|
|
output_public: None,
|
|
})
|
|
},
|
|
builder::OperationMode::Help => Ok(Operation::Help),
|
|
}
|
|
}
|
|
}
|