obnam_benchmark/
tlsgen.rs1use 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}