brynja_protocol/tls/
dtls12_ciphertext.rs1use brynja_core::{ProtocolVersion, ReadCursor, WriteCursor};
4
5use super::{
6 ContentType, ContentTypeCode, LegacyRecordVersion, MAX_TLS12_CIPHERTEXT_LENGTH, RecordError,
7 WirePolicy,
8};
9
10const HEADER_LENGTH: usize = 13;
11
12#[derive(Clone, Copy, Eq, PartialEq)]
14pub struct Dtls12Ciphertext<'input> {
15 content_type: ContentType,
16 legacy_record_version: LegacyRecordVersion,
17 epoch: u16,
18 sequence_number: [u8; 6],
19 fragment: &'input [u8],
20}
21
22impl<'input> Dtls12Ciphertext<'input> {
23 pub fn new(
25 policy: WirePolicy,
26 content_type: ContentTypeCode,
27 legacy_record_version: LegacyRecordVersion,
28 epoch: u16,
29 sequence_number: [u8; 6],
30 fragment: &'input [u8],
31 ) -> Result<Self, RecordError> {
32 if !matches!(policy.version(), ProtocolVersion::Dtls12) {
33 return Err(RecordError::ProfileMismatch);
34 }
35 let content_type = policy.admit_ciphertext(content_type)?;
36 validate_length(fragment.len())?;
37 Ok(Self {
38 content_type,
39 legacy_record_version,
40 epoch,
41 sequence_number,
42 fragment,
43 })
44 }
45
46 pub fn parse(
48 policy: WirePolicy,
49 input: &'input [u8],
50 ) -> Result<(Self, &'input [u8]), RecordError> {
51 if !matches!(policy.version(), ProtocolVersion::Dtls12) {
52 return Err(RecordError::ProfileMismatch);
53 }
54 let mut cursor = ReadCursor::new(input);
55 let code = read_byte(&mut cursor)?;
56 let content_type = policy.admit_ciphertext(ContentTypeCode::classify(code))?;
57 let version = read_version(&mut cursor)?;
58 let epoch = read_u16(&mut cursor)?;
59 let sequence_number = *cursor
60 .take_array::<6>()
61 .map_err(|_| RecordError::Truncated)?;
62 let length = usize::from(read_u16(&mut cursor)?);
63 validate_length(length)?;
64 let fragment = cursor.take(length).map_err(|_| RecordError::Truncated)?;
65 let remaining = cursor.remaining();
66 Ok((
67 Self {
68 content_type,
69 legacy_record_version: version,
70 epoch,
71 sequence_number,
72 fragment,
73 },
74 remaining,
75 ))
76 }
77
78 #[must_use]
80 pub const fn content_type(&self) -> ContentType {
81 self.content_type
82 }
83
84 #[must_use]
86 pub const fn legacy_record_version(&self) -> LegacyRecordVersion {
87 self.legacy_record_version
88 }
89
90 #[must_use]
92 pub const fn epoch(&self) -> u16 {
93 self.epoch
94 }
95
96 #[must_use]
98 pub const fn sequence_number(&self) -> [u8; 6] {
99 self.sequence_number
100 }
101
102 #[must_use]
104 pub const fn fragment(&self) -> &'input [u8] {
105 self.fragment
106 }
107
108 #[must_use]
110 pub const fn encoded_len(&self) -> usize {
111 HEADER_LENGTH.saturating_add(self.fragment.len())
112 }
113
114 pub fn encode(&self, output: &mut [u8]) -> Result<usize, RecordError> {
116 let total = HEADER_LENGTH
117 .checked_add(self.fragment.len())
118 .ok_or(RecordError::LengthOverflow)?;
119 if output.len() < total {
120 return Err(RecordError::InsufficientOutput);
121 }
122 let length = u16::try_from(self.fragment.len())
123 .map_err(|_| RecordError::RecordOverflow)?
124 .to_be_bytes();
125 let content_type = [self.content_type.code()];
126 let version = self.legacy_record_version.bytes();
127 let epoch = self.epoch.to_be_bytes();
128 let mut cursor = WriteCursor::new(output);
129 cursor
130 .write_parts(&[
131 &content_type,
132 &version,
133 &epoch,
134 &self.sequence_number,
135 &length,
136 self.fragment,
137 ])
138 .map_err(|_| RecordError::InsufficientOutput)?;
139 Ok(total)
140 }
141}
142
143fn read_byte(cursor: &mut ReadCursor<'_>) -> Result<u8, RecordError> {
144 cursor
145 .take(1)
146 .map_err(|_| RecordError::Truncated)?
147 .first()
148 .copied()
149 .ok_or(RecordError::Truncated)
150}
151
152fn read_u16(cursor: &mut ReadCursor<'_>) -> Result<u16, RecordError> {
153 let bytes = cursor
154 .take_array::<2>()
155 .map_err(|_| RecordError::Truncated)?;
156 Ok(u16::from_be_bytes(*bytes))
157}
158
159fn read_version(cursor: &mut ReadCursor<'_>) -> Result<LegacyRecordVersion, RecordError> {
160 let bytes = cursor
161 .take_array::<2>()
162 .map_err(|_| RecordError::Truncated)?;
163 Ok(LegacyRecordVersion::from_bytes(*bytes))
164}
165
166fn validate_length(length: usize) -> Result<(), RecordError> {
167 if length > MAX_TLS12_CIPHERTEXT_LENGTH {
168 Err(RecordError::RecordOverflow)
169 } else {
170 Ok(())
171 }
172}