obnam_benchmark/
tlsgen.rs

1use std::fs::read;
2use std::path::Path;
3use std::process::{Command, Stdio};
4use tempfile::NamedTempFile;
5
6#[derive(Debug, thiserror::Error)]
7pub enum TlsError {
8    #[error("failed to create temporary file: {0}")]
9    TempFile(std::io::Error),
10
11    #[error("failed to read temporary file: {0}")]
12    ReadTemp(std::io::Error),
13
14    #[error("failed to run openssl {0}: {1}")]
15    RunOpenSsl(String, std::io::Error),
16
17    #[error("openssl {0} failed: {1}")]
18    OpenSsl(String, String),
19}
20
21#[derive(Debug)]
22pub struct Tls {
23    key: Vec<u8>,
24    cert: Vec<u8>,
25}
26
27impl Tls {
28    pub fn new() -> Result<Self, TlsError> {
29        let (key, cert) = generate()?;
30        Ok(Self { key, cert })
31    }
32
33    pub fn key(&self) -> &[u8] {
34        &self.key
35    }
36
37    pub fn cert(&self) -> &[u8] {
38        &self.cert
39    }
40}
41
42fn generate() -> Result<(Vec<u8>, Vec<u8>), TlsError> {
43    let key = NamedTempFile::new().map_err(TlsError::TempFile)?;
44    let csr = NamedTempFile::new().map_err(TlsError::TempFile)?;
45    genrsa(key.path())?;
46    let key_data = rsa(key.path())?;
47    req(key.path(), csr.path())?;
48    let cert_data = x509(key.path(), csr.path())?;
49    Ok((key_data, cert_data))
50}
51
52fn openssl() -> Command {
53    let mut command = Command::new("openssl");
54    command
55        .stdin(Stdio::null())
56        .stdout(Stdio::piped())
57        .stderr(Stdio::piped());
58    command
59}
60
61fn genrsa(filename: &Path) -> Result<(), TlsError> {
62    let output = openssl()
63        .arg("genrsa")
64        .arg("-out")
65        .arg(filename)
66        .arg("2048")
67        .output()
68        .map_err(|err| TlsError::RunOpenSsl("genrsa".to_string(), err))?;
69
70    if !output.status.success() {
71        let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
72        return Err(TlsError::OpenSsl("genrsa".to_string(), stderr));
73    }
74
75    Ok(())
76}
77
78fn rsa(filename: &Path) -> Result<Vec<u8>, TlsError> {
79    let output = openssl()
80        .arg("rsa")
81        .arg("-in")
82        .arg(filename)
83        .arg("-out")
84        .arg(filename)
85        .output()
86        .map_err(|err| TlsError::RunOpenSsl("rsa".to_string(), err))?;
87
88    if !output.status.success() {
89        let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
90        return Err(TlsError::OpenSsl("rsa".to_string(), stderr));
91    }
92
93    read(filename).map_err(TlsError::ReadTemp)
94}
95
96fn req(key: &Path, csr: &Path) -> Result<(), TlsError> {
97    let output = openssl()
98        .arg("req")
99        .arg("-sha256")
100        .arg("-new")
101        .arg("-key")
102        .arg(key)
103        .arg("-out")
104        .arg(csr)
105        .arg("-subj")
106        .arg("/CN=localhost")
107        .output()
108        .map_err(|err| TlsError::RunOpenSsl("req".to_string(), err))?;
109
110    if !output.status.success() {
111        let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
112        return Err(TlsError::OpenSsl("req".to_string(), stderr));
113    }
114
115    Ok(())
116}
117
118fn x509(key: &Path, csr: &Path) -> Result<Vec<u8>, TlsError> {
119    let output = openssl()
120        .arg("x509")
121        .arg("-req")
122        .arg("-sha256")
123        .arg("-days")
124        .arg("1")
125        .arg("-in")
126        .arg(csr)
127        .arg("-signkey")
128        .arg(key)
129        .output()
130        .map_err(|err| TlsError::RunOpenSsl("req".to_string(), err))?;
131
132    if !output.status.success() {
133        let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
134        return Err(TlsError::OpenSsl("req".to_string(), stderr));
135    }
136
137    Ok(output.stdout)
138}