1use rand::CryptoRng;
6use std::{
7 eprintln,
8 io::{Read, Write},
9 net::TcpStream,
10 println,
11 string::ToString,
12 vec,
13 vec::Vec,
14};
15use super::bertie_stream::{read_record, BertieError, BertieStream, TlsStream};
18use crate::{tls13crypto::*, tls13utils::*, Client};
19
20pub struct ClientState<Stream: Read + Write> {
21 stream: Stream,
22 read_buffer: Vec<u8>,
23 cstate: Option<Client>,
24}
25
26impl<Stream: Read + Write> ClientState<Stream> {
27 pub(crate) fn new(stream: Stream) -> Self {
29 Self {
30 stream,
31 read_buffer: vec![],
32 cstate: None,
33 }
34 }
35}
36
37impl<T: Read + Write> TlsStream<T> for ClientState<T> {
38 fn write_tls(&mut self, bytes: &[u8]) -> Result<(), BertieError> {
39 let cstate = match self.cstate.take() {
40 Some(state) => state,
41 None => return Err(BertieError::InvalidState),
42 };
43
44 let (wire_bytes, new_state) = cstate.write(AppData::new(bytes.into()))?;
45 self.cstate = Some(new_state);
46
47 self.stream
49 .write_all(&wire_bytes.declassify())
50 .map_err(|e| e.into())
51 }
52
53 fn read_tls(&mut self) -> Result<Vec<u8>, BertieError> {
54 let mut state = match self.cstate.take() {
55 Some(state) => state,
56 None => return Err(BertieError::InvalidState),
57 };
58
59 let application_data = loop {
60 let record = read_record(&mut self.read_buffer, &mut self.stream)?;
61 let ad;
62 (ad, state) = state.read(&record.into())?;
63 match ad {
64 Some(application_data) => break application_data,
65 None => continue,
66 }
67 };
68 self.cstate = Some(state);
69 Ok(application_data.into_raw().declassify())
70 }
71
72 fn stream_mut(&mut self) -> &mut T {
73 &mut self.stream
74 }
75}
76
77impl<Stream: Read + Write> BertieStream<ClientState<Stream>> {
78 pub fn open_with_stream(
80 host: &str,
81 ciphersuite: Algorithms,
82 stream: Stream,
83 ) -> Result<Self, BertieError> {
84 Ok(Self {
85 state: ClientState::new(stream),
86 ciphersuite,
87 host: host.to_string(),
88 })
89 }
90}
91
92impl BertieStream<ClientState<TcpStream>> {
93 pub fn client(
99 host: &str,
100 port: u16,
101 ciphersuite: Algorithms,
102 rng: &mut impl CryptoRng,
103 ) -> Result<Self, BertieError> {
104 let mut stream = Self::open(host, port, ciphersuite)?;
105 stream.ciphersuite = ciphersuite;
106 stream.start(rng)?;
107 Ok(stream)
108 }
109
110 pub fn open(host: &str, port: u16, ciphersuite: Algorithms) -> Result<Self, BertieError> {
112 let stream = TcpStream::connect((host, port))?;
113 stream.set_nodelay(true)?;
114 Ok(Self {
115 state: ClientState::new(stream),
116 ciphersuite,
117 host: host.to_string(),
118 })
119 }
120
121 pub fn start(&mut self, rng: &mut impl CryptoRng) -> Result<(), BertieError> {
125 if self.state.cstate.is_some() {
127 return Err(BertieError::InvalidState);
128 }
129
130 let (client_hello, cstate) = {
132 let sni = self.host.as_bytes();
133 Client::connect(self.ciphersuite, &Bytes::from(sni), None, None, rng)?
134 };
135 self.write_all(&client_hello.declassify())?;
138
139 let mut read_buffer = Vec::new();
140
141 let server_hello = read_record(&mut read_buffer, &mut self.state.stream)?;
143
144 if server_hello[0] == 21 {
146 eprintln!(
147 "Server does not support proposed algorithms. {:?}",
148 self.ciphersuite
149 );
150 return Err(UNSUPPORTED_ALGORITHM.into());
151 }
152
153 let cstate = match cstate.read_handshake(&Bytes::from(server_hello)) {
155 Ok((_, cstate)) => cstate,
156 Err(e) => {
157 println!(" >>> ERROR {e}");
158 match e {
159 UNSUPPORTED_ALGORITHM => {
160 eprintln!("Server does not support proposed algorithms.")
161 }
162 PROTOCOL_VERSION_ALERT => eprintln!("Wrong TLS protocol version TLS({:?})", e),
163 APPLICATION_DATA_INSTEAD_OF_HANDSHAKE => {
164 eprintln!("Server sent application data instead of a handshake message.")
165 }
166 MISSING_KEY_SHARE => eprintln!("Hello message was missing a key share."),
167 DECODE_ERROR => eprintln!("Decode error."), _ => eprintln!("Bertie client error {}", e),
169 }
170 return Err(e.into());
171 }
172 };
173
174 let change_cipher_spec = read_record(&mut read_buffer, &mut self.state.stream)?;
178
179 let mut cf_rec = None;
181 let mut cstate = cstate;
182 while cf_rec.is_none() {
183 let rec = read_record(&mut read_buffer, &mut self.state.stream)?;
184
185 let (new_cf_rec, new_cstate) = match cstate.read_handshake(&rec.into()) {
186 Ok((new_cf_rec, new_cstate)) => (new_cf_rec, new_cstate),
187 Err(e) => {
188 match e {
189 INVALID_SIGNATURE => eprintln!("Invalid server signature"),
191 _ => eprintln!("Bertie client error {}", e),
192 }
193 return Err(e.into());
194 }
195 };
196 cf_rec = new_cf_rec;
197 cstate = new_cstate;
198 }
199
200 let change_cipher_spec = Bytes::from_hex("140303000101");
202 self.write_all(&change_cipher_spec.declassify())?;
203
204 let cf_rec = cf_rec.unwrap();
205 self.write_all(&cf_rec.declassify())?;
206
207 self.state.cstate = Some(cstate);
209
210 Ok(())
211 }
212
213 fn write_all(&mut self, bytes: &[u8]) -> Result<(), BertieError> {
214 self.state
215 .stream_mut()
216 .write_all(bytes)
217 .map_err(|e| e.into())
218 }
219
220 pub fn read(&mut self) -> Result<Vec<u8>, BertieError> {
224 self.state.read_tls()
225 }
226
227 pub fn write(&mut self, bytes: &[u8]) -> Result<usize, BertieError> {
231 self.state.write_tls(bytes)?;
232 Ok(bytes.len())
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use rand::{self, rng};
239
240 use crate::tls13crypto::SHA256_Chacha20Poly1305_EcdsaSecp256r1Sha256_X25519;
241
242 use super::*;
243 use std::{format, string::String};
244
245 #[test]
246 fn client() {
247 let host = "google.com";
248 let port = 443;
249 let mut stream = BertieStream::client(
250 host,
251 port,
252 SHA256_Chacha20Poly1305_EcdsaSecp256r1Sha256_X25519,
253 &mut rng(),
254 )
255 .expect("Error connecting to server");
256
257 let request = format!("GET / HTTP/1.1\r\nHost: {}\r\n\r\n", host);
259 stream
260 .write(request.as_bytes())
261 .expect("Error writing to Bertie stream");
262 let response = stream.read().unwrap();
263 let response_string = String::from_utf8_lossy(&response);
264 eprintln!("{response_string:}");
265 }
266}