1use super::{
4 error::TlsBuildError,
5 extensions::extension::{TlsExtension, TlsExtensions},
6 hello::{ClientHello, ServerHello},
7 types::{TlsCipherSuite, TlsCompressionMethod, TlsExtensionType, TlsProtocolVersion},
8};
9
10pub struct TlsExtensionBuilder<'a, 'b> {
12 buffer: &'a mut [u8],
13 extension_type: TlsExtensionType,
14 data: &'b [u8],
15}
16
17impl<'a, 'b> TlsExtensionBuilder<'a, 'b> {
18 pub fn new(buffer: &'a mut [u8], extension_type: TlsExtensionType, data: &'b [u8]) -> Self {
20 Self {
21 buffer,
22 extension_type,
23 data,
24 }
25 }
26
27 pub fn build(self) -> Result<TlsExtension<'a>, TlsBuildError> {
29 let data_length =
30 u16::try_from(self.data.len()).map_err(|_| TlsBuildError::LengthTooLarge)?;
31 let required = 4usize
32 .checked_add(self.data.len())
33 .ok_or(TlsBuildError::LengthTooLarge)?;
34 ensure_capacity(self.buffer.len(), required)?;
35
36 let bytes = &mut self.buffer[..required];
37 bytes[..2].copy_from_slice(&self.extension_type.raw().to_be_bytes());
38 bytes[2..4].copy_from_slice(&data_length.to_be_bytes());
39 bytes[4..].copy_from_slice(self.data);
40
41 Ok(TlsExtensions::new(bytes)
42 .next()
43 .expect("built extension is present")
44 .expect("validated extension encoding"))
45 }
46}
47
48pub struct ClientHelloBuilder<'a, 'b> {
50 buffer: &'a mut [u8],
51 legacy_version: TlsProtocolVersion,
52 random: &'b [u8; 32],
53 session_id: &'b [u8],
54 cipher_suites: &'b [u8],
55 compression_methods: &'b [u8],
56 extensions: Option<&'b [u8]>,
57}
58
59impl<'a, 'b> ClientHelloBuilder<'a, 'b> {
60 pub fn new(
62 buffer: &'a mut [u8],
63 legacy_version: TlsProtocolVersion,
64 random: &'b [u8; 32],
65 session_id: &'b [u8],
66 cipher_suites: &'b [u8],
67 compression_methods: &'b [u8],
68 extensions: Option<&'b [u8]>,
69 ) -> Self {
70 Self {
71 buffer,
72 legacy_version,
73 random,
74 session_id,
75 cipher_suites,
76 compression_methods,
77 extensions,
78 }
79 }
80
81 pub fn build(self) -> Result<ClientHello<'a>, TlsBuildError> {
83 let session_length = validate_u8_vector(self.session_id, 0, 32)?;
84 let cipher_length = validate_u16_vector(self.cipher_suites, 2, true)?;
85 let compression_length = validate_u8_vector(self.compression_methods, 1, u8::MAX as usize)?;
86 let extension_length = validate_extensions(self.extensions)?;
87
88 let required = 34usize
89 .checked_add(1 + self.session_id.len())
90 .and_then(|value| value.checked_add(2 + self.cipher_suites.len()))
91 .and_then(|value| value.checked_add(1 + self.compression_methods.len()))
92 .and_then(|value| {
93 value.checked_add(self.extensions.map_or(0, |extensions| 2 + extensions.len()))
94 })
95 .ok_or(TlsBuildError::LengthTooLarge)?;
96 ensure_capacity(self.buffer.len(), required)?;
97
98 let bytes = &mut self.buffer[..required];
99 bytes[..2].copy_from_slice(&self.legacy_version.raw().to_be_bytes());
100 bytes[2..34].copy_from_slice(self.random);
101 let mut offset = 34;
102 bytes[offset] = session_length;
103 offset += 1;
104 bytes[offset..offset + self.session_id.len()].copy_from_slice(self.session_id);
105 offset += self.session_id.len();
106 bytes[offset..offset + 2].copy_from_slice(&cipher_length.to_be_bytes());
107 offset += 2;
108 bytes[offset..offset + self.cipher_suites.len()].copy_from_slice(self.cipher_suites);
109 offset += self.cipher_suites.len();
110 bytes[offset] = compression_length;
111 offset += 1;
112 bytes[offset..offset + self.compression_methods.len()]
113 .copy_from_slice(self.compression_methods);
114 offset += self.compression_methods.len();
115 if let (Some(extensions), Some(length)) = (self.extensions, extension_length) {
116 bytes[offset..offset + 2].copy_from_slice(&length.to_be_bytes());
117 offset += 2;
118 bytes[offset..offset + extensions.len()].copy_from_slice(extensions);
119 }
120
121 Ok(ClientHello::parse(bytes).expect("validated ClientHello builder fields"))
122 }
123}
124
125pub struct ServerHelloBuilder<'a, 'b> {
127 buffer: &'a mut [u8],
128 legacy_version: TlsProtocolVersion,
129 random: &'b [u8; 32],
130 session_id: &'b [u8],
131 cipher_suite: TlsCipherSuite,
132 compression_method: TlsCompressionMethod,
133 extensions: Option<&'b [u8]>,
134}
135
136impl<'a, 'b> ServerHelloBuilder<'a, 'b> {
137 pub fn new(
139 buffer: &'a mut [u8],
140 legacy_version: TlsProtocolVersion,
141 random: &'b [u8; 32],
142 session_id: &'b [u8],
143 cipher_suite: TlsCipherSuite,
144 compression_method: TlsCompressionMethod,
145 extensions: Option<&'b [u8]>,
146 ) -> Self {
147 Self {
148 buffer,
149 legacy_version,
150 random,
151 session_id,
152 cipher_suite,
153 compression_method,
154 extensions,
155 }
156 }
157
158 pub fn build(self) -> Result<ServerHello<'a>, TlsBuildError> {
160 let session_length = validate_u8_vector(self.session_id, 0, 32)?;
161 let extension_length = validate_extensions(self.extensions)?;
162 let required = 38usize
163 .checked_add(self.session_id.len())
164 .and_then(|value| {
165 value.checked_add(self.extensions.map_or(0, |extensions| 2 + extensions.len()))
166 })
167 .ok_or(TlsBuildError::LengthTooLarge)?;
168 ensure_capacity(self.buffer.len(), required)?;
169
170 let bytes = &mut self.buffer[..required];
171 bytes[..2].copy_from_slice(&self.legacy_version.raw().to_be_bytes());
172 bytes[2..34].copy_from_slice(self.random);
173 let mut offset = 34;
174 bytes[offset] = session_length;
175 offset += 1;
176 bytes[offset..offset + self.session_id.len()].copy_from_slice(self.session_id);
177 offset += self.session_id.len();
178 bytes[offset..offset + 2].copy_from_slice(&self.cipher_suite.raw().to_be_bytes());
179 offset += 2;
180 bytes[offset] = self.compression_method.raw();
181 offset += 1;
182 if let (Some(extensions), Some(length)) = (self.extensions, extension_length) {
183 bytes[offset..offset + 2].copy_from_slice(&length.to_be_bytes());
184 offset += 2;
185 bytes[offset..offset + extensions.len()].copy_from_slice(extensions);
186 }
187
188 Ok(ServerHello::parse(bytes).expect("validated ServerHello builder fields"))
189 }
190}
191
192fn validate_u8_vector(bytes: &[u8], minimum: usize, maximum: usize) -> Result<u8, TlsBuildError> {
193 if bytes.len() > u8::MAX as usize {
194 return Err(TlsBuildError::LengthTooLarge);
195 }
196 if bytes.len() < minimum || bytes.len() > maximum {
197 return Err(TlsBuildError::InvalidValue);
198 }
199 Ok(bytes.len() as u8)
200}
201
202fn validate_u16_vector(bytes: &[u8], minimum: usize, even: bool) -> Result<u16, TlsBuildError> {
203 if bytes.len() < minimum || (even && !bytes.len().is_multiple_of(2)) {
204 return Err(TlsBuildError::InvalidValue);
205 }
206 u16::try_from(bytes.len()).map_err(|_| TlsBuildError::LengthTooLarge)
207}
208
209fn validate_extensions(extensions: Option<&[u8]>) -> Result<Option<u16>, TlsBuildError> {
210 let Some(extensions) = extensions else {
211 return Ok(None);
212 };
213 let length = u16::try_from(extensions.len()).map_err(|_| TlsBuildError::LengthTooLarge)?;
214 for extension in TlsExtensions::new(extensions) {
215 extension.map_err(|_| TlsBuildError::InvalidValue)?;
216 }
217 Ok(Some(length))
218}
219
220fn ensure_capacity(available: usize, required: usize) -> Result<(), TlsBuildError> {
221 if available < required {
222 Err(TlsBuildError::BufferTooShort {
223 required,
224 available,
225 })
226 } else {
227 Ok(())
228 }
229}