certtool + fix ncpf/netcatpf

This commit is contained in:
c0repwn3r 2023-05-04 09:40:05 -04:00
parent d8ca1bbb90
commit cbe8dee8a3
Signed by: core
GPG Key ID: FDBF740DADDCEECF
9 changed files with 517 additions and 24 deletions

View File

@ -2,6 +2,7 @@
<module type="CPP_MODULE" version="4"> <module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/certtoolpf/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/libepf/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/libepf/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/ncpf/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/ncpf/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/netcatpf/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/netcatpf/src" isTestSource="false" />

122
Cargo.lock generated
View File

@ -149,6 +149,16 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "certtoolpf"
version = "0.1.0"
dependencies = [
"clap",
"inquire",
"libepf",
"rand",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -296,6 +306,31 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crossterm"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -399,6 +434,12 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "dyn-clone"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
[[package]] [[package]]
name = "ed25519" name = "ed25519"
version = "2.2.1" version = "2.2.1"
@ -641,6 +682,22 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "inquire"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4bf420bd01f298a3ed8f57af9babedb296b4edfc1dbd8b159cee883aa90edaa"
dependencies = [
"bitflags",
"crossterm",
"dyn-clone",
"lazy_static",
"newline-converter",
"thiserror",
"unicode-segmentation",
"unicode-width",
]
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.10" version = "1.0.10"
@ -792,6 +849,15 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "newline-converter"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.45" version = "0.1.45"
@ -1106,6 +1172,36 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "signal-hook"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "signature" name = "signature"
version = "2.1.0" version = "2.1.0"
@ -1212,6 +1308,26 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.45" version = "0.1.45"
@ -1292,6 +1408,12 @@ version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.10" version = "0.1.10"

View File

@ -2,5 +2,6 @@
members = [ members = [
"libepf", "libepf",
"ncpf", "ncpf",
"netcatpf" "netcatpf",
"certtoolpf"
] ]

12
certtoolpf/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "certtoolpf"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
inquire = "0.6.1"
libepf = { version = "0.1.0", path = "../libepf" }
clap = { version = "4", features = ["derive", "cargo"] }
rand = "0.8.5"

328
certtoolpf/src/main.rs Normal file
View File

@ -0,0 +1,328 @@
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use clap::{Parser, Subcommand};
use inquire::{Confirm, Select, Text};
use rand::rngs::OsRng;
use libepf::ca_pool::load_ca_pool;
use libepf::pki::{EPFCertificate, EPFCertificateDetails, EpfPkiCertificateOps, EpfPkiSerializable, EpfPrivateKey, EpfPublicKey};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
/// Generate a public and private keypair
GenerateKeypair {
#[arg(short = 'p', long)]
out_public_key: PathBuf,
#[arg(short = 'k', long)]
out_private_key: PathBuf
},
/// Create a new **unsigned** certificate
CreateCertificate {
#[arg(short, long)]
output: PathBuf,
#[arg(short, long)]
public_key: PathBuf
},
/// Dump information about the certificate
DumpCertificate {
cert: PathBuf
},
/// Verify the certificate
VerifyCertificate {
cert: PathBuf
},
/// Sign the certificate using the given private key
SignCertificate {
cert: PathBuf,
#[arg(short, long)]
key: PathBuf,
#[arg(short, long)]
output: PathBuf
}
}
fn main() {
let args = Cli::parse();
if let Some(subcommand) = args.command {
match subcommand {
Commands::GenerateKeypair { out_private_key, out_public_key } => {
let private_key = EpfPrivateKey::generate(&mut OsRng);
let public_key = private_key.verifying_key();
let private_key_pem = match private_key.as_pem() {
Ok(pem) => pem,
Err(e) => {
println!("Error while serializing private key: {}", e);
std::process::exit(1);
}
};
let public_key_pem = match public_key.as_pem() {
Ok(pem) => pem,
Err(e) => {
println!("Error while serializing public key: {}", e);
std::process::exit(1);
}
};
match std::fs::write(out_private_key, private_key_pem) {
Ok(_) => (),
Err(e) => {
println!("Error saving private key: {}", e);
std::process::exit(1);
}
}
match std::fs::write(out_public_key, public_key_pem) {
Ok(_) => (),
Err(e) => {
println!("Error saving public key: {}", e);
std::process::exit(1);
}
}
},
Commands::CreateCertificate { output, public_key } => {
// load public key
let public_key_pem = match fs::read(public_key) {
Ok(pem) => pem,
Err(e) => {
println!("Unable to load public key: {}", e);
std::process::exit(1);
}
};
let public_key = match EpfPublicKey::from_pem(&public_key_pem) {
Ok(k) => k,
Err(e) => {
println!("Error parsing public key: {}", e);
std::process::exit(1);
}
};
let name = match Text::new("Certificate name?").prompt() {
Ok(n) => n,
Err(e) => {
println!("Error with prompt: {}", e);
std::process::exit(1);
}
};
let options = vec!["1 day", "1 week", "1 month", "6 months", "1 year", "2 years", "5 years", "10 years", "Forever"];
let expires_in = match Select::new("How long should the certificate be valid for?", options).prompt() {
Ok(expires_in) => expires_in,
Err(e) => {
println!("Error with prompt: {}", e);
std::process::exit(1);
}
};
let expires_in = match expires_in {
"1 day" => 60 * 60 * 24,
"1 week" => 60 * 60 * 24 * 7,
"1 month" => 60 * 60 * 24 * 7 * 4,
"6 months" => 60 * 60 * 24 * 7 * 4 * 6,
"1 year" => 60 * 60 * 24 * 7 * 4 * 6 * 2,
"2 years" => 60 * 60 * 24 * 7 * 4 * 6 * 2 * 2,
"5 years" => 60 * 60 * 24 * 7 * 4 * 6 * 2 * 5,
"10 years" => 60 * 60 * 24 * 7 * 4 * 6 * 2 * 10,
"Forever" => 60 * 60 * 24 * 7 * 4 * 6 * 2 * 100000, // 100,000 years
_ => unreachable!()
};
let add_claims = match Confirm::new("Would you like to add additional claims to the certificate?").with_default(false).prompt() {
Ok(ac) => ac,
Err(e) => {
println!("Error with prompt: {}", e);
std::process::exit(1);
}
};
let mut claims = HashMap::new();
if add_claims {
loop {
let name = match Text::new("Claim name:").prompt() {
Ok(n) => n,
Err(e) => {
println!("Error with prompt: {}", e);
std::process::exit(1);
}
};
let value = match Text::new("Claim value:").prompt() {
Ok(n) => n,
Err(e) => {
println!("Error with prompt: {}", e);
std::process::exit(1);
}
};
claims.insert(name, value);
let add_another = match Confirm::new("Would you like to add additional claims to the certificate?").with_default(true).prompt() {
Ok(ac) => ac,
Err(e) => {
println!("Error with prompt: {}", e);
std::process::exit(1);
}
};
if !add_another {
break;
}
}
}
let mut cert = EPFCertificate {
details: EPFCertificateDetails {
name,
not_before: SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs(),
not_after: SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() + expires_in,
public_key: public_key.to_bytes(),
issuer_public_key: [0u8; 32],
claims,
},
fingerprint: "".to_string(),
signature: [0u8; 64],
};
match cert.recalculate_fingerprint() {
Ok(_) => (),
Err(e) => {
println!("Error calculating certificate fingerprint: {}", e);
std::process::exit(1);
}
}
let cert_pem = match cert.as_pem() {
Ok(pem) => pem,
Err(e) => {
println!("Error serializing certificate: {}", e);
std::process::exit(1);
}
};
match fs::write(output, cert_pem) {
Ok(_) => (),
Err(e) => {
println!("Error saving certificate: {}", e);
}
}
},
Commands::DumpCertificate { cert } => {
// load cert
let cert_pem = match fs::read(cert) {
Ok(pem) => pem,
Err(e) => {
println!("Unable to load certificate: {}", e);
std::process::exit(1);
}
};
let cert = match EPFCertificate::from_pem(&cert_pem) {
Ok(k) => k,
Err(e) => {
println!("Error parsing certificate: {}", e);
std::process::exit(1);
}
};
println!("{}", cert);
},
Commands::SignCertificate { cert, key, output }=> {
// load cert
let cert_pem = match fs::read(cert) {
Ok(pem) => pem,
Err(e) => {
println!("Unable to load certificate: {}", e);
std::process::exit(1);
}
};
let mut cert = match EPFCertificate::from_pem(&cert_pem) {
Ok(k) => k,
Err(e) => {
println!("Error parsing certificate: {}", e);
std::process::exit(1);
}
};
// load key
let key_pem = match fs::read(key) {
Ok(pem) => pem,
Err(e) => {
println!("Unable to load private key: {}", e);
std::process::exit(1);
}
};
let key = match EpfPrivateKey::from_pem(&key_pem) {
Ok(k) => k,
Err(e) => {
println!("Error parsing private key: {}", e);
std::process::exit(1);
}
};
match cert.sign(&key) {
Ok(_) => (),
Err(e) => {
println!("Error signing certificate: {}", e);
std::process::exit(1);
}
}
let cert_pem = match cert.as_pem() {
Ok(pem) => pem,
Err(e) => {
println!("Error serializing certificate: {}", e);
std::process::exit(1);
}
};
match fs::write(output, cert_pem) {
Ok(_) => (),
Err(e) => {
println!("Error saving certificate: {}", e);
}
}
},
Commands::VerifyCertificate { cert } => {
// load cert
let cert_pem = match fs::read(cert) {
Ok(pem) => pem,
Err(e) => {
println!("Unable to load certificate: {}", e);
std::process::exit(1);
}
};
let cert = match EPFCertificate::from_pem(&cert_pem) {
Ok(k) => k,
Err(e) => {
println!("Error parsing certificate: {}", e);
std::process::exit(1);
}
};
let ca_pool = match load_ca_pool() {
Ok(pool) => pool,
Err(e) => {
println!("Unable to load trusted certs pool: {}", e);
std::process::exit(1);
}
};
match cert.verify(&ca_pool) {
Ok(trusted) => {
if !trusted {
println!("Certificate valid but not trusted");
std::process::exit(3);
}
},
Err(e) => {
println!("Certificate invalid: {}", e);
std::process::exit(2);
}
}
},
}
} else {
println!("No subcommand specified. Run with -h/--help for help.");
}
}

