From e8345b374d8c52cd292d33e80370a53e251349f0 Mon Sep 17 00:00:00 2001 From: Kiyomichi Kosaka Date: Thu, 22 Dec 2022 12:07:13 +0000 Subject: [PATCH] Implement gen command, to generate samples. --- Cargo.toml | 1 + src/commands.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/parser.rs | 8 +++++++- src/password.rs | 13 +++++++++++++ src/repl.rs | 5 ++--- src/structs.rs | 3 ++- 6 files changed, 72 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 858d3ff..8e4641f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ rpassword = "7.2.0" shlex = "1.1.0" shellexpand = "3.0.0" scopeguard = "1.1.0" +rand = "0.8.5" diff --git a/src/commands.rs b/src/commands.rs index 3cdc227..0d47cb9 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -5,10 +5,13 @@ use std::collections::{HashMap, HashSet}; use std::fs; use std::io::{BufRead, BufReader}; use std::io::{BufWriter, Write}; +use std::rc::Rc; +use std::cmp::min; +use rand::{thread_rng, Rng}; use crate::parser::command_parser; use crate::password::fix_password_recursion; -use crate::password::{Name, PasswordRef}; +use crate::password::{Name, Password, PasswordRef}; use crate::repl::LKEval; use crate::structs::{LKOut, Radix, CORRECT_FILE, DUMP_FILE}; use crate::utils::{call_cmd_with_input, get_cmd_args_from_command, get_copy_command_from_env}; @@ -409,4 +412,47 @@ impl<'a> LKEval<'a> { Err(e) => out.e(format!("error: failed to write: {}", e.to_string())), }; } + + pub fn cmd_gen(&self, out: &LKOut, num: &u32, name: &PasswordRef) { + lazy_static! { + static ref RE: Regex = Regex::new(r"^.+?(G+|X+)$").unwrap(); + } + let mut rng = thread_rng(); + let num: usize = (*num).try_into().unwrap(); + let pwd = name.borrow(); + let secret = "a".to_string(); + let mut genpwds: Vec<(PasswordRef, String)> = Vec::new(); + match RE.captures(pwd.name.as_ref()) { + Some(caps) => { + let gen = &caps[1]; + if gen.starts_with("G") { + let name = pwd.name.trim_end_matches('G'); + for num in 1..10_u32.pow(gen.len().try_into().unwrap()) { + let npwd = Password::from_password(&pwd); + npwd.borrow_mut().name = Rc::new(format!("{}{}", name, num).to_string()); + let pass = npwd.borrow().encode(&secret); + genpwds.push((npwd, pass)); + } + } else { + let name = gen.trim_end_matches('X'); + let num = rng.gen_range(1..10_u32.pow(gen.len().try_into().unwrap())); + let npwd = Password::from_password(&pwd); + npwd.borrow_mut().name = Rc::new(format!("{}{}", name, num).to_string()); + let pass = npwd.borrow().encode(&secret); + genpwds.push((npwd, pass)); + } + } + None => { + let npwd = Password::from_password(&pwd); + let pass = npwd.borrow().encode(&secret); + genpwds.push((npwd, pass)); + } + } + genpwds.sort_by(|a, b| b.1.len().cmp(&a.1.len())); + out.o(format!("{:>30}{:>4} {}", "Name", "Len", "Password")); + for num in (genpwds.len()-min(genpwds.len(), num))..genpwds.len() { + let (pwd, pass) = (genpwds[num].0.borrow(), genpwds[num].1.to_string()); + out.o(format!("{:>30}{:>4} {}", pwd.to_string(), pass.len(), pass)); + } + } } diff --git a/src/parser.rs b/src/parser.rs index 5d4536f..aeaa6f5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -21,6 +21,7 @@ peg::parser! { / dump_cmd() / dump_def_cmd() / enc_cmd() + / gen_cmd() / pass_cmd() / unpass_cmd() / correct_cmd() @@ -51,12 +52,14 @@ peg::parser! { match name { Some(n) => Ok(n), None => Err("failed to parse password description") } } - rule date() -> NaiveDate = y:$("-"? ['0'..='9']*<1,4>) "-" m:$(['0'..='9']*<1,2>) "-" d:$(['0'..='9']*<1,2>) {? + rule ndate() -> NaiveDate = y:$("-"? ['0'..='9']*<1,4>) "-" m:$(['0'..='9']*<1,2>) "-" d:$(['0'..='9']*<1,2>) {? let year: i32 = match y.parse() { Ok(n) => n, Err(_) => return Err("year") }; let month: u32 = match m.parse() { Ok(n) => n, Err(_) => return Err("month") }; let day: u32 = match d.parse() { Ok(n) => n, Err(_) => return Err("day") }; NaiveDate::from_ymd_opt(year, month, day).ok_or("date") } + rule cdate() -> NaiveDate = "now" { Local::now().naive_local().date() } + rule date() -> NaiveDate = d:(ndate() / cdate()) { d } rule umode() -> Mode = ("U" / "u") m:$("R" / "r" / "N" / "n" / "H" / "h" / "B" / "b") {? match m.to_uppercase().as_str() { "R" => Ok(Mode::RegularUpcase), @@ -88,6 +91,9 @@ peg::parser! { rule source_cmd() -> Command<'input> = "source" _ s:$(([' '..='~'])+) { Command::Source(s.to_string()) } rule ls_cmd() -> Command<'input> = "ls" f:comment()? { Command::Ls(f.unwrap_or(".".to_string())) } rule add_cmd() -> Command<'input> = "add" _ name:name() { Command::Add(Rc::new(RefCell::new(name))) } + rule gen_cmd() -> Command<'input> = "gen" n:num()? _ name:name() { + Command::Gen(match n { Some(n) => n, None => 10_u32 }, Rc::new(RefCell::new(name))) + } rule error_cmd() -> Command<'input> = "error" _ e:$(([' '..='~'])+) { Command::Error(LKErr::Error(e)) } rule mv_cmd() -> Command<'input> = "mv" _ name:word() _ folder:word() { Command::Mv(name, folder) } rule pass_cmd() -> Command<'input> = "pass" _ name:word() { Command::Pass(name) } diff --git a/src/password.rs b/src/password.rs index f63f074..c1b5881 100644 --- a/src/password.rs +++ b/src/password.rs @@ -47,6 +47,19 @@ impl Password { } } + pub fn from_password(password: &Password) -> PasswordRef { + Rc::new(RefCell::new(Self { + parent: password.parent.clone(), + prefix: password.prefix.clone(), + name: password.name.clone(), + length: password.length.clone(), + mode: password.mode.clone(), + seq: password.seq, + date: password.date.clone(), + comment: password.comment.clone(), + })) + } + pub fn encode(&self, secret: &str) -> String { let skey = SKey::new(&self.name, self.seq, secret); let (sep, len) = match (&self.length, &self.mode) { diff --git a/src/repl.rs b/src/repl.rs index 176a40e..898bbc2 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -106,9 +106,8 @@ impl<'a> LKEval<'a> { } None => out.e(format!("error: password {} not found", name)), }, - Command::Enc(name) => { - self.cmd_enc(&out, name); - } + Command::Enc(name) => { self.cmd_enc(&out, name); } + Command::Gen(num, name) => self.cmd_gen(&out, &num, &name), Command::PasteBuffer(command) => self.cmd_pb(&out, command), Command::Source(script) => self.cmd_source(&out, script), Command::Dump(script) => self.cmd_dump(&out, script), diff --git a/src/structs.rs b/src/structs.rs index 308e7b2..75f3d13 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -60,6 +60,7 @@ pub enum Command<'a> { Mv(Name, Name), Rm(Name), Enc(Name), + Gen(u32, PasswordRef), Pass(Name), UnPass(Name), Correct(Name), @@ -74,7 +75,7 @@ pub enum Command<'a> { Quit, } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub enum Mode { Regular, RegularUpcase,