veridian_kernel/net/tls/
record.rs1#[cfg(feature = "alloc")]
7use alloc::vec::Vec;
8
9use super::{
10 cipher::{aead_decrypt, aead_encrypt},
11 CipherSuite, AEAD_TAG_LEN, MAX_RECORD_SIZE, NONCE_LEN, TLS_LEGACY_VERSION,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(u8)]
21pub enum ContentType {
22 ChangeCipherSpec = 20,
23 Alert = 21,
24 Handshake = 22,
25 ApplicationData = 23,
26}
27
28impl ContentType {
29 pub(crate) fn from_u8(v: u8) -> Option<Self> {
30 match v {
31 20 => Some(Self::ChangeCipherSpec),
32 21 => Some(Self::Alert),
33 22 => Some(Self::Handshake),
34 23 => Some(Self::ApplicationData),
35 _ => None,
36 }
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct RecordHeader {
47 pub content_type: ContentType,
48 pub legacy_version: u16,
49 pub length: u16,
50}
51
52impl RecordHeader {
53 pub fn encode(&self, buf: &mut [u8]) -> usize {
55 if buf.len() < 5 {
56 return 0;
57 }
58 buf[0] = self.content_type as u8;
59 buf[1..3].copy_from_slice(&self.legacy_version.to_be_bytes());
60 buf[3..5].copy_from_slice(&self.length.to_be_bytes());
61 5
62 }
63
64 pub fn decode(buf: &[u8]) -> Option<Self> {
66 if buf.len() < 5 {
67 return None;
68 }
69 let content_type = ContentType::from_u8(buf[0])?;
70 let legacy_version = u16::from_be_bytes([buf[1], buf[2]]);
71 let length = u16::from_be_bytes([buf[3], buf[4]]);
72
73 if length as usize > MAX_RECORD_SIZE + AEAD_TAG_LEN + 1 {
75 return None;
76 }
77
78 Some(Self {
79 content_type,
80 legacy_version,
81 length,
82 })
83 }
84}
85
86#[derive(Debug, Clone)]
92pub struct TlsRecord {
93 pub content_type: ContentType,
94 pub fragment: Vec<u8>,
95}
96
97impl TlsRecord {
98 pub fn new(content_type: ContentType, fragment: Vec<u8>) -> Self {
100 Self {
101 content_type,
102 fragment,
103 }
104 }
105
106 pub fn encode(&self) -> Vec<u8> {
108 let header = RecordHeader {
109 content_type: self.content_type,
110 legacy_version: TLS_LEGACY_VERSION,
111 length: self.fragment.len() as u16,
112 };
113 let mut buf = alloc::vec![0u8; 5 + self.fragment.len()];
114 header.encode(&mut buf);
115 buf[5..].copy_from_slice(&self.fragment);
116 buf
117 }
118
119 pub fn decode(data: &[u8]) -> Option<(Self, usize)> {
121 let header = RecordHeader::decode(data)?;
122 let total_len = 5 + header.length as usize;
123 if data.len() < total_len {
124 return None;
125 }
126 let fragment = data[5..total_len].to_vec();
127 Some((
128 Self {
129 content_type: header.content_type,
130 fragment,
131 },
132 total_len,
133 ))
134 }
135}
136
137pub struct FragmentBuffer {
143 buffer: Vec<u8>,
144 expected_type: Option<ContentType>,
145}
146
147impl Default for FragmentBuffer {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153impl FragmentBuffer {
154 pub fn new() -> Self {
155 Self {
156 buffer: Vec::new(),
157 expected_type: None,
158 }
159 }
160
161 pub fn append(&mut self, record: &TlsRecord) -> Option<Vec<u8>> {
163 match self.expected_type {
164 None => {
165 self.expected_type = Some(record.content_type);
166 }
167 Some(ct) if ct != record.content_type => {
168 self.buffer.clear();
170 self.expected_type = Some(record.content_type);
171 }
172 _ => {}
173 }
174
175 self.buffer.extend_from_slice(&record.fragment);
176
177 if record.content_type == ContentType::Handshake && self.buffer.len() >= 4 {
179 let msg_len = ((self.buffer[1] as usize) << 16)
180 | ((self.buffer[2] as usize) << 8)
181 | (self.buffer[3] as usize);
182 let total = 4 + msg_len;
183 if self.buffer.len() >= total {
184 let message = self.buffer[..total].to_vec();
185 self.buffer = self.buffer[total..].to_vec();
186 if self.buffer.is_empty() {
187 self.expected_type = None;
188 }
189 return Some(message);
190 }
191 }
192
193 None
194 }
195
196 pub fn reset(&mut self) {
198 self.buffer.clear();
199 self.expected_type = None;
200 }
201}
202
203pub fn encrypt_record(
212 record: &TlsRecord,
213 key: &[u8],
214 iv: &[u8; NONCE_LEN],
215 seq_num: u64,
216 cipher: CipherSuite,
217) -> Option<TlsRecord> {
218 let mut inner = record.fragment.clone();
220 inner.push(record.content_type as u8);
221
222 let nonce = compute_nonce(iv, seq_num);
224
225 let encrypted_len = inner.len() + AEAD_TAG_LEN;
227 let mut aad = [0u8; 5];
228 aad[0] = ContentType::ApplicationData as u8;
229 aad[1..3].copy_from_slice(&TLS_LEGACY_VERSION.to_be_bytes());
230 aad[3..5].copy_from_slice(&(encrypted_len as u16).to_be_bytes());
231
232 let ciphertext = aead_encrypt(cipher, key, &nonce, &aad, &inner)?;
233
234 Some(TlsRecord::new(ContentType::ApplicationData, ciphertext))
235}
236
237pub fn decrypt_record(
239 record: &TlsRecord,
240 key: &[u8],
241 iv: &[u8; NONCE_LEN],
242 seq_num: u64,
243 cipher: CipherSuite,
244) -> Option<TlsRecord> {
245 if record.content_type != ContentType::ApplicationData {
246 return None;
247 }
248
249 let nonce = compute_nonce(iv, seq_num);
250
251 let mut aad = [0u8; 5];
253 aad[0] = ContentType::ApplicationData as u8;
254 aad[1..3].copy_from_slice(&TLS_LEGACY_VERSION.to_be_bytes());
255 aad[3..5].copy_from_slice(&(record.fragment.len() as u16).to_be_bytes());
256
257 let plaintext = aead_decrypt(cipher, key, &nonce, &aad, &record.fragment)?;
258
259 if plaintext.is_empty() {
260 return None;
261 }
262
263 let real_ct = ContentType::from_u8(*plaintext.last()?)?;
265 let payload = plaintext[..plaintext.len() - 1].to_vec();
266
267 Some(TlsRecord::new(real_ct, payload))
268}
269
270pub(crate) fn compute_nonce(iv: &[u8; NONCE_LEN], seq_num: u64) -> [u8; NONCE_LEN] {
272 let mut nonce = *iv;
273 let seq_bytes = seq_num.to_be_bytes();
274 for i in 0..8 {
276 nonce[NONCE_LEN - 8 + i] ^= seq_bytes[i];
277 }
278 nonce
279}