diff options
Diffstat (limited to 'src/tlsgen.rs')
-rw-r--r-- | src/tlsgen.rs | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/src/tlsgen.rs b/src/tlsgen.rs new file mode 100644 index 0000000..03b58ad --- /dev/null +++ b/src/tlsgen.rs @@ -0,0 +1,138 @@ +use std::fs::read; +use std::path::Path; +use std::process::{Command, Stdio}; +use tempfile::NamedTempFile; + +#[derive(Debug, thiserror::Error)] +pub enum TlsError { + #[error("failed to create temporary file: {0}")] + TempFile(std::io::Error), + + #[error("failed to read temporary file: {0}")] + ReadTemp(std::io::Error), + + #[error("failed to run openssl {0}: {1}")] + RunOpenSsl(String, std::io::Error), + + #[error("openssl {0} failed: {1}")] + OpenSsl(String, String), +} + +#[derive(Debug)] +pub struct Tls { + key: Vec<u8>, + cert: Vec<u8>, +} + +impl Tls { + pub fn new() -> Result<Self, TlsError> { + let (key, cert) = generate()?; + Ok(Self { key, cert }) + } + + pub fn key(&self) -> &[u8] { + &self.key + } + + pub fn cert(&self) -> &[u8] { + &self.cert + } +} + +fn generate() -> Result<(Vec<u8>, Vec<u8>), TlsError> { + let key = NamedTempFile::new().map_err(TlsError::TempFile)?; + let csr = NamedTempFile::new().map_err(TlsError::TempFile)?; + genrsa(key.path())?; + let key_data = rsa(key.path())?; + req(key.path(), csr.path())?; + let cert_data = x509(key.path(), csr.path())?; + Ok((key_data, cert_data)) +} + +fn openssl() -> Command { + let mut command = Command::new("openssl"); + command + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + command +} + +fn genrsa(filename: &Path) -> Result<(), TlsError> { + let output = openssl() + .arg("genrsa") + .arg("-out") + .arg(filename) + .arg("2048") + .output() + .map_err(|err| TlsError::RunOpenSsl("genrsa".to_string(), err))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr).into_owned(); + return Err(TlsError::OpenSsl("genrsa".to_string(), stderr)); + } + + Ok(()) +} + +fn rsa(filename: &Path) -> Result<Vec<u8>, TlsError> { + let output = openssl() + .arg("rsa") + .arg("-in") + .arg(filename) + .arg("-out") + .arg(filename) + .output() + .map_err(|err| TlsError::RunOpenSsl("rsa".to_string(), err))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr).into_owned(); + return Err(TlsError::OpenSsl("rsa".to_string(), stderr)); + } + + read(filename).map_err(TlsError::ReadTemp) +} + +fn req(key: &Path, csr: &Path) -> Result<(), TlsError> { + let output = openssl() + .arg("req") + .arg("-sha256") + .arg("-new") + .arg("-key") + .arg(key) + .arg("-out") + .arg(csr) + .arg("-subj") + .arg("/CN=localhost") + .output() + .map_err(|err| TlsError::RunOpenSsl("req".to_string(), err))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr).into_owned(); + return Err(TlsError::OpenSsl("req".to_string(), stderr)); + } + + Ok(()) +} + +fn x509(key: &Path, csr: &Path) -> Result<Vec<u8>, TlsError> { + let output = openssl() + .arg("x509") + .arg("-req") + .arg("-sha256") + .arg("-days") + .arg("1") + .arg("-in") + .arg(csr) + .arg("-signkey") + .arg(key) + .output() + .map_err(|err| TlsError::RunOpenSsl("req".to_string(), err))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr).into_owned(); + return Err(TlsError::OpenSsl("req".to_string(), stderr)); + } + + Ok(output.stdout) +} |