diff options
author | Galen Guyer <galen@galenguyer.com> | 2022-06-20 21:25:07 -0400 |
---|---|---|
committer | Galen Guyer <galen@galenguyer.com> | 2022-06-20 21:25:07 -0400 |
commit | 5df81c937fbf71d7d81aa19a0c3820aa3d2f436e (patch) | |
tree | edcf692d599d67ccc235ba4934dd2aefecad0a05 | |
parent | 83924a3d3699186169c8c67b9f7ac5cc53978de9 (diff) |
reorganize project file structure
-rw-r--r-- | Cargo.toml | 8 | ||||
-rw-r--r-- | src/cli.rs | 116 | ||||
-rw-r--r-- | src/lib/cert.rs (renamed from src/cert.rs) | 0 | ||||
-rw-r--r-- | src/lib/mod.rs (renamed from src/lib.rs) | 1 | ||||
-rw-r--r-- | src/lib/ops.rs | 209 | ||||
-rw-r--r-- | src/lib/path.rs (renamed from src/path.rs) | 6 | ||||
-rw-r--r-- | src/lib/pkey.rs (renamed from src/pkey.rs) | 0 | ||||
-rw-r--r-- | src/lib/req.rs (renamed from src/req.rs) | 0 | ||||
-rw-r--r-- | src/lib/root.rs (renamed from src/root.rs) | 0 | ||||
-rw-r--r-- | src/main.rs | 109 |
10 files changed, 230 insertions, 219 deletions
@@ -3,6 +3,14 @@ name = "hancock" version = "1.0.0" edition = "2021" + +[lib] +path = "src/lib/mod.rs" + +[[bin]] +name = "hancock" +path = "src/cli.rs" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -1,4 +1,6 @@ -use clap::{Args, Parser, Subcommand}; +use clap::{Parser, Subcommand}; + +use hancock::ops::*; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -14,113 +16,11 @@ pub enum Commands { Issue(Issue), } -#[derive(Args, Debug)] -#[clap(about = "Generate a new root certificate")] -pub struct Init { - /// Base directory to store certificates - #[clap(long, default_value = "~/.hancock", env = "CA_BASE_DIR")] - pub base_dir: String, - - /// Algorithm to generate private keys ('RSA' or 'ECDSA') - #[clap(long, short = 't', default_value = "RSA", validator = validate_key_type)] - pub key_type: String, - - /// Length to use when generating an RSA key. Ignored for ECDSA - #[clap(long, short = 'b', default_value_t = 4096)] - pub key_length: u32, - - /// Lifetime in days of the generated certificate - #[clap(long, short = 'd', default_value_t = 365 * 10)] - pub lifetime: u32, - - /// Certificate CommonName - #[clap(long, short = 'n')] - pub common_name: Option<String>, - - /// Certificate Country - #[clap(long, short = 'c')] - pub country: Option<String>, - - /// Certificate State or Province - #[clap(long, short = 's')] - pub state: Option<String>, - - /// Certificate Locality - #[clap(long, short = 'l')] - pub locality: Option<String>, - - /// Certificate Organization - #[clap(long, short = 'o')] - pub organization: Option<String>, - - /// Certificate Organizational Unit - #[clap(long, short = 'u')] - pub organizational_unit: Option<String>, - - /// Password for private key - #[clap(long, short = 'p', env = "CA_PASSWORD")] - pub password: Option<String>, -} -#[derive(Args, Debug)] -#[clap(about = "Issue a new certificate")] -pub struct Issue { - /// Base directory to store certificates - #[clap(long, default_value = "~/.hancock", env = "CA_BASE_DIR")] - pub base_dir: String, - - /// Algorithm to generate private keys ('RSA' or 'ECDSA') - #[clap(long, short = 't', default_value = "RSA", validator = validate_key_type)] - pub key_type: String, - - /// Length to use when generating an RSA key. Ignored for ECDSA - #[clap(long, short = 'b', default_value_t = 2048)] - pub key_length: u32, - - /// Lifetime in days of the generated certificate - #[clap(long, short = 'd', default_value_t = 90)] - pub lifetime: u32, - - /// Certificate CommonName - #[clap(long, short = 'n')] - pub common_name: String, - - /// Certificate Country - #[clap(long, short = 'c')] - pub country: Option<String>, - - /// Certificate State or Province - #[clap(long, short = 's')] - pub state: Option<String>, - - /// Certificate Locality - #[clap(long, short = 'l')] - pub locality: Option<String>, - - /// Certificate Organization - #[clap(long, short = 'o')] - pub organization: Option<String>, - - /// Certificate Organizational Unit - #[clap(long, short = 'u')] - pub organizational_unit: Option<String>, - - /// Subject Alternative Names - #[clap(long)] - pub subject_alt_names: Option<String>, - - /// Password for private key - #[clap(long, short = 'p', env = "CA_PASSWORD")] - pub password: Option<String>, -} +fn main() { + let cli = dbg!(Cli::parse()); -fn validate_key_type(input: &str) -> Result<(), String> { - let input = input.to_string().to_uppercase(); - if input == "RSA" || input == "ECDSA" { - Ok(()) - } else { - Err(format!( - "{} is not a valid key type ['rsa', 'ecdsa']", - input - )) + match cli.command { + Commands::Init(args) => init(args), + Commands::Issue(args) => issue(args), } } diff --git a/src/cert.rs b/src/lib/cert.rs index 14ac9fd..14ac9fd 100644 --- a/src/cert.rs +++ b/src/lib/cert.rs diff --git a/src/lib.rs b/src/lib/mod.rs index 745c200..654585a 100644 --- a/src/lib.rs +++ b/src/lib/mod.rs @@ -3,6 +3,7 @@ pub mod path; pub mod pkey; pub mod req; pub mod root; +pub mod ops; #[derive(Clone, Copy)] pub enum KeyType { diff --git a/src/lib/ops.rs b/src/lib/ops.rs new file mode 100644 index 0000000..8abe832 --- /dev/null +++ b/src/lib/ops.rs @@ -0,0 +1,209 @@ +use clap::Args; +use std::path::Path; + +use crate::KeyType; +use crate::*; + +#[derive(Args, Debug)] +#[clap(about = "Generate a new root certificate")] +pub struct Init { + /// Base directory to store certificates + #[clap(long, default_value = "~/.hancock", env = "CA_BASE_DIR")] + pub base_dir: String, + + /// Algorithm to generate private keys ('RSA' or 'ECDSA') + #[clap(long, short = 't', default_value = "RSA", validator = validate_key_type)] + pub key_type: String, + + /// Length to use when generating an RSA key. Ignored for ECDSA + #[clap(long, short = 'b', default_value_t = 4096)] + pub key_length: u32, + + /// Lifetime in days of the generated certificate + #[clap(long, short = 'd', default_value_t = 365 * 10)] + pub lifetime: u32, + + /// Certificate CommonName + #[clap(long, short = 'n')] + pub common_name: Option<String>, + + /// Certificate Country + #[clap(long, short = 'c')] + pub country: Option<String>, + + /// Certificate State or Province + #[clap(long, short = 's')] + pub state: Option<String>, + + /// Certificate Locality + #[clap(long, short = 'l')] + pub locality: Option<String>, + + /// Certificate Organization + #[clap(long, short = 'o')] + pub organization: Option<String>, + + /// Certificate Organizational Unit + #[clap(long, short = 'u')] + pub organizational_unit: Option<String>, + + /// Password for private key + #[clap(long, short = 'p', env = "CA_PASSWORD")] + pub password: Option<String>, +} + +#[derive(Args, Debug)] +#[clap(about = "Issue a new certificate")] +pub struct Issue { + /// Base directory to store certificates + #[clap(long, default_value = "~/.hancock", env = "CA_BASE_DIR")] + pub base_dir: String, + + /// Algorithm to generate private keys ('RSA' or 'ECDSA') + #[clap(long, short = 't', default_value = "RSA", validator = validate_key_type)] + pub key_type: String, + + /// Length to use when generating an RSA key. Ignored for ECDSA + #[clap(long, short = 'b', default_value_t = 2048)] + pub key_length: u32, + + /// Lifetime in days of the generated certificate + #[clap(long, short = 'd', default_value_t = 90)] + pub lifetime: u32, + + /// Certificate CommonName + #[clap(long, short = 'n')] + pub common_name: String, + + /// Certificate Country + #[clap(long, short = 'c')] + pub country: Option<String>, + + /// Certificate State or Province + #[clap(long, short = 's')] + pub state: Option<String>, + + /// Certificate Locality + #[clap(long, short = 'l')] + pub locality: Option<String>, + + /// Certificate Organization + #[clap(long, short = 'o')] + pub organization: Option<String>, + + /// Certificate Organizational Unit + #[clap(long, short = 'u')] + pub organizational_unit: Option<String>, + + /// Subject Alternative Names + #[clap(long)] + pub subject_alt_names: Option<String>, + + /// Password for private key + #[clap(long, short = 'p', env = "CA_PASSWORD")] + pub password: Option<String>, +} + +pub fn init(args: Init) { + let base_dir = path::base_dir(&args.base_dir); + + let key_type = match args.key_type.to_uppercase().as_str() { + "RSA" => KeyType::Rsa(args.key_length), + "ECDSA" => KeyType::Ecdsa, + _ => panic!("key_type not ECDSA or RSA after validation. This should never happen"), + }; + + let pkey_path = path::ca_pkey(&base_dir, key_type); + + let pkey = match Path::new(&pkey_path).exists() { + true => pkey::read_pkey(&pkey_path, args.password), + false => { + let pkey = pkey::generate_pkey(key_type); + pkey::save_pkey(&pkey_path, &pkey, args.password); + pkey + } + }; + + let cert_path = path::ca_crt(&base_dir, key_type); + if !Path::new(&cert_path).exists() { + let cert = root::generate_root_cert( + args.lifetime, + &args.common_name, + &args.country, + &args.state, + &args.locality, + &args.organization, + &args.organizational_unit, + &pkey, + ); + cert::save_cert(&cert_path, &cert); + } +} + +pub fn issue(args: Issue) { + let base_dir = path::base_dir(&args.base_dir); + + let key_type = match args.key_type.to_uppercase().as_str() { + "RSA" => KeyType::Rsa(args.key_length), + "ECDSA" => KeyType::Ecdsa, + _ => panic!("key_type not ECDSA or RSA after validation. This should never happen"), + }; + + let ca_pkey_path = path::ca_pkey(&base_dir, key_type); + + let ca_pkey = match Path::new(&ca_pkey_path).exists() { + true => pkey::read_pkey(&ca_pkey_path, args.password), + false => { + let pkey = pkey::generate_pkey(key_type); + pkey::save_pkey(&ca_pkey_path, &pkey, args.password); + pkey + } + }; + + let ca_cert_path = path::ca_crt(&base_dir, key_type); + let ca_cert = cert::read_cert(&ca_cert_path); + + let pkey_path = path::cert_pkey(&base_dir, &args.common_name, key_type); + let pkey = match Path::new(&pkey_path).exists() { + true => pkey::read_pkey(&pkey_path, None), + false => { + let pkey = pkey::generate_pkey(key_type); + pkey::save_pkey(&pkey_path, &pkey, None); + pkey + } + }; + + let x509_req_path = path::cert_csr(&base_dir, &args.common_name, key_type); + let x509_req = { + let req = req::generate_req( + &Some(args.common_name.clone()), + &args.country, + &args.state, + &args.locality, + &args.organization, + &args.organizational_unit, + &args.subject_alt_names, + &pkey, + ); + req::save_req(&x509_req_path, &req); + req + }; + + let cert = cert::generate_cert(args.lifetime, &x509_req, &ca_cert, &ca_pkey); + cert::save_cert( + &path::cert_crt(&base_dir, &args.common_name, key_type), + &cert, + ); +} + +fn validate_key_type(input: &str) -> Result<(), String> { + let input = input.to_string().to_uppercase(); + if input == "RSA" || input == "ECDSA" { + Ok(()) + } else { + Err(format!( + "{} is not a valid key type ['rsa', 'ecdsa']", + input + )) + } +} diff --git a/src/path.rs b/src/lib/path.rs index a65a8ad..d77fd59 100644 --- a/src/path.rs +++ b/src/lib/path.rs @@ -41,7 +41,8 @@ pub fn cert_csr(base_dir: &str, name: &str, key_type: KeyType) -> String { _ => { format!("{base_dir}/{name}/{name}.{}.csr", key_type.to_string()) } - }} + } +} pub fn cert_crt(base_dir: &str, name: &str, key_type: KeyType) -> String { match key_type { KeyType::Rsa(_) => { @@ -50,7 +51,8 @@ pub fn cert_crt(base_dir: &str, name: &str, key_type: KeyType) -> String { _ => { format!("{base_dir}/{name}/{name}.{}.crt", key_type.to_string()) } - }} + } +} pub fn base_dir(raw_base: &str) -> String { Path::new(&shellexpand::tilde(&raw_base).to_string()) diff --git a/src/pkey.rs b/src/lib/pkey.rs index 41bb3e9..41bb3e9 100644 --- a/src/pkey.rs +++ b/src/lib/pkey.rs diff --git a/src/req.rs b/src/lib/req.rs index 2b5974f..2b5974f 100644 --- a/src/req.rs +++ b/src/lib/req.rs diff --git a/src/root.rs b/src/lib/root.rs index 9b8393c..9b8393c 100644 --- a/src/root.rs +++ b/src/lib/root.rs diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 4f63a00..0000000 --- a/src/main.rs +++ /dev/null @@ -1,109 +0,0 @@ -use clap::Parser; - -use hancock::*; - -use std::path::Path; - -mod cli; -use crate::cli::*; - -fn main() { - let cli = dbg!(Cli::parse()); - - match cli.command { - Commands::Init(args) => init(args), - Commands::Issue(args) => issue(args), - } -} - -fn init(args: Init) { - let base_dir = path::base_dir(&args.base_dir); - - let key_type = match args.key_type.to_uppercase().as_str() { - "RSA" => KeyType::Rsa(args.key_length), - "ECDSA" => KeyType::Ecdsa, - _ => panic!("key_type not ECDSA or RSA after validation. This should never happen"), - }; - - let pkey_path = path::ca_pkey(&base_dir, key_type); - - let pkey = match Path::new(&pkey_path).exists() { - true => pkey::read_pkey(&pkey_path, args.password), - false => { - let pkey = pkey::generate_pkey(key_type); - pkey::save_pkey(&pkey_path, &pkey, args.password); - pkey - } - }; - - let cert_path = path::ca_crt(&base_dir, key_type); - if !Path::new(&cert_path).exists() { - let cert = root::generate_root_cert( - args.lifetime, - &args.common_name, - &args.country, - &args.state, - &args.locality, - &args.organization, - &args.organizational_unit, - &pkey, - ); - cert::save_cert(&cert_path, &cert); - } -} - -fn issue(args: Issue) { - let base_dir = path::base_dir(&args.base_dir); - - let key_type = match args.key_type.to_uppercase().as_str() { - "RSA" => KeyType::Rsa(args.key_length), - "ECDSA" => KeyType::Ecdsa, - _ => panic!("key_type not ECDSA or RSA after validation. This should never happen"), - }; - - let ca_pkey_path = path::ca_pkey(&base_dir, key_type); - - let ca_pkey = match Path::new(&ca_pkey_path).exists() { - true => pkey::read_pkey(&ca_pkey_path, args.password), - false => { - let pkey = pkey::generate_pkey(key_type); - pkey::save_pkey(&ca_pkey_path, &pkey, args.password); - pkey - } - }; - - let ca_cert_path = path::ca_crt(&base_dir, key_type); - let ca_cert = cert::read_cert(&ca_cert_path); - - let pkey_path = path::cert_pkey(&base_dir, &args.common_name, key_type); - let pkey = match Path::new(&pkey_path).exists() { - true => pkey::read_pkey(&pkey_path, None), - false => { - let pkey = pkey::generate_pkey(key_type); - pkey::save_pkey(&pkey_path, &pkey, None); - pkey - } - }; - - let x509_req_path = path::cert_csr(&base_dir, &args.common_name, key_type); - let x509_req = { - let req = req::generate_req( - &Some(args.common_name.clone()), - &args.country, - &args.state, - &args.locality, - &args.organization, - &args.organizational_unit, - &args.subject_alt_names, - &pkey, - ); - req::save_req(&x509_req_path, &req); - req - }; - - let cert = cert::generate_cert(args.lifetime, &x509_req, &ca_cert, &ca_pkey); - cert::save_cert( - &path::cert_crt(&base_dir, &args.common_name, key_type), - &cert, - ); -} |