1use alloc::vec::Vec;
2use core::fmt::{Debug, Formatter};
3use std::env::var_os;
4use std::ffi::OsString;
5use std::fs::{File, OpenOptions};
6use std::io;
7use std::io::Write;
8use std::sync::Mutex;
9
10use crate::log::warn;
11use crate::KeyLog;
12
13struct KeyLogFileInner {
15 file: Option<File>,
16 buf: Vec<u8>,
17}
18
19impl KeyLogFileInner {
20 fn new(var: Option<OsString>) -> Self {
21 let path = match &var {
22 Some(path) => path,
23 None => {
24 return Self {
25 file: None,
26 buf: Vec::new(),
27 };
28 }
29 };
30
31 #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
32 let file = match OpenOptions::new()
33 .append(true)
34 .create(true)
35 .open(path)
36 {
37 Ok(f) => Some(f),
38 Err(e) => {
39 warn!("unable to create key log file {:?}: {}", path, e);
40 None
41 }
42 };
43
44 Self {
45 file,
46 buf: Vec::new(),
47 }
48 }
49
50 fn try_write(&mut self, label: &str, client_random: &[u8], secret: &[u8]) -> io::Result<()> {
51 let mut file = match self.file {
52 None => {
53 return Ok(());
54 }
55 Some(ref f) => f,
56 };
57
58 self.buf.truncate(0);
59 write!(self.buf, "{} ", label)?;
60 for b in client_random.iter() {
61 write!(self.buf, "{:02x}", b)?;
62 }
63 write!(self.buf, " ")?;
64 for b in secret.iter() {
65 write!(self.buf, "{:02x}", b)?;
66 }
67 writeln!(self.buf)?;
68 file.write_all(&self.buf)
69 }
70}
71
72impl Debug for KeyLogFileInner {
73 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
74 f.debug_struct("KeyLogFileInner")
75 .field("file", &self.file)
77 .finish()
78 }
79}
80
81pub struct KeyLogFile(Mutex<KeyLogFileInner>);
90
91impl KeyLogFile {
92 pub fn new() -> Self {
95 let var = var_os("SSLKEYLOGFILE");
96 Self(Mutex::new(KeyLogFileInner::new(var)))
97 }
98}
99
100impl KeyLog for KeyLogFile {
101 fn log(&self, label: &str, client_random: &[u8], secret: &[u8]) {
102 #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
103 match self
104 .0
105 .lock()
106 .unwrap()
107 .try_write(label, client_random, secret)
108 {
109 Ok(()) => {}
110 Err(e) => {
111 warn!("error writing to key log file: {}", e);
112 }
113 }
114 }
115}
116
117impl Debug for KeyLogFile {
118 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
119 match self.0.try_lock() {
120 Ok(key_log_file) => write!(f, "{:?}", key_log_file),
121 Err(_) => write!(f, "KeyLogFile {{ <locked> }}"),
122 }
123 }
124}
125
126#[cfg(all(test, target_os = "linux"))]
127mod tests {
128 use super::*;
129
130 fn init() {
131 let _ = env_logger::builder()
132 .is_test(true)
133 .try_init();
134 }
135
136 #[test]
137 fn test_env_var_is_not_set() {
138 init();
139 let mut inner = KeyLogFileInner::new(None);
140 assert!(inner
141 .try_write("label", b"random", b"secret")
142 .is_ok());
143 }
144
145 #[test]
146 fn test_env_var_cannot_be_opened() {
147 init();
148 let mut inner = KeyLogFileInner::new(Some("/dev/does-not-exist".into()));
149 assert!(inner
150 .try_write("label", b"random", b"secret")
151 .is_ok());
152 }
153
154 #[test]
155 fn test_env_var_cannot_be_written() {
156 init();
157 let mut inner = KeyLogFileInner::new(Some("/dev/full".into()));
158 assert!(inner
159 .try_write("label", b"random", b"secret")
160 .is_err());
161 }
162}