pwtool 0.4.0

pwtool, user account password tool
Documentation
use getopts::Options;
use pwhash::*;
use pwtool::*;
use sha1::{Digest, Sha1};
use std::env;

const VERSION: &str = env!("CARGO_PKG_VERSION");

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn set_config_options(
    matches: &getopts::Matches,
    c: &mut Config,
    args: Vec<String>,
    opts: Options,
) {
    if matches.opt_present("version") {
        println!("pwtool {VERSION}");
        std::process::exit(0);
    }

    if matches.opt_present("words") {
        c.word_list = Some("/usr/share/dict/words".to_string());
    }

    if matches.opt_present("wordsfile") {
        let words = matches.opt_str("wordsfile").unwrap();
        c.word_list = Some(words);
    }

    let mut found = false;
    for opt in [
        "onlynumeric",
        "onlyalpha",
        "onlyuppercase",
        "onlyuppercase",
        "onlylowercase",
        "onlyextended",
    ] {
        if matches.opt_present(opt) {
            if found {
                eprintln!("cannot have multiple --only* options");
                std::process::exit(1);
            }
            found = true;
        }
    }

    if matches.opt_present("onlynumeric") {
        c.pw_type = Some(PwClass::Num as u32);
    }
    if matches.opt_present("onlyalpha") {
        c.pw_type = Some(PwClass::Alpha as u32);
    }
    if matches.opt_present("onlyuppercase") {
        c.pw_type = Some(PwClass::Upper as u32);
    }
    if matches.opt_present("onlylowercase") {
        c.pw_type = Some(PwClass::Lower as u32);
    }
    if matches.opt_present("onlyextended") {
        c.pw_type = Some(PwClass::Ext as u32);
    }
    if matches.opt_present("onlyextended") {
        c.pw_type = Some(PwClass::Ext as u32);
    }

    if matches.opt_present("help") {
        print_usage(&args[0], opts);
        std::process::exit(0);
    }

    if matches.opt_present("username") {
        c.username = matches.opt_str("username");
    }

    if matches.opt_present("database") {
        c.database = matches.opt_str("database");
    }

    c.len = if matches.opt_present("length") {
        let n = matches.opt_str("length").unwrap();

        match n.parse::<u32>() {
            Ok(l) => l,
            Err(_) => {
                eprintln!("cannot convert {} to number", n);
                std::process::exit(1);
            }
        }
    } else if c.word_list.is_some() {
        3
    } else {
        10
    };

    c.number = if matches.opt_present("number") {
        let n = matches.opt_str("number").unwrap();

        match n.parse::<u32>() {
            Ok(l) => l,
            Err(_) => {
                eprintln!("cannot convert {} to number", n);
                std::process::exit(1);
            }
        }
    } else {
        20
    };

    if matches.opt_present("numeric") {
        c.pw_type = Some(match c.pw_type {
            Some(s) => s | PwClass::Num as u32,
            None => {
                PwClass::Alpha as u32
                    | PwClass::Lower as u32
                    | PwClass::Upper as u32
                    | PwClass::Num as u32
            }
        });
    }

    if matches.opt_present("alpha") {
        c.pw_type = Some(match c.pw_type {
            Some(s) => s | PwClass::Alpha as u32,
            None => {
                PwClass::Alpha as u32
                    | PwClass::Lower as u32
                    | PwClass::Upper as u32
                    | PwClass::Num as u32
            }
        });
    }

    if matches.opt_present("extended") {
        c.pw_type = Some(match c.pw_type {
            Some(s) => s | PwClass::Ext as u32,
            None => {
                PwClass::Alpha as u32
                    | PwClass::Lower as u32
                    | PwClass::Upper as u32
                    | PwClass::Num as u32
                    | PwClass::Ext as u32
            }
        });
    }

    if matches.opt_present("uppercase") {
        c.pw_type = Some(match c.pw_type {
            Some(s) => s | PwClass::Upper as u32,
            None => {
                PwClass::Alpha as u32
                    | PwClass::Lower as u32
                    | PwClass::Upper as u32
                    | PwClass::Num as u32
            }
        });
    }

    if matches.opt_present("lowercase") {
        c.pw_type = Some(match c.pw_type {
            Some(s) => s | PwClass::Lower as u32,
            None => {
                PwClass::Alpha as u32
                    | PwClass::Lower as u32
                    | PwClass::Upper as u32
                    | PwClass::Num as u32
            }
        });
    }

    if matches.opt_present("help") {
        print_usage(&args[0], opts);
        std::process::exit(0);
    }

    c.len = if matches.opt_present("length") {
        let n = matches.opt_str("length").unwrap();

        match n.parse::<u32>() {
            Ok(l) => l,
            Err(_) => {
                eprintln!("cannot convert {} to number", n);
                std::process::exit(1);
            }
        }
    } else if c.word_list.is_some() {
        3
    } else {
        10
    };

    c.number = if matches.opt_present("number") {
        let n = matches.opt_str("number").unwrap();

        match n.parse::<u32>() {
            Ok(l) => l,
            Err(_) => {
                eprintln!("cannot convert {} to number", n);
                std::process::exit(1);
            }
        }
    } else {
        20
    };
}
#[allow(deprecated)]
fn main() {
    let mut c = Config::new();

    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.optflag("h", "help", "display help");
    opts.optflag("", "alpha", "use alpha characters (default)");
    opts.optflag("", "numeric", "use numeric characters (default)");
    opts.optflag("", "extended", "use extended characters");
    opts.optflag("", "lowercase", "use lowercase characters (default)");
    opts.optflag("", "uppercase", "use uppercase characters (default)");

    opts.optflag("", "onlyuppercase", "use uppercase characters");
    opts.optflag("", "onlylowercase", "use lowercase characters");
    opts.optflag("", "onlynumeric", "use numeric characters");
    opts.optflag("", "onlyextended", "use extended characters");
    opts.optflag("", "onlyalpha", "use alpha characters");

    opts.optflag("", "md5", "use MD5");
    opts.optflag("", "bcrypt", "use bcrypt");
    opts.optflag("", "des", "use traditional unix crypt");
    opts.optflag("", "sha1", "use SHA1");
    opts.optflag("", "sha256", "use SHA256");
    opts.optflag("", "sha512", "use SHA512");
    opts.optopt("", "username", "for %{username} formatter", "STRING");
    opts.optopt("", "database", "for %{database} formatter", "STRING");
    opts.optflag("v", "version", "display version");
    opts.optopt("n", "number", "number of passwords", "NUMBER");
    opts.optopt(
        "l",
        "length",
        "length of passwords (default is three with a wordlist)",
        "NUMBER",
    );
    opts.optflag("w", "words", "use default wordlist");
    opts.optopt("", "wordsfile", "use wordsfile", "FILE");
    opts.optopt(
        "",
        "format",
        "output using a string: %{VAL} where VAL is md5, bcrypt, des, sha1, sha256, sha512, mysql, postgres, database, password or username",
        "FORMAT",
    );

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            eprintln!("{}", f);
            std::process::exit(1);
        }
    };

    set_config_options(&matches, &mut c, args, opts);

    for _ in 0..c.number {
        if matches.opt_present("md5") {
            let pw = prng_string(&mut c);
            println!("{} {}", pw, md5_crypt::hash(&pw).unwrap());
            continue;
        }
        if matches.opt_present("sha1") {
            let pw = prng_string(&mut c);
            println!("{} {}", pw, sha1_crypt::hash(&pw).unwrap());
            continue;
        }
        if matches.opt_present("sha256") {
            let pw = prng_string(&mut c);
            println!("{} {}", pw, sha256_crypt::hash(&pw).unwrap());
            continue;
        }
        if matches.opt_present("sha512") {
            let pw = prng_string(&mut c);
            println!("{} {}", pw, sha512_crypt::hash(&pw).unwrap());
            continue;
        }
        if matches.opt_present("bcrypt") {
            let pw = prng_string(&mut c);
            println!("{} {}", pw, bcrypt::hash(&pw).unwrap());
            continue;
        }
        if matches.opt_present("des") {
            let pw = prng_string(&mut c);
            println!("{} {}", pw, unix_crypt::hash(&pw).unwrap());
            continue;
        }

        if matches.opt_present("format") {
            let pw = prng_string(&mut c);

            let mut format_string = matches.opt_str("format").unwrap().to_string();

            if format_string.contains("%{md5}") {
                format_string =
                    format_string.replace("%{md5}", &md5_crypt::hash(&pw).unwrap().to_string());
            }
            if format_string.contains("%{bcrypt}") {
                format_string =
                    format_string.replace("%{bcrypt}", &bcrypt::hash(&pw).unwrap().to_string());
            }
            if format_string.contains("%{des}") {
                format_string =
                    format_string.replace("%{des}", &unix_crypt::hash(&pw).unwrap().to_string());
            }
            if format_string.contains("%{sha1}") {
                format_string =
                    format_string.replace("%{sha1}", &sha1_crypt::hash(&pw).unwrap().to_string());
            }
            if format_string.contains("%{sha256}") {
                format_string = format_string
                    .replace("%{sha256}", &sha256_crypt::hash(&pw).unwrap().to_string());
            }
            if format_string.contains("%{sha512}") {
                format_string = format_string
                    .replace("%{sha512}", &sha512_crypt::hash(&pw).unwrap().to_string());
            }

            if format_string.contains("%{password}") {
                format_string = format_string.replace("%{password}", &pw);
            }
            if format_string.contains("%{username}") && c.username.is_some() {
                format_string = format_string.replace("%{username}", c.username.as_ref().unwrap());
            }
            if format_string.contains("%{database}") && c.database.is_some() {
                format_string = format_string.replace("%{database}", c.database.as_ref().unwrap());
            }

            if format_string.contains("%{mysql}") {
                let mut h = Sha1::new();
                h.update(&pw);
                let r = h.finalize();
                let mut h = Sha1::new();
                h.update(r);
                let r = h.finalize();

                format_string = format_string.replace("%{mysql}", &format!("*{:x}", r).to_string());
            }

            if format_string.contains("%{postgres}") {
                let postgres_pass = postgres_pass(&pw);
                format_string = format_string.replace("%{postgres}", &postgres_pass);
            }

            format_string = format_string.replace(r#"\n"#, "\n");

            print!("{}", format_string);
            continue;
        }

        for j in 1..8 {
            let pw = prng_string(&mut c);
            print!("{}{}", pw, if j != 8 { " " } else { "" },);
            if c.word_list.is_some() {
                break;
            }
        }
        println!();
    }
}