View File

@ -56,7 +56,7 @@ pub fn load_ca_pool() -> Result<EpfCaPool, Box<dyn Error>> {
for entry in fs::read_dir("/etc/e3pf/certs")? { for entry in fs::read_dir("/etc/e3pf/certs")? {
let entry = entry?; let entry = entry?;
if entry.path().extension() == Some(OsStr::new(".pem")) { if entry.path().extension() == Some(OsStr::new("pem")) {
cert_strings.push(fs::read_to_string(entry.path())?); cert_strings.push(fs::read_to_string(entry.path())?);
} }
} }

View File

@ -250,7 +250,7 @@ impl Display for EPFCertificate {
)?; )?;
writeln!( writeln!(
f, f,
"\t\tIssuer Fingerprint: {}", "\t\tIssuer Public Key: {}",
hex::encode(self.details.issuer_public_key) hex::encode(self.details.issuer_public_key)
)?; )?;
writeln!(f, "\t}}")?; writeln!(f, "\t}}")?;

View File

@ -7,19 +7,20 @@ use tokio::net::{TcpSocket};
use tokio::select; use tokio::select;
use libepf::ca_pool::load_ca_pool; use libepf::ca_pool::load_ca_pool;
use libepf::handshake_stream::{ClientAuthentication, EpfClientHandshaker, EpfClientUpgradable, EpfStreamOps}; use libepf::handshake_stream::{ClientAuthentication, EpfClientHandshaker, EpfClientUpgradable, EpfStreamOps};
use std::io;
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Cli { pub struct Cli {
bind_ip: IpAddr, connect_ip: IpAddr,
bind_port: u16 connect_port: u16
} }
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> { async fn main() -> Result<(), Box<dyn Error>> {
let cli = Cli::parse(); let cli = Cli::parse();
let bind_addr = SocketAddr::new(cli.bind_ip, cli.bind_port); let bind_addr = SocketAddr::new(cli.connect_ip, cli.connect_port);
let tcp_stream = TcpSocket::new_v4()?.connect(bind_addr).await?; let tcp_stream = TcpSocket::new_v4()?.connect(bind_addr).await?;
@ -30,7 +31,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let mut client = handshake_client.upgrade().await; let mut client = handshake_client.upgrade().await;
let mut stdin = tokio::io::BufReader::new(tokio::io::stdin()); let mut stdin = tokio::io::BufReader::new(tokio::io::stdin());
let mut input = String::new(); let mut input = [0u8; 32767];
loop { loop {
select! { select! {
@ -38,21 +39,35 @@ async fn main() -> Result<(), Box<dyn Error>> {
match packet_data { match packet_data {
Ok(d) => std::io::stdout().write_all(&d).unwrap(), Ok(d) => std::io::stdout().write_all(&d).unwrap(),
Err(e) => { Err(e) => {
println!("{}", e); match e.downcast_ref::<io::Error>() {
std::process::exit(1); Some(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
std::process::exit(0);
},
Some(_) | None => {
println!("{}", e);
std::process::exit(1);
}
}
} }
} }
}, },
stdin_data = stdin.read_to_string(&mut input) => { stdin_data = stdin.read(&mut input) => {
match stdin_data { match stdin_data {
Ok(amt) => { Ok(amt) => {
match client.write(input.as_bytes()).await { match client.write(&input[..amt]).await {
Ok(_) => { Ok(_) => {
input = String::new(); input = [0u8; 32767];
}, },
Err(e) => { Err(e) => {
println!("{}", e); match e.downcast_ref::<io::Error>() {
std::process::exit(1); Some(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
std::process::exit(0);
},
Some(_) | None => {
println!("{}", e);
std::process::exit(1);
}
}
} }
} }
if amt == 0 { if amt == 0 {

View File

@ -1,5 +1,5 @@
use std::error::Error; use std::error::Error;
use std::fs; use std::{fs, io};
use std::io::{Write}; use std::io::{Write};
use std::net::{IpAddr, SocketAddr}; use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf; use std::path::PathBuf;
@ -17,7 +17,7 @@ pub struct Cli {
bind_ip: IpAddr, bind_ip: IpAddr,
bind_port: u16, bind_port: u16,
certificate: PathBuf, certificate: PathBuf,
key: PathBuf key: PathBuf,
} }
#[tokio::main] #[tokio::main]
@ -45,7 +45,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let mut client = handshake_server.upgrade().await; let mut client = handshake_server.upgrade().await;
let mut stdin = tokio::io::BufReader::new(tokio::io::stdin()); let mut stdin = tokio::io::BufReader::new(tokio::io::stdin());
let mut input = String::new(); let mut input = [0u8; 32767];
loop { loop {
select! { select! {
@ -53,21 +53,35 @@ async fn main() -> Result<(), Box<dyn Error>> {
match packet_data { match packet_data {
Ok(d) => std::io::stdout().write_all(&d).unwrap(), Ok(d) => std::io::stdout().write_all(&d).unwrap(),
Err(e) => { Err(e) => {
println!("{}", e); match e.downcast_ref::<io::Error>() {
std::process::exit(1); Some(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
std::process::exit(0);
},
Some(_) | None => {
println!("{}", e);
std::process::exit(1);
}
}
} }
} }
}, },
stdin_data = stdin.read_to_string(&mut input) => { stdin_data = stdin.read(&mut input) => {
match stdin_data { match stdin_data {
Ok(amt) => { Ok(amt) => {
match client.write(input.as_bytes()).await { match client.write(&input[..amt]).await {
Ok(_) => { Ok(_) => {
input = String::new(); input = [0u8; 32767];
}, },
Err(e) => { Err(e) => {
println!("{}", e); match e.downcast_ref::<io::Error>() {
std::process::exit(1); Some(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
std::process::exit(0);
},
Some(_) | None => {
println!("{}", e);
std::process::exit(1);
}
}
} }
} }
if amt == 0 { if amt == 0 {