summaryrefslogtreecommitdiff
path: root/src/tlsgen.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tlsgen.rs')
-rw-r--r--src/tlsgen.rs138
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)
+